Rails: случайный уникальный массив фиксированной длины - PullRequest
1 голос
/ 30 июня 2019

Как я могу гарантировать уникальность в этом массиве, поддерживая его длину на 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 ]

7 голосов
/ 30 июня 2019

Когда количество возможных значений небольшое (например, 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, чтобы обеспечить уникальность значений во второй версии.

2 голосов
/ 30 июня 2019

Просто из любопытства, используя 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}>
2 голосов
/ 30 июня 2019

Использование 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 и использовать shuffle или sample для получения случайных чисел.

Использование 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] 

Использование образец

> (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] 
2 голосов
/ 30 июня 2019

Один из способов - создать диапазон чисел от 0 до 10 и затем перемешайте их, чтобы получить уникальные случайные числа.

Вы можете преобразовать этот диапазон в массив, используя to_a, и перетасовать их, используя shuffle

Вы можете сделать что-то вроде этого:

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

[0..4] даст вам первые 5 перемешанных элементов.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...