範囲をランダムに繰り返したいと思います。各値は1回だけ訪問され、すべての値が最終的に訪問されます。例えば:
class Array
def shuffle
ret = dup
j = length
i = 0
while j > 1
r = i + rand(j)
ret[i], ret[r] = ret[r], ret[i]
i += 1
j -= 1
end
ret
end
end
(0..9).to_a.shuffle.each{|x| f(x)}
ここで、f(x)
は各値を操作する関数です。フィッシャー-イェーツシャッフルは、ランダムな順序を効率的に提供するために使用されます。
私の問題はshuffle
、配列を操作する必要があることです。これは、天文学的に大きな数を処理しているため、クールではありません。Rubyは、巨大な配列を作成しようとすると、すぐに大量のRAMを消費します。に置き換える(0..9)
ことを想像してみてください(0..99**99)
。これが、次のコードが機能しない理由でもあります。
tried = {} # store previous attempts
bigint = 99**99
bigint.times {
x = rand(bigint)
redo if tried[x]
tried[x] = true
f(x) # some function
}
このコードは非常に単純で、tried
より多くのエントリを取得するとすぐにメモリが不足します。
私がやろうとしていることを達成できるのはどのようなアルゴリズムですか?
[編集1]:なぜこれをしたいのですか?部分的な衝突を探すN長の入力文字列のハッシュアルゴリズムの検索スペースを使い果たしようとしています。私が生成する各数値は、一意の入力文字列、エントロピー、およびすべてに相当します。基本的に、私はカスタムアルファベットを使用して「カウント」しています。
[Edit2]:これはf(x)
、上記の例では、ハッシュを生成し、それを定数のターゲットハッシュと比較して部分的な衝突を行うメソッドであることを意味します。x
呼び出した後の値を保存する必要がないf(x)
ので、メモリは時間の経過とともに一定に保たれるはずです。
[編集3/4/5/6]:さらなる説明/修正。
[解決策]:次のコードは@btaの解決策に基づいています。簡潔にするために、next_prime
は表示されていません。それは許容できるランダム性を生み出し、各番号を1回だけ訪問します。詳細については、実際の投稿を参照してください。
N = size_of_range
Q = ( 2 * N / (1 + Math.sqrt(5)) ).to_i.next_prime
START = rand(N)
x = START
nil until f( x = (x + Q) % N ) == START # assuming f(x) returns x