我会这样做:
- 将数字1至49放入数组中。
- 对数组进行洗牌。
- 取出前6个元素。
这可能不是最高效的方法,但易于实现、易于理解,并且最重要的是易于推断抽样方法的分布特性。
对于洗牌,请使用
Fisher-Yates shuffle。我用以下通用方法来实现:
procedure TRandomNumberGenerator.Permute<T>(var Values: array of T);
var
i, Count: Integer;
begin
Count := Length(Values);
for i := 0 to Count-2 do
TGeneric.Swap<T>(Values[i], Values[i + Uniform(Count-i)]);
end;
这段代码中,Uniform(N)
是一个随机数生成器函数,返回一个从 0..N-1
的均匀分布中抽取的值。你可以在你的代码中将其替换为 Random
。而 TGeneric.Swap<T>
则是交换两个元素。
如果你想要对整数数组进行操作,可以这样修改代码:
procedure Swap(var lhs, rhs: Integer);
var
tmp: Integer;
begin
tmp := lhs;
lhs := rhs;
rhs := tmp;
end;
procedure Permute(var Values: array of Integer);
var
i, Count: Integer;
begin
Count := Length(Values);
for i := 0 to Count-2 do
Swap(Values[i], Values[i + Random(Count-i)]);
end;
当然,你只需要执行循环的前六次迭代,因此一个非常高效的版本应该是这样的:
function Choose(M, N: Integer): TArray<Integer>;
var
i: Integer;
Values: TArray<Integer>;
begin
Assert(M>0);
Assert(N>=M);
SetLength(Values, N);
for i := 0 to N-1 do
Values[i] := i+1;
for i := 0 to Min(M-1, N-2) do
Swap(Values[i], Values[i + Random(N-i)]);
Result := Copy(Values, 0, M);
end;
您会称之为传递 6 和 49:
Values := Choose(6, 49);
如果您是一个对性能要求特别高的人,那么我认为很难超过这个:
type
TArr6 = array [0..5] of Integer;
PArr6 = ^TArr6;
TArr49 = array [0..48] of Integer;
const
OrderedArr49: TArr49 = (
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
);
function Choose6: TArr6;
var
i: Integer;
Values: TArr49;
begin
Values := OrderedArr49;
for i := 0 to high(Result) do begin
Swap(Values[i], Values[i + Random(Length(Values)-i)]);
end;
Result := PArr6(@Values)^;
end;
我应该说,我怀疑性能将成为主导因素。
random(count(list))
随机选择并删除一个数字。 - 500 - Internal Server Error