Проблема состоит в том, чтобы выбрать «случайную» последовательность из N уникальных чисел из диапазона 1..M, где нет ограничений на отношения между N и M (M может быть намного больше, примерно таким же или даже меньше чем N; они не могут быть относительно простыми).
Расширение ответа на регистр сдвига с линейной обратной связью: для данного M постройте максимальную LFSR для наименьшей степени двух, которая больше M. Затем просто возьмите свои числа из LFSR, выбрасывая числа больше M. В среднем , вы выбросите не более половины сгенерированных чисел (поскольку по построению более половины диапазона LFSR меньше, чем M), поэтому ожидаемое время получения числа равно O (1). Вы не храните ранее сгенерированные числа, поэтому потребление пространства также составляет O (1). Если вы выполняете цикл до получения N чисел, тогда M меньше N (или LFSR построен неправильно).
Здесь вы можете найти параметры для максимальной длины LFSR до 168 бит (из википедии): 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;
}
}