(原創) 如何交換兩個變數,而不動用第三個變數? (C/C++) (C) (.NET) (C#)

Abstract
一般要交換兩個變數,會用到一個新的變數當暫存,是否能只用兩個變數做交換呢?

Introduction
這是我一個網友問我的,他說他同學去工作面試時所考的題目,一般我們要交換兩個變數會這樣寫。
C++


執行結果

= 1, y = 2
= 2, y = 1


假如你也這樣寫,表是你是一個很正常的coder,:D,但這樣寫還必須多一個變數tmp當暫存,能否不需tmp也能交換呢?

神奇的xor
xor全名為exclusive or,其truth table為

x y x xor y
0 0
0 1 1
1 0 1
1 1 0

簡單的說,就是當x和y不同時,其質為true。但xor最神奇的,是它具可逆性,這是其他and、or、or等邏輯做不到的。

x xor y = z
z xor y 
= x
z xor x 
= y


所以z可以看成是一個暫存變數,只要在xor x回來就可以得到y,或xor y回來等於x。

所以若要兩數交換,可以這樣寫。

= x xor y
= x xor y
= x xor y


為什麼三次xor就可以呢?以上的code原本應該寫成

= x xor y
= z xor y
= z xor y (此時y已經等於x,所以相當於z xor x)


但這樣寫多了一個變數z,且x在過程中已經不會用到了,所以將變數z放在x中,可以省下一個變數,所以就變成了

= x xor y
= x xor y
= x xor y


一切的神奇都歸因於xor具有可逆性。

Working Code

C


執行結果

= 1, y = 2
= 2, y = 1


C++


執行結果

= 1, y = 2
= 2, y = 1


C#


執行結果

= 1, y = 2
= 2, y = 1


使用offset
這種解法的想法是,用一個64bit int同時包含這兩個數,所以先將x左移32bit後,再和y相加,此時新的數就同時包含x和y。若要取出y,直接對0xffff做and遮罩(mask),若要取出x,先右移32bit後,再對0xffff做and遮罩。

Working code
C++


執行結果

= 1, y = 2
= 2, y = 1


13行

void swap(long long& x, long long& y) {


int為32bit,long long為64bit,如此寫法可保證32 bit的int都可以正確swap,負數也可以。

C#


執行結果

= 1, y = 2
= 2, y = 1


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有壓倒性的勝利,原因不明。

posted on 2007-09-09 12:04  真 OO无双  阅读(15111)  评论(8编辑  收藏  举报

导航