web-dev-qa-db-ja.com

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

面接で尋ねられた非常に難しい質問の1つ。

a=10b=15などの2つの変数の値を交換します。

通常、2つの変数値を交換するには、次のような3番目の変数が必要です。

temp=a;
a=b;
b=temp;

ここでの要件は、3番目の変数を使用せずに2つの変数の値を交換することです。

94
Muhammad Akhtar

xorスワップアルゴリズム の使用

void xorSwap (int* x, int* y) {
    if (x != y) { //ensure that memory locations are different
       *x ^= *y;
       *y ^= *x;
       *x ^= *y;
    }
}


テストを行う理由

このテストでは、xとyのメモリ位置が(異なる値ではなく)異なることを確認します。それの訳は (p xor p) = 0およびxとyの両方が同じメモリ位置を共有する場合、1つが0に設定されると、両方が0に設定されます。 (同じであるため)、つまり、関数は* xと* yの両方を0に設定します。

それらが同じ値を持っているが同じメモリ位置ではない場合、すべてが期待どおりに動作します

*x = 0011
*y = 0011
//Note, x and y do not share an address. x != y

*x = *x xor *y  //*x = 0011 xor 0011
//So *x is 0000

*y = *x xor *y  //*y = 0000 xor 0011
//So *y is 0011

*x = *x xor *y  //*x = 0000 xor 0011
//So *x is 0011


これを使用する必要がありますか?

一般的な場合、いいえ。コンパイラは一時変数を最適化して取り除き、スワッピングが一般的な手順である場合、プラットフォームに最適なマシンコードを出力する必要があります。

Cで書かれたこのクイックテストプログラムを例に取ります。

#include <stdlib.h>
#include <math.h>

#define USE_XOR 

void xorSwap(int* x, int *y){
    if ( x != y ){
        *x ^= *y;
        *y ^= *x;
        *x ^= *y;
    }
}

void tempSwap(int* x, int* y){
    int t;
    t = *y;
    *y = *x;
    *x = t;
}


int main(int argc, char* argv[]){
    int x = 4;
    int y = 5;
    int z = pow(2,28); 
    while ( z-- ){
#       ifdef USE_XOR
            xorSwap(&x,&y);
#       else
            tempSwap(&x, &y);
#       endif
    }
    return x + y;    
}

次を使用してコンパイル:

gcc -Os main.c -o swap

Xorバージョンは

real    0m2.068s
user    0m2.048s
sys  0m0.000s

一時変数のあるバージョンが取る場所:

real    0m0.543s
user    0m0.540s
sys  0m0.000s
146
Yacoby

一般的な形式は次のとおりです。

A = A operation B
B = A inverse-operation B
A = A inverse-operation B 

ただし、潜在的にオーバーフローに注意する必要があります。また、すべての操作に、その操作が定義されているすべての値に対して適切に定義された逆があるわけではありません。例えば*および/ AまたはBが0になるまで動作

xorは、すべてのintに対して定義されており、それ自体が逆であるため、特に快適です。

86
jk.
a = a + b
b = a - b // b = a
a = a - b
79
Dor

まだstd::swapの使用を提案している人はいません。

std::swap(a, b);

