Как мне сгенерировать список из n уникальных случайных чисел в Ruby? - PullRequest
34 голосов
/ 23 сентября 2008

Это то, что я имею до сих пор:

myArray.map!{ rand(max) }

Очевидно, однако, что иногда числа в списке не являются уникальными. Как я могу убедиться, что мой список содержит только уникальные номера без необходимости создавать больший список, из которого я затем просто выбираю n уникальных номеров?

Edit:
Мне бы очень хотелось, чтобы это было сделано без цикла - если это вообще возможно.

Ответы [ 14 ]

1 голос
/ 23 сентября 2008

Вы можете использовать хеш для отслеживания случайных чисел, которые вы использовали до сих пор:

seen = {}
max = 100
(1..10).map { |n|
  x = rand(max)
  while (seen[x]) 
    x = rand(max)
  end
  x
}
0 голосов
/ 19 октября 2012

Метод 1

Используя подход Кента, можно генерировать массив произвольной длины, сохраняя все значения в ограниченном диапазоне:

# 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

Другой, возможно более эффективный , метод, модифицированный из того же Кента другой ответ :

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 :)
0 голосов
/ 23 сентября 2008

Насколько заранее известно максимальное значение, вы можете сделать это так:

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 голосов
/ 23 сентября 2008

Вот одно из решений:

Предположим, вы хотите, чтобы эти случайные числа были между 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 на 2 соседних элемента, которые вы уже вычислили, и просто повторить процесс. Это эффективно запускает тот же алгоритм в течение интервала, где уже нет используемых чисел. Вы можете продолжать делать это, пока у вас не будет заполнен список.

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