奇技淫巧(二)—— 不使用中间变量交换两个值

使用中间变量的三行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);
求对数
求底数

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值