这是我目前的进展:
myArray.map!{ rand(max) }
显然,有时列表中的数字不是唯一的。 如何确保我的列表只包含唯一的数字,而无需创建一个更大的列表,然后从中选择n个唯一的数字?
编辑:
如果可能的话,我真的想看到这样做没有循环。
基于Kent Fredric上面的解决方案,这就是我最终使用的:
def n_unique_rand(number_to_generate, rand_upper_limit)
return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end
谢谢,肯特。
这种方法没有循环
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
这里有一个解决方案:
假设你想要生成的随机数在 r_min
和 r_max
之间。对于列表中的每个元素,生成一个随机数 r
,并使 list[i]=list[i-1]+r
。这将给你单调递增的随机数,只要满足以下条件就保证唯一性:
r+list[i-1]
不会溢出r
> 0对于第一个元素,你应该使用 r_min
而不是 list[i-1]
。完成后,你可以打乱列表,使元素不那么明显地按顺序排列。
这种方法唯一的问题是当你超过 r_max
并且仍然需要生成更多元素时。在这种情况下,你可以将 r_min
和 r_max
重置为已经计算过的两个相邻元素,并简单地重复该过程。这实际上是在一个没有使用过数字的区间内运行相同的算法。你可以一直这样做,直到列表被填满。
虽然提前知道最大值是好的,但你可以这样做:
class NoLoopRand
def initialize(max)
@deck = (0..max).to_a
end
def getrnd
return @deck.delete_at(rand(@deck.length - 1))
end
end
你可以通过以下方式获取随机数据:
aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd
当牌堆中的所有值都被用完时,你将获得nil
。
使用肯特的方法,可以生成一个任意长度的数组,并将所有值保持在一个有限范围内:
# Generates a random array of length n.
#
# @param n length of the desired array
# @param lower minimum number in the array
# @param upper maximum number in the array
def ary_rand(n, lower, upper)
values_set = (lower..upper).to_a
repetition = n/(upper-lower+1) + 1
(values_set*repetition).sample n
end
另一种可能更加高效的方法,改编自同样Kent的另一个答案:
def ary_rand2(n, lower, upper)
v = (lower..upper).to_a
(0...n).map{ v[rand(v.length)] }
end
puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # [] no such range :)