Мне нравится разделять счет в массиве из 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.

2
Smek 1 Май 2019 в 15:35

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  
3
Cary Swoveland 2 Май 2019 в 20:36

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, но это резко увеличит загрузку памяти и время выполнения.

3
Aleksei Matiushkin 1 Май 2019 в 15:51

Мне нравится ответ Кэри Свовеланд, но на самом деле это можно сделать, не создавая множество решений.

Давайте рассмотрим несколько примеров.

Учитывая размер = 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. Количество возможных выходных данных 𝑛 для заданных размера и показателя определяется как:

    𝑛 = мин. (⌊ оценка ⌋, размер - ⌈ оценка ⌉) + 1

  2. С ростом the число единиц уменьшается. Количество единиц определяется как:

    count (1) = ⌊ счет ⌋ - 𝑖

  3. С увеличением, количество половинок (½) увеличивается. Количество половин определяется как:

    count (½) = 2 (𝑖 + mod ( оценка , 1))

    Другими словами, это 2𝑖 + 1, если счет имеет дробную часть, или 2𝑖 в противном случае.

  4. При увеличении 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 на сервере и передает результат обратно в браузер.)

3
Jordan Running 2 Май 2019 в 19:29