【问题标题】:A + B without arithmetic operators, Python vs C++A + B 没有算术运算符,Python vs C++
【发布时间】:2015-06-07 17:33:10
【问题描述】:

我试图解决一个老问题:

编写一个将两个 [integer] 数字 A 和 B 相加的函数。不应使用 + 或任何算术运算符。

最好的解决方案是这样的,引用自“LintCode-A+B Problem”:

对于任何底数的 a + b,我们可以将加号视为两部分: 1. a + b 不带进位; 2. a+b产生的进位。 a+b 等于第 1 部分加上第 2 部分。如果第 1 部分+第 2 部分产生更多进位,我们可以重复此过程,直到没有进位。

我能理解这个算法,一切似乎都很好,所以我在lintcode 上测试了它,代码粘贴在下面。

class Solution:
    """
    @param a: The first integer
    @param b: The second integer
    @return:  The sum of a and b
    """
    def aplusb(self, a, b):

        while b != 0:
            carry = a & b
            a = a ^ b
            b = carry << 1

        return a

但令人惊讶的是,它在测试用例[100, -100] 中给了我Time Limit Exceeded 错误。所以我在本地运行它并为每个循环打印 a、b:

(-8, 8)
(-16, 16)
(-32, 32)
(-64, 64)
(-128, 128)
(-256, 256)
(-512, 512)
(-1024, 1024)
(-2048, 2048)
(-4096, 4096)
(-8192, 8192)
(-16384, 16384)
(-32768, 32768)
(-65536, 65536)
(-131072, 131072)
...

计算是正确的,所以我认为这个算法不适用于这样的输入,但是当我用 C++ 编写相同的算法时,它就可以了:

class Solution {
public:
    int aplusb(int a, int b) {
        while (b!=0){
            int carry = a & b;
            a = a^b; 
            b = carry << 1;
        }
        return a;
    }
};

我不知道具体应该问什么,基本上问题是:

  1. 为什么 C++ 给出正确的输出 0 而 Python 没有?
  2. 如果我使用 Python,如何修改此算法以使其正常工作?

【问题讨论】:

    标签: python c++ algorithm bit-manipulation


    【解决方案1】:

    -4 的二进制 2 的补码表示是

    ...11100
    

    是的,我的意思是在左边有无数个1;这是一个二进制重复数字。从技术上讲,4 也是一个重复数字:

    ...00100
    

    它只是在左边重复0

    你的加法问题是

       ...11100
    +  ...00100
    --------------------
       ...00000
    

    运算符^&lt;&lt;&amp; 可以毫无问题地计算无限多的二进制数字,但问题是有无限多的进位,而您正在计算它们一个数字时间。这永远不会结束。

    因此,您必须认识到该算法何时会陷入这种情况并采取其他措施来解决此问题。


    你不会在 C/C++ 中遇到这个问题,因为例如,如果 int 是 32 位,那么除了最右边的 31 位之外的所有数字都会折叠成一个位,所以它其余的是否一次全部进行。

    但是,从技术上讲,左移 int 的含义是将值作为整数,而不是作为位模式,因此如果两者都调用未定义的行为 carry 的最高有效位永远不同,因为那样 carry &lt;&lt; 1 会产生溢出)。

    【讨论】:

    • 有符号整数溢出是未定义的行为。 OP 很幸运,C 版本可以正常工作。
    • 你能解释一下“然后除了最右边的 31 位数字之外的所有数字都被折叠成一个位,所以它会一次完成剩下的所有进位。”更详细?谢谢!
    • @laike9m:从相反的方向考虑可能更容易:当您从int 转换为(2 的补码)二进制数字时,您必须签署扩展:最重要的数字展开为无限重复的数字。
    • 为了获得与 C++ 版本类似的行为,您可以提供具有有限二进制表示的 numpy 整数:aplusb(numpy.int32(100), numpy.int32(-100)) 给出0 符合预期。
    【解决方案2】:

    问题是负数,或者,它们是如何表示的。在 Python 中,整数具有任意精度,而 C++ 整数是 32 位或 64 位。所以在 Python 中,你必须处理负数,例如减法,单独,或手动限制位数。

    【讨论】:

    • C++ 可以有 8 位或 16 位整数吗?上次我记得,整数有两个以上的大小in C++ numeric types
    • 我想知道-x是否算作这里被禁止的算术运算符之一;只是像这样翻转符号以将其更改为减法问题可能是作弊。
    • @Smac89 正如您从两个链接中看到的,整数的大小比 32 位或 64 位要多。例如,C++ 语言定义了 unsigned char、unsigned short int、unsigned int 和 unsigned long 必须支持的最小范围。没有指定最大尺寸。一个平台可以使用 128 位来表示所有这些类型。
    【解决方案3】:

    根据@Hurkyl 的精彩解释,我使用python 实现无限二进制补码表示这一事实逐步介绍了a=4b=-4 的算法:

    Step 0:
    
    a = ...(0)...000100
    b = ...(1)...111100
    
    carry = a & b = ...(0)...000100
    a = a ^ b = ...(1)...111000
    b = carry << 1 = ...(0)...001000
    
    Step 1:
    
    a = ...(1)...111000
    b = ...(0)...001000
    
    carry = a & b = ...(0)...001000
    a = a ^ b = ...(1)...110000
    b = carry << 1 = ...(0)...010000
    
    Step 2:
    
    a = ...(1)...110000
    b = ...(0)...010000
    
    carry = a & b = ...(0)...010000
    a = a ^ b = ...(1)...100000
    b = carry << 1 = ...(0)...100000
    

    很明显,需要一个有效的截止值来模拟像 32 位有符号二进制补码整数之类的东西。一旦进位位冒泡超过最高位,算法就需要停止。以下似乎有效:

    MAX_BIT = 2**32
    MAX_BIT_COMPLIMENT = -2**32
    
    def aplusb(a, b):
    
        while b != 0:
            if b == MAX_BIT:
                return a ^ MAX_BIT_COMPLIMENT
            carry = a & b
            a = a ^ b
            b = carry << 1
    
        return a
    

    结果:

    >>> aplusb(100,-100)
    0
    >>> aplusb(100,-99)
    1
    >>> aplusb(97,-99)
    -2
    >>> aplusb(1000,-500)
    500
    >>> aplusb(-1000,8000)
    7000
    

    【讨论】:

    • 如果您采用这种方法,那么您可以考虑动态确定截止值,而不是硬编码上限;例如以便您的程序在添加两个 87 位数字时能够给出正确的结果,或者如果添加两个 4 位数字,它可以提前停止。例如见bit_length。我认为,正如您目前编写的那样,如果数字大于 32 位,您的实现将再次陷入循环。
    【解决方案4】:

    如果禁止 1 位二进制数学运算 (^),请选择一元!

    from itertools import chain
    
    def unary(x):
        "Unary representation of x"
        return ''.join(['x' for _ in range(0,x)])
    
    def uplus(x, y):
        "Unary sum of x and y"
        return [c for c in chain(x,y)]
    
    def plus(i, j):
        "Return sum calculated using unary math"
        return len(uplus(unary(i), unary(j)))
    

    【讨论】:

      【解决方案5】:

      那是因为 python 通常不使用 32 位有符号整数。

      见:ctypes.c_int32

      接受的解决方案:

      class Solution:
      """
      @param a: The first integer
      @param b: The second integer
      @return:  The sum of a and b
      """
      def aplusb(self, a, b):
          import ctypes
          a = ctypes.c_int32(a).value
          a = ctypes.c_int32(a).value
          while b != 0:
              carry = ctypes.c_int32(a & b).value
              a = ctypes.c_int32(a ^ b).value
              b = ctypes.c_int32(carry << 1).value
      
          return a
      

      【讨论】:

      • 或者我们可以只像这样导入 c_int32:from ctypes import c_int32 并像这样使用:a = c_int32(a).value
      【解决方案6】:

      我的解决方案:

      def foo(a, b):
      """iterate through a and b, count iteration via a list, check len"""
          x = []
          for i in range(a):
                  x.append(a)
          for i in range(b):
                  x.append(b)
          print len(x)
      

      如前所述,按位更好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-14
        • 1970-01-01
        • 2011-03-28
        • 2012-10-10
        • 2013-07-12
        • 1970-01-01
        • 2015-01-04
        • 1970-01-01
        相关资源
        最近更新 更多