问题是从范围 1..M 中选择 N 个唯一数字的“随机”序列,其中对 N 和 M 之间的关系没有限制(M 可以大得多,大致相同,甚至更小比 N;它们可能不是互质的)。
扩展线性反馈移位寄存器答案:对于给定的 M,为大于 M 的 2 的最小幂构造一个最大 LFSR。然后从 LFSR 中获取您的数字,丢弃大于 M 的数字。平均,您将最多丢弃一半生成的数字(因为通过构造超过一半的 LFSR 范围小于 M),因此获取数字的预期运行时间为 O(1)。您没有存储以前生成的数字,因此空间消耗也是 O(1)。如果您在获得 N 个数字之前循环,则 M 小于 N(或 LFSR 构造不正确)。
您可以在此处(来自维基百科)找到最大长度为 168 位的 LFSR 的参数:http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
这是一些java代码:
/**
* 在 [0,M) 中生成一系列唯一的“随机”数字
* @author dkoes
*
*/
公共类 UniqueRandom
{
长 lfsr;
长面具;
长最大值;
private static long seed = 1;
//indexed by number of bits
private static int [][] taps = {
null, // 0
null, // 1
null, // 2
{3,2}, //3
{4,3},
{5,3},
{6,5},
{7,6},
{8,6,5,4},
{9,5},
{10,7},
{11,9},
{12,6,4,1},
{13,4,3,1},
{14,5,3,1},
{15,14},
{16,15,13,4},
{17,14},
{18,11},
{19,6,2,1},
{20,17},
{21,19},
{22,21},
{23,18},
{24,23,22,17},
{25,22},
{26,6,2,1},
{27,5,2,1},
{28,25},
{29,27},
{30,6,4,1},
{31,28},
{32,22,2,1},
{33,20},
{34,27,2,1},
{35,33},
{36,25},
{37,5,4,3,2,1},
{38,6,5,1},
{39,35},
{40,38,21,19},
{41,38},
{42,41,20,19},
{43,42,38,37},
{44,43,18,17},
{45,44,42,41},
{46,45,26,25},
{47,42},
{48,47,21,20},
{49,40},
{50,49,24,23},
{51,50,36,35},
{52,49},
{53,52,38,37},
{54,53,18,17},
{55,31},
{56,55,35,34},
{57,50},
{58,39},
{59,58,38,37},
{60,59},
{61,60,46,45},
{62,61,6,5},
{63,62},
};
//m is upperbound; things break if it isn't positive
UniqueRandom(long m)
{
max = m;
lfsr = seed; //could easily pass a starting point instead
//figure out number of bits
int bits = 0;
long b = m;
while((b >>>= 1) != 0)
{
bits++;
}
bits++;
if(bits < 3)
bits = 3;
mask = 0;
for(int i = 0; i < taps[bits].length; i++)
{
mask |= (1L << (taps[bits][i]-1));
}
}
//return -1 if we've cycled
long next()
{
long ret = -1;
if(lfsr == 0)
return -1;
do {
ret = lfsr;
//update lfsr - from wikipedia
long lsb = lfsr & 1;
lfsr >>>= 1;
if(lsb == 1)
lfsr ^= mask;
if(lfsr == seed)
lfsr = 0; //cycled, stick
ret--; //zero is stuck state, never generated so sub 1 to get it
} while(ret >= max);
return ret;
}
}