O(1)中的唯一(非重复)随机数?

时间:2008-10-12 20:34:22

标签: algorithm math random language-agnostic

我想生成0到1000之间永远不会重复的唯一随机数(即6不会出现两次),但这并不是像O(N)搜索之前的值那样它。这可能吗?

22 个答案:

答案 0 :(得分:234)

初始化一个1001个整数的数组,其值为0-1000,并将变量max设置为数组的当前最大索引(从1000开始)。选择0到最大值之间的随机数r,将位置r处的数字与位置max处的数字进行交换,并在位置max处返回数字。减去最大值1并继续。当max为0时,将max设置为数组的大小 - 1并重新开始,而无需重新初始化数组。

<强>更新 虽然当我回答这个问题时,我自己想出了这个方法,经过一些研究后我发现这是Fisher-Yates的修改版本,称为Durstenfeld-Fisher-Yates或Knuth-Fisher-Yates。由于描述可能有点难以理解,我在下面提供了一个示例(使用11个元素而不是1001):

数组从11个初始化为array [n] = n的元素开始,max从10开始:

+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|
+--+--+--+--+--+--+--+--+--+--+--+
                                ^
                               max    

在每次迭代中,在0和max之间选择随机数r,交换数组[r]和数组[max],返回新数组[max],并递减max:

max = 10, r = 3
           +--------------------+
           v                    v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2|10| 4| 5| 6| 7| 8| 9| 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 9, r = 7
                       +-----+
                       v     v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2|10| 4| 5| 6| 9| 8| 7: 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 8, r = 1
     +--------------------+
     v                    v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 8| 2|10| 4| 5| 6| 9| 1: 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 7, r = 5
                 +-----+
                 v     v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 8| 2|10| 4| 9| 6| 5: 1| 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

...

经过11次迭代后,数组中的所有数字都被选中,max == 0,并且数组元素被洗牌:

+--+--+--+--+--+--+--+--+--+--+--+
| 4|10| 8| 6| 2| 0| 9| 5| 1| 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

此时,max可以重置为10,并且该过程可以继续。

答案 1 :(得分:71)

你可以这样做:

  1. 创建一个列表,0..1000。
  2. 随机播放清单。 (有关这方面的好方法,请参阅Fisher-Yates shuffle。)
  3. 从随机列表中按顺序返回数字。
  4. 因此,这不需要每次都搜索旧值,但仍需要O(N)作为初始shuffle。但正如尼尔斯在评论中指出的那样,这是摊销的O(1)。

答案 2 :(得分:57)

使用Maximal Linear Feedback Shift Register

它可以在几行C中实现,并且在运行时只需要几个测试/分支,一点点添加和位移。这不是随意的,但它会欺骗大多数人。

答案 3 :(得分:20)

您可以使用A Linear Congruential Generator。其中m(模数)是最接近的大于1000的素数。当你得到一个超出范围的数字时,只需得到下一个。只有在发生所有元素后,序列才会重复,您不必使用表格。请注意这个生成器的缺点(包括缺乏随机性)。

答案 4 :(得分:16)

您可以使用Format-Preserving Encryption加密计数器。你的计数器从0开始向上,加密使用你选择的一个键将它变成一个看似随机的值,你想要的基数和宽度。例如。对于此问题中的示例:基数10,宽度3。

分组密码通常具有固定的块大小,例如, 64或128位。但格式保留加密允许您采用AES之类的标准密码,并使用一种仍然具有加密功能的算法制作一个小宽度的密码,无论您需要什么基数和宽度。

保证永远不会发生冲突(因为加密算法会创建1:1映射)。它也是可逆的(双向映射),因此您可以获取结果数字并返回到您开始时的计数器值。

这种技术不需要内存来存储混洗数组等,这对于内存有限的系统来说是一个优势。

AES-FFX是一种提出标准的方法来实现这一目标。我已经尝试了一些基于AES-FFX思想的基本Python代码,尽管不完全一致 - see Python code here。它可以是例如将计数器加密为随机查看的7位十进制数或16位数。这是一个基数为10的例子,宽度为3(给出0到999之间的数字),如下所述:

