【发布时间】:2011-01-28 10:19:24
【问题描述】:
我想随机遍历一个范围。每个值只会被访问一次,所有值最终都会被访问。例如:
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) 是对每个值进行操作的某个函数。 Fisher-Yates shuffle 用于有效地提供随机排序。
我的问题是 shuffle 需要对数组进行操作,这并不酷,因为我正在处理 天文数 大数。 Ruby 会很快消耗大量 RAM 来尝试创建一个巨大的数组。想象一下用(0..99**99) 替换(0..9)。这也是以下代码不起作用的原因:
tried = {} # store previous attempts
bigint = 99**99
bigint.times {
x = rand(bigint)
redo if tried[x]
tried[x] = true
f(x) # some function
}
这段代码非常幼稚,随着tried 获得更多条目,很快就会耗尽内存。
什么样的算法可以完成我想做的事情?
[Edit1]:我为什么要这样做?我试图用尽哈希算法的搜索空间来寻找 N 长度的输入字符串以寻找部分冲突。我生成的每个数字都相当于一个唯一的输入字符串、熵等等。基本上,我正在使用custom alphabet“计数”。
[Edit2]:这意味着上述示例中的f(x) 是一种生成哈希并将其与常量目标哈希进行比较以进行部分冲突的方法。在调用f(x) 之后,我不需要存储x 的值,因此内存应该随着时间的推移保持不变。
[Edit3/4/5/6]:进一步澄清/修复。
[解决方案]:以下代码基于@bta 的解决方案。为简洁起见,next_prime 未显示。它产生可接受的随机性,并且每个数字只访问一次。详情请查看实际帖子。
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
【问题讨论】:
-
您显然没有存储函数调用的结果,因为这也会占用大量内存。那么你到底在做什么呢?为什么需要以随机顺序执行此操作?如果您只是累积值,则顺序可能无关紧要。如果您需要解决方案,我想了解更多信息。
-
如果您不需要将结果返回到数组中,请将示例代码
(0..9).sort_by{rand}.map{|x| f(x)}更改为使用each而不是map。这将使问题更清楚。 -
sort_by rand也不正确;它会给出有偏见的结果。请参阅robweir.com/blog/2010/02/microsoft-random-browser-ballot.html(JavaScript,但概念相同)。 -
正如@Matthew Flaschen 所写,您尝试随机化列表顺序的尝试被严重破坏,并且会返回可能看起来随机但并非随机的结果。他的链接很好地描述了这个问题。
-
无效,你没有抓住重点。该链接是不要做的。您不能按任何随机函数排序(移位随机函数也好不到哪里去)。
标签: ruby random range loops brute-force