Мне нравится разделять счет в массиве из n позиций.
Допустим, мой счет равен 11, а массив имеет размер 12. Тогда мне нравится иметь некоторый массив, который заполнен, например, 11 единицами или 10 единицами и 2 половинками (0,5). В итоге он должен составить 11.
Тогда возможные оценки:
size = 12
possible_scores = (0..size).step(0.5).to_a
Я могу создать массив из 12 позиций:
scores = Array.new(size) {0}
Я мог бы выбрать случайное значение из следующих возможных значений:
[0, 0.5, 1].sample
Я ищу эффективный способ получения случайного массива без большого количества переменных состояния, если это возможно. Я уже пытался сделать это в цикле while:
while score < 0
И уменьшите значение оценки со случайным значением и отследите установленные позиции массива. Но это стало довольно грязный кусок кода.
Есть идеи как решить это? Спасибо!
Редактировать:
Для этого примера я хочу массив, который суммирует до 11. Таким образом, любой из
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.5, 0.5]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1]
Или любую комбинацию, которая суммирует до 11.
3 ответа
Параметры и переменные
Данный:
tot
, желаемая сумма, целое или нечетное кратное0.5
; а такжеsize
, общее количество0
,0.5
и1
, общее количествоtot
, с требованиемsize >= tot
,
Мы определяем три переменные:
n0
равно количеству нулей;n0pt5_pairs
равно числу пар0.5
'; а такжеn1
равно числу единиц.
Случай 1: tot
является целым числом
Мы требуем:
0 <= n0pt5_pairs <= [tot, size-tot].min
Обратите внимание, потому что n1 = tot - n0pt5_pairs
, 2 * n0pt5_pairs + n1 = n0pt5_pairs + tot > size
if n0pt5_pairs > size-tot
. Таким образом, общее число 0.5
и единиц превышает size
, если количество пар 0.5
превышает size-tot
.
Учитывая значение для n0pt5_pairs
, которое удовлетворяет вышеуказанному требованию, n0
и n1
определяются:
n1 = tot - n0pt5_pairs
n0 = size - 2*n0pt5_pairs - n1
= size - tot - n0pt5_pairs
Поэтому мы можем случайным образом выбрать случайную тройку [n0, 2*n0pt5_pairs, n1]
следующим образом:
def random_combo(size, tot)
n0pt5_pairs = rand(1+[tot, size-tot].min)
[size-tot-n0pt5_pairs, 2*n0pt5_pairs, tot-n0pt5_pairs]
end
Например:
arr = random_combo(17, 11)
#=> [3, 6, 8]
Это используется для генерации массива
arr1 = [*[0]*arr[0], *[0.5]*arr[1], *[1]*arr[2]]
#=> [0, 0, 0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1, 1, 1, 1, 1]
Которые мы тасуем
arr1.shuffle
#=> [1, 0, 0.5, 1, 0.5, 0, 1, 1, 0, 1, 1, 1, 0.5, 0.5, 1, 0.5, 0.5]
Примечание arr1.size #=> 17
и arr.sum #=> 11
.
Случай 2: tot
- кратное 0,5
Если
tot = n + 0.5
Где n
является целым числом, каждая комбинация 0
, 0.5
и 1
будет иметь по крайней мере один 0.5
. Поэтому мы можем вычислить число 0
и 1
вместе с числом <{em>} {em>, превышающим один . Для этого мы просто уменьшаем tot
на 0.5
(делая его равным целому числу) и size
на единицу, используем generate_for_integer
для решения этой проблемы, а затем для каждых трех- массив элементов, возвращаемый этим методом, увеличивает количество 0.5
на единицу.
def generate(size, tot)
return nil if size.zero?
is_int = (tot == tot.floor)
tot = tot.floor
size -= 1 unless is_int
n0pt5_pairs = rand(1+[tot, size-tot].min)
[*[0]*(size-tot-n0pt5_pairs), *[0.5]*(2*n0pt5_pairs + (is_int ? 0 : 1)),
*[1]*(tot-n0pt5_pairs)].
shuffle
end
ge = generate(17, 10)
#=> [0, 1, 0, 1, 0.5, 0.5, 0, 0.5, 0.5, 1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5]
ge.size #=> 17
ge.sum #=> 10.0
go = generate(17, 10.5)
#=> [0.5, 0.5, 0.5, 1, 0, 0.5, 0.5, 1, 1, 0.5, 1, 1, 0.5, 1, 0.5, 0.5, 0]
go.size #=> 17
go.sum #=> 10.5
Ruby предоставляет все, что вам нужно, не нужно писать никакого алгоритмического кода. Array#repeated_combination
здесь ваш друг:
[0, 0.5, 1].
repeated_combination(12). # 91 unique variant
to_a. # unfortunately it cannot be lazy
shuffle. # to randomize array outcome
detect { |a| a.sum == 11 }.
shuffle # to randomize numbers inside array
#⇒ [0.5, 0.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Sidenote: можно избежать необходимости тасовать дважды (как массив сгенерированных массивов, так и полученный массив), используя Array#repeated_permutation
, но это резко увеличит загрузку памяти и время выполнения.
Мне нравится ответ Кэри Свовеланд, но на самом деле это можно сделать, не создавая множество решений.
Давайте рассмотрим несколько примеров.
Учитывая размер = 6 и счет = 3, без тасования, это возможные выходные данные (пронумерованы слева по причинам, которые станут очевидными):
i ones halves zeroes
0│ 1 1 1 0 0 0 3 0 3
1│ 1 1 ½ ½ 0 0 2 2 2
2│ 1 ½ ½ ½ ½ 0 1 4 1
3│ ½ ½ ½ ½ ½ ½ 0 6 0
Данный размер = 6 и оценка = 3,5:
i ones halves zeroes
0│ 1 1 1 ½ 0 0 3 1 2
1│ 1 1 ½ ½ ½ 0 2 3 1
2│ 1 ½ ½ ½ ½ ½ 1 5 0
Данный размер = 11 и оценка = 4,5:
i ones halves zeroes
0│ 1 1 1 1 ½ 0 0 0 0 0 0 4 1 6
1│ 1 1 1 ½ ½ ½ 0 0 0 0 0 3 3 5
2│ 1 1 ½ ½ ½ ½ ½ 0 0 0 0 2 5 4
3│ 1 ½ ½ ½ ½ ½ ½ ½ 0 0 0 1 7 3
4│ ½ ½ ½ ½ ½ ½ ½ ½ ½ 0 0 0 9 2
Данный размер = 12 и оценка = 11:
i ones halves zeroes
0│ 1 1 1 1 1 1 1 1 1 1 1 0 11 0 1
1│ 1 1 1 1 1 1 1 1 1 1 ½ ½ 10 2 0
Можете ли вы увидеть шаблоны? После небольшого почесывания подбородка мы обнаруживаем следующие факты:
Количество возможных выходных данных 𝑛 для заданных размера и показателя определяется как:
𝑛 = мин. (⌊ оценка ⌋, размер - ⌈ оценка ⌉) + 1
С ростом the число единиц уменьшается. Количество единиц определяется как:
count (1) = ⌊ счет ⌋ - 𝑖
С увеличением, количество половинок (½) увеличивается. Количество половин определяется как:
count (½) = 2 (𝑖 + mod ( оценка , 1))
Другими словами, это 2𝑖 + 1, если счет имеет дробную часть, или 2𝑖 в противном случае.
При увеличении decreases число нулей уменьшается, определяемое как:
count (0) = размер - ⌈ оценка ⌉ - 𝑖
Имея в виду эти четыре факта, мы можем сгенерировать любой из outputs возможных выходных данных случайным образом, выбрав случайный 𝑖, где 0 ≤ 𝑖 <𝑛:
random = случайный ([0..𝑛))
Эти факты легко перевести на код Ruby:
n = [score.floor, size - score.ceil].min + 1
i = rand(n)
num_ones = score.floor - i
num_halves = 2 * (i + score % 1)
num_zeroes = (size - score.floor) - i
Теперь нам просто нужно немного его почистить и поместить в функцию, которая принимает size
и score
в качестве аргументов, поворачивает num_ones
, num_halves
и {{X4} } в массив 0
с, 0.5
с и 1
с, и перемешивает результат:
def generate(size, score)
init_ones = score.floor
init_zeroes = size - score.ceil
i = rand([init_ones, init_zeroes].min + 1)
num_ones = init_ones - i
num_halves = 2 * (i + score % 1)
num_zeroes = init_zeroes - i
[ *[1]*num_ones, *[0.5]*num_halves, *[0]*num_zeroes ].shuffle
end
generate(6, 3.5)
# => [0.5, 1, 0, 0.5, 0.5, 1]
Вы можете увидеть результат в действии на repl.it: https://repl.it/@jrunning/UnpleasantDimpledLegacysystem (Обратите внимание, что когда вы запускаете его на repl.it, вывод выводится очень медленно. Это только потому, что repl.it выполняет код Ruby на сервере и передает результат обратно в браузер.)
Похожие вопросы
Новые вопросы
ruby
Ruby - это многоплатформенный динамический объектно-ориентированный интерпретируемый язык с открытым исходным кодом. Тег [ruby] предназначен для вопросов, связанных с языком Ruby, включая его синтаксис и его библиотеки. Вопросы Ruby on Rails должны быть помечены [ruby-on-rails].