У меня есть несколько массивов с неизвестным количеством элементов, например

a = []
a << [:a, :c, :e]
a << [:b, :f, :g, :h, :i, :j]
a << [:d]

Результат должен быть что-то вроде ~ (мне все равно детали из-за округления и т. д.)

r = [:b, :a, :f, :g, :d, :c, :h, :i, :e, :j]

Вот как я думаю это можно сделать

Сначала нам нужно расширить / распределить одинаково элементы в каждом массиве на одинаковую длину, поэтому мы получаем что-то вроде

a << [nil, :a, nil, :c, nil, :e]
a << [:b, :f, :g, :h, :i, :j]
a << [nil, nil, :d, nil, nil]

Затем мы чередуем их, как обычно

r = a.shift
a.each { |e| r = r.zip(e) }
r = r.flatten.compact

Моя текущая проблема заключается в том, как равномерно (насколько это возможно) распределить эти элементы по массиву? Может быть один массив с 4 элементами, а другой с 5, но, вероятно, первым должен быть самый большой.

Конечно, было бы неплохо посмотреть, есть ли другой способ добиться этого :)

-1
davispuh 26 Мар 2013 в 07:35
Ваш вопрос действительно не имеет смысла. Почему [nil, :a, nil, :c, nil, :e] вместо [:a, :c, :e, nil, nil]?
 – 
the Tin Man
26 Мар 2013 в 08:25
Потому что необходимо равномерно распределить элементы (так, чтобы пространство между элементами было почти равным) внутри расширенного массива, а не просто расширенного массива.
 – 
davispuh
26 Мар 2013 в 18:35

1 ответ

Лучший ответ

Я бы использовал для этого сортировку, основанную на позиции индекса элемента, разделенной на размер массива, плюс некоторое смещение на основе идентификатора массива, чтобы все было согласовано (если вам не нужна согласованность, вы можете вместо этого использовать небольшое случайное смещение ).

a = [:a,:b]
b = [:c]
c = [:d,:e,:f]
d = [:g:,:h,:i,:j]

def sort_pos array, id
  (1..array.size).map { |i| (i - 0.5 + id/1000.0)/(array.size + 1e-6) }
end

# Combine all the arrays with their sort index, assigning ids to each array for consistency.
# Depending on how you receive these arrays, this structure can be built up programatically, 
# as long as you add an array plus its sort index numbers at the same time
combined = (a + b + c + d).zip( sort_pos(a, 1) +  sort_pos(b, 2) +  sort_pos(c, 3) +  sort_pos(d, 4) )


# Extract the values from the original arrays in their new order
combined.sort_by { |zipped| zipped[1] }.map { |zipped| zipped[0] }

=> [:g, :d, :a, :h, :e, :i, :b, :f, :j, :c]

В Ruby может быть более чистый способ сделать это. . . но я думаю, что конечный результат - это то, что вам нужно - «ровное» сочетание нескольких массивов.

Если вы заботитесь о равномерности смеси только со статистической точки зрения (т.е. со временем она «справедлива»), вы можете просто сделать это:

(a+b+c+d).shuffle

=> [:g, :b, :i, :c, :a, :h, :e, :j, :f, :d]
2
Neil Slater 26 Мар 2013 в 18:08
Спасибо, только что проверил и действительно работает. Сначала казалось сложным решением, но на самом деле это не так. Я подожду несколько дней, прежде чем принять, посмотрим, найдет ли кто-нибудь другое решение :) И я не могу использовать перемешивание, потому что каждый раз он случайным образом распределяет элементы, и порядок больше не логичен.
 – 
davispuh
26 Мар 2013 в 18:54
На самом деле кажется, что он также работает только с def sort_pos array (1..array.size) .map {| i | (i - 0,5) /array.size} конец
 – 
davispuh
26 Мар 2013 в 19:21
Ненавижу то, что не может правильно показать код в комментариях. в любом случае я создал суть gist.github.com/davispuh/5246323, почему именно это id магия? без них вроде нормально работает :)
 – 
davispuh
26 Мар 2013 в 20:03
«Идентификатор» на самом деле должен называться «смещением» и предназначен только для обеспечения контроля над связями при сортировке. Вероятно, это чрезмерная разработка и вам не нужна - вы можете безопасно удалить параметр id, если вас устраивает сортировка по умолчанию в Ruby, которая, я думаю, должна сохранять исходный порядок при наличии связей для каждого элемента (например, если вы добавляете два 4-элементных массивы, каждый элемент из второго, который вы добавляете, будет отображаться сразу после каждого элемента из первого)
 – 
Neil Slater
26 Мар 2013 в 20:47