000   733
001   374
002   882
003   684
004   593
005   578
006   233
007   811
008   072
009   337
010   119
011   103
012   797
013   257
014   932
015   433
...   ...

要获得不同的非重复伪随机序列,请更改加密密钥。每个加密密钥产生一个不同的非重复伪随机序列。

答案 5 :(得分:7)

对于0到1000之类的低数字,创建一个包含所有数字并且随机播放的列表是直截了当的。但是如果要绘制的数字集非常大,那么还有另一种优雅方式:您可以使用密钥和加密散列函数构建伪随机置换。请参阅以下C ++ - ish示例伪代码:

unsigned randperm(string key, unsigned bits, unsigned index) {
  unsigned half1 =  bits    / 2;
  unsigned half2 = (bits+1) / 2;
  unsigned mask1 = (1 << half1) - 1;
  unsigned mask2 = (1 << half2) - 1;
  for (int round=0; round<5; ++round) {
    unsigned temp = (index >> half1);
    temp = (temp << 4) + round;
    index ^= hash( key + "/" + int2str(temp) ) & mask1;
    index = ((index & mask2) << half1) | ((index >> half2) & mask1);
  }
  return index;
}

这里,hash只是一些任意伪随机函数,它将字符串映射到可能很大的无符号整数。函数randperm是0 ... pow(2,bits)-1内所有数字的排列,假设一个固定的密钥。这是从构造开始的,因为改变变量index的每个步骤都是可逆的。这受到Feistel cipher的启发。

答案 6 :(得分:6)

您可以使用我在此描述的Xincrol算法:

http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html

这是一种纯算法方法,可以生成随机但唯一的数字,而不需要数组,列表,排列或繁重的CPU负载。

最新版本还允许设置数字范围,例如,如果我想要0-1073741821范围内的唯一随机数。

我几乎将它用于

  • 随机播放每首歌曲的MP3播放器,但每张专辑/目录只播放一次
  • 像素智能视频帧溶解效果(快速而流畅)
  • 在图像上为签名和标记创建一个秘密的“噪音”雾(隐写术)
  • 通过数据库序列化大量Java对象的数据对象ID
  • 三重多数内存位保护
  • 地址+值加密(每个字节不仅加密,而且还移动到缓冲区中的新加密位置)。这真让密码分析研究员对我很生气: - )
  • 纯文本到普通文件加密文本加密用于短信,电子邮件等。
  • 我的德州扑克计算器(THC)
  • 我的几款模拟游戏,“洗牌”,排名
  • 更多

开放,免费。试一试......

答案 7 :(得分:5)

你甚至不需要一个数组来解决这个问题。

你需要一个位掩码和一个计数器。

将计数器初始化为零,并在连续调用时递增计数器。使用位掩码(在启动时随机选择或固定)对计数器进行异或,以生成伪随机数。如果您的数字不能超过1000,请不要使用大于9位的位掩码。 (换句话说,位掩码是一个不高于511的整数。)

确保当计数器超过1000时,将其重置为零。此时,您可以选择另一个随机位掩码 - 如果您愿意 - 以不同的顺序生成相同的数字集。

答案 8 :(得分:3)

这是我输入的一些代码,它使用了第一个解决方案的逻辑。我知道这是“语言无关”,但只是想在C#中将其作为一个例子,以防有人在寻找快速实用的解决方案。

// Initialize variables
Random RandomClass = new Random();
int RandArrayNum;
int MaxNumber = 10;
int LastNumInArray;
int PickedNumInArray;
int[] OrderedArray = new int[MaxNumber];      // Ordered Array - set
int[] ShuffledArray = new int[MaxNumber];     // Shuffled Array - not set

// Populate the Ordered Array
for (int i = 0; i < MaxNumber; i++)                  
{
    OrderedArray[i] = i;
    listBox1.Items.Add(OrderedArray[i]);
}

