標(biāo)題: 不用中間數(shù)交換兩個變量的方法 [打印本頁]

作者: f556    時間: 2019-11-26 10:49
標(biāo)題: 不用中間數(shù)交換兩個變量的方法
本帖最后由 f556 于 2019-11-26 10:50 編輯

再次翻看多年前的日志,看到自己記錄的算法,聯(lián)想到前些天論壇有人問了一個簡單的問題:“怎么交換兩組數(shù)?”
當(dāng)時有人回答用中間變量,這是一種常用的方法。這個思路好比以下例子:一杯果汁A、一碗牛奶B,現(xiàn)在要互換一下容器,即把A換到碗里,B換到杯里。只有借助第3個容器C。操作是A--->C,B---->A,C----->B,這樣才能完成交換。
有人回答用加減法,思路不錯:
a=3;b=5;
a=a+b;         
b=a-b;
a=a-b;
但當(dāng)時我提出有溢出問題,如235+122怎么辦,設(shè)定a、b均為unsigned char;如果均為unsigned int同樣有問題,不能用程序來限定a、b值的范圍,特別是ADC的值。

我日志記錄的不用中間數(shù)交換兩個變量的方法,均利用C語言的異或運(yùn)算。
異或邏輯:對應(yīng)的位相比較,同=0,異=1,實際上可以理解就“按位求異”,即“異為真,同為假”
舉例:
xxxx 1001  ^   0000 1111 = xxxx 0110(后4位翻轉(zhuǎn))
xxx1 xxxx  ^      1 0000 =xxx0 xxxx   
xxx0 xxxx  ^      1 0000 =xxx1 xxxx  

程序如下:
unsigned int aa=3456,bb=7890;
void main( void )
{
  aa=aa^bb;
  bb=bb^aa;
  aa=aa^bb;
}
運(yùn)行結(jié)果見圖片。

stest.jpg (102.67 KB, 下載次數(shù): 291)

還未運(yùn)行

還未運(yùn)行

stest2.jpg (62.3 KB, 下載次數(shù): 314)

運(yùn)行完

運(yùn)行完

作者: hi毀人不倦    時間: 2019-11-28 08:36
夭壽,答案確實如此為何腦子還是轉(zhuǎn)不太過來。
作者: hange_v    時間: 2019-11-30 12:33
這個太厲害了,精彩
作者: 15806853199mmm    時間: 2019-12-2 12:35
好厲害,電賽初學(xué)者剛好

作者: shibadong    時間: 2019-12-5 09:22
沒看懂。。。
作者: 歐陽來洪    時間: 2019-12-10 09:49
神仙算法
作者: f556    時間: 2019-12-10 17:07
解釋一下,試算:
a=2   ----b 10
b=3   ----b 11             以下用二進(jìn)制
a=a^b=10^11=01     這個數(shù)相當(dāng)于一個中間值,不是原a,也不是原b
b=b^a=11^01=10  ---->2     使用了中間值01 (a)
a=a^b=01^10=11  ----->3     使用了中間值01 ,也使用了交換好的b

異或象加法一樣,不分先后順序
改程序如下圖,也可

sss.jpg (21.03 KB, 下載次數(shù): 271)

sss.jpg

作者: lzj527095842    時間: 2019-12-13 17:59
牛逼,學(xué)習(xí)了
作者: rotga    時間: 2019-12-16 08:51
這種交換方法有一個前提,就是aa和bb不能相等,如果相等,則會得出錯誤的結(jié)果。所以在做異或運(yùn)算前,需要進(jìn)行判斷。
作者: f556    時間: 2019-12-17 14:13
rotga 發(fā)表于 2019-12-16 08:51
這種交換方法有一個前提,就是aa和bb不能相等,如果相等,則會得出錯誤的結(jié)果。所以在做異或運(yùn)算前,需要進(jìn) ...

真的嗎?純屬你的主觀意斷!
作者: f556    時間: 2019-12-17 14:18
否定樓上的想當(dāng)然,兩個相同數(shù)的交換。見運(yùn)行圖片
人算:
a=1;  b=1;
a=a^b=0;
b=a^b=0^1=1;
a=a^b=0^1=1;
錯在哪里!

ssss.jpg (52.97 KB, 下載次數(shù): 225)

ssss.jpg

