Подтвердить что ты не робот

Как создать список n уникальных случайных чисел в Ruby?

Это то, что у меня есть до сих пор:

myArray.map!{ rand(max) }

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

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

4b9b3361

Ответ 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

Ответ 2

(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)

Ответ 3

Чтобы дать вам представление о скорости, я выполнил четыре версии этого:

  • Использование наборов, таких как предложение Райана.
  • Использование массива немного больше необходимого, а затем выполнение uniq! в конце.
  • Использование хэша, как предложил Кайл.
  • Создание массива требуемого размера, а затем его сортировка случайным образом, например, предложение Кента (но без постороннего "- 0,5", которое ничего не делает).

Все они быстро развиваются в небольших масштабах, поэтому у меня их каждый составил список из 1 000 000 номеров. Вот несколько раз, в секундах:

  • Наборы: 628
  • Array + uniq: 629
  • Хэш: 645
  • fixed Array + sort: 8

И нет, это последнее не опечатка. Поэтому, если вам нужна скорость, и это нормально, чтобы числа были целыми числами от 0 до любого, тогда мой точный код был:

a = (0...1000000).sort_by{rand}

Ответ 4

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

Ответ 6

Как насчет игры на этом? Уникальные случайные числа без необходимости использования Set или Hash.

x = 0
(1..100).map{|iter| x += rand(100)}.shuffle

Ответ 7

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

seen = {}
max = 100
(1..10).map { |n|
  x = rand(max)
  while (seen[x]) 
    x = rand(max)
  end
  x
}

Ответ 8

Вместо добавления элементов в список/массив добавьте их в набор.

Ответ 9

Если у вас есть конечный список возможных случайных чисел (т.е. от 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, если вы хотите:)

Ответ 10

Основываясь на решении 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

Спасибо Кенту.

Ответ 11

Нет циклов с помощью этого метода

Array.new(size) { rand(max) }

require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
  Array.new(size) { rand(max) }
end

=> 1.9114e-05 

Ответ 12

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

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

Ответ 13

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

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, когда все значения будут высланы из колоды.

Ответ 14

Метод 1

Используя подход 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

Метод 2

Другой, возможно более эффективный, метод, модифицированный из того же 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 :)