// Execute the Shuffle                
for (int i = MaxNumber - 1; i > 0; i--)
{
    RandArrayNum = RandomClass.Next(i + 1);         // Save random #
    ShuffledArray[i] = OrderedArray[RandArrayNum];  // Populting the array in reverse
    LastNumInArray = OrderedArray[i];               // Save Last Number in Test array
    PickedNumInArray = OrderedArray[RandArrayNum];  // Save Picked Random #
    OrderedArray[i] = PickedNumInArray;             // The number is now moved to the back end
    OrderedArray[RandArrayNum] = LastNumInArray;    // The picked number is moved into position
}

for (int i = 0; i < MaxNumber; i++)                  
{
    listBox2.Items.Add(ShuffledArray[i]);
}

答案 9 :(得分:3)

当限制时,此方法结果合适,并且您只想生成一些随机数。

#!/usr/bin/perl

($top, $n) = @ARGV; # generate $n integer numbers in [0, $top)

$last = -1;
for $i (0 .. $n-1) {
    $range = $top - $n + $i - $last;
    $r = 1 - rand(1.0)**(1 / ($n - $i));
    $last += int($r * $range + 1);
    print "$last ($r)\n";
}

请注意,这些数字是按升序生成的,但您可以随后进行随机播放。

答案 10 :(得分:2)

你可以使用10位的好pseudo-random number generator并扔掉1001到1023,留下0到1000.

here我们得到了10位PRNG的设计..

  • 10位,反馈多项式x ^ 10 + x ^ 7 + 1(期间1023)

  • 使用Galois LFSR获取快速代码

答案 11 :(得分:2)

public static int[] randN(int n, int min, int max)
{
    if (max <= min)
        throw new ArgumentException("Max need to be greater than Min");
    if (max - min < n)
        throw new ArgumentException("Range needs to be longer than N");

    var r = new Random();

    HashSet<int> set = new HashSet<int>();

    while (set.Count < n)
    {
        var i = r.Next(max - min) + min;
        if (!set.Contains(i))
            set.Add(i);
    }

    return set.ToArray();
}

N非重复随机数将根据需要具有O(n)复杂度 注意:随机应该是静态的,并且应用了线程安全性。

答案 12 :(得分:2)

我认为Linear congruential generator是最简单的解决方案。

enter image description here

a c m只有3个限制

  1. m c 是比较优质的,
  2. a-1 可被 m
  3. 的所有素数因子整除 如果 m
  4. a-1 可被 4 整除可被 4
  5. 整除

    PS 已经提到了这个方法,但帖子对常量值有错误的假设。下面的常量应该适合您的情况

    在您的情况下,您可以使用a = 1002c = 757m = 1001

    X = (1002 * X + 757) mod 1001
    

答案 13 :(得分:2)

让我们说你想要一遍又一遍地浏览洗牌后的列表,而不是每次重新洗牌时都会O(n)延迟,在这种情况下我们可以这样做:

  1. 创建2个列表A和B,0到1000,占用2n空格。

  2. 使用Fisher-Yates的随机播放列表A需要n次。

  3. 在绘制数字时,在另一个列表上执行一步Fisher-Yates随机播放。

  4. 当光标位于列表末尾时,切换到另一个列表。

  5. <强>预处理

    cursor = 0
    
    selector = A
    other    = B
    
    shuffle(A)
    

    <强>绘图

    temp = selector[cursor]
    
    swap(other[cursor], other[random])
    
    if cursor == N
    then swap(selector, other); cursor = 0
    else cursor = cursor + 1
    
    return temp
    

答案 14 :(得分:1)

另一个可能性:

您可以使用一组标志。当它被选中时,接下一个。

但是,请注意1000次通话后,该功能将永远不会结束,因此您必须采取保护措施。

答案 15 :(得分:1)

问题How do you efficiently generate a list of K non-repeating integers between 0 and an upper bound N被链接为重复 - 如果你想要每个生成的随机数为O(1)的东西(没有O(n)启动成本)),可以对接受进行简单的调整答案。

