Вот что у меня есть на данный момент:
myArray.map!{ rand(max) }
Однако очевидно, что иногда числа в списке не уникальны. Как я могу убедиться, что мой список содержит только уникальные номера, без необходимости создавать более крупный список, из которого я затем просто выбираю n уникальных номеров?
Редактировать:
Я бы очень хотел, чтобы это было сделано без цикла - если это вообще возможно.

Вы можете использовать хеш для отслеживания случайных чисел, которые вы использовали до сих пор:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Вместо того, чтобы добавлять элементы в список / массив, добавьте их в Set.
Это использует 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
Любой способ сделать это без цикла? Есть ли способ сделать это с помощью карты?
Предполагая, что Ruby's Set не позволяет вставлять дубликаты, randoms будет менее случайным, чем rand(max), поскольку вы просто выбрасываете числа, которые «вам не нравятся».
@ Аллен, это неверно. Алгоритм не выбрасывает числа «вам не нравятся». Примером этого может быть отбрасывание числа, потому что оно больше или меньше определенного значения. Он пропускает номер, если он уже был включен в набор, что является требованием варианта использования. Это не делает набор чисел менее случайным. Он останавливается, когда набирает достаточно номеров.
@ Тони, ты прав, что я был неправ. Не уверен, что я курил в 2014 году.
Вот одно из решений:
Предположим, вы хотите, чтобы эти случайные числа находились между 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 соседних элемента, которые вы уже вычислили, и просто повторить процесс. Это эффективно запускает тот же алгоритм в интервале, где еще не используются числа. Вы можете продолжать делать это, пока не заполните список.
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a можно заменить любым массивом.
0 - минимальное значение, 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)
разве не [0,1,2,3,4,5] .shuffle проще?
хехехе :) Я должен был сказать @Federico. Молодец :))
Посмотрите на дату @Federico, у ruby не было такого метода в начале 2008 года с 1.8.6 ruby-doc.org/core-1.8.6/Array.html, было добавлено в 1.8.7, насколько я могу судить. svn.ruby-lang.org/repos/ruby/tags/v1_8_7/ChangeLog
(1.8.7, вероятно, в то время вышла, но я, вероятно, еще не обновился до нее или узнал о добавлении .shuffle)
(0..50) .to_a.shuffle.take (x) молодец !!
Если у вас есть динамический верхний предел, этот метод может создавать очень большие массивы. Думаю, лучше бы использовать n.times.map { rand(lower_limit..upper_limit) }
@DanielRichter, и такой подход, конечно же, не дает никаких гарантий уникальности.
Если у вас есть конечный список возможных случайных чисел (например, от 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
Я мог бы написать этот код, чтобы он больше походил на Array.map, если хотите :)
Насколько приятно заранее знать максимальное значение, вы можете сделать это следующим образом:
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, когда все значения будут исчерпаны из колоды.
Чтобы дать вам представление о скорости, я выполнил четыре версии этого:
Все они быстры в небольших масштабах, поэтому я попросил их создать список из 1 000 000 номеров. Вот время в секундах:
И нет, это не опечатка. Итак, если вам важна скорость и это нормально, если числа должны быть целыми числами от 0 до любого другого, то мой точный код был:
a = (0...1000000).sort_by{rand}
Регистр сдвига с линейной обратной связью должен завершиться менее чем за секунду.
`посторонний" - 0,5 ", который ничего не делает` Пробовали? Без - 0.5 rand () всегда возвращает> 0, а когда> 0, a всегда меньше, чем b, а затем вместо перетасовки ... вы просто получаете список в том порядке, в котором их сравнивал базовый алгоритм. Что в некоторых случаях возвращает список в указанном порядке. (0..10).to_a.sort{ Random.rand() } на tryruby.org/levels/1/challenges/0 дает мне вход == выход. Итак, вам нужно ДЕЙСТВИТЕЛЬНО - 0,5 или сам случайный ничего не делает
Кроме того, вы действительно отвечаете на крошечные отступления в SO-ветке через шесть лет после того, как это произошло ?!
Как насчет того, чтобы поиграть на этом? Уникальные случайные числа без использования Set или Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
Мне почему-то кажется, что эти числа будут значительно менее случайными, чем при выборе 100 уникальных чисел из диапазона от 0 до 10000.
yerp, его нужно улучшить, чем выше число, тем меньше шансов, что вы его получите. Но, безусловно, есть способ заставить что-то в этом роде работать.
Да, это можно сделать без цикла и без отслеживания выбранных номеров. Это называется регистром сдвига с линейной обратной связью: Создать последовательность случайных чисел без повторов
Основываясь на решении Кента Фредрика, приведенном выше, я в конечном итоге использовал следующее:
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
Спасибо, Кент.
Ruby 1.9 предлагает метод Array # sample, который возвращает элемент или элементы, случайно выбранные из массива. Результаты #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
Есть идеи относительно сроков по сравнению с тем, что сообщает гленн макдональд?
Используя подход Кента, можно сгенерировать массив произвольной длины, сохраняя все значения в ограниченном диапазоне:
# 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
Другой, возможно более эффективным, метод, модифицированный из другой ответ того же Кента:
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 :)
Никаких циклов с этим методом
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
[*1..99].sample(4) #=> [64, 99, 29, 49]
Согласно документам Array#sample,
The elements are chosen by using random and unique indices
Если вам нужен SecureRandom (который использует компьютерный шум вместо псевдослучайных чисел):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
К вашему сведению, мой ответ показывает шаблон, который работает без цикла