Abstract
一般要交換兩個變數,會用到一個新的變數當暫存,是否能只用兩個變數做交換呢?
Introduction
這是我一個網友問我的,他說他同學去工作面試時所考的題目,一般我們要交換兩個變數會這樣寫。
C++
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_normal.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable in C++
Release : 09/08/2007 1.0
*/
#include <iostream>
using namespace std;
void swap(int& x, int& y) {
int tmp = x;
x = y;
y = tmp;
}
int main() {
int x = 1;
int y = 2;
cout << "x = " << x << ", y = " << y << endl;
swap(x,y);
cout << "x = " << x << ", y = " << y << endl;
}
執行結果
x = 1, y = 2
x = 2, y = 1
假如你也這樣寫,表是你是一個很正常的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等邏輯做不到的。
x xor y = z
z xor y = x
z xor x = y
所以z可以看成是一個暫存變數,只要在xor x回來就可以得到y,或xor y回來等於x。
所以若要兩數交換,可以這樣寫。
x = x xor y
y = x xor y
x = x xor y
為什麼三次xor就可以呢?以上的code原本應該寫成
z = x xor y
y = z xor y
x = z xor y (此時y已經等於x,所以相當於z xor x)
但這樣寫多了一個變數z,且x在過程中已經不會用到了,所以將變數z放在x中,可以省下一個變數,所以就變成了
x = x xor y
y = x xor y
x = x xor y
一切的神奇都歸因於xor具有可逆性。
Working Code
C
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_c.c
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable without other variable in C
Release : 09/08/2007 1.0
*/
#include "stdio.h"
void swap(int* x, int* y) {
*x = *x ^ *y;
*y = *x ^ *y;
*x = *x ^ *y;
}
int main() {
int x = 1;
int y = 2;
printf("x = %d, y = %d\n", x, y);
swap(&x,&y);
printf("b = %d, y = %d\n", x, y);
}
執行結果
x = 1, y = 2
x = 2, y = 1
C++
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_xor.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable without other variable in C++
Release : 09/08/2007 1.0
*/
#include <iostream>
using namespace std;
void swap(int& x, int& y) {
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
int main() {
int x = 1;
int y = 2;
cout << "x = " << x << ", y = " << y << endl;
swap(x,y);
cout << "x = " << x << ", y = " << y << endl;
}
執行結果
x = 1, y = 2
x = 2, y = 1
C#
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to swap 2 variable without other variable in C#
Release : 09/08/2007 1.0
*/
using System;
class Client {
static void swap(ref int x, ref int y) {
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
static void Main() {
int x = 1;
int y = 2;
Console.WriteLine("x = {0}, y = {1}", x, y);
swap(ref x, ref y);
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}
執行結果
x = 1, y = 2
x = 2, y = 1
使用offset
這種解法的想法是,用一個64bit int同時包含這兩個數,所以先將x左移32bit後,再和y相加,此時新的數就同時包含x和y。若要取出y,直接對0xffff做and遮罩(mask),若要取出x,先右移32bit後,再對0xffff做and遮罩。
Working code
C++
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_offset.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable without other variable in C++
Release : 09/16/2007 1.0
*/
#include <iostream>
using namespace std;
void swap(long long& x, long long& y) {
x = (x << 32) + y;
y = (x >> 32) & 0xffff;
x = x & 0xffff;
}
int main() {
int x = 1;
int y = 2;
cout << "x = " << x << ", y = " << y << endl;
swap(x,y);
cout << "x = " << x << ", y = " << y << endl;
}
執行結果
x = 1, y = 2
x = 2, y = 1
13行
void swap(long long& x, long long& y) {
int為32bit,long long為64bit,如此寫法可保證32 bit的int都可以正確swap,負數也可以。
C#
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_offset.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to swap 2 variable without other variable in C#
Release : 09/16/2007 1.0
*/
using System;
class Client {
static void swap(ref int x, ref int y) {
x = (x << 16) + y;
y = (x >> 16) & 0xff;
x = x & 0xff;
}
static void Main() {
int x = 1;
int y = 2;
Console.WriteLine("x = {0}, y = {1}", x, y);
swap(ref x, ref y);
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}
執行結果
x = 1, y = 2
x = 2, y = 1
C#就不太一樣了,C#的ref不允許int直接轉long,所以13行,14行只敢offset 16bit,也就是說,這種寫法只允許用在16bit的int,更大的數會有問題。
哪種方式速度較快?
以下是我的測試程式
C++ by temp
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_tmp_benchmark.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable in C++
Release : 09/08/2007 1.0
*/
#include <iostream>
#include <ctime>
using namespace std;
void swap(int& x, int& y) {
int tmp = x;
x = y;
y = tmp;
}
int main() {
int x = 1;
int y = 2;
clock_t t = clock();
for(int i = 0; i != 10000; ++i)
for(int j = 0; j != 100000; ++j)
swap(x, y);
t = clock() -t;
cout << "Process time:" << (double)t/CLOCKS_PER_SEC << " sec (" << t << " clocks)" << endl;
}
C++ by xor
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_xor_benchmark.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable without other variable in C++
Release : 09/08/2007 1.0
*/
#include <iostream>
#include <ctime>
using namespace std;
void swap(int& x, int& y) {
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
int main() {
int x = 1;
int y = 2;
clock_t t = clock();
for(int i=0; i != 10000; ++i)
for(int j=0; j != 100000; ++j)
swap(x, y);
t = clock() -t;
cout << "Process time:" << (double)t/CLOCKS_PER_SEC << " sec (" << t << " clocks)" << endl;
}
C++ by offset
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_offset_benchmark.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to swap 2 variable without other variable in C++
Release : 09/16/2007 1.0
*/
#include <iostream>
#include <ctime>
using namespace std;
void swap(long long& x, long long& y) {
x = (x << 32) + y;
y = (x >> 32) & 0xffff;
x = x & 0xffff;
}
int main() {
int x = 1;
int y = 2;
clock_t t = clock();
for(int i = 0; i != 10000; ++i)
for(int j = 0; j != 100000; ++j)
swap(x, y);
t = clock() -t;
cout << "Process time:" << (double)t/CLOCKS_PER_SEC << " sec (" << t << " clocks)" << endl;
}
C# by temp
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_tmp_benchmark.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to swap 2 variable in C#
Release : 09/08/2007 1.0
*/
using System;
class Client {
static void swap(ref int x, ref int y) {
int tmp = x;
x = y;
y = tmp;
}
static void Main() {
int x = 1;
int y = 2;
DateTime t = DateTime.Now;
for (int i = 0; i != 10000; ++i)
for (int j = 0; j != 100000; ++j)
swap(ref x, ref y);
TimeSpan span = DateTime.Now.Subtract(t);
Console.WriteLine("Process time:{0}.{1} sec", span.Seconds.ToString(), span.Milliseconds.ToString());
}
}
C# by xor
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_xor_benchmark.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to swap 2 variable without other variable in C#
Release : 09/08/2007 1.0
*/
using System;
class Client {
static void swap(ref int x, ref int y) {
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
static void Main() {
int x = 1;
int y = 2;
DateTime t = DateTime.Now;
for (int i = 0; i != 10000; ++i)
for (int j = 0; j != 100000; ++j)
swap(ref x, ref y);
TimeSpan span = DateTime.Now.Subtract(t);
Console.WriteLine("Process time:{0}.{1} sec", span.Seconds.ToString(), span.Milliseconds.ToString());
}
}
C# by offset
/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : swap_offset_benchmark.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to swap 2 variable without other variable in C#
Release : 09/08/2007 1.0
*/
using System;
class Client {
static void swap(ref int x, ref int y) {
x = (x << 16) + y;
y = (x >> 16) & 0xffff;
x = x & 0xffff;
}
static void Main() {
int x = 1;
int y = 2;
DateTime t = DateTime.Now;
for (int i = 0; i != 10000; ++i)
for (int j = 0; j != 100000; ++j)
swap(ref x, ref y);
TimeSpan span = DateTime.Now.Subtract(t);
Console.WriteLine("Process time:{0}.{1} sec", span.Seconds.ToString(), span.Milliseconds.ToString());
}
}
執行結果
|
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
Conclusionxor是最慢無庸置疑,但offset在C++比temp快一點,但差距有限,但在C#卻是temp有壓倒性的勝利,原因不明。