创建一个空的无序映射(一个空的有序映射,每个元素需要O(log k))从整数到整数 - 而不是使用初始化的数组。 如果是最大值,则将max设置为1000,

  1. 选择一个0到最大的随机数r,
  2. 确保无序地图中存在地图元素r和max。如果它们不存在,则使用等于其索引的值创建它们。
  3. 交换元素r和max
  4. 返回元素max并将max减1(如果max变为负数) 你完成了)。
  5. 返回第1步。
  6. 与使用初始化数组相比,唯一的区别是元素的初始化被推迟/跳过 - 但它将从同一个PRNG生成完全相同的数字。

答案 16 :(得分:1)

当N大于1000并且您需要绘制K个随机样本时,您可以使用包含目前样本的集合。对于每次绘制,您使用rejection sampling,这将是一个“几乎”O(1)操作,因此总运行时间几乎为O(K),存储有O(N)。

当K“接近”N时,该算法会发生碰撞。这意味着运行时间将比O(K)差很多。一个简单的解决方法是颠倒逻辑,以便对于K> 1。 N / 2,您保留所有尚未绘制的样本的记录。每次抽取都会从拒绝集中删除样本。

拒绝抽样的另一个明显问题是它是O(N)存储,如果N在数十亿或更多,这是坏消息。但是,有一种算法可以解决这个问题。该算法在其发明者之后称为Vitter算法。该算法被描述为here。 Vitter算法的要点是每次绘制后,使用一定的分布计算随机跳跃,保证均匀采样。

答案 17 :(得分:1)

这里的大部分答案都无法保证他们不会两次返回相同的号码。这是一个正确的解决方案:

int nrrand(void) {
  static int s = 1;
  static int start = -1;
  do {
    s = (s * 1103515245 + 12345) & 1023;
  } while (s >= 1001);
  if (start < 0) start = s;
  else if (s == start) abort();

  return s;
}

我不确定约束是否已明确指定。一个假设在1000个其他输出之后允许重复一个值,但是只要它们都出现在1000的集合的结束和开始,那么天真地允许0在0之后立即跟随。相反,尽管可以保持距离为0。重复之间的1000个其他值,这样做会强制每次序列以完全相同的方式重放自身的情况,因为没有其他值发生在该限制之外。

这是一种在重复值之前始终保证至少500个其他值的方法:

int nrrand(void) {
  static int h[1001];
  static int n = -1;

  if (n < 0) {
    int s = 1;
    for (int i = 0; i < 1001; i++) {
      do {
        s = (s * 1103515245 + 12345) & 1023;
      } while (s >= 1001);
      /* If we used `i` rather than `s` then our early results would be poorly distributed. */
      h[i] = s;
    }
    n = 0;
  }

  int i = rand(500);
  if (i != 0) {
      i = (n + i) % 1001;
      int t = h[i];
      h[i] = h[n];
      h[n] = t;
  }
  i = h[n];
  n = (n + 1) % 1001;

  return i;
}

答案 18 :(得分:1)

