【问题标题】:Counting in binary ruby用二进制红宝石计数
【发布时间】:2015-05-22 15:03:23
【问题描述】:

我有两个数组

[a0 b0 c0]

[a1 b1 c1]

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

a0 + b1 + c1

a1 + b1 + c1

但不是a1 + a0 + b0 + c0

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

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

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

你会收到备忘录。

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

【问题讨论】:

    标签: arrays ruby combinations


    【解决方案1】:
    ▶ 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
    

    【讨论】:

    • 我知道有更好的办法!
    • 据我了解,结果也是按顺序计算的吧?从 000 开始到 111 结束
    • 是的。每个等级依次增加。
    • 做得很好(但我对reduce(&:<method>) 的任何事情都很感兴趣)!考虑map { |e| e.flatten.reduce :+ }
    • @CarySwoveland 我需要显示map(&:flatten) 的中间结果,这就是所有东西都被链接起来的原因。
    【解决方案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
    

    【讨论】:

    • 我喜欢这种方法,应该比我的快。但它太脏了:在"%03b" 中硬编码数组长度,重复sum += .. 而不是a = [arr1, arr2]sum += a[e.to_i][i] 等。至少我会使用[0,1].repeated_permutation(3).to_a 而不是在bina 周围跳舞。
    • @mudasobwa: [0,1].repeated_permutation(3).to_a,这个方法就是我要找的。你太棒了!
    • 不客气。您可能想检查我对您的想法的实施(我更新了答案。)
    【解决方案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
    

    【讨论】:

      【解决方案4】:

      这是实现@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的第一个元素被传递给block并赋值给block变量:

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

      【讨论】:

        猜你喜欢
        • 2013-01-13
        • 2012-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多