作者: 沒有你    時間: 2019-12-20 15:56
你這種方法雖然可行,涉及到異或處理,編譯出的匯編代碼會比中間變量法多一些,這樣的話,單片機(jī)就需要花更多是時間去處理,可以說效率低。而且這樣寫,代碼的可讀性非常差。
作者: qq923004243    時間: 2019-12-21 12:37
多學(xué)一種思路,至少以后看的懂 謝謝樓主的講解 樓主說的兩種我看過,這個沒看過
作者: rotga    時間: 2019-12-22 22:26
如果只是在main中這樣做,結(jié)果沒有問題,但是在實際編程中,以調(diào)用函數(shù)的方式,那么只能用指針傳遞實參,如果兩個數(shù)值相等的話,這樣就會出錯。你可以試一試。
作者: rotga    時間: 2019-12-22 22:54
#include <stdio.h>
void swap(int *a,int *b)
{
        *a^=*b;
        *b^=*a;
        *a^=*b;

}
void main(void)
{
        int test[2] = {21,23};
        swap(&test[0],&test[0]);
        printf("test[0]=%d test[1]=%d",test[0],test[1]);
}
運(yùn)行結(jié)果:test[0]=0 test[1]=23
作者: 學(xué)習(xí)使我快樂呢    時間: 2019-12-25 15:29
學(xué)到了,很有幫助,這個平臺很不錯
作者: f556    時間: 2019-12-27 16:31
rotga 發(fā)表于 2019-12-22 22:54
#include
void swap(int *a,int *b)
{

你程序有一句明顯錯誤。
作者: 113265    時間: 2019-12-27 22:18
太麻煩了
作者: f556    時間: 2019-12-28 12:36
rotga 發(fā)表于 2019-12-22 22:54
#include
void swap(int *a,int *b)
{

有一個語句明顯錯了,請改正。
另外,我個人非常厭惡printf,這個純無用的垃圾語句。從你用printf可以看出受了唐大師的毒害,還是重新找本書或者混一下網(wǎng)站、論壇。
作者: rotga    時間: 2019-12-31 10:08
不是明顯錯了,而是模擬在復(fù)雜環(huán)境里面,很有可能產(chǎn)生待比較的兩個數(shù)的指針指向同一地址,而結(jié)果你也看到了,是否和你之前想象的結(jié)果一致?你的心態(tài)很浮躁,我的水平是不高,但三人行必有我?guī)熝。是否你認(rèn)為給你指出潛在的錯誤邏輯只能是水平比你高的人才有資格?這個帖子我無意和你扯這些口舌之爭。如果技術(shù)上還有需討論的地方,歡迎發(fā)表觀點。關(guān)于用異或交換數(shù)字,是個思路,但第一,對于直接交換,如果操作對象是同一個數(shù),會有錯誤結(jié)果。第二,運(yùn)行效率低,代碼可讀性差。這個早已是定論。
作者: woxue100    時間: 2019-12-31 10:33
好厲害,初學(xué)者剛好
作者: hfj2011    時間: 2020-1-11 12:11
#include <stdio.h>
void swap(int *a,int *b)
{
        *a^=*b;
        *b^=*a;
        *a^=*b;

}
void main(void)
{
        int test[2] = {21,21};
        swap(&test[0],&test[1]);
        printf("test[0]=%d test[1]=%d",test[0],test[1]);
}
運(yùn)行結(jié)果:
test[0]=21 test[1]=21Press any key to continue

作者: qinshuai    時間: 2020-1-17 11:05
感謝,漲知識了
作者: Daniel008    時間: 2020-1-20 16:11
學(xué)習(xí)了,很有幫助
作者: Daniel008    時間: 2020-1-20 16:30
rotga 發(fā)表于 2019-12-16 08:51
這種交換方法有一個前提,就是aa和bb不能相等,如果相等,則會得出錯誤的結(jié)果。所以在做異或運(yùn)算前,需要進(jìn) ...

相等就不用交換了
作者: forpeople    時間: 2020-2-2 20:11
這么好的貼子不頂不行
作者: wodexiang    時間: 2020-2-3 01:00
不具通用性,只能在特殊場景里使用
作者: yk_yyy    時間: 2020-2-15 13:18
這個方法好,學(xué)習(xí)了
作者: sgb957431795    時間: 2020-2-15 15:07
謝謝大佬 學(xué)習(xí)了
作者: IKNOW    時間: 2020-2-20 13:32
有意思

作者: barlai    時間: 2020-3-11 07:45
過來學(xué)習(xí)!
作者: hbcqd    時間: 2020-3-15 19:59
先學(xué)習(xí)這個方法,在實踐中運(yùn)用,感謝大家的分享!
作者: jhy8341    時間: 2020-3-15 21:19
兩次對同一個數(shù)異或還等于自己
作者: wanghz12    時間: 2020-3-22 13:53
int x,y;
x=x+y;
y=x-y;
x=x-y;
作者: dongqing    時間: 2020-4-12 11:04
學(xué)習(xí)學(xué)習(xí),感謝!
作者: 權(quán)威人物    時間: 2020-4-21 13:54
rotga 發(fā)表于 2019-12-16 08:51
這種交換方法有一個前提,就是aa和bb不能相等,如果相等,則會得出錯誤的結(jié)果。所以在做異或運(yùn)算前,需要進(jìn) ...

你的基礎(chǔ)不行啊,樓主這么詳細(xì)的說明了你都還沒明白么?
作者: 權(quán)威人物    時間: 2020-4-21 13:57
wanghz12 發(fā)表于 2020-3-22 13:53
int x,y;
x=x+y;
y=x-y;

這個的前提是相加不能超出int范圍
作者: 新洲羅遠(yuǎn)    時間: 2020-4-22 20:26
天才算法!
作者: 1823711995    時間: 2020-4-30 16:52
把加法換為減法不就可以了?減法不會溢出,也比異或效率高
作者: 1823711995    時間: 2020-4-30 16:54
a=3;b=5;
a=a-b;
b=a+b;
a=b-a;

作者: guyunfeng    時間: 2020-5-16 10:48
樓主有點武斷了,用加法交換雖然會溢出但結(jié)果依然正確。在指出問題之前最少要動手試一試。
作者: guyunfeng    時間: 2020-5-16 10:50
溢出不影響結(jié)果,樓主方向錯了。
作者: dzbj    時間: 2020-5-20 17:09
請教兩個問題 如下應(yīng)該是最傻愣的方法吧 第一這樣也不限制長度 不管是char還是int 因為過程變量的長度是相同的 第二不需要運(yùn)算 只是賦值 會比你的方法速度快 是么

uchar a=123;
uchar b=234;

void swap()
{
uchar c;
c=a;
a=b;
b=c;
}
作者: romking11    時間: 2020-5-25 15:53
好厲害,電賽初學(xué)者剛好
作者: luohujiu    時間: 2020-5-27 14:58
好東西值得去想和學(xué)習(xí)
作者: specialClass    時間: 2020-5-30 17:26
樓主的這個帖子很有意義,用到了異或的性質(zhì):
只要有: a^b=c
那么對于()^()=()可以隨便填入a、b、c 都成立,交換數(shù)來説也就很簡單了
MOV A,#33H
MOV 30H,#66H
XRL A,30H
XRL 30H,A
XRL A,30H
在一些寄存器不夠的地方用剛剛好,學(xué)習(xí)了
作者: 塞北江南    時間: 2020-8-29 16:16
有那么點意思,值得發(fā)揚(yáng)光大!
作者: 正規(guī)007    時間: 2020-8-29 16:31
頂起!值得推廣!
作者: 長風(fēng)007    時間: 2020-9-23 16:47
你這種方法可行,只是單片機(jī)就需要花更多是時間去處理,效率有點低。
作者: passanyworld    時間: 2020-11-6 14:28
你的思路不錯
作者: 溫xyz    時間: 2020-11-28 07:54
wanghz12 發(fā)表于 2020-3-22 13:53
int x,y;
x=x+y;
y=x-y;

x=x+y可能會溢出。
作者: rundstedt    時間: 2021-1-23 18:06
溫xyz 發(fā)表于 2020-11-28 07:54
x=x+y可能會溢出。

溢出不影響結(jié)果的正確性。
作者: zpmpok001    時間: 2021-1-28 13:56
Daniel008 發(fā)表于 2020-1-20 16:30
相等就不用交換了

所謂的相等,是認(rèn)為分析,
在程序運(yùn)行時 A  B  有時相等,有時不相等
例如  A 是用戶指定的數(shù)據(jù)
        B  是函數(shù)得出的返回值

那么:我們控制不了的兩個數(shù)據(jù),他們有可能相等(作者認(rèn)為相等的會出錯),有時候不相等
作者: 山烏木    時間: 2021-3-24 17:23
不同的思維方式,看了很受益
作者: cfqde    時間: 2021-3-27 15:56
好方法呀!
作者: nakete    時間: 2021-5-12 14:53
方法不錯~~不過大型的程序會在意這個小算法嗎?
作者: USHODO    時間: 2021-5-25 09:28
討論熱烈拓展思路
作者: yzwzfyz    時間: 2021-6-23 15:42
1、程序運(yùn)行時間長;
2、占用存儲器多;
3、不實用。
將這段程序編譯后,看看所用的代碼就知道有多蠢了。
比起:A--->C,B---->A,C----->B 的方案差遠(yuǎn)了。
作者: 單片機(jī)愛好者223    時間: 2021-7-14 19:16
時間換空間
作者: 單片機(jī)愛好者223    時間: 2021-7-14 19:17
PUSH A
MOV A,B
POP B
最簡單
作者: 這個好難。    時間: 2021-7-23 11:28
rotga 發(fā)表于 2019-12-16 08:51
這種交換方法有一個前提,就是aa和bb不能相等,如果相等,則會得出錯誤的結(jié)果。所以在做異或運(yùn)算前,需要進(jìn) ...

1.相等了 就可以不用交換了;
2.可以加個判斷,只有不相等的時候才會有下面的交換
作者: jizhongbiao    時間: 2021-8-2 17:05
rotga 發(fā)表于 2019-12-31 10:08
不是明顯錯了,而是模擬在復(fù)雜環(huán)境里面,很有可能產(chǎn)生待比較的兩個數(shù)的指針指向同一地址,而結(jié)果你也看到了 ...

你用其它方法指向同一個地址也是會有問題的,這個和樓主的算法無關(guān)。
作者: zyluglugl    時間: 2021-8-3 22:26
這是什么原理?可以解釋一下嗎?
作者: zyluglugl    時間: 2021-8-3 22:29
rotga 發(fā)表于 2019-12-16 08:51
這種交換方法有一個前提,就是aa和bb不能相等,如果相等,則會得出錯誤的結(jié)果。所以在做異或運(yùn)算前,需要進(jìn) ...

你是最牛的人,給你點贊先!
作者: 1075402835    時間: 2021-8-3 22:48
初學(xué)者的天堂
作者: hhdsdy    時間: 2021-8-4 00:57
單片機(jī)愛好者223 發(fā)表于 2021-7-14 19:17
PUSH A
MOV A,B
POP B

我也想這樣說的,這個占用的是預(yù)留的空間,過后歸還,不會額外占用空間
作者: lsh04    時間: 2021-8-7 23:56
rotga 發(fā)表于 2019-12-22 22:54
#include
void swap(int *a,int *b)
{

這句錯了swap(&test[0],&test[0]);
應(yīng)為  swap(&test[0],&test[1]);
作者: 188610329    時間: 2021-9-24 23:20
specialClass 發(fā)表于 2020-5-30 17:26
樓主的這個帖子很有意義,用到了異或的性質(zhì):
只要有: a^b=c
那么對于()^()=()可以隨便填入a、b、 ...

兄弟,你都用匯編寫了……
你直接一個  XCH A,30H 不就換過來了么? 還那么幾個 XRL 干嘛。
作者: 188610329    時間: 2021-9-24 23:27
表面上看,不占用中間變量,實際上至少占用了累加器A,根據(jù)不同的編譯方式還可能占用寄存器B,一樣動用了累加器A,你先移動到累加器A,再互相移位遠(yuǎn)比反復(fù)運(yùn)算要好得多。
作者: rsx9583    時間: 2021-11-3 11:37
1823711995 發(fā)表于 2020-4-30 16:54
a=3;b=5;
a=a-b;
b=a+b;

你這種算法很好。運(yùn)行效率與可讀性應(yīng)該是比較強(qiáng)的。
不過我覺得還是設(shè)個中間數(shù)的最簡單明了,不用去規(guī)避一些未知的坑。
作者: chinomango    時間: 2022-3-19 05:10
異或效率該比加減高吧?這個出來40年了吧?我好奇還有折磨多人發(fā)帖
作者: zhaobolove    時間: 2022-3-19 09:28
#include <stdio.h>

int main(void)
{
   unsigned int a=65530;
   unsigned int b=65535;
   
printf("交換前a,b的值分別為:\n");
    printf("a=%d\n",a);
    printf("b=%d\n",b);
   
a=a+b;   //a=a*b;
   
b=a-b;   //b=a/b;
   
a=a-b;   //a=a/b;
   
printf("交換前a,b的值分別為:\n");
    printf("a=%d\n",a);
    printf("b=%d\n",b);
       
        return 0;
}


已經(jīng)測試了, 沒有問題, 不會溢出問題
作者: wukouyyq    時間: 2022-4-16 14:43
就用一下一個中間變量又能咋滴?

 想起了一個笑話,3個專家和一個農(nóng)民在說下雨時雨滴的問題,這個故事 就不在這里說了。

原來這個笑話是說我們程序猿的。。。。。。。
作者: 鵜鶘    時間: 2022-6-11 20:02
zyluglugl 發(fā)表于 2021-8-3 22:26
這是什么原理?可以解釋一下嗎?

有兩條:
1是,異或運(yùn)算既滿足交換律也滿足結(jié)合律;
2是,與自己異或結(jié)果是0且0與一個值異或結(jié)果就是那個值。
作者: 鵜鶘    時間: 2022-6-11 20:17
dzbj 發(fā)表于 2020-5-20 17:09
請教兩個問題 如下應(yīng)該是最傻愣的方法吧 第一這樣也不限制長度 不管是char還是int 因為過程變量的長度是相 ...

異或運(yùn)算是CPU最拿手的運(yùn)算之一,與賦值也不相上下,都可以是單周期單字節(jié)指令,速度上差不了多少只是程序的可讀性差點。賦值的方法要開辟第三個變量并也是要對其操作的,時間上不見得有多少優(yōu)越性。
作者: 鵜鶘    時間: 2022-6-11 20:40
guyunfeng 發(fā)表于 2020-5-16 10:50
溢出不影響結(jié)果,樓主方向錯了。

溢出的確不影響結(jié)果,但樓主的方向哪里錯了?他這個方法的結(jié)果不對嗎?這兩種方法都有可讀性問題,但相比之下很顯然樓主的更專業(yè),可讀性、嚴(yán)格性要好得多,常用C語言尤其是匯編語言的都對溢出、反碼補(bǔ)碼很在乎,這是習(xí)慣。我還是第一次看到這么做,看到加減法時也立即想到了有溢出問題,假如是別人寫的程序要我來檢查的話,我肯定要多花時間精力甚至詢問編程者來肯定它的正確性,而樓主的方法嚴(yán)格明了,不必跟任何人商量,可讀性、專業(yè)性樓主的要好得多,何來“方向錯誤”?
作者: 鵜鶘    時間: 2022-6-11 20:49
單片機(jī)愛好者223 發(fā)表于 2021-7-14 19:17
PUSH A
MOV A,B
POP B

你這是匯編語言,你這么做恰恰是舍近求遠(yuǎn),微處理器、單片機(jī)都有交換指令,一條指令即可,早年的Z80是XCHG,86處理器也是,51的匯編寫法好像是XCH,當(dāng)目標(biāo)和原都是寄存器時,交換指令是典型的單周期單字節(jié)指令。
作者: 鵜鶘    時間: 2022-6-11 21:05
1823711995 發(fā)表于 2020-4-30 16:52
把加法換為減法不就可以了?減法不會溢出,也比異或效率高

換為減法也可以,但也有問題,小減大會得到補(bǔ)碼,這個你想過沒有?但是,也不影響結(jié)果,也行得通,可讀性那就比加法還差。。。!皽p法比異或效率高”——這個是錯的,【異或】運(yùn)算是CPU最拿手的指令,無出其右,你大概不太熟悉數(shù)字電路,加法電路就是異或電路,如果是單純的異或那就連進(jìn)位都不需要,完整的加法電路就是帶進(jìn)位的異或電路。
作者: 鵜鶘    時間: 2022-6-11 21:19
這個方法不會比更流行的賦值方法慢,異或運(yùn)算是CPU上處理起來最快的指令,不會比賦值慢,更不會比加減法慢,它們都可以是單字節(jié)單周期指令,樓主的方法比加減法要好得多,相比之下嚴(yán)格、明了得多,老手都會用樓主的方法而不會去用加減法的雖然它也正確,加減法明顯的很笨拙、不專業(yè)。
作者: 鵜鶘    時間: 2022-6-11 21:56
yzwzfyz 發(fā)表于 2021-6-23 15:42
1、程序運(yùn)行時間長;
2、占用存儲器多;
3、不實用。

不會的,如果是匯編語言,這個算法只快不會慢,它少用了一個變量怎么會“占用存儲器多”呢?
作者: f556    時間: 2023-9-23 12:08
鵜鶘 發(fā)表于 2022-6-11 21:56
不會的,如果是匯編語言,這個算法只快不會慢,它少用了一個變量怎么會“占用存儲器多”呢?

高手!謝謝你的多個回復(fù),分析的到位、專業(yè)!




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1