如何在pascal中生成彼此唯一的随机整数

时间:2013-12-03 20:38:43

标签: delphi random integer

我想在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};

2 个答案:

答案 0 :(得分:18)

我会这样做:

  1. 将数字1到49放入数组中。
  2. 在阵列上执行随机播放。
  3. 拉出前6个元素。
  4. 这可能不是最有效的方法,但它易于实现,易于理解,最重要的是,易于推理采样方法的分布属性。

    对于shuffle,请使用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)是我的RNG的函数,它返回从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;
    

    我应该说我怀疑性能是否会成为驱动因素。

答案 1 :(得分:7)

我认为对于一个简单的解决方案,考虑到你想要的数量相对较少的值与可能值的数量,你可以强制它。

如果这是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;

这个解决方案可能引起一些争论,因为有些人可能认为理论上可能永远不会回归,但考虑到德尔福的PRNG,情况并非如此。