C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でのバイナリ表現の循環順列:グレイコードによる効率的な実装

問題の概要

2つの整数 nstart が与えられたとき、0 から 2^n − 1 までの整数の順列 p を、以下の条件を満たすように求める問題を考えます。

  • p[0] = start であること
  • 隣接する要素 p[i] と p[i+1] の2進数(バイナリ)表現は、1ビットのみ異なること
  • 最初と最後の要素である p[0] と p[2^n − 1] も、1ビットのみ異なること

たとえば、n = 2、start = 3 が入力された場合、答えは [3, 2, 0, 1] となります。これを2進数で表すと [11, 10, 00, 01] であり、隣り合う値同士(最後の要素と最初の要素を含む)が必ず1ビットだけ異なっていることが確認できます。

解法のアプローチ:グレイコードの活用

この問題を効率的に解く鍵となるのがグレイコード(Gray code)です。グレイコードとは、隣接する数値の2進表現が必ず1ビットしか異ならないように並べた数列のことです。

n ビットのグレイコードは、次のシンプルな式で生成できます。

g(i) = i XOR (i >> 1)

さらに、このグレイコード全体に start をXOR演算で適用することで、任意の値から始まる循環順列が得られます。XORの性質により、「隣接要素が1ビット違い」という性質は保たれたまま、最初の要素が必ず start になります。

アルゴリズムの手順

  1. 結果を格納する配列 ans を用意する
  2. i を 0 から 2^n − 1 まで繰り返し、ans に「start XOR i XOR (i / 2)」を挿入する
  3. ans を返す

C++での実装例

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
    cout << "[";
    for(int i = 0; i<v.size(); i++){
        cout << v[i] << ", ";
    }
    cout << "]"<<endl;
}
class Solution {
public:
    vector<int> circularPermutation(int n, int start) {
        vector <int> ans;
        for(int i = 0 ; i < 1<<n; i++){
            ans.push_back(start ^ i ^(i>>1));
        }
        return ans;
    }
};
main(){
    Solution ob;
    print_vector(ob.circularPermutation(5,3));
}

入力

5
3

出力

[3, 2, 0, 1, 5, 4, 6, 7, 15, 14, 12, 13, 9, 8, 10, 11, 27, 26, 24, 25, 29, 28,
30, 31, 23, 22, 20, 21, 17, 16, 18, 19]

計算量の評価

このアルゴリズムは 0 から 2^n − 1 までの各値を一度ずつ処理するため、時間計算量は O(2^n)、結果を格納するための空間計算量も O(2^n) となります。ビット演算のみで構成されているためオーバーヘッドが極めて小さく、n = 20 程度の規模でも実用的な速度で動作する点が大きなメリットです。

  1. C++で数値を2進数表現に変換する方法【再帰処理を解説】

    2進数(バイナリ数)とは、0と1という2つの数字のみで構成される数値表現のことです。例えば、01010111 のような形で表されます。コンピュータの内部では、すべてのデータがこの2進数として扱われています。 ある数値を2進数形式で表現する方法はいくつかあります。本記事では、代表的な「再帰を使った方法」を中心に解説します。 再帰を用いた方法 この方法では、再帰呼び出しを利用して数値を2進数形式で表現します。数値を2で割り続けながら、その余りを順に出力していくことで、2進数表現を得ることができます。 アルゴリズム ステップ1: 数値が1より大きい場合、ステップ2とステップ3を実行します。 ステップ

  2. Pythonで数値の2進表現が回文かどうかを判定する方法

    ある整数 n が与えられたとき、その2進表現が回文(前から読んでも後ろから読んでも同じ並び)になっているかどうかを判定する方法を解説します。 例えば、入力が n = 9 の場合を考えてみましょう。9の2進表現は「1001」であり、これは回文であるため、出力は True になります。 解法のアプローチ この問題は、数値の2進表現をビット単位で反転し、元の数値と比較することで解けます。具体的な手順は以下の通りです。 ans を 0 で初期化する num が 0 より大きい間、次の処理を繰り返す ans を1ビット左シフトする(ans × 2 と同等) num が奇数(最下位ビットが1)なら、