ホームページ  >  記事  >  Java  >  3 番目の変数を使用せずに 2 つの変数の値を交換する 4 つの方法

3 番目の変数を使用せずに 2 つの変数の値を交換する 4 つの方法

巴扎黑
巴扎黑オリジナル
2017-06-26 09:19:292097ブラウズ
通常、私たちが行うこと (特に学習段階) は、新しい変数を定義し、それを使用して交換を完了することです。コードは次のとおりです:
int a,b;
a=10; b=15;int t;
t=a; a=b; b=t;

このアルゴリズムは理解しやすく、初心者がコンピュータ プログラムの特性を理解するのに特に適しています。代入ステートメントの古典的な応用です。実際のソフトウェア開発では、このアルゴリズムは単純明快で曖昧さがなく、プログラマ間のコミュニケーションが容易であり、変数値の交換の問題が発生した場合には、通常、このアルゴリズム (以下、標準アルゴリズムと呼びます) を使用する必要があります。


上記のアルゴリズムの最大の欠点は、一時変数の使用が必要なことです。それでは、一時変数の助けを借りずに交換を実現できるのでしょうか?答えは「はい」です!ここでは、1) 算術演算、2) ポインター アドレス演算、4) スタック実装の 3 つのアルゴリズムを使用できます。

1)四則演算
int a,b;
a=10;b=12;
a=b-a; //a=2;b=12b=b-a; //a=2;b=10a=b+a; //a=10;b=10

原理は、aとbを数値軸上の点として扱い、2点間の距離を中心に計算します。

具体的なプロセス: 最初の文「a=b-a」は 2 点 ab 間の距離を求め、それを a に保存します。2 番目の文「b=b-a」は、a から原点までの距離 (b 間の距離) を求めます。 3 番目の文「a=b+a」は、b から原点までの距離 (a から原点までの距離と 2 点間の距離の合計) を求めます。 2 点間の距離 ab) を取得し、a に保存します。交換を完了します。
標準アルゴリズムと比較して、このアルゴリズムには計算プロセスが 3 つ増えていますが、一時変数は使用されません。 (以下、算術アルゴリズムと呼びます)
欠点: 数値型にのみ使用でき、文字列などには使用できません。 a+b はオーバーフローする可能性があります (int の範囲を超えます)。オーバーフローは、+ がオーバーフローした場合、- が戻ってきた場合は問題ありません。つまり、オーバーフローするかどうかは問題ではありません。

2) ポインタアドレス演算
アドレスの演算は実際には整数演算を実行するため、たとえば: 2 つのアドレスが減算されて、メモリ内の 2 つの変数の格納位置が何バイト離れているかを示す整数が追加されます。つまり、「a+10」は、a をベース アドレスとして、a の後の 10 クラス a データ単位のアドレスを表します。したがって、理論的には、アドレスの交換は算術アルゴリズムと同様の操作で完了し、変数を交換する目的を達成できます。つまり、
int *a,*b; //假设*a=new int(10);*b=new int(20); //&a=0x00001000h,&b=0x00001200ha=(int*)(b-a); //&a=0x00000200h,&b=0x00001200hb=(int*)(b-a); //&a=0x00000200h,&b=0x00001000ha=(int*)(b+int(a)); //&a=0x00001200h,&b=0x00001000h

上記の操作により、実際に a と b のアドレスが交換され、a は元々 b が指した値を指し、b は元々 a が指した値を指すのでしょうか?上記のコードはコンパイルできますが、実行結果は驚くべきものです。なぜ?

まず、オペレーティングシステムがメモリをシステムコード/データ領域、アプリケーションコード/データ領域、スタック領域、グローバルデータ領域などのいくつかの領域に分割していることを理解する必要があります。ソースプログラムをコンパイルする際、定数やグローバル変数などはグローバルデータ領域に配置され、ローカル変数やダイナミック変数はスタック領域に配置されます。このように、「a=(int*)(b-a)」というアルゴリズムを実行すると、aの値は0x00000200hではなく、変数aが配置されているメモリ領域のベースアドレスとなり、実際の結果は0x008f0200hとなります。ここで、0x008f はベース アドレス 0200 で、メモリ領域内の a の変位です。コンパイラによって自動的に追加されます。その結果、今後のアドレス計算が正しくなくなり、a と b がエリア内の他のメモリ ユニットを指すようになります。第三に、負の数はアドレス演算で使用できません。つまり、a のアドレスが b のアドレスより大きい場合 (b-a解決策はありますか?確かに!改良されたアルゴリズムは次のとおりです:
if(a<b)
{
a=(int*)(b-a);
b=(int*)(b-(int(a)&0x0000ffff));
a=(int*)(b+(int(a)&0x0000ffff));
}else{
b=(int*)(a-b);
a=(int*)(a-(int(b)&0x0000ffff));
b=(int*)(a+(int(b)&0x0000ffff));
}

アルゴリズムの最大の改良点は、アドレスの上位 16 ビットがセグメント アドレスであり、アドレスの上位 16 ビットがセグメント アドレスであり、アドレスの上位 16 ビットがセグメント アドレスであり、アドレスの上位 16 ビットがセグメント アドレスであり、アドレスの上位 16 ビットがセグメント アドレスであり、最後の 16 ビットであるため、ビット演算で AND 演算「int(a)&0x0000ffff」を使用することです。 16 ビットはディスプレースメント アドレスです。0x0000ffff との AND 演算の後、セグメント アドレスはマスクされ、ディスプレースメント アドレスのみが保持されます。これは元のアルゴリズムと一致し、正しい結果が得られます。

このアルゴリズムも、算術アルゴリズムと比較すると、3番目の変数を使用せずに値の交換を完了しますが、大きなデータ型を交換する場合、その実行速度が算術アルゴリズムよりも速いという利点があります。アドレスは交換しますが、変数値はメモリ内に移動されていないためです。 (以下、アドレスアルゴリズムと呼びます)

3) ビット演算
int a=10,b=12; //a=1010^b=1100;a=a^b; //a=0110^b=1100;b=a^b; //a=0110^b=1010;a=a^b; //a=1100=12;b=1010;

このアルゴリズムの実装は、XOR 演算を通じてデータ内の一部のビットを反転することができます。他のビットは変更されません。これは、任意の数値と任意の値が連続 2 回 XOR 演算され、値は変更されないことを意味します。

4) スタックの実装。スタックおよび関連する関数の定義は省略されているため、これ以上の説明は不要です。
int exchange(int x,int y) 
{ 
     stack S; 
     push(S,x); 
     push(S,y); 
     x=pop(S); 
     y=pop(S); 
}

上記のアルゴリズムはすべて、他の変数の助けを借りずに 2 つの変数値の交換を実現します。それに比べて、算術アルゴリズムとビット演算は同じ量の計算を必要とします。アドレス アルゴリズムの計算はより複雑です。ただし、最初の 2 つは整数データのみを交換できるのに対し、大規模な計算 (カスタム クラスや構造体など) を簡単に実現できます (理論上、「^」演算子をオーバーロードすると、任意の構造体の交換も実現できます)。 )。

これら 3 つのアルゴリズムの紹介は、実際に適用することを目的としたものではなく、テクノロジーについて説明し、プログラミングの魅力を説明することを目的としています。このことからも、数学のちょっとしたスキルがプログラミングに大きな影響を及ぼし、正しく使えば思わぬ魔法のような効果を発揮することが分かります。実際のソフトウェア開発の観点から見ると、標準アルゴリズムが間違いなく最適であり、あらゆる種類の交換問題を解決できます。

以上が3 番目の変数を使用せずに 2 つの変数の値を交換する 4 つの方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。