前提
不知道有什么巧妙解法,这里说一个通解,那就是用位运算实现加减乘除。
为什么要用位运算实现加减乘除?
了解计算机通过半导体实现加减乘除的逻辑 不装了,其实根本就是面试可能会问 加法这里可能会啰嗦一些,后面基于加法就没有这么痛苦了
加法
加法,其实是由两种状态组成的: 第一是各个数位都没有进位,这种最简单,就是将对应位置的数值直接相加即可 第二是则是数位中有进位,这种也不难,首先是将对应位置数值加起来之后,要模上进制数,然后考虑进位数怎么计算,回忆下加法的过程,进位数其实就是当前数字除以进制数的下整数,然后再将其加到前面一个数位中,迭代执行,直到没有进位。 这就引发了一个问题,既然进位数需要通过除法得到,但是除法又不允许使用,这是否陷入了困境? 幸运的是,二进制数的进位其实更简单,因为进位数非0即1,因此,我们只需要知道如何得到正确的进位数即可。 我们发现,二进制中,如果有相加数的对应位不都是1,那么就没有进位,此时进位是0,如果有进位,那么对应位置往前进1位,用二进制实现该逻辑就是(a&b)<<1 进位问题解决了,那么其他部分呢,其他部分就如刚刚所说,数位数字模上进制数即可,对于二进制的模2加法,就是异或,也就是a^b
int add(int a,int b) {
int carry,add;
do{
add = a^b;
carry = (a&b)<<1;
a = add;
b=carry;
}while(carry!=0);
return add;
}
减法,重捡大一知识
因为a+b = a+(-b),我们只需要明白二进制如何取相反数即可 千万要注意,虽然一般数据类型的第一位都是符号位,但是并不是说0000…0001的相反数就是1000…0001,而是严格遵循相反数的定义,a+(-a)=0 二进制中的取反操作比较巧妙,是将数位取反之后+1,也就是-a = ~a+1,这里就不做展开,清楚取反之后,减法就非常简单了,因为我们已经实现了加法
int substract(int a, int b){
return add(a, ~b+1);
}
乘法,n次加法
int multiply(int a,int b){
int ans = 0;
while(b--){
ans = add(ans, a);
}
return ans;
}
优化,二分加法,因为左移操作相当于乘2, 因此我们配合左移操作与add操作,即可减少大量运算,该思想就是快速幂的思想,在这里我们就叫它快速乘吧,复杂度降低到o(log n)
int multiply(int a,int b){
int ans = 0;
while(b){
if(b&1) add(ans, a);
a<<1;
b>>1;
}
return ans;
}
除法
与乘法类似,除法对应的就是n次减法 直观解(只考虑整数除法):
int divide(int a, int b){
int ans = 0;
while(a>=b){
ans = add(ans,1);
a = substract(a,b);
}
return ans;
}
优化,与乘法相反的过程,每次从1寻找距离最大倍数的一半倍数, 减去该数字之后,再比较剩余数字。
int div(int a,int b){
int ans=0;
while(a >=b){
int multi=1;
while(multiply(multi, b) <= (a>>1))
{
multi=multi<<1;
}
ans= add(result, multi);
a = substract(a, multipy(b, multi) );
}
return ans;
}
总结
感觉自己在写汇编代码 不得不佩服计算机研究者们的智慧
参考