2015-05-22 4 views
1

У меня есть два массиваCounting в двоичном рубин

[a0 b0 c0] 

[a1 b1 c1] 

Я хочу, чтобы рассчитать все возможные суммы между ними. Возможная сумма состоит всего из 1 элемента для каждого слота столбца. Например, возможно сумма

a0 + b1 + c1 

или

a1 + b1 + c1 

, но не a1 + a0 + b0 + c0

Другими словами, сумма в примере будет иметь 3 слота, каждый из которых содержит только один элемент из двух массивы. С моей точки зрения это выглядит как подсчет в двоичном формате, где каждый слот может принимать только 1 из двух чисел (0 или 1). Таким образом, в этом примере

000 означает, что все элементы в сумме поступают из первого массива

sum(000) = a0 + b0 + c0. 
sum(111) = a1 + b1 + c1 
sum(010) = a0 + b1 + c0 

вы получите напоминание.

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

ответ

6
▶ a1 = [11,12,13] 
#⇒ [11, 12, 13] 
▶ b1 = [21,22,23] 
#⇒ [21, 22, 23] 
▶ a1.zip(b1).reduce(&:product).map(&:flatten) 
#⇒ [[11, 12, 13], [11, 12, 23], [11, 22, 13], [11, 22, 23], 
#⇒ [21, 12, 13], [21, 12, 23], [21, 22, 13], [21, 22, 23]] 
▶ a1.zip(b1).reduce(&:product).map(&:flatten).map { |e| e.reduce &:+ } 
#⇒ [36, 46, 46, 56, 46, 56, 56, 66] 

UPD Просто из любопытства это решение @ pangpang написано на рубин:

[0,1].repeated_permutation([a1.length, a2.length].min).map do |bits| 
    bits.each_with_index.reduce(0) do |memo, (e, i)| 
    memo + (e.zero? ? a1[i] : a2[i]) 
    end 
end 
+0

Я знал, что есть лучший способ! –

+0

и, по моему мнению, результат тоже в порядке с правом подсчета? начиная с 000, заканчивающегося на 111 – Crone

+1

Да, это так. Каждый ранг увеличивается в свою очередь. – mudasobwa

1

Это подход грубой силы. Я уверен, что есть способ более элегантный способ сделать это с помощью лямбда, но мой мозг не работает таким образом в это время суток:

2.1.2 :003 > a=[1,2,3] 
=> [1, 2, 3] 
2.1.2 :005 > b=[4,5,6] 
=> [4, 5, 6] 
2.1.2 :006 > 1.downto(0) do |outer| 
2.1.2 :007 >  1.downto(0) do |middle| 
2.1.2 :008 >  1.downto(0) do |inner| 
2.1.2 :009 >   puts (outer==1 ? b[0] : a[0]) + (middle==1 ? b[1] : a[1]) + (inner==1 ? b[2] : a[2]) 
2.1.2 :010?>  end 
2.1.2 :011?>  end 
2.1.2 :012?> end 
15 
12 
12 
9 
12 
9 
9 
6 
2
arr1 = [0,0,0] 
arr2 = [1,1,1] 

(0..(2**arr1.length-1)).each do |i| 
    sum = 0 
    bina = "%0#{arr1.length}b" % i # convert int to binary 
    bina.split("").each_with_index do |e,i| 
     e.to_i == 0 ? sum += arr1[i] : sum += arr2[i] 
    end 
    puts "#{bina} and #{sum}" 
end 

выход:

000 sum 0 
001 sum 1 
010 sum 1 
011 sum 2 
100 sum 1 
101 sum 2 
110 sum 2 
111 sum 3 
+0

Мне нравится этот подход, он должен быть быстрее моего. Но он слишком грязный: длина жестко заданного массива в '"% 03b "', повторяя 'sum + = ..' вместо' a = [arr1, arr2] 'и' sum + = a [e.to_i] [i] 'и т. д. По крайней мере, я бы использовал' [0,1] .repeated_permutation (3) .to_a' вместо танцев вокруг 'bina'. – mudasobwa

+0

@mudasobwa: '[0,1] .repeated_permutation (3) .to_a', этот метод является тем, что я ищу. Ты удивительный! – pangpang

+0

Добро пожаловать. Возможно, вы захотите проверить мою реализацию своей идеи (я обновил ответ). – mudasobwa

1

Вот еще один способ реализовать ответ @ pangpang. Я также попытался объяснить основную идею этого подхода.

код

def perm_sums(arr0, arr1) 
    sz = arr0.size 
    at = [arr0, arr1].transpose 
    (0...2**sz).map { |n| sz.times.reduce(0) { |t,i| t + at[i][n[i]] } } 
end 

Пример

arr0 = [1,2,3] 
arr1 = [6,7,8] 

perm_sums(arr0, arr1) #=> [6, 11, 11, 16, 11, 16, 16, 21] 

Объяснение

В приведенном выше примере:

sz = arr0.size #=> 3 

at = [arr0, arr1].transpose #=> [[1, 6], [2, 7], [3, 8]] 

Это, конечно же, arr0.zip(arr1).

e0 = (0...2**sz).map #=> #<Enumerator: 0...8:map> 

Мы можем рассматривать элементы этого счетчику путем преобразования его в массив:

e0.to_a #=> [0, 1, 2, 3, 4, 5, 6, 7] 

Первый элемент e0 передается к блоку и присвоенного переменной блока:

n = e0.next #=> 0 

n=0 не так интересно, так как его двоичное представление - это все нулевые биты. Давайте вместо того, чтобы смотреть на n=3:

n = e0.next #=> 1 
n = e0.next #=> 2 
n = e0.next #=> 3 

e1 = sz.times #=> #<Enumerator: 3:times> 
e1.to_a   #=> [0, 1, 2] 

Расчеты блоков используют Fixnum#[]. Двоичное представление n=3 отображается в строке:

3.to_s(2).rjust(sz,'0') #=> "011" 

3[i] дает Ith наиболее значащий разряд двоичного значения:

3[0] #=> 1 
3[1] #=> 1 
3[2] #=> 0 

Блок вычисления поступим следующим образом. reduce устанавливает переменный блок t к начальному значению 0, а затем передает каждый из трех элементов e1 к блоку:

t = 0 
i = e1.next  #=> 0 
t + at[i][n[i]] #=> 0 + at[0][n[0]] => [1, 6][3[0]] => [1, 6][1] => 6 

t = 6 
i = e1.next  #=> 1 
t + at[i][n[i]] #=> 1 + at[1][3[1]] => 1 + [2,7][1] => 8 

t = 8 
i = e1.next  #=> 2 
t + at[i][n[i]] #=> 8 + at[2][n[2]] => 8 + [3,8][3[2]] => 8 + [3,8][0] => 11 

i = e1.next 
    #=> StopIteration: iteration reached an end 

Таким образом, число 3 отображается на 11. Другие вычисления выполняются аналогичным образом.

Обратите внимание, что мы получим тот же ответ, если мы заменим at[i][n[i]] на at[i][n[sz-1-i]] (т. Е. Извлечение бит с высокого на низкий).

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