Lintcode_1.A+B Problem(由位操作进行加法计算想到的乘法及幂运算的思路)

今天刷lintcode遇到了这道easy的A+B Problem,题目的要求是不使用‘+’算出整型数据a和b的和,主要考察的是位操作与四则运算的相互转化。


算法分析

不用‘+’计算数据的和,乍一看一点思路也没有。事实上,对于任何十进制数的加减,我们一般对其做加法时从末尾开始依次相加,逢10进1。同理,对于计算机中常用的二进制数,我们也是从末尾开始逐位相加,不过是逢2进1,借1当2。

事实上有:0+0=0,0+1=1+0=1,1+1=10;对比C语言的XOR操作我们知道:0^0=0,0^1=1^0=1,1^1=0;到这里想必大家已经可以看出共同点,即——当不考虑二进制数的进位时,两数的相加可以看作是二数的异或操作

再考虑C语言的&操作可得:0&0=0,0&1=1&0=0,1&1=1,即只有二进制数的位同为1时&操作得到1,也即真值为真。而相加时只有都为1时发生进位。所以我们发现——考虑二进制数的进位,&操作可以标明需要进位的位

对于需要进位的位,我们很容易通过将原数*2^1次完成,而这又可以由左移<<1完成。

根据上述,我们可以得到: (a+b)=(a^b)+(a&b<<1)。(其中,右边需要多次计算,直到a+b的各位已经完全考虑完进位,以得到最后的答案。)

例如:

0110+0100=1010

0110^0100=0010,(0110&0100)<<1=1000;

0010^1000=1010, (0010&1000)<<1=1010;

1010^1010=0000, (0000&1010)<<1=0000.        

可知答案为1010.


C++代码如下(采用递归): 

class Solution {
public:
    /**
     * @param a: An integer
     * @param b: An integer
     * @return: The sum of a and b 
     */
    int aplusb(int a, int b) {
    while (b == 0)
		return a;
	while (b != 0)
	{
		int x, y;
		x = a; y = b;
		a = x ^ y;
		b = (x & y) << 1;
		return aplusb(a^b, (a&b) << 1);
	}
        // write your code here
    }
};

分析可知该算法复杂度为O(n),其中n为int类型数据的宽度。


乘法及幂运算(multiplication& exponentiation

解决完这个问题以后,我们很容易想到,如果是乘法和幂运算是不是也可以用位操作代替‘*’或者pow函数呢?

事实上,根据乘法的结合律我们知道a*(b+c)=a*b+a*c,同样的,对于二进制的运算也有相同的规则。例如:

4*6=4*(4+2)=(1*2^2+0*2^1+0*2^0)*(1*2^2+1*2^1+0*2^0)=1*2^4+1*2^3=24.

一般的,我们有:

http://images.cnitblog.com/blog/558323/201309/30000132-d89e0bf436a249869b5509233c7e22fd.jpg

二进制数的乘法运算,实际上就是对于每一位的权值的相加。

对于幂运算,如果不运用pow函数实际上很难操作,这里使用的是暴力求解,算法复杂度较高,有时间我会进一步优化算法。


C代码如下(将三者全部整理在一起):

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
	/**
	* @param a: An integer
	* @param b: An integer
	* @return: The sum of a and b
	* @supplement:The mutiplication and exponentiation of a and b
	*/
int aplusb(int a, int b) {
	if(b == 0)
		return a;
	while (b != 0)
	{
		int x, y;
		x = a; y = b;
		a = x ^ y;
		b = (x & y) << 1;
		return aplusb(a^b, (a&b) << 1);
	}
}

int multiply(int a, int b){
	int sum = 0;
	while (b == 0)
	{
		return 0;
	}
	while (b != 0)
	{
		int i;
		for (i = 0;b != 0; ++i)
		{
			if ((b & 1) == 1)
			{
				sum = aplusb(sum, a << i);
				b >>= 1;
			}
			else b >>= 1;
		}
	}
	return sum;

}

int exponentiation(int a, int b){
	int exp = 1;
	if(b != 0)
	{
		int i;
		for ( i = 0; b != 0; i++)
		{
			if ((b & 1) == 1)
			{
				int temp = 1;
				int j;
				int y = 1;
				if (i == 0)
				{
					exp = a;
					continue;
				}
				for ( j = 0; j < i; j++)
				{
					temp = multiply(temp, 2); //i次循环使得temp=2^i
				}
				for ( j = 0; j < temp; j++)//temp个a相乘
				{
					y = multiply(y , a);
				}
				exp = multiply(exp, y);//exp记录每一项,再与新的一项相乘
				b >>= 1;
			}
			else b >>= 1;
		}
	}
	return exp;
}


int main(){
	int a, b;
	printf("Enter the numbers to be calculated:");
	scanf("%d,%d", &a, &b);
	printf("The sum of a and b is %d.", aplusb(a, b));
	printf("\nThe multiplication of a and b is %d.", multiply(a, b));
	printf("\nThe exponentiation result of a and b is %d", exponentiation(a, b));
	system("pause");
	return 0;
}

 

最后

由一道easy的LintcodeProblem所引出的思考其实并不那么浅薄,由这道题目我们发现其实位操作进一步揭体现了二进制的特点。

 

THANK YOU.

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值