Как сгенерировать предсказуемую перетасовку последовательности, не создавая ее заранее?

Следующий код Python точно описывает то, что я хочу достичь для последовательности произвольного размера (совокупности):

import random
fixed_seed = 1 #generate the same sequence every time with a fixed seed
population = 1000
sample_count = 5 #demonstration number
num_retries = 3  #just enough to show the repeatable behaviour
for trynum in xrange(num_retries):
    #generate the fresh/ordered sequence (0->population)...
    seq = range(population)
    #seed the random number generator the same way every time...
    random.seed(fixed_seed)
    #shuffle the sequence...
    random.shuffle(seq)
    #display results for this try...
    sample_sequence = [str(x) for x in seq[:sample_count]]
    print "try %s: %s..." % (trynum + 1, ", ".join(sample_sequence))
#Sample output...
#try 1: 995, 721, 62, 326, 541...
#try 2: 995, 721, 62, 326, 541...
#try 3: 995, 721, 62, 326, 541...

Проблема с этим методом заключается в том, что он требует генерации всего последовательность в памяти первой. Это может быть проблемой для огромных групп населения.

Обратите внимание, что потенциально большим преимуществом этого метода является то, что вы можете выбрать любую позицию массива в любое время.

Теперь - если проблема под рукой. позволяет установить размер популяции в степени двойки (минус 1), регистр сдвига с линейной обратной связью можно использовать для получения предсказуемой случайной последовательности. LFSR аккуратны и довольно хорошо объяснены в статье википедии .

Код Python ниже демонстрирует это (и я провел кучу тестов на уникальность, чтобы убедиться, что он работает так, как рекламируется). См. статью в википедии еще раз для объяснения того, как работает код ( Конфигурация Галуа ).

TAP_MASKS = { #only one needed, but I included 3 to make the code more useful
    10: 0x00000240, #taps at 10, 7
    16: 0x0000B400, #taps at 16, 14, 13, 11
    32: 0xE0000200, #taps at 32, 31, 30, 10
}

def MaxLengthLFSR(seed, register_length):
    "Gets next value from seed in max-length LFSR using Galois configuration."
    lsb = seed & 1
    next_val = seed >> 1
    if lsb == 1:
        mask = TAP_MASKS[register_length]
        next_val ^= mask
    return next_val

reglen = 16  #number of bits in register
population = (2**reglen) - 1 #not used, just showing it
fixed_seed = 1   #seed == startval in this case (could randomize in population)
sample_count = 5 #demonstration number
num_retries = 3  #just enough to show the repeatable behaviour
for trynum in xrange(num_retries):
    next_val = fixed_seed
    seq = [fixed_seed, ]
    for x in xrange(sample_count - 1):
        next_val = MaxLengthLFSR(next_val, reglen)
        seq.append(next_val)
    seq = [str(x) for x in seq]
    print "try %s: %s..." % (trynum + 1, ", ".join(seq))
#Sample output...
#try 1: 1, 46080, 23040, 11520, 5760...
#try 2: 1, 46080, 23040, 11520, 5760...
#try 3: 1, 46080, 23040, 11520, 5760...

Это хорошо, потому что вы можете иметь ОГРОМНУЮ популяцию и легко вычислить повторяемую непроверенную повторение последовательности случайных чисел без использования большого фрагмента памяти.

Недостатками являются: а) то, что она ограничена "перетасовкой" последовательностей размера (2 ** N - 1), и б) то, что вы не можете определить, что значение конкретной позиции в случайной последовательности находится в произвольном месте. Вам нужно знать значение в определенной точке и пройти последовательность оттуда.

Последнее (b) в основном нормально, так как большую часть времени вы будете генерировать последовательность по порядку, поэтому вам просто нужно запомнить последний стоимость. Сила ограничения 2 (а) - своего рода убийца сделки, хотя ... в зависимости от приложения.

Как добиться неповторяющихся результатов, подобных LFSR максимальной длины, для произвольной длины последовательности?

В качестве бонуса было бы неплохо иметь решение, в котором вы можете узнать номер в данной позиции последовательности без необходимости переходить через последовательность к этой позиции.


Примечание: если вам нужен хороший начальный набор LFSR точки подключения для LFSR максимальной длины (которые генерируют всю совокупность регистров без единого повторения), эта ссылка неплохая и имеет огромное количество ответвлений на размер регистра (в любом случае до 32 бит).

Также обратите внимание, что я видел много вопросов, тесно связанных с моим вопросом и shuffling / LFSR, но ни один из них точно не относится к тому, что мне нужно (предсказуемое перемешивание линейной последовательности произвольного размера). По крайней мере, насколько мне удалось их понять.

Недавно я изучал Линейные конгруэнтные генераторы , которые кажутся многообещающими, но я не смог их найти пока работать. Вместо того, чтобы сидеть на вопросе, я задам его и отправлю ответ, если у меня получится, и они сработают.

5
задан Russ 11 October 2010 в 21:29
поделиться