2015-10-14 3 views
0

Мне нужно заполнить массив Ruby N случайными числами. Тем не менее, мне нужно убедиться, что они являются X номерами друг от друга.Установить случайные числа в массиве с интервалом

Например, мне нужно, чтобы заполнить массив с 5 номеров, сгенерированных случайным образом в диапазоне от 1 до 100, но гарантируя, что ни один номер не ближе, чем 3.

Так это справедливо:

[1, 4, 7, 22, 84] # the differences between the numbers are >= 3 

Это недопустимо:

[1, 2, 50, 70, 90] # "1" and "2" are too close together 

Любые идеи?

+1

Должно ли это быть возрастающей последовательностью? Имеют ли только последовательные цифры? Является ли '[1, 4, 1, 4, 1]' okay (при условии, что он был создан случайным образом? – sawa

+0

Что такое случайная последовательность в этом случае? Я могу представить два способа рисования случайного набора чисел M между 1 и N так что никакие два числа не находятся внутри D друг от друга. Первый - перечислить все такие наборы чисел, а затем выбрать один случайным образом. (В зависимости от N, M и D может быть довольно много наборов.) Второй способ было бы случайным образом нарисовать числа N, а затем проверить, не находится ли какая-либо пара внутри D друг от друга. Если нет, у вас есть случайное множество, если это так, отбросьте набор и нарисуйте еще один случайный набор из N, продолжая до тех пор, пока не пройдет множество test (cont ...) –

+0

(... продолжение) Опять же, это может занять некоторое время. (Все зависит от значений трех параметров.) Оба этих метода приводят к случайному набору. Если, однако, оба этих подхода являются вычислительно неосуществимыми, и вы прибегаете к какой-либо процедуре * ad hoc *, вы, вероятно, не будете рисовать набор случайным образом и не поймут, как может быть искажен ваш образец. –

ответ

1
i = 1 
while i < 100 do 
    final = rand(i+3..i+20) 
    (a ||= []).push(final) 
    i +=20 
end 
p a 

Некоторые случайные примеры.

[18, 39, 48, 65, 83] 
[9, 27, 56, 66, 100] 
[10, 29, 46, 68, 86] 
[11, 34, 57, 64, 86] 
[3, 31, 46, 70, 99] 
[16, 36, 43, 75, 92] 
+0

Вы добавляете дополнительное ограничение в последовательность. Это один из способов понять смысл плохого вопроса. Это ошибка OP, что вопрос не ясен, но я не уверен, что это было предназначено OP. – sawa

+0

Однако не используйте глобальные переменные для таких очень временных вещей. – sawa

+0

@sawa, вы правы, ответьте на обновления, tks. –

3

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

def gen_rand_arr(arr, diff) 
    sample = arr.sample 
    arr.delete_if {|x| (x - sample).abs < diff } 
    sample 
end 

Затем используйте это нравится:

arr = (1..100).to_a 
result = [] 
1.upto(5) { result << gen_rand_arr(arr, 3) } 
p result.sort 
+0

Это должно быть ' = diff'. – Stefan

+0

Обратите внимание, что ваш массив кандидатов должен быть достаточно большим, чтобы гарантировать 5 результатов, в этом случае не менее 1..21 (если я не ошибаюсь). – Stefan

+0

@Stefan Вы правы, текущий код не проверяет правильность ввода. Это должно быть более защитным, чтобы его можно было использовать в реальности, однако я буду держать его здесь просто, просто показывая эту идею. –

0

Как насчет этой случайной версии один?

sample_result = [] 
arr = (1..100).to_a 
diff = 15 
result_len = 5 
1.upto(result_len) { 
    sample = arr.sample 
    sample_result << sample 
    (sample-diff..sample+diff).each do 
     |i| arr.delete(i) 
    end 
} 
p sample_result.sort 
0

Вот еще один способ приблизиться к этому.

Наименьшие цифры можно делать:

a = Array.new(5) { |i| i * 3 + 1 } 
#=> [1, 4, 7, 10, 13] 

Мы можем увеличивать эти цифры (или эту группу номеров) 100 - 13 = 87 раз, пока мы не попали в конец:

87.times { a[0..-1] = a[0..-1].map(&:next) } 
a #=> [88, 91, 94, 97, 100] 

Это максимально допустимые числа.

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

def spread(size, count, step) 
    arr = Array.new(count) { |i| i * step + 1 } 
    (size - arr.last).times do 
    i = rand(0..arr.size) 
    arr[i..-1] = arr[i..-1].map(&:next) 
    end 
    arr 
end 

5.times do 
    p spread(100, 5, 3) 
end 

Выход:

[21, 42, 56, 73, 86] 
[6, 21, 45, 61, 81] 
[20, 33, 48, 63, 81] 
[12, 38, 55, 75, 90] 
[11, 29, 50, 71, 86] 
[26, 44, 64, 79, 95] 

К сожалению, мы должны циклу несколько раз для получения окончательных значений. Это не только медленно, но и приводит к распределению неравномерной:

Distribution 1

Было бы лучше, чтобы определить 6 случайных смещений, что сумма в 87 и перемещать элементы соответственно. Почему 6? Поскольку смещения являются расстояниями между нашими 5 числами, т. Е.: Метод

 n1  n2     n3  n4   n5    
|<-o1->|<--o2-->|<-------o3------->|<-o4->|<----o5---->|<----o6---->| 
0                 max 

Этот помощник возвращает такие смещения: (украдено из here)

def offsets(size, count) 
    offsets = Array.new(count) { rand(0..size) }.sort 
    [0, *offsets, size].each_cons(2).map { |a, b| b - a } 
end 

o = offsets(87, 5) #=> [3, 0, 15, 4, 64, 1] 
o.inject(:+)  #=> 87 

Мы можем добавить смещения к нашему исходному массиву номер:

def spread(size, count, step) 
    arr = Array.new(count) { |i| i * step } 
    offsets(size - arr.last, count).each_with_index do |offset, index| 
    arr[index..-1] = arr[index..-1].map { |i| i + offset } 
    end 
    arr 
end 

5.times do 
    p spread(99, 5, 3) 
end 

Выход:

[1, 14, 48, 60, 94] 
[12, 46, 54, 67, 72] 
[8, 14, 35, 40, 45] 
[27, 30, 51, 81, 94] 
[63, 79, 86, 90, 96] 

Как и следовало ожидать, это приводит к случайному распределению:

Distribution 2

Это выглядит лучше. Обратите внимание, что эти результаты основаны на нуле.

Мы можем даже удалить начальный массив и рассчитать окончательные значения из смещений. Начиная с первого смещения, нам просто нужно добавить каждое следующее смещение плюс 3:

def spread(size, count, step) 
    offs = offsets(size - (count - 1) * step, count) 
    (1...offs.size).each { |i| offs[i] += offs[i-1] + step } 
    offs[0, count] 
end 
Смежные вопросы