[〜#〜] i [〜#〜]一時変数を使用しないでください。また、aおよびbのタイプによっては、実装に特殊化がある場合があります。どちらでもない。実装は、「トリック」が適切かどうかを知って書かれている必要があります。 2番目の推測を試みても意味がありません。

より一般的には、可能であればADLがより適切なオーバーロードを見つけることができるクラス型で機能するため、おそらくこのようなことをしたいと思います。

using std::swap;
swap(a, b);

もちろん、この答えに対するインタビュアーの反応は、空席について多くを語っているかもしれません。

76
CB Bailey

すでにmanuで述べたように、XORアルゴリズムは、すべての整数値で動作する一般的なアルゴリズムです(ポインターを含み、運とキャストがあります)。加減算を使用した別のそれほど強力ではないアルゴリズム:

A = A + B
B = A - B
A = A - B

ここでは、オーバーフロー/アンダーフローに注意する必要がありますが、それ以外は同じように機能します。 XORがそれらで許可されていない場合、float/doublesでこれを試すこともできます。

17
Vilx-

愚かな質問は適切な答えに値する:

void sw2ap(int& a, int& b) {
  register int temp = a; // !
  a = b;
  b = temp;
}

registerキーワードの唯一の適切な使用法。

10
MSalters

XORスワップアルゴリズム を見ましたか?

5
MBO
#include<iostream.h>
#include<conio.h>
void main()
{
int a,b;
clrscr();
cout<<"\n==========Vikas==========";
cout<<"\n\nEnter the two no=:";
cin>>a>>b;
cout<<"\na"<<a<<"\nb"<<b;
a=a+b;
b=a-b;
a=a-b;

cout<<"\n\na="<<a<<"\nb="<<b;
getch();
}
2
vikas

元のソリューションは次のとおりです。

temp = x; y = x; x = temp;

以下を使用して、2ライナーにすることができます。

temp = x; y = y + temp -(x=y);

次に、以下を使用して1つのライナーにします。

x = x + y -(y=x);
2
user3258202

a=10b=15

加算と減算を使用する

a = a + b //a=25
b = a - b //b=10
a = a - b //a=15

除算と乗算の使用

a = a * b //a=150
b = a / b //b=10
a = a / b //a=15
1
Venkat

3番目の変数を使用して2つの数値を交換するには、次のようにします。

int temp;
int a=10;
int b=20;
temp = a;
a = b;
b = temp;
printf ("Value of a", %a);
printf ("Value of b", %b);

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

int a = 10;
int b = 20;
a = a+b;
b = a-b;
a = a-b;
printf ("value of a=", %a);
printf ("value of b=", %b);
1
Ashwini

もちろん、C++の答えはstd::swap

ただし、次のswapの実装には3番目の変数もありません。

template <typename T>
void swap (T &a, T &b) {
    std::pair<T &, T &>(a, b) = std::make_pair(b, a);
}

または、ワンライナーとして:

std::make_pair(std::ref(a), std::ref(b)) = std::make_pair(b, a);
1
jxh

もう1つのソリューションがありますが、リスクは1つです。

コード:

#include <iostream>
#include <conio.h>
void main()
{

int a =10 , b =45;
*(&a+1 ) = a;
a =b;
b =*(&a +1);
}

ロケーションa + 1の値はすべてオーバーライドされます。

1
DareDevil
#include <iostream>
using namespace std;
int main(void)
{   
 int a,b;
 cout<<"Enter a integer" <<endl;
 cin>>a;
 cout<<"\n Enter b integer"<<endl;
 cin>>b;

  a = a^b;
  b = a^b;
  a = a^b;

  cout<<" a= "<<a <<"   b="<<b<<endl;
  return 0;
}

更新:これでは、ユーザーから2つの整数を入力しています。次に、ビット単位の[〜#〜] xor [〜#〜]操作を使用してそれらを交換します。

a=4b=9の2つの整数があり、その後:

a=a^b --> 13=4^9 
b=a^b --> 4=13^9 
a=a^b --> 9=13^9
1
Naeem Ul Hassan

質問を少し変更して、変数ではなく2つのアセンブリレジスタについて尋ねる場合は、xchg操作を1つのオプションとして使用し、スタック操作を別のオプションとして使用することもできます。

1
rkellerm

それが正しいXORスワップアルゴリズム

void xorSwap (int* x, int* y) {
   if (x != y) { //ensure that memory locations are different
      if (*x != *y) { //ensure that values are different
         *x ^= *y;
         *y ^= *x;
         *x ^= *y;
      }
   }
}

a XOR A = 0

0
public void swapnumber(int a,int b){
    a = a+b-(b=a);
    System.out.println("a = "+a +" b= "+b);
}
0
Ashish Bhavsar

できること:

std::tie(x, y) = std::make_pair(y, x);

または、3つ以上の変数を交換するときにmake_Tupleを使用します。

std::tie(x, y, z) = std::make_Tuple(y, z, x);

しかし、内部的にstd :: tieが一時変数を使用するかどうかはわかりません!

0
pooya13

3番目の変数を使用せずに2つの数値を交換する単純なcの例を見てみましょう。

プログラム1:

#include<stdio.h>
#include<conio.h>
main()
{
int a=10, b=20;
clrscr();
printf("Before swap a=%d b=%d",a,b);
a=a+b;//a=30 (10+20)
b=a-b;//b=10 (30-20)
a=a-b;//a=20 (30-10)
printf("\nAfter swap a=%d b=%d",a,b);
getch();
}

出力:

スワップ前a = 10 b = 20スワップ後a = 20 b = 10

プログラム2:*および/を使用

*と/を使用して2つの数値を交換する別の例を見てみましょう。

#include<stdio.h>
#include<conio.h>
main()
{
int a=10, b=20;
clrscr();
printf("Before swap a=%d b=%d",a,b);
a=a*b;//a=200 (10*20)
b=a/b;//b=10 (200/20)
a=a/b;//a=20 (200/10)
printf("\nAfter swap a=%d b=%d",a,b);
getch();
}

出力:

スワップ前a = 10 b = 20スワップ後a = 20 b = 10

プログラム3:ビット単位の使用XOR operator:

ビット単位のXOR演算子を使用して、2つの変数を交換できます。2つの数値xおよびyのXORは、たとえば、XOR of 10(バイナリ1010)および5(バイナリ0101))は1111で、XOR of 7(0111 )および5(0101)は(0010)です。

#include <stdio.h>
int main()
{
 int x = 10, y = 5;
 // Code to swap 'x' (1010) and 'y' (0101)
 x = x ^ y;  // x now becomes 15 (1111)
 y = x ^ y;  // y becomes 10 (1010)
 x = x ^ y;  // x becomes 5 (0101)
 printf("After Swapping: x = %d, y = %d", x, y);
 return 0;

出力:

交換後:x = 5、y = 10

プログラム4:

まだ誰もstd :: swapの使用を提案していません。

std::swap(a, b);

私は一時変数を使用せず、aとbのタイプによっては、実装にも特殊化が行われる場合があります。実装は、「トリック」が適切かどうかを知って書かれている必要があります。

上記の方法の問題:

1)乗算と除算に基づくアプローチは、一方の数値が0の場合、他の数値に関係なく積が0になるため機能しません。

2)両方の算術ソリューションが算術オーバーフローを引き起こす可能性があります。 xとyが大きすぎると、加算と乗算が整数の範囲外になる可能性があります。

