二进制计数 ruby
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
你收到备忘录了。
我想知道如何在 ruby 中执行此操作。我在想一个复杂的解决方案,我在二进制字符串中计数,每次计数我 "select" 数组中的正确元素。因为我想要所有可能的组合 (2^n),我可以在一行或接近一行中编写代码吗?
这是一种蛮力方法。我确信有一种更优雅的方法可以用 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
▶ 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
这是实现@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]]
(即从高位到低位提取位),我们会得到相同的答案。
我有两个数组
[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
你收到备忘录了。
我想知道如何在 ruby 中执行此操作。我在想一个复杂的解决方案,我在二进制字符串中计数,每次计数我 "select" 数组中的正确元素。因为我想要所有可能的组合 (2^n),我可以在一行或接近一行中编写代码吗?
这是一种蛮力方法。我确信有一种更优雅的方法可以用 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
▶ 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
这是实现@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]]
(即从高位到低位提取位),我们会得到相同的答案。