以下是您可以使用的一些示例COBOL代码 我可以发送给你RANDGEN.exe文件,这样你就可以玩它来查看它是否真的需要你。

   IDENTIFICATION DIVISION.
   PROGRAM-ID.  RANDGEN as "ConsoleApplication2.RANDGEN".
   AUTHOR.  Myron D Denson.
   DATE-COMPILED.
  * ************************************************************** 
  *  SUBROUTINE TO GENERATE RANDOM NUMBERS THAT ARE GREATER THAN
  *    ZERO AND LESS OR EQUAL TO THE RANDOM NUMBERS NEEDED WITH NO
  *    DUPLICATIONS.  (CALL "RANDGEN" USING RANDGEN-AREA.)
  *     
  *  CALLING PROGRAM MUST HAVE A COMPARABLE LINKAGE SECTION
  *    AND SET 3 VARIABLES PRIOR TO THE FIRST CALL IN RANDGEN-AREA     
  *
  *    FORMULA CYCLES THROUGH EVERY NUMBER OF 2X2 ONLY ONCE. 
  *    RANDOM-NUMBERS FROM 1 TO RANDOM-NUMBERS-NEEDED ARE CREATED 
  *    AND PASSED BACK TO YOU.
  *
  *  RULES TO USE RANDGEN:
  *
  *    RANDOM-NUMBERS-NEEDED > ZERO 
  *     
  *    COUNT-OF-ACCESSES MUST = ZERO FIRST TIME CALLED.
  *         
  *    RANDOM-NUMBER = ZERO, WILL BUILD A SEED FOR YOU
  *    WHEN COUNT-OF-ACCESSES IS ALSO = 0 
  *     
  *    RANDOM-NUMBER NOT = ZERO, WILL BE NEXT SEED FOR RANDGEN
  *    (RANDOM-NUMBER MUST BE <= RANDOM-NUMBERS-NEEDED)       
  *     
  *    YOU CAN PASS RANDGEN YOUR OWN RANDOM-NUMBER SEED
  *     THE FIRST TIME YOU USE RANDGEN.
  *     
  *    BY PLACING A NUMBER IN RANDOM-NUMBER FIELD
  *      THAT FOLLOWES THESE SIMPLE RULES:
  *        IF COUNT-OF-ACCESSES = ZERO AND 
  *        RANDOM-NUMBER > ZERO AND 
  *        RANDOM-NUMBER <= RANDOM-NUMBERS-NEEDED
  *       
  *    YOU CAN LET RANDGEN BUILD A SEED FOR YOU
  *     
  *      THAT FOLLOWES THESE SIMPLE RULES:
  *        IF COUNT-OF-ACCESSES = ZERO AND 
  *        RANDOM-NUMBER = ZERO AND 
  *        RANDOM-NUMBER-NEEDED > ZERO  
  *         
  *     TO INSURING A DIFFERENT PATTERN OF RANDOM NUMBERS
  *        A LOW-RANGE AND HIGH-RANGE IS USED TO BUILD
  *        RANDOM NUMBERS.
  *        COMPUTE LOW-RANGE =
  *             ((SECONDS * HOURS * MINUTES * MS) / 3).         
  *        A HIGH-RANGE = RANDOM-NUMBERS-NEEDED + LOW-RANGE
  *        AFTER RANDOM-NUMBER-BUILT IS CREATED 
  *        AND IS BETWEEN LOW AND HIGH RANGE
  *        RANDUM-NUMBER = RANDOM-NUMBER-BUILT - LOW-RANGE
  *               
  * **************************************************************         
   ENVIRONMENT DIVISION.
   INPUT-OUTPUT SECTION.
   FILE-CONTROL.
   DATA DIVISION.
   FILE SECTION.
   WORKING-STORAGE SECTION.
   01  WORK-AREA.
       05  X2-POWER                     PIC 9      VALUE 2. 
       05  2X2                          PIC 9(12)  VALUE 2 COMP-3.
       05  RANDOM-NUMBER-BUILT          PIC 9(12)  COMP.
       05  FIRST-PART                   PIC 9(12)  COMP.
       05  WORKING-NUMBER               PIC 9(12)  COMP.
       05  LOW-RANGE                    PIC 9(12)  VALUE ZERO.
       05  HIGH-RANGE                   PIC 9(12)  VALUE ZERO.
       05  YOU-PROVIDE-SEED             PIC X      VALUE SPACE.
       05  RUN-AGAIN                    PIC X      VALUE SPACE.
       05  PAUSE-FOR-A-SECOND           PIC X      VALUE SPACE.   
   01  SEED-TIME.
       05  HOURS                        PIC 99.
       05  MINUTES                      PIC 99.
       05  SECONDS                      PIC 99.
       05  MS                           PIC 99. 
  *
  * LINKAGE SECTION.
  *  Not used during testing  
   01  RANDGEN-AREA.
       05  COUNT-OF-ACCESSES            PIC 9(12) VALUE ZERO.
       05  RANDOM-NUMBERS-NEEDED        PIC 9(12) VALUE ZERO.
       05  RANDOM-NUMBER                PIC 9(12) VALUE ZERO.
       05  RANDOM-MSG                   PIC X(60) VALUE SPACE.
  *    
  * PROCEDURE DIVISION USING RANDGEN-AREA.
  * Not used during testing 
  *  
   PROCEDURE DIVISION.
   100-RANDGEN-EDIT-HOUSEKEEPING.
       MOVE SPACE TO RANDOM-MSG. 
       IF RANDOM-NUMBERS-NEEDED = ZERO
         DISPLAY 'RANDOM-NUMBERS-NEEDED ' NO ADVANCING
         ACCEPT RANDOM-NUMBERS-NEEDED.
       IF RANDOM-NUMBERS-NEEDED NOT NUMERIC 
         MOVE 'RANDOM-NUMBERS-NEEDED NOT NUMERIC' TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF RANDOM-NUMBERS-NEEDED = ZERO
         MOVE 'RANDOM-NUMBERS-NEEDED = ZERO' TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF COUNT-OF-ACCESSES NOT NUMERIC
         MOVE 'COUNT-OF-ACCESSES NOT NUMERIC' TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF COUNT-OF-ACCESSES GREATER THAN RANDOM-NUMBERS-NEEDED
         MOVE 'COUNT-OF-ACCESSES > THAT RANDOM-NUMBERS-NEEDED'
           TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF YOU-PROVIDE-SEED = SPACE AND RANDOM-NUMBER = ZERO
         DISPLAY 'DO YOU WANT TO PROVIDE SEED  Y OR N: '
           NO ADVANCING
           ACCEPT YOU-PROVIDE-SEED.  
       IF RANDOM-NUMBER = ZERO AND
          (YOU-PROVIDE-SEED = 'Y' OR 'y')
         DISPLAY 'ENTER SEED ' NO ADVANCING
         ACCEPT RANDOM-NUMBER. 
       IF RANDOM-NUMBER NOT NUMERIC
         MOVE 'RANDOM-NUMBER NOT NUMERIC' TO RANDOM-MSG
         GO TO 900-EXIT-RANDGEN.
   200-RANDGEN-DATA-HOUSEKEEPING.      
       MOVE FUNCTION CURRENT-DATE (9:8) TO SEED-TIME.
       IF COUNT-OF-ACCESSES = ZERO
         COMPUTE LOW-RANGE =
                ((SECONDS * HOURS * MINUTES * MS) / 3).
       COMPUTE RANDOM-NUMBER-BUILT = RANDOM-NUMBER + LOW-RANGE.  
       COMPUTE HIGH-RANGE = RANDOM-NUMBERS-NEEDED + LOW-RANGE.
       MOVE X2-POWER TO 2X2.             
   300-SET-2X2-DIVISOR.
       IF 2X2 < (HIGH-RANGE + 1) 
          COMPUTE 2X2 = 2X2 * X2-POWER
           GO TO 300-SET-2X2-DIVISOR.    
  * *********************************************************         
  *  IF FIRST TIME THROUGH AND YOU WANT TO BUILD A SEED.    *
  * ********************************************************* 
       IF COUNT-OF-ACCESSES = ZERO AND RANDOM-NUMBER = ZERO
          COMPUTE RANDOM-NUMBER-BUILT =
                ((SECONDS * HOURS * MINUTES * MS) + HIGH-RANGE).
       IF COUNT-OF-ACCESSES = ZERO        
         DISPLAY 'SEED TIME ' SEED-TIME 
               ' RANDOM-NUMBER-BUILT ' RANDOM-NUMBER-BUILT 
               ' LOW-RANGE  ' LOW-RANGE.          
  * *********************************************     
  *    END OF BUILDING A SEED IF YOU WANTED TO  * 
  * *********************************************               
  * ***************************************************
  * THIS PROCESS IS WHERE THE RANDOM-NUMBER IS BUILT  *  
  * ***************************************************   
   400-RANDGEN-FORMULA.
       COMPUTE FIRST-PART = (5 * RANDOM-NUMBER-BUILT) + 7.
       DIVIDE FIRST-PART BY 2X2 GIVING WORKING-NUMBER 
         REMAINDER RANDOM-NUMBER-BUILT. 
       IF RANDOM-NUMBER-BUILT > LOW-RANGE AND
          RANDOM-NUMBER-BUILT < (HIGH-RANGE + 1)
         GO TO 600-RANDGEN-CLEANUP.
       GO TO 400-RANDGEN-FORMULA.
  * *********************************************     
  *    GOOD RANDOM NUMBER HAS BEEN BUILT        *               
  * *********************************************
   600-RANDGEN-CLEANUP.
       ADD 1 TO COUNT-OF-ACCESSES.
       COMPUTE RANDOM-NUMBER = 
            RANDOM-NUMBER-BUILT - LOW-RANGE. 
  * *******************************************************
  * THE NEXT 3 LINE OF CODE ARE FOR TESTING  ON CONSOLE   *  
  * *******************************************************
       DISPLAY RANDOM-NUMBER.
       IF COUNT-OF-ACCESSES < RANDOM-NUMBERS-NEEDED
        GO TO 100-RANDGEN-EDIT-HOUSEKEEPING.     
   900-EXIT-RANDGEN.
       IF RANDOM-MSG NOT = SPACE
        DISPLAY 'RANDOM-MSG: ' RANDOM-MSG.
        MOVE ZERO TO COUNT-OF-ACCESSES RANDOM-NUMBERS-NEEDED RANDOM-NUMBER. 
        MOVE SPACE TO YOU-PROVIDE-SEED RUN-AGAIN.
       DISPLAY 'RUN AGAIN Y OR N '
         NO ADVANCING.
       ACCEPT RUN-AGAIN.
       IF (RUN-AGAIN = 'Y' OR 'y')
         GO TO 100-RANDGEN-EDIT-HOUSEKEEPING.
       ACCEPT PAUSE-FOR-A-SECOND.
       GOBACK.