3)変数へのポインターを使用して関数をスワップすると、両方のポインターが同じ変数を指している場合、上記のメソッドはすべて失敗します。両方が同じ変数を指している場合、この場合に何が起こるか見てみましょう。

//ビット単位XORベースのメソッド

x = x ^ x; // x becomes 0
x = x ^ x; // x remains 0
x = x ^ x; // x remains 0

//算術ベースのメソッド

x = x + x; // x becomes 2x
x = x – x; // x becomes 0
x = x – x; // x remains 0

次のプログラムを見てみましょう。

#include <stdio.h>
void swap(int *xp, int *yp)
{
    *xp = *xp ^ *yp;
    *yp = *xp ^ *yp;
    *xp = *xp ^ *yp;
}

int main()
{
  int x = 10;
  swap(&x, &x);
  printf("After swap(&x, &x): x = %d", x);
  return 0;
}

出力

スワップ後(&x、&x):x = 0

多くの標準アルゴリズムでは、変数をそれ自体と交換する必要がある場合があります。たとえば、変数をそれ自体と交換するQuickSortの実装を参照してください。上記の問題は、スワッピングの前に条件を設定することで回避できます。

#include <stdio.h>
void swap(int *xp, int *yp)
{
    if (xp == yp) // Check if the two addresses are same
      return;
    *xp = *xp + *yp;
    *yp = *xp - *yp;
    *xp = *xp - *yp;
}
int main()
{
  int x = 10;
  swap(&x, &x);
  printf("After swap(&x, &x): x = %d", x);
  return 0;
}

出力

スワップ後(&x、&x):x = 10

0
shobhit2905

トピックから外れているかもしれませんが、2つの異なる値の間で1つの変数を交換していることがわかっている場合は、配列ロジックを実行できる場合があります。このコード行が実行されるたびに、値が1と2の間でスワップされます。

n = [2, 1][n - 1]
0
quemeful

Javascriptの場合:

function swapInPlace(obj) {
    obj.x ^= obj.y
    obj.y ^= obj.x
    obj.x ^= obj.y
}

function swap(obj) {
    let temp = obj.x
    obj.x = obj.y
    obj.y = temp
}

両方のオプションの実行時間に注意してください。

このコードを実行して、測定しました。

console.time('swapInPlace')
swapInPlace({x:1, y:2})
console.timeEnd('swapInPlace') // swapInPlace: 0.056884765625ms

console.time('swap')
swap({x:3, y:6})
console.timeEnd('swap')        // swap: 0.01416015625ms

ご覧のように(そして多くの人が言っているように)、所定の場所でのスワップ(xor)は、temp変数を使用する他のオプションよりもかなり時間がかかります。

0
ofir_aghai
#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    a ^= b;
    b ^= a;
    a ^= b;
    printf("\nValue of A=%d B=%d ",a,b);
    return 1;
}
0
Siddiqui

簡単な方法で... 1行のロジックで

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    int a = 1,b = 2;
    a=a^b^(b=a);
    printf("\nValue of A=%d B=%d ",a,b);

    return 1;
}

または

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    int a = 1,b = 2;
    a=a+b-(b=a);
    printf("\nValue of A=%d B=%d ",a,b);

    return 1;
}
0

最良の答えは、XORを使用し、1行で使用することはクールです。

    (x ^= y), (y ^= x), (x ^= y);

x、yは変数であり、それらの間のコンマはシーケンスポイントを導入するため、コンパイラに依存しません。乾杯!

0
Ankit Sharma