Unique (non-repeating) random numbers in O(1)?
我想生成0到1000之间的唯一随机数,这些数字永远不会重复(即6不会出现两次),但这并不需要像o(n)搜索以前的值来实现。这有可能吗?
初始化值为0-1000的1001个整数数组,并将变量max设置为数组的当前max索引(从1000开始)。选择一个介于0和max之间的随机数r,将位置r处的数字与位置max处的数字交换,并返回位置max处的数字,最大递减1,然后继续。当max为0时,将max设置回数组-1的大小,然后在不需要重新初始化数组的情况下重新启动。
更新:尽管我在回答这个问题时自己提出了这个方法,但经过一些研究,我意识到这是费希尔·耶茨的一个改进版本,被称为杜尔斯滕费尔德·费希尔·耶茨或克努斯·费希尔·耶茨。由于描述可能有点难理解,我提供了下面的示例(使用11个元素而不是1001):
数组以初始化为数组[n]=n的11个元素开始,max以10开始:
1 2 3 4 5 | +--+--+--+--+--+--+--+--+--+--+--+ | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10| +--+--+--+--+--+--+--+--+--+--+--+ ^ max |
在每次迭代中,在0和max之间选择一个随机数r,交换数组[r]和数组[max],返回新数组[max],并递减max:
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 | 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,数组元素将随机排列:
1 2 3 | +--+--+--+--+--+--+--+--+--+--+--+ | 4|10| 8| 6| 2| 0| 9| 5| 1| 7| 3| +--+--+--+--+--+--+--+--+--+--+--+ |
此时,max可以重置为10,该过程可以继续。
您可以这样做:
因此,这不需要每次搜索旧值,但它仍然需要O(N)作为初始无序排列。但正如尼尔斯在评论中指出的,这是摊销o(1)。
使用最大线性反馈移位寄存器。
它可以在C的几行代码中实现,在运行时只做几个测试/分支,一点添加和位移动。这不是随机的,但它愚弄了大多数人。
可以使用线性同余生成器。其中,
您可以使用保留格式的加密来加密计数器。您的计数器从0开始向上,加密使用您选择的一个键将其转换为一个看似随机的值,不管您想要什么基数和宽度。例如,对于这个问题的例子:基数10,宽度3。
块密码通常具有固定的块大小,例如64或128位。但是,保留格式的加密允许您采用标准密码(如AES)并使用一种仍具有密码学鲁棒性的算法,根据您需要的基数和宽度,生成一个较小宽度的密码。
它保证永远不会发生冲突(因为加密算法创建1:1映射)。它也是可逆的(双向映射),因此您可以获取结果号并返回到您开始使用的计数器值。
这种技术不需要内存来存储无序数组等,这在内存有限的系统中是一个优势。
AES-FFX是实现这一目标的一种标准方法。我已经尝试了一些基于aes-ffx思想的基本python代码,尽管不完全一致——请参阅这里的python代码。它可以将计数器加密为随机的7位十进制数或16位数字。下面是一个基数10,宽度3的示例(给出一个介于0和999之间的数字),如问题所述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 ... ... |
要获得不同的非重复伪随机序列,请更改加密密钥。每个加密密钥产生不同的非重复伪随机序列。
对于0…1000这样的低数字,创建一个包含所有数字的列表,然后直接向前移动。但是,如果要从中提取的数字集非常大,还有另一种优雅的方法:可以使用密钥和加密散列函数构建伪随机排列。参见下面的C++-ISH示例伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
这里,
您可以使用下面描述的我的xincrol算法:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
这是一种生成随机但唯一的数字的纯算法方法,没有数组、列表、排列或沉重的CPU负载。
最新版本还允许设置数字范围,例如,如果我希望唯一的随机数在0-1073741821范围内。
实际上我用它是为了
- MP3播放器,随机播放每首歌曲,但每个专辑/目录只能播放一次。
- 像素级视频帧溶解效果(快速平滑)
- 为签名和标记在图像上创建一个秘密的"噪音"雾(隐写术)
- 通过数据库实现大量Java对象序列化的数据对象ID
- 三重多数存储器位保护
- 地址+值加密(每个字节不仅加密,而且移动到缓冲区的新加密位置)。这真的让密码分析的人对我很生气。
- 纯文本到纯文本,如短信、电子邮件等的加密文本。
- 我的德州扑克计算器(THC)
- 我的几款模拟游戏"洗牌",排名
- 更多
它是开放的,免费的。试试看……
你甚至不需要一个数组来解决这个问题。
你需要一个位掩码和一个计数器。
将计数器初始化为零,并在连续调用时递增。xor计数器与位掩码(启动时随机选择或固定)一起生成psuedRandom数。如果数字不能超过1000,请不要使用大于9位的位掩码。(换句话说,位掩码是一个不高于511的整数。)
确保当计数器通过1000时,您将其重置为零。此时,如果您希望以不同的顺序生成相同的数字集,则可以选择另一个随机位掩码。
下面是我键入的一些代码,它使用第一个解决方案的逻辑。我知道这是"语言不可知论",但我只是想在C中举个例子,以防有人在寻找快速实用的解决方案。
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 | // 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]); } |
号
这种方法在极限值较高时会产生合适的结果,您只需要生成一些随机数。
1 2 3 4 5 6 7 8 9 10 11 12 | #!/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) "; } |
请注意,这些数字是按升序生成的,但您可以在之后进行无序排列。
比如说,你想一遍又一遍地翻阅无序的列表,而不需要每次重新开始洗牌时都延迟
创建2个列表a和b,0到1000,占用
使用Fisher Yates洗牌列表A,需要
画一个数字时,费希尔·耶茨在另一张单子上做一步移动。
当光标位于列表末尾时,切换到另一个列表。
预处理
1 2 3 4 5 6 | cursor = 0 selector = A other = B shuffle(A) |
画
1 2 3 4 5 6 7 8 9 | temp = selector[cursor] swap(other[cursor], other[random]) if cursor == N then swap(selector, other); cursor = 0 else cursor = cursor + 1 return temp |
您可以使用一个10位的好的伪随机数生成器,丢弃1001到1023,保留0到1000。
从这里我们得到了10位prng的设计。
10位,反馈多项式x^10+x^7+1(周期1023)
使用galois lfsr获取快速代码
我认为线性同余发生器是最简单的解。
对a、c和m值只有3个限制
ps该方法已经被提到,但是post对常量值的假设是错误的。下面的常量对于您的情况应该可以正常工作
在您的情况下,您可以使用
1 | X = (1002 * X + 757) mod 1001 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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)复杂性。注:随机应是静态的,螺纹安全适用。
下面是一些可以使用的COBOL代码示例。我可以给你发送randgen.exe文件,这样你就可以玩它,看看它是否需要你。
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | 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. |
这里的大多数答案不能保证他们不会两次返回相同的数字。以下是正确的解决方案:
1 2 3 4 5 6 7 8 9 10 11 | 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个其他输出之后,允许重复一个值,但是天真地允许0紧跟在0之后,只要它们都出现在1000个集合的末尾和开头。相反,虽然在重复之间可以保持1000个其他值的距离,但这样做会强制出现这样一种情况,即序列每次都以完全相同的方式重放自身,因为没有其他值超出了该限制。
这里有一个方法,它总是在一个值可以重复之前保证至少500个其他值:
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 | 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; } |
当n大于1000并且需要随机抽取k个样本时,可以使用一个包含到目前为止样本的集合。对于每个绘图,都使用拒绝采样,这将是一个"几乎"的O(1)操作,因此总运行时间几乎是O(k)和O(n)存储。
当k接近n时,该算法会发生碰撞,这意味着运行时间比o(k)要差得多。一个简单的解决方法是反转逻辑,这样,对于k>n/2,您就可以保存所有尚未绘制的样本的记录。每次抽取都会从拒绝集合中删除一个样本。
拒绝抽样的另一个明显问题是它是O(N)存储,如果N在数十亿或更多,这是坏消息。然而,有一种算法可以解决这个问题。该算法以其发明者的名字命名为维特算法。这里描述了算法。维特算法的要点是,在每次绘制之后,使用一定的分布来计算一个随机跳跃,从而保证均匀采样。
你如何有效地生成一个在0和上界n之间的k个非重复整数的列表被链接为一个副本-如果你想要的是每个生成的随机数的0(1)(没有O(n)启动成本)有一个简单的调整接受的答案。
创建一个空的无序映射(一个空的有序映射将把每个元素的o(log k)从整数变为整数-而不是使用一个初始化的数组。如果这是最大值,则将max设置为1000,
与使用初始化数组相比,唯一的区别是元素的初始化被推迟/跳过,但它将从相同的prng生成完全相同的数字。
另一种可能性:
您可以使用一组标志。当下一个已经被选中的时候再拿它。
但是,注意在1000次调用之后,函数将永远不会结束,因此必须进行保护。
请参阅我的答案:https://stackoverflow.com/a/46807110/8794687
它是最简单的算法之一,具有表示样本大小的平均时间复杂性。还有一些链接指向散列表算法,这些算法的复杂性被称为O(S)。
费希尔耶茨
1 2 3 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |