2012-07-01 2 views
2

Я знаю, что я могу выбрать случайный элемент из массива с помощью метода образца, но это оставляет возможность выбора элемента более одного раза. Сначала я мог сначала перетасовать массив, а затем перейти от первого к последнему элементу по порядку, но я понимаю, что это интенсивный процесс памяти, и я ищу менее интенсивный метод, если это возможно!Выбор случайного элемента из массива в Ruby ONCE ONLY

+0

Почему вы говорите, что перетасовка массива интенсивна в памяти? Самые наивные реализации перетасовывают массив на месте. – aromero

+0

Документы заявляют, что '# sample' не будет выбирать элементы более одного раза. '[1, 2] .sample (3)' возвращает '[1, 2]' или '[2, 1]'. – Nick

ответ

8

Шаркая массив не большой объем памяти. Ruby имеет стандартную реализацию тасования по умолчанию, она называется Array.shuffle!. Глядя на исходный код для этого вы можете увидеть (это C):

rb_ary_shuffle_bang(ary) 
    VALUE ary; 
{ 
    long i = RARRAY(ary)->len; 

    rb_ary_modify(ary); 
    while (i) { 
     long j = rb_genrand_real()*i; 
     VALUE tmp = RARRAY(ary)->ptr[--i]; 
     RARRAY(ary)->ptr[i] = RARRAY(ary)->ptr[j]; 
     RARRAY(ary)->ptr[j] = tmp; 
    } 
    return ary; 
} 

Эта реализация следует классический Fisher-Yates algorithm.

Итак:

  1. Перемешать массив на месте с помощью shuffle!. Сложность времени - O(n), дополнительной памяти не требуется.
  2. Итерации над массивом. Сложность времени - O(n), дополнительная память не требуется (только целое число для хранения текущего индекса).

В целом у вас есть то, что вам нужно без дополнительной памяти и временной сложности O(n).

0

Вы должны отслеживать те элементы, которые вы уже выбрали; что вы собираетесь делать, поместите их в свой собственный массив?

Shuffle and iterate. Это не занимает больше памяти, чем отслеживание уже выбранных элементов.

1

Если элементы массива действительно очень большой, то можно попробовать просто перетасовать список индексов и перебирать, что:

a = %w{a b c d e f g h} 

[*0...a.size].shuffle.each do |index| 
    puts a[index] 
end 
10

sample принимает аргумент:

[*(1..10)].sample(5) #=>[3, 4, 1, 8, 9] 

Ни один элемент не будет выбран в два раза.

+0

Не мой вопрос, но это то, чего я не знал, и может предвидеть, что буду полезным в будущем. +1 – KChaloux

Смежные вопросы