我想用pascal编写一个程序,选择1到49之间的6个随机整数。每个数字都应该是唯一的,即不能有'8,22'22'32'37'43',因为'22'是重复的。我如何在Delphi中实现这一点。
使用以下代码,我可以得到1-49之间的6个随机数。
for i := 1 to 6 do
begin
num[i] := random(49) + 1
end
{next};
我会这样做:
- 将数字1到49放入一个数组中
- 对阵列执行洗牌
- 拔出前6个元件
这可能不是最有效的方法,但它易于实现,易于理解,最重要的是,易于推理采样方法的分布特性。
对于洗牌,请使用Fisher Yates洗牌。我用一种通用方法实现了这一点:
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;
其中CCD_ 1是我的RNG的函数,其返回从CCD_。您可以在代码中用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;
我应该说,我怀疑性能是否会成为这里的驱动因素。
我认为对于一个简单的解决方案,考虑到你想要的值的数量与可能的值的数目相对较少,你可以强行使用它。
如果这是Perl或PHP,我会使用关联数组,但泛型也很好:
uses
System.Generics.Collections;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
numbers: TList<Integer>;
num: Integer;
output: String;
begin
numbers := TList<Integer>.Create;
try
// We need six integers
for i := 1 to 6 do
begin
// Generate a "random" integer
num := Random(49) + 1;
// Keep going until it isn't in our list
while numbers.Contains(num) do
begin
num := Random(49) + 1;
end;
// Add it to the list
numbers.Add(num);
end;
// Display the list
output := '';
for num in numbers do
begin
output := output + IntToStr(num) + ':';
end;
ShowMessage(output);
finally
numbers.Free;
end;
end;
这个解决方案可能会引发一些争论,因为有些人可能认为理论上这种情况永远不会发生,但考虑到Delphi的PRNG,情况不会如此。