使用中间变量的三行temp代码就不贴了,这里看一下不使用中间变量的方案:
① "a=a+b"
用a将a,b的和存储起来,之后利用减法,b换成a,a再换成b
a = a+b; // a存变量之和
b = a-b; // 和-原b得到a
a = a-b; // 和-原a得到b
同理,可以使用"a=a-b":
a = a-b; // a存变量之差
b = a+b; // 差+原b得到a
a = b-a; // 原a-差得到b
② "a=a*b"
其实和①同理,只是这里存二者之积。
用a将a,b的积存储起来,之后利用乘法,b换成a,a再换成b
a = a*b; // a存变量之积
b = a/b; // 积除以原b得到a
a = a/b; // 积除以原a得到b
同理,可以使用"a=a/b":
a = a/b; // a存变量之商
b = a*b; // 商乘原b得到a
a = b/a; // 原a除以商得到b
③"a=a^b"
不存和/差/积/商,存二者异或值。
a=a^b;
b=a^b;
a=a^b;
背景知识:
异或,bit1和bit2不同则为1,相同则为0: 1^0=1;0^1=1;0^0=0;1^1=0;
可以得到两个规律:1)bit和0异或还是bit: b^0=b
2)bit和自己异或得零: b^b=0
并且异或满足交换律和结合律,即异或式的值只与式中各因子的个数有关,与位置无关
又因为异或本身是位运算符,不产生进位则我们能进一步得到以下结论:
对于等长多位(字节)变量a,b,有: a^a=0; a^0=a;b^0=b;
进而有: a^b^a=a^a^b=0^b=b; a^b^b=a^0=a;
即有:
a^a^b=b;
a^b^b=a;
故有代码:
a=a^b;
b=a^b;
a=a^b;
PS:对应异或的还有同或,当然,大多数语言并不提供同或运算符,因为 同或=!(^),
同或的规则和异或相反(按位),假设同或运算符为#,则故有:0#0=1;1#0=0;1#1=1;
推广至多位,则a#0=对a取反;a#a=全1;
或者由a^0=a;a^a=0;而同或#是异或^的非,故对a取反,对0(更准确地说应该是全0)取反,也可以得到相同结论。
三种方法都可以是实现交换,但是很明显,a+b,a-b,a*b,a/b全都存在溢出的风险,并且,a*b和a/b在a或者b任意一个为0时不能使用(具体原因看代码,看哪一步会报错),并且除法可能会因为不能整除而出现精度损失的问题(从结果来看就是结果错误),而a^b是完美的方案,且在CPU中,移位运算要比加法,乘法快的多(具体速度:移位>加法>乘法),所以无论是从溢出可能性还是运算效率考虑,都是异或操作a^b的方法更好。
当然,如果变量值不是整数类型,浮点型或者对象,则只推荐使用temp三行交换代码。
当然,能够得到的启发远不止于此,可以看到,上面的所有方案的一致性:使用a存储一个”同时存有ab信息的结果“,再通过这个结果和b作用恢复出a,进而通过这个结果和a作用恢复出b。
对于和/差/积/商,”同时存有ab信息的结果“都有损失信息的可能性——溢出,乘0信息全部丢失,除不尽的小数部分丢失等等,而异或则完美地存储了”同时存有ab信息的结果“。从这个角度看,我们可以完全可以找到除加减乘除之外中间结果,如a的b次幂。
int a = Math.pow(a,b);
求对数
求底数