Abstract
一般要交換兩個變數,會用到一個新的變數當暫存,是否能只用兩個變數做交換呢?
Introduction
這是我一個網友問我的,他說他同學去工作面試時所考的題目,一般我們要交換兩個變數會這樣寫。
C++
執行結果
假如你也這樣寫,表是你是一個很正常的coder,:D,但這樣寫還必須多一個變數tmp當暫存,能否不需tmp也能交換呢?
神奇的xor
xor全名為exclusive or,其truth table為
| x | y | x xor y |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
簡單的說,就是當x和y不同時,其質為true。但xor最神奇的,是它具可逆性,這是其他and、or、or等邏輯做不到的。
所以z可以看成是一個暫存變數,只要在xor x回來就可以得到y,或xor y回來等於x。
所以若要兩數交換,可以這樣寫。
為什麼三次xor就可以呢?以上的code原本應該寫成
但這樣寫多了一個變數z,且x在過程中已經不會用到了,所以將變數z放在x中,可以省下一個變數,所以就變成了
一切的神奇都歸因於xor具有可逆性。
Working Code
C
執行結果
C++
執行結果
C#
執行結果
使用offset
這種解法的想法是,用一個64bit int同時包含這兩個數,所以先將x左移32bit後,再和y相加,此時新的數就同時包含x和y。若要取出y,直接對0xffff做and遮罩(mask),若要取出x,先右移32bit後,再對0xffff做and遮罩。
Working code
C++
執行結果
13行
int為32bit,long long為64bit,如此寫法可保證32 bit的int都可以正確swap,負數也可以。
C#
執行結果
C#就不太一樣了,C#的ref不允許int直接轉long,所以13行,14行只敢offset 16bit,也就是說,這種寫法只允許用在16bit的int,更大的數會有問題。
哪種方式速度較快?
以下是我的測試程式
C++ by temp
C++ by xor
C++ by offset
C# by temp
C# by xor
C# by offset
執行結果
| temp | xor | offset | |
| C++ | 2.333s | 3.154s | 2.293s |
| C# | 3.615s | 16.633s | 12.648s |
測試平台 Pentium M 733 1.2G / Memory : 2G
C++ : offset > temp > xor
C# : temp > offset > xor
Conclusion
xor是最慢無庸置疑,但offset在C++比temp快一點,但差距有限,但在C#卻是temp有壓倒性的勝利,原因不明。