繁体   English   中英

二进制红宝石计数

[英]Counting in binary ruby

我有两个数组

[a0 b0 c0]

[a1 b1 c1]

我想计算两者之间所有可能的和。 每个列插槽的可能总和仅包含1个元素。 例如,可能的总和是

a0 + b1 + c1

要么

a1 + b1 + c1

但不是a1 + a0 + b0 + c0

换句话说,示例中的总和将具有3个插槽,每个插槽只有两个数组中的1个元素。 从我的角度来看,这似乎是二进制计数,其中每个插槽只能从两个数字(0或1)中取1。 所以在这个例子中

000表示总和中的所有元素都来自第一个数组

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

你得到备忘录。

我想知道如何在红宝石中做到这一点。 我在考虑一个复杂的解决方案,我在二进制字符串中计数,并为每个计数从数组中“选择”正确的元素。 由于我希望所有可能的组合(2 ^ n),我可以在一行中或接近一行的地方进行编码吗?

▶ 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用ruby编写的解决方案:

[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
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

这是蛮力的方法。 我敢肯定,有一种方法可以用lambda做到这一点,但我的大脑在一天中的这个时候并不能这样工作:

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

这是实现@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]给出二进制值的第i个最高有效位:

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]] (即从高到低提取位),我们将得到相同的答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM