不用 +,-,*,/四则运算实现加法
2014-07-18 22:56
309 查看
问题:不用基本四则运算 + - * / 实现加法运算。
1.我的思路
我想到的一种办法是,使用二进制的位运算来避开使用基本四则运算。任何数字可以表示为二进制(废话,在内存里面,什么数不是二进制表示的?)。考虑 a + b,写成二制制形式:
011010010 + 101010110,两个数字的相同对应位分别进行计算,包括第 i 位的 ai,bi,和第 i - 1 位上的进位 ci-1,有以面两个东西需要求解:
1.当前位的数字即为:ai ^ bi ^ ci-1。
2.当前位的进位。为了求这个值,我们来看一下,只有 ai,bi,ci-1 这三个数字中,有二个或三个 1 时才会发生进位,即 ci 为1。为了得到 ci 的等式,我们列出值表如下:
ai bi
ci-1 异或 与
或 情况说明
0 1 1
0 0
1 两个1,进位为1
1
1 1 1
1 1
三个1,进位为1
0 0
0 0 0 0 零个1,进位为0
0 0
1 1 0 1 一个1,进位为0
...其它重复的情况就不写出,ai bi ci-1 可以认为只有四种取值:三者中有一个1,两个1,三个1,零个1
由上面的表,可以得出结果,能够进位的,只有:“异或值”等于“与值”,且“或值”为1。因此,可以写出下面的等式:
ci = ((ai ^ bi ^ c-1) == (ai | bi | ci-1) &&(1== ai | bi | ci-1))。
这种思路的源代码如下:
也许你想到了以下三点:
1.最高位(符号位)也使用上面的运算步骤,是否是正确的行为。
2.倒数第二位向最高位进位,并且最高位接受这个进位并参与计算,是否是正确的行为。
3.最高位计算时还有进位,也就是溢出了,而上面的运算步骤没有处理这个溢出,是否是正确的行为。
这些问题加起来其实是一个最本质的问题:为什么数字的补码形式直接运算是正确的运算?我们假定这是正确的,那么,上面的步骤必然也是正确的,因为我们完全是在模拟这个步骤。
如果你还是想知道为什么数字的补码运算是正确的运算,请参考我的另一篇文章:《补码的本质》。
2.拿来主义-何海涛的解法(在此)
我认为何海涛的这个算法比我的算法的层次要高,要更抽象,更注重整体,因而是更好的解法。我的算法是完全模拟运算,而何的解法虽然也是完全模拟,但层次比我的高,中间有些跳跃。来看一下他的解法。
15 + 185 可以看成以下三个计算:
1.不考虑进位
1 5 1 5
+ 8 5 8 5
_______
9 0 9 0
2.考虑所有的进位,将第一步中的结果,加上所有的进位;上面的计算中,所有的(或者称为总体的)进位包括,个位和百位上的进位,分别等于10,1000,那么,这个总体的进位等于 10 + 1000
因此,最终的结果等于 9090 + (10 + 1000)
3.递归完成第二步中新产生的相加式
这三个步骤对于所有的 a + b 都是成立的。有了这个思路,程序就好写了。关键在于,如何求那个总体的进位。其实这个也好求,放到二进制中去看,只有加数的两个二进制位(ai , bi )都是 1 时,才会有进位,因此,直接使用 (ai & bi) << (i+ 1) 即得到第 i 位上的进位。
而总体的进位即是对 (ai & bi) << (i+ 1) 进行求和,即总体进位c = (a1 & b1) << 2 + (a2 & b2) << 3 + (a3 & b3) << 4 + ....= (a & b)
<< 1,即直接使用 (a & b) << 1 即可得到总体进位。
上面的求解揭示了以下结论:
1.一位二进制 1 和 0 相加的第一步结果(不进位结果)是 1 ^ 0;多位二进制数 a 和 b 相加的第一步结果(不进位结果)是 a ^ b。
2.一位二进制 1 和 0 相加总体进位是 (1 & 0) << 1;多位二进制数 a 和 b 相加的第一步结果(不进位结果)是 (a & b)<<1。
据此,我们可以得到很大的一个启发:满足一位二进制的运算规则,都满足由二进制构成的数字的运算规则。
这里似乎是一个哲学的观点:(最小粒子)的规则 与(纯粹由这个最小粒子构成的系统)的规则是完全一致的。
给出这种实现的代码如下:
3.拿来主义-163博友(superddr)之法
这位网友的方法很精妙,说破了很简单,但能想到这个方法的人实在独具慧眼!
他利用了“数组下标”天生含有的加法功能。如 d[1] = *(d + 1)这里就有加法了,如果要实现 a + b 只需要将 a 和 b 纳入到数组下标中去计算即可。
先把握一点,数组下标中的加法是内存地址的加法,所以 a 和 b 必须都要与地址挂勾。我们先设 a 为(类型 A 的数组s的首地址)的值,如 0x000800110,也即 A *a = 0x000800110,那么 s[b]就表示取数组 s 的第 b 个元素,这个元素的地址是 &(s[b]),它是由首地址 &(s[0]) (这个值等于 a)向内存增长方向增加 b * sizeof(A) 字节的内存地址。 也即 a + b * sizeof(A) =
&(s[b]);如果我们令 sizeof(A ) 为 1,那么 &(s[b]) 就是 a + b 的结果,所以 A 类型可以选择为 char 类型。
下面是代码:
1.我的思路
我想到的一种办法是,使用二进制的位运算来避开使用基本四则运算。任何数字可以表示为二进制(废话,在内存里面,什么数不是二进制表示的?)。考虑 a + b,写成二制制形式:
011010010 + 101010110,两个数字的相同对应位分别进行计算,包括第 i 位的 ai,bi,和第 i - 1 位上的进位 ci-1,有以面两个东西需要求解:
1.当前位的数字即为:ai ^ bi ^ ci-1。
2.当前位的进位。为了求这个值,我们来看一下,只有 ai,bi,ci-1 这三个数字中,有二个或三个 1 时才会发生进位,即 ci 为1。为了得到 ci 的等式,我们列出值表如下:
ai bi
ci-1 异或 与
或 情况说明
0 1 1
0 0
1 两个1,进位为1
1
1 1 1
1 1
三个1,进位为1
0 0
0 0 0 0 零个1,进位为0
0 0
1 1 0 1 一个1,进位为0
...其它重复的情况就不写出,ai bi ci-1 可以认为只有四种取值:三者中有一个1,两个1,三个1,零个1
由上面的表,可以得出结果,能够进位的,只有:“异或值”等于“与值”,且“或值”为1。因此,可以写出下面的等式:
ci = ((ai ^ bi ^ c-1) == (ai | bi | ci-1) &&(1== ai | bi | ci-1))。
这种思路的源代码如下:
也许你想到了以下三点:
1.最高位(符号位)也使用上面的运算步骤,是否是正确的行为。
2.倒数第二位向最高位进位,并且最高位接受这个进位并参与计算,是否是正确的行为。
3.最高位计算时还有进位,也就是溢出了,而上面的运算步骤没有处理这个溢出,是否是正确的行为。
这些问题加起来其实是一个最本质的问题:为什么数字的补码形式直接运算是正确的运算?我们假定这是正确的,那么,上面的步骤必然也是正确的,因为我们完全是在模拟这个步骤。
如果你还是想知道为什么数字的补码运算是正确的运算,请参考我的另一篇文章:《补码的本质》。
2.拿来主义-何海涛的解法(在此)
我认为何海涛的这个算法比我的算法的层次要高,要更抽象,更注重整体,因而是更好的解法。我的算法是完全模拟运算,而何的解法虽然也是完全模拟,但层次比我的高,中间有些跳跃。来看一下他的解法。
15 + 185 可以看成以下三个计算:
1.不考虑进位
1 5 1 5
+ 8 5 8 5
_______
9 0 9 0
2.考虑所有的进位,将第一步中的结果,加上所有的进位;上面的计算中,所有的(或者称为总体的)进位包括,个位和百位上的进位,分别等于10,1000,那么,这个总体的进位等于 10 + 1000
因此,最终的结果等于 9090 + (10 + 1000)
3.递归完成第二步中新产生的相加式
这三个步骤对于所有的 a + b 都是成立的。有了这个思路,程序就好写了。关键在于,如何求那个总体的进位。其实这个也好求,放到二进制中去看,只有加数的两个二进制位(ai , bi )都是 1 时,才会有进位,因此,直接使用 (ai & bi) << (i+ 1) 即得到第 i 位上的进位。
而总体的进位即是对 (ai & bi) << (i+ 1) 进行求和,即总体进位c = (a1 & b1) << 2 + (a2 & b2) << 3 + (a3 & b3) << 4 + ....= (a & b)
<< 1,即直接使用 (a & b) << 1 即可得到总体进位。
上面的求解揭示了以下结论:
1.一位二进制 1 和 0 相加的第一步结果(不进位结果)是 1 ^ 0;多位二进制数 a 和 b 相加的第一步结果(不进位结果)是 a ^ b。
2.一位二进制 1 和 0 相加总体进位是 (1 & 0) << 1;多位二进制数 a 和 b 相加的第一步结果(不进位结果)是 (a & b)<<1。
据此,我们可以得到很大的一个启发:满足一位二进制的运算规则,都满足由二进制构成的数字的运算规则。
这里似乎是一个哲学的观点:(最小粒子)的规则 与(纯粹由这个最小粒子构成的系统)的规则是完全一致的。
给出这种实现的代码如下:
3.拿来主义-163博友(superddr)之法
这位网友的方法很精妙,说破了很简单,但能想到这个方法的人实在独具慧眼!
他利用了“数组下标”天生含有的加法功能。如 d[1] = *(d + 1)这里就有加法了,如果要实现 a + b 只需要将 a 和 b 纳入到数组下标中去计算即可。
先把握一点,数组下标中的加法是内存地址的加法,所以 a 和 b 必须都要与地址挂勾。我们先设 a 为(类型 A 的数组s的首地址)的值,如 0x000800110,也即 A *a = 0x000800110,那么 s[b]就表示取数组 s 的第 b 个元素,这个元素的地址是 &(s[b]),它是由首地址 &(s[0]) (这个值等于 a)向内存增长方向增加 b * sizeof(A) 字节的内存地址。 也即 a + b * sizeof(A) =
&(s[b]);如果我们令 sizeof(A ) 为 1,那么 &(s[b]) 就是 a + b 的结果,所以 A 类型可以选择为 char 类型。
下面是代码:
相关文章推荐
- 不用加减乘除实现加法运算
- 不用加减乘除实现加法运算
- 不用‘+’实现加法运算
- 不用加减乘除实现加法运算
- 不用+-*/实现加法运算
- 不用 + -× /实现加法运算
- 不用加法操作符(+)实现加法运算
- 不用+-*/实现加法运算
- 不用加减乘除实现加法运算
- LintCode-第一题:A+B problem (不用“+”等运算符)(位运算实现加法)
- 剑指offer 不用加减乘除做加法(位运算实现)
- 1.A+B问题,不用+实现加法运算
- 加法的实现-不用加减乘除运算
- 不用加减乘除实现加法运算
- c笔试面试 之 不用加法操作(用逻辑运算)实现两个正整数的除法
- 面试算法题:不用+、-、×、÷数字运算符做加法 (位运算实现加法操作)
- 只使用++运算实现加法,减法,乘法,除法PHP实现
- JAVA--第3周实验--任务2--实现二维数组的一种加法运算(编程思想)
- 不用+-*/%实现整数的+-*/%运算
- 数论之不用除法运算,如何实现A/3