💨

[c++] n個からr個の組合せ列挙

2022/10/10に公開

n個からr個の組合せ列挙するライブラリを作成しました。
列挙した組合せを2次元配列(vector)に格納して、返すようにしています。
おそらく、あまり性能は良くないです。。。

再帰的に走査して組合せ作成する関数dfs_combinationを作成しましたが、引数が多くなってしまったので、最初の呼び出し用に関数combinationを用意しました。

combination("元になる配列arr", "取り出す要素数r")
といった感じにして、引数2つで呼べるようにしてます。

内容

関数dfs_combination
結果を変数resultに格納するようにしています

配列arr: 元になる配列
変数idx: 配列arrのindex。現在みている要素を表す。
変数 r: 列挙する数
配列pattern: 今作成中の組合せのパターン1つ
2次元配列 result: 生成した全ての組合せパターン

template <class T>
void dfs_combination(vector<T> &arr, int idx, int r, vector<T> &pattern, vector<vector<T>> &result) {
    if (r == 0) {
        result.push_back(pattern);
        return;
    }
    if ((idx + r) > arr.size()) {
        return;
    }

    // select
    pattern.push_back(arr[idx]);
    dfs_combination(arr, idx + 1, r - 1, pattern, result);
    pattern.pop_back();
    // not select
    dfs_combination(arr, idx + 1, r, pattern, result);
}

関数combination
関数dfs_combinationを呼び出すための関数です。

配列arr: 元になる配列
変数 r: 列挙する数

template <class T>
vector<vector<T>> combination(vector<T> &arr, int r) {
    if (arr.size() < r) {
        printf("combination ERROR: r is larger than arr.size()\n");
        exit(1);
    }

    vector<T> pattern;
    vector<vector<T>> result;
    dfs_combination(arr, 0, r, pattern, result);
    return result;
}

code

動作例になります。
以下の2つの例になります
・配列[0, 1, 2, 3, 4]から3個取り出す組合せ
・配列["aa", "aa", "cc", "dd", "ee"]から3個取り出す組合せ

デバッグ用に、2次元配列の内容を表示する関数を用意していますが、こちらの説明は割愛します。

#include <iostream>
#include <vector>
using namespace std;
#include <numeric>


// Combination  nCr ----------
template <class T>
void dfs_combination(vector<T> &arr, int idx, int r, vector<T> &pattern, vector<vector<T>> &result) {
    if (r == 0) {
        result.push_back(pattern);
        return;
    }
    if ((idx + r) > arr.size()) {
        return;
    }

    // select
    pattern.push_back(arr[idx]);
    dfs_combination(arr, idx + 1, r - 1, pattern, result);
    pattern.pop_back();
    // not select
    dfs_combination(arr, idx + 1, r, pattern, result);
}

template <class T>
vector<vector<T>> combination(vector<T> &arr, int r) {
    if (arr.size() < r) {
        printf("combination ERROR: r is larger than arr.size()\n");
        exit(1);
    }

    vector<T> pattern;
    vector<vector<T>> result;
    dfs_combination(arr, 0, r, pattern, result);
    return result;
}
// ------------------------------

// Debug用  配列の表示 ----------
template <class T>
void print_vector(vector<T> arr) {
    printf("[ ");
    for (int i = 0; i < arr.size(); i++) {
        //printf("%d", arr[i]);
        cout << arr[i];
        if (i != (arr.size() - 1)) printf(", ");
    }
    printf(" ]\n");
}

template <class T>
void print_vector2(vector<T> arr) {
    for (int i = 0; i < arr.size(); i++) {
        print_vector(arr[i]);
    }
}
// ------------------------------


int main() {
    vector<int> arr(5);
    iota(arr.begin(), arr.end(), 0);
    vector<vector<int>> res = combination(arr, 3);
    print_vector2(res);

    vector<string> arr2 = {"aa", "aa", "cc", "dd", "ee"};
    vector<vector<string>> res2 = combination(arr2, 3);
    print_vector2(res2);
}

実行結果は、以下のような感じ。

[ 0, 1, 2 ]
[ 0, 1, 3 ]
[ 0, 1, 4 ]
[ 0, 2, 3 ]
[ 0, 2, 4 ]
[ 0, 3, 4 ]
[ 1, 2, 3 ]
[ 1, 2, 4 ]
[ 1, 3, 4 ]
[ 2, 3, 4 ]
[ aa, aa, cc ]
[ aa, aa, dd ]
[ aa, aa, ee ]
[ aa, cc, dd ]
[ aa, cc, ee ]
[ aa, dd, ee ]
[ aa, cc, dd ]
[ aa, cc, ee ]
[ aa, dd, ee ]
[ cc, dd, ee ]

Discussion