Snowballの回答とコメントに基づいて、完全な例を作成しました。set == list
小さい数の比較を使用できます。48^5-1
私は記憶に収まりませんでした。
この問題を回避するためにa < m
、ターゲットを数回インクリメントして、可能な数(素因数が重複している)をa
見つけ< m
ますm
。驚くべきことに、多くの数には+2で十分です。いくつかの余分な数字は、後で反復中にスキップされます。
import random
def __prime_factors(n):
"""
https://stackoverflow.com/a/412942/6078370
Returns all the prime factors of a positive integer
"""
factors = []
d = 2
while n > 1:
while n % d == 0:
factors.append(d)
n //= d
d += 1
if d * d > n:
if n > 1: factors.append(n)
break
return factors
def __multiply_numbers(numbers):
"""multiply all numbers in array"""
result = 1
for n in numbers:
result *= n
return result
def __next_good_number(start):
"""
https://en.wikipedia.org/wiki/Linear_congruential_generator#c%E2%89%A00
some conditions apply for good/easy rotation
"""
number = start
factors = __prime_factors(number)
while len(set(factors)) == len(factors) or number % 4 == 0:
number += 1
factors = __prime_factors(number)
return number, set(factors)
# primes < 100 for coprime calculation. add more if your target is large
PRIMES = set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
def create_new_seed(target):
"""be aware, m might become > target"""
m, factors = __next_good_number(target)
a = __multiply_numbers(factors) + 1
# https://en.wikipedia.org/wiki/Coprime_integers
otherPrimes = [p for p in PRIMES if p not in factors]
# the actual random part to get differnt results
random.shuffle(otherPrimes)
# I just used arbitary 3 of the other primes
c = __multiply_numbers(otherPrimes[:3])
# first number
state = random.randint(0, target-1)
return state, m, a, c
def next_number(state, m, a ,c, limit):
newState = (a * state + c) % m
# skip out of range (__next_good_number increases original target)
while newState >= limit:
newState = (a * newState + c) % m
return newState
if __name__ == "__main__":
target = 48**5-1
state, m, a, c = create_new_seed(target)
print(state, m, a, c, 'target', target)
# list and set can't fit into 16GB of memory
checkSum = sum(range(target))
randomSum = 0
for i in range(target):
state = newState = next_number(state, m, a ,c, target)
randomSum += newState
print(checkSum == randomSum) # true
LCGは非常に魅力的で、ゲームなどで使用できます。
決定論的なランダムな順序で物事の巨大なリストを繰り返すことができます。リスト全体をシャッフルして保存する必要はありません。
def riter(alist):
""" iterate list using LCG """
target = len(alist)
state, m, a, c = create_new_seed(target)
for _ in range(target):
yield alist[state]
state = next_number(state, m, a ,c, target)
反復ステップの間に状態を保存するのは簡単です。
savedState = '18:19:25:6:12047269:20'
print('loading:', savedState)
i, state, m, a, c, target = (int(i) for i in savedState.split(':'))
state = next_number(state, m, a, c, target)
i += 1
print('i:', i, 'is random number:', state, 'list done:', i+1 == target)
print('saving:', '{}:{}:{}:{}:{}:{}'.format(i, state, m, a, c, target))