Rails:固定长度的随机唯一数组

时间:2019-06-30 03:46:48

标签: ruby-on-rails ruby ruby-on-rails-5

如何在保持数组长度为5的同时确保此数组的唯一性?

def fixed
  5.times.collect { SecureRandom.random_number(10) }
end

这种行为似乎很奇怪:

5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 2, 3, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 3]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 3, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 4]
5.times.collect.uniq { SecureRandom.random_number(10) }
# => [0, 1, 2, 3]

4 个答案:

答案 0 :(得分:7)

当可能值的数量很少时(例如您的示例中为10),那么我将生成一个包含所有选项的数组,然后随机选择一个sample条目:

(0..9).to_a.sample(5)

如果可能值的数量巨大,那么首先生成所有值肯定不是一个选择。然后我将生成一个随机值,只要数组中没有足够的条目即可:

require 'set'
values = Set.new
until values.length == 5 do
  values.add(SecureRandom.random_number(1_000_000))
end
values.to_a

请注意,我正在使用Set来确保第二个版本中值的唯一性。

答案 1 :(得分:2)

  

一种方法是生成从010的数字范围,然后   然后将它们混洗以获得唯一的随机数。

您可以使用to_a将该范围转换为Array并使用shuffle对其进行随机播放

您可以执行以下操作:

 (0..10).to_a.shuffle[0..4] # => [8, 6, 1, 9, 10]

[0..4]将为您提供前5个经过改组的元素。

希望有帮助。

答案 2 :(得分:2)

使用SecureRandom

def fixed
  unique_numbers = []

  5.times.collect do 
    loop do
      number = SecureRandom.random_number(10)
      break number unless unique_numbers.include?(number)
    end
  end
end

如果要生成1到10之间的唯一数字,则可以创建1到10的数组,并使用shufflesample来获取随机数。

使用shuffle

> (0...10).to_a.shuffle.take(5)
=> [4, 0, 1, 3, 7] 
> (0...10).to_a.shuffle.take(5)
=> [6, 2, 3, 9, 1] 
> (0...10).to_a.shuffle.take(5)
=> [9, 2, 5, 8, 4] 
> (0...10).to_a.shuffle.take(5)
=> [5, 0, 6, 8, 7] 
> (0...10).to_a.shuffle.take(5)
=> [2, 7, 1, 5, 0] 

使用sample

> (1..10).to_a.sample(5)
=> [4, 6, 3, 2, 7] 
> (1..10).to_a.sample(5)
=> [5, 8, 2, 3, 7] 
> (1..10).to_a.sample(5)
=> [2, 5, 6, 1, 3] 
> (1..10).to_a.sample(5)
=> [8, 5, 10, 9, 3] 
> (1..10).to_a.sample(5)
=> [8, 1, 5, 3, 4]

您还可以将SecureRandom自定义随机数生成器作为参数传递给sample

> (1..10).to_a.sample(5, random: SecureRandom)
 => [6, 3, 4, 7, 10] 
> (1..10).to_a.sample(5, random: SecureRandom)
 => [7, 4, 8, 1, 5] 
> (1..10).to_a.sample(5, random: SecureRandom)
 => [8, 3, 9, 5, 10] 
> (1..10).to_a.sample(5, random: SecureRandom)
 => [6, 8, 9, 2, 1] 
> (1..10).to_a.sample(5, random: SecureRandom)
 => [9, 10, 1, 8, 2] 

答案 3 :(得分:2)

出于好奇,使用Enumerable#cycle无限生成器。

MAX = 10
SIZE = 5

[MAX].cycle.inject(Set.new) do |acc, max|
  break acc if acc.size >= SIZE
  acc << SecureRandom.random_number(max)
end
#⇒ #<Set: {2, 1, 7, 0, 9}>

,甚至使用通用loop

loop.each_with_object(Set.new) do |_, acc|
  break acc if acc.size >= SIZE
  acc << SecureRandom.random_number(10)
end
#⇒ #<Set: {2, 6, 7, 1, 3}>