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

时间:2008-09-23 04:19:16

标签: ruby random

这是我到目前为止所做的:

myArray.map!{ rand(max) }

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

修改
我真的很想看到这个没有循环 - 如果可能的话。

15 个答案:

答案 0 :(得分:64)

(0..50).to_a.sort{ rand() - 0.5 }[0..x] 

(0..50).to_a可以替换为任何数组。 0是“minvalue”,50是“max value” x是“我想要多少值”

当然,x不可能大于max-min:)

扩展其运作方式

(0..5).to_a  ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 }  ==>  [0, 1, 2, 4, 3, 5]  # constant
[0,1,2,3,4,5].sort{  1 }  ==>  [5, 3, 0, 4, 2, 1]  # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 }   ==>  [1, 5, 0, 3, 4, 2 ]  # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ]   ==>  [1, 5, 0 ]

脚注:

值得一提的是,当这个问题最初于2008年9月被回答时,Array#shuffle要么不可用,要么我不知道,因此Array#sort中的近似值

结果就是有一连串的建议编辑。

所以:

.sort{ rand() - 0.5 }

使用

在现代ruby实现上可以更好,更短
.shuffle

此外,

[0..x]

可以更明显地用Array#take写成:

.take(x)

因此,在现代红宝石上生成一系列随机数的最简单方法是:

(0..50).to_a.shuffle.take(x)

答案 1 :(得分:23)

这使用Set:

require 'set'

def rand_n(n, max)
    randoms = Set.new
    loop do
        randoms << rand(max)
        return randoms.to_a if randoms.size >= n
    end
end

答案 2 :(得分:22)

为了让您了解速度,我运行了四个版本:

  1. 使用集合,就像Ryan的建议一样。
  2. 使用比需要稍大的数组,然后执行uniq!最后。
  3. 使用哈希,就像凯尔建议的那样。
  4. 创建一个所需大小的数组,然后随机排序,就像肯特的建议一样(但没有无关的“ - 0.5”,什么都不做)。
  5. 他们都是小规模的快速,所以我让它们各自创建一个1,000,000个数字的列表。以下是时间,以秒为单位:

    1. 设置:628
    2. Array + uniq:629
    3. 哈希:645
    4. fixed Array + sort:8
    5. 不,最后一个不是拼写错误。所以,如果你关心速度,并且数字从0到任何整数都可以,那么我的确切代码是:

      a = (0...1000000).sort_by{rand}
      

答案 3 :(得分:22)

Ruby 1.9提供了Array#sample方法,该方法返回一个元素或从Array中随机选择的元素。 #sample的结果将不包括两次相同的Array元素。

(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]

to_a.sort_by方法相比,sample方法似乎明显更快。在一个简单的场景中,我将sort_bysample进行了比较,得到了以下结果。

require 'benchmark'
range = 0...1000000
how_many = 5

Benchmark.realtime do
  range.to_a.sample(how_many)
end
=> 0.081083

Benchmark.realtime do
  (range).sort_by{rand}[0...how_many]
end
=> 2.907445

答案 4 :(得分:4)

是的,可以在没有循环的情况下执行此操作,并且无需跟踪已选择的数字。它被称为线性反馈移位寄存器:Create Random Number Sequence with No Repeats

答案 5 :(得分:2)

这个游戏怎么样?独特的随机数,无需使用Set或Hash。

x = 0
(1..100).map{|iter| x += rand(100)}.shuffle

答案 6 :(得分:1)

您可以使用哈希来跟踪到目前为止使用的随机数:

seen = {}
max = 100
(1..10).map { |n|
  x = rand(max)
  while (seen[x]) 
    x = rand(max)
  end
  x
}

答案 7 :(得分:1)

不是将项目添加到列表/数组,而是将它们添加到Set中。

答案 8 :(得分:1)

如果你有一个有限的可能随机数列表(即1到100),肯特的解决方案是好的。

否则没有循环就没有其他好方法可以做到。问题是如果你得到重复,你必须做一个循环。我的解决方案应该是高效的,并且循环不应该比数组的大小多得多(即如果你想要20个独特的随机数,平均可能需要25次迭代。)虽然迭代次数越来越多,但你的数字越多需要和更小的最大值。以下是我上面的代码修改,以显示给定输入需要多少次迭代:

require 'set'

def rand_n(n, max)
    randoms = Set.new
    i = 0
    loop do
        randoms << rand(max)
        break if randoms.size > n
        i += 1
    end
    puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
    return randoms.to_a
end

如果您愿意,我可以将此代码写入LOOK,更像是Array.map:)

答案 9 :(得分:1)

基于肯特弗雷德里克的解决方案,这就是我最终使用的方法:

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

谢谢肯特。

答案 10 :(得分:1)

使用此方法无循环

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

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

=> 1.9114e-05 

答案 11 :(得分:1)

[*1..99].sample(4) #=> [64, 99, 29, 49]

根据Array#sample个文档

使用随机和唯一索引选择元素

如果您需要SecureRandom(使用计算机噪声而不是伪随机数):

require 'securerandom'

[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]

答案 12 :(得分:0)

这是一个解决方案:

假设您希望这些随机数介于r_minr_max之间。对于列表中的每个元素,生成一个随机数r,并生成list[i]=list[i-1]+r。这将为您提供单调递增的随机数,保证唯一性,只要

  • r+list[i-1]不会过度流动
  • r&gt; 0

对于第一个元素,您将使用r_min而不是list[i-1]。完成后,您可以对列表进行洗牌,因此元素的顺序不是很明显。

此方法的唯一问题是当您查看r_max并且仍有更多元素要生成时。在这种情况下,您可以将r_minr_max重置为已计算的2个相邻元素,然后重复此过程。这有效地在没有已使用数字的区间内运行相同的算法。您可以继续这样做,直到填写完列表。

答案 13 :(得分: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

答案 14 :(得分:0)

方法1

使用Kent的方法,可以生成任意长度的数组,使所有值保持在有限的范围内:

# 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

方法2

从同一个肯特another answer修改的另一个可能效率更高的方法:

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