【问题标题】:Better solution for multiplying numbers without multiply operator没有乘法运算符的乘法数字的更好解决方案
【发布时间】:2021-02-13 02:40:21
【问题描述】:

我为“破解编码面试”中的以下问题提出了这个解决方案,我认为它比我在他们的解决方案中看到的更快、更优雅,但不确定它是否适用于所有情况。谁能告诉我是否遗漏了一些边缘案例?

CTCI 的原始问题是(关于递归和动态规划的问题 8.5):

编写一个递归函数以在不使用 * 运算符的情况下将两个正整数相乘。您可以使用加法、减法和位移,但应尽量减少这些操作的数量。

我的解决办法是:

def foo(a, b, mult = 0):
    if a == 0 or b == 0:
        return 0

    if (a & 1) == 1:
        return (b << mult) + foo(a >> 1, b, mult + 1)

    return foo(a >> 1, b, mult + 1)

谁能告诉我是否遗漏了什么?

【问题讨论】:

  • 我不会过多介绍建议的解决方案。该代码的作者甚至把第一个幼稚的实现弄错了:D

标签: python recursion bit-manipulation


【解决方案1】:

您可以使用位移来执行以 2 为底的乘法:

def multiply(A,B):
    result = 0
    while A:
        if A&1: result = result + B # Add shifted B if last bit of A is 1
        A >>= 1 # next bit of A
        B <<= 1 # shift B to next bit position
    return result

print(multiply(7,13)) # 91

如果不允许使用&amp; 运算符if (A&gt;&gt;1)&lt;&lt;1 != A: 将执行与if A&amp;1: 相同的操作(即测试最后一位)。

如果您查看乘法的位组合,您可以看到位移的作用:

#  bitPosition   A=13 (1101)   B=7 (111)  A*B=91 (1011011)
#
#       0        1                  111         111     7
#       1        0                 1110          
#       2        1                11100       11100    28
#       3        1               111000      111000    56
#                                            ------    --
#                                           1011011    91

由此,编写递归版本相对容易:

def multiply(A,B):
    if A == 0: return 0
    result = multiply(A>>1,B)<<1  # get the higher bit product
    if A&1: result = result + B   # add the last bit multiplier (B)
    return result

【讨论】:

    【解决方案2】:

    位移位可用于乘以 2 的倍数。它有助于以二进制形式写下您的乘法,以了解其有何帮助。

    考虑5 * 7,二进制是101 * 111。应用分布,你得到:

    101 * 111 = 111 * (100 + 001)
              = 111 * 100 + 111 * 001
              = 7 * 4 + 7 * 1
              = 7 * 2² + 7*2⁰
    

    一般来说,给定一个乘法X * Y,您可以将X 划分为n 2 的幂的和,并将乘法写为2^k * Y 形式的各项之和。您系列中出现的术语对应于您在X 的二进制表示中具有1 的那些索引。

    因此,递归算法是从右到左查看您的一个操作数的二进制数字,并将您看到 1 的项相加。

    def mult(a, b):
        if a & 1:
            return b + mult(a >> 1, b << 1)
        if a:
            return mult(a >> 1, b << 1)
        else:
            return 0
    
    print(mult(9, 7))  # 63
    

    您的算法非常接近,但请注意您不需要保留 mult 参数。相反,您只需在每次查看下一个数字时将右操作数移动 1

    另请注意,如果a &lt; 0,这不会终止,但这很容易通过进行正乘法并在a 为负数时加回符号来解决。

    【讨论】:

      【解决方案3】:

      使用乘法的基础,即 5 x 3 是“5 加 3 次”(5 + 5 + 5)或“3 加 5 次”(3 + 3 + 3 + 3 + 3)。

      这个解有1个加法和1个减法:

      def multiply(a, b):
          if b == 1:
              return a
          return a + multiply(a, b-1)  # addition and subtraction
      
      multiply(5, 3)
      # 15
      multiply(7, 4)
      # 28
      multiply(2000, 1000)
      # 2000000
      

      您可以通过将 b 指定为两个数字中较小的一个来减少递归发生的次数 - 将其放在函数的开头:

      a, b = max(a, b), min(a, b)
      # or
      a, b = (a, b) if a >= b else (b, a)
      

      将这些结合起来并制作一个几乎是一条线:

      def multiply(a, b):
          a, b = (a, b) if a >= b else (b, a)
          return a if b == 1 else a + multiply(a, b-1)
      
      multiply(2, 1_000_000)
      # 2000000
      

      【讨论】:

      • 我的另一个答案是非递归的(所以我删除了它),而是使用重复加法的简单单行:def multiply(a, b): return sum(a for _ in range(b))
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-04
      • 1970-01-01
      • 2011-01-18
      • 1970-01-01
      相关资源
      最近更新 更多