答案 19 :(得分:0)

Fisher Yates

for i from n−1 downto 1 do
     j ← random integer such that 0 ≤ j ≤ i
     exchange a[j] and a[i]

实际上是O(n-1),因为最后两个只需要一次交换 这是C#

public static List<int> FisherYates(int n)
{
    List<int> list = new List<int>(Enumerable.Range(0, n));
    Random rand = new Random();
    int swap;
    int temp;
    for (int i = n - 1; i > 0; i--)
    {
        swap = rand.Next(i + 1);  //.net rand is not inclusive
        if(swap != i)  // it can stay in place - if you force a move it is not a uniform shuffle
        {
            temp = list[i];
            list[i] = list[swap];
            list[swap] = temp;
        }
    }
    return list;
}

答案 20 :(得分:0)

请在https://stackoverflow.com/a/46807110/8794687

查看我的回答

这是最简单的算法之一,具有平均时间复杂度 O s log s ), s 表示样本大小。哈希表算法也有一些链接,其复杂性声称是 O s )。

答案 21 :(得分:-1)

有人张贴“在excel中创建随机数”。我正在使用这个理想。 创建一个由str.index和str.ran两部分组成的结构; 对于10个随机数,创建10个结构的数组。 将str.index设置为0到9,并将str.ran设置为其他随机数。

for(i=0;i<10; ++i) {
      arr[i].index = i;
      arr[i].ran   = rand();
}

将数组按arr [i] .ran中的值排序。 现在,str.index处于随机顺序。 下面是c代码:

#include <stdio.h>
#include <stdlib.h>

struct RanStr { int index; int ran;};
struct RanStr arr[10];

int sort_function(const void *a, const void *b);

int main(int argc, char *argv[])
{
   int cnt, i;

   //seed(125);

   for(i=0;i<10; ++i)
   {
      arr[i].ran   = rand();
      arr[i].index = i;
      printf("arr[%d] Initial Order=%2d, random=%d\n", i, arr[i].index, arr[i].ran);
   }

   qsort( (void *)arr, 10, sizeof(arr[0]), sort_function);
   printf("\n===================\n");
   for(i=0;i<10; ++i)
   {
      printf("arr[%d] Random  Order=%2d, random=%d\n", i, arr[i].index, arr[i].ran);
   }

   return 0;
}

int sort_function(const void *a, const void *b)
{
   struct RanStr *a1, *b1;

   a1=(struct RanStr *) a;
   b1=(struct RanStr *) b;

   return( a1->ran - b1->ran );
}