如何在C中生成数组的笛卡尔积?

时间:2015-08-15 20:40:34

标签: c combinations cartesian-product

我在尝试找出获取阵列的笛卡尔积并指定多少次的方法时遇到了麻烦。 这是我想要做的一个例子(伪代码):

int arr = {0, 1, 2, 3};
int n = 2;

int[][] result = cartesian(arr, n);
//result would be {{0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0},..., {3, 3}}

n = 3;
result = cartesian(arr, n);
//since n is now 3, result would be{{0, 0, 0}, {0, 0, 1},..., {3, 3, 3}} and so on.

非常感谢帮助

3 个答案:

答案 0 :(得分:2)

最简单的方法是递归地执行此操作。如果你有一个大小为N的序列,并且你想要找到它自身的第K阶笛卡尔积,那么就会产生N ^ K个元素。这里有一些伪代码可以让你开始:

-webkit-margin-before: 0;
    -webkit-margin-after: 0;
    -webkit-padding-start: 0;

这里,这将递归生成所有元素。为了生成长度为k的序列,我们首先选择一个元素,修复它,并从所选元素开始生成长度为k-1的所有可能序列。如果我们对序列中的所有元素执行此操作,我们就会生成笛卡尔积中的每个元素。

现在,既然你有伪代码,我相信它应该是常规的实现它。 :)

答案 1 :(得分:1)

没有递归:)在C ++ 11中你会这样做(其中cartesian()是你想要的功能)

#include <deque>
#include <iostream>
using namespace std;

void add_all_once(deque<deque<int>>& result, const deque<int>& vec, int which) {
    for (decltype(vec.size()) i = 0; i < vec.size(); ++i) {
        deque<int> to_be_pushed = result[which];
        to_be_pushed.push_back(vec[i]);
        result.emplace_back(std::move(to_be_pushed));
    }
}

deque<deque<int>> cartesian(const deque<int>& vec, int n) {
    deque<deque<int>> cartesian_product;
    decltype(vec.size()) size = vec.size();

    // Fill 1 dimension
    for (decltype(vec.size()) i = 0; i < size; ++i) {
        cartesian_product.push_back(deque<int>({vec[i]}));
    }

    // Fill all possibilities once
    for (int i = 0; i < n; ++i) {
        decltype(cartesian_product.size()) current_size = cartesian_product.size();

        for (decltype(cartesian_product.size()) j = 0; j < current_size; ++j) {
            add_all_once(cartesian_product, vec, j);
        }

        for (decltype(cartesian_product.size()) j = 0; j < current_size; ++j) {
            cartesian_product.pop_front();
        }
    }


    return cartesian_product;
}

int main() {
    deque<int> vec = {1, 2, 3};

    auto n = 2;
    auto return_val = cartesian(vec, n);

    for (int i = 0; i < return_val.size(); ++i) {
        for (int j = 0; j < return_val.at(i).size(); ++j) {
            cout << return_val[i][j] << ' ';
        } cout << endl;
    }

    return 0;
}

答案 2 :(得分:0)

您正在尝试生成一组k-fold笛卡尔积。这基本上等同于k-combinations of the set with repetitions。有一个简单的算法from Rosetta Code在C中实现它:

#include <stdio.h>

const char * donuts[] = { "iced", "jam", "plain", "something completely different" };

long choose(int * got, int n_chosen, int len, int at, int max_types)
{
        int i;
        long count = 0;
        if (n_chosen == len)
        {
                if (!got) return 1;

                for (i = 0; i < len; i++)
                {
                        printf("%s\t", donuts[got[i]]);
                }
                printf("\n");
                return 1;
        }

        for (i = at; i < max_types; i++)
        {
                if (got) got[n_chosen] = i;
                count += choose(got, n_chosen + 1, len, i, max_types);
        }
        return count;
}

int main()
{
        int chosen[3];
        choose(chosen, 0, 2, 0, 3);

        printf("\nWere there ten donuts, we'd have had %ld choices of three\n",
                choose(0, 0, 3, 0, 10));
        return 0;
}

输出:

iced    iced

iced    jam
iced    plain
jam     jam
jam     plain
plain   plain

Were there ten donuts, we'd have had 220 choices of three

您可以轻松修改此项以处理数字数组而不是C风格的字符串。