如何在Ruby中生成n个唯一随机数的列表?

36

这是我目前的进展:

myArray.map!{ rand(max) }

显然,有时列表中的数字不是唯一的。 如何确保我的列表只包含唯一的数字,而无需创建一个更大的列表,然后从中选择n个唯一的数字?

编辑:
如果可能的话,我真的想看到这样做没有循环。


FYI,我的答案展示了一种不需要循环就能工作的模式。 - Sam Saffron
15个回答

1

基于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

谢谢,肯特。


1

这种方法没有循环

Array.new(size) { rand(max) }

require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
  Array.new(size) { rand(max) }
end

=> 1.9114e-05 

0

这里有一个解决方案:

假设你想要生成的随机数在 r_minr_max 之间。对于列表中的每个元素,生成一个随机数 r,并使 list[i]=list[i-1]+r。这将给你单调递增的随机数,只要满足以下条件就保证唯一性:

  • r+list[i-1] 不会溢出
  • r > 0

对于第一个元素,你应该使用 r_min 而不是 list[i-1]。完成后,你可以打乱列表,使元素不那么明显地按顺序排列。

这种方法唯一的问题是当你超过 r_max 并且仍然需要生成更多元素时。在这种情况下,你可以将 r_minr_max 重置为已经计算过的两个相邻元素,并简单地重复该过程。这实际上是在一个没有使用过数字的区间内运行相同的算法。你可以一直这样做,直到列表被填满。


0

虽然提前知道最大值是好的,但你可以这样做:

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


0

方法一

使用肯特的方法,可以生成一个任意长度的数组,并将所有值保持在一个有限范围内:

# 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 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接