Ответ 1
Для этого используется 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
Это то, что у меня есть до сих пор:
myArray.map!{ rand(max) }
Очевидно, однако, иногда числа в списке не уникальны. Как я могу убедиться, что мой список содержит только уникальные номера, не создавая более крупный список, из которого я тогда просто выбираю n уникальных номеров?
Edit:
Мне бы очень хотелось, чтобы это было сделано без петли - если это вообще возможно.
Для этого используется 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
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a
можно заменить любым массивом.
0 - "minvalue", 50 - "максимальное значение",
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 года, что Array#shuffle
был либо недоступен, либо еще не был известное мне, следовательно, приближение в Array#sort
И в этом есть заграждение, предлагаемое для этого.
Итак:
.sort{ rand() - 0.5 }
Может быть лучше и короче выражено в современных реализациях ruby с использованием
.shuffle
Кроме того,
[0..x]
Может быть более очевидно написано с Array#take
как:
.take(x)
Таким образом, самый простой способ создать последовательность случайных чисел на современном рубине:
(0..50).to_a.shuffle.take(x)
Чтобы дать вам представление о скорости, я выполнил четыре версии этого:
Все они быстро развиваются в небольших масштабах, поэтому у меня их каждый составил список из 1 000 000 номеров. Вот несколько раз, в секундах:
И нет, это последнее не опечатка. Поэтому, если вам нужна скорость, и это нормально, чтобы числа были целыми числами от 0 до любого, тогда мой точный код был:
a = (0...1000000).sort_by{rand}
Ruby 1.9 предлагает метод образца Array #, который возвращает элемент или элементы, случайным образом выбранные из массива. Результаты #sample не будут включать один и тот же элемент массива дважды.
(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
По сравнению с подходом to_a.sort_by
метод sample
оказывается значительно быстрее. В простом сценарии я сравнил sort_by
с sample
и получил следующие результаты.
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
Да, это возможно сделать без цикла и не отслеживать, какие номера были выбраны. Он называется Линейным регистром сдвига обратной связи: Создать последовательность случайных чисел без повторений
Как насчет игры на этом? Уникальные случайные числа без необходимости использования Set или Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
Вы можете использовать хэш для отслеживания случайных чисел, которые вы использовали до сих пор:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Вместо добавления элементов в список/массив добавьте их в набор.
Если у вас есть конечный список возможных случайных чисел (т.е. от 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, если вы хотите:)
Основываясь на решении 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
Спасибо Кенту.
Нет циклов с помощью этого метода
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
Вот одно из решений:
Предположим, вы хотите, чтобы эти случайные числа находились между r_min
и r_max
. Для каждого элемента вашего списка создайте случайное число r
и сделайте list[i]=list[i-1]+r
. Это даст вам случайные числа, которые монотонно увеличиваются, гарантируя уникальность при условии, что
r+list[i-1]
не работает над потокомr
> 0Для первого элемента вы должны использовать r_min
вместо list[i-1]
. Как только вы закончите, вы можете перетасовать список, чтобы элементы были не так явно в порядке.
Единственная проблема с этим методом - когда вы переходите r_max
и все еще имеете больше элементов для генерации. В этом случае вы можете reset r_min
и r_max
на 2 смежных элемента, которые вы уже вычислили, и просто повторите этот процесс. Это эффективно работает по тому же алгоритму в течение интервала, где уже нет номеров. Вы можете продолжать делать это, пока у вас не будет списка.
Насколько хорошо известно заранее максимальное значение, вы можете сделать так:
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
, когда все значения будут высланы из колоды.
Используя подход 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
Другой, возможно более эффективный, метод, модифицированный из того же 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 :)