N选择k表示大n和k

时间:2013-10-11 21:54:02

标签: c++ arrays algorithm combinations

我有n个元素存储在一个数组中,并且数量为k的可能子集超过n(n选择k)。 我必须在长度为n的数组中找到k个元素的所有可能组合,并且对于每个组(长度为k),对选择的元素进行一些计算。

我编写了一个递归算法(用C ++编写),但是对于大量数据,它会在堆空间中崩溃。

我该如何解决这个问题?如何计算所有n组n为大n和k选择k? 有没有可以帮助我的C ++库?

我知道这是一个np问题,但我会编写最好的代码来计算可能的最大数字。

哪个大概是最大的数字(n和k),超出这个数字是不可行的?

我只是要求最好的算法,而不是不可行的空间/工作。

这是我的代码

vector<int> people;
vector<int> combination;

void pretty_print(const vector<int>& v) 
{
    static int count = 0;
    cout << "combination no " << (++count) << ": [ ";
    for (int i = 0; i < v.size(); ++i) { cout << v[i] << " "; }
        cout << "] " << endl;
}

void go(int offset, int k) 
{
    if (k == 0) {
        pretty_print(combination);
        return;
    }
    for (int i = offset; i <= people.size() - k; ++i) {
        combination.push_back(people[i]);
        go(i+1, k-1);
        combination.pop_back();
    }
}

int main() {
    int n = #, k = #;

    for (int i = 0; i < n; ++i) { people.push_back(i+1); }
        go(0, k);

    return 0;
}

4 个答案:

答案 0 :(得分:2)

这是非递归算法:

const int n = ###;
const int k = ###;

int currentCombination[k];
for (int i=0; i<k; i++)
    currentCombination[i]=i;
currentCombination[k-1] = k-1-1; // fill initial combination is real first combination -1 for last number, as we will increase it in loop

do
{
    if (currentCombination[k-1] == (n-1) ) // if last number is just before overwhelm
    {
        int i = k-1-1;
        while (currentCombination[i] == (n-k+i))
            i--;

        currentCombination[i]++;

        for (int j=(i+1); j<k; j++)
            currentCombination[j] = currentCombination[i]+j-i;
    }
    else
        currentCombination[k-1]++;

    for (int i=0; i<k; i++)
        _tprintf(_T("%d "), currentCombination[i]);
    _tprintf(_T("\n"));

} while (! ((currentCombination[0] == (n-1-k+1)) && (currentCombination[k-1] == (n-1))) );

答案 1 :(得分:1)

您的递归算法可能会破坏堆栈。如果你使它非递归,那么这将有所帮助,但如果你的情况真的100选择10,它可能无法解决问题。你有两个问题。世界上很少有计算机拥有17兆兆字节的内存。通过17万亿次迭代来生成所有组合将花费太长时间。您需要重新考虑问题,并提出更合理的N选择K案例,或者只处理组合的某个子集。

您可能不希望最多处理超过十亿或两种组合 - 甚至需要一些时间。这意味着大约41选择10到大约44选择10.减少N或K将有所帮助。尝试编辑您的问题并发布您要解决的问题以及您认为需要完成所有组合的原因。可能有一种方法可以在不经过所有组合的情况下解决它。

如果事实证明你确实需要经历所有这些组合,那么你应该考虑使用像genetic algorithmsimulated annealing这样的搜索技术。这两种爬山搜索技术都能够在相对较短的时间内搜索大空间,以获得接近最优的解决方案,但无法保证找到最佳解决方案。

答案 2 :(得分:0)

您可以使用next_permutation()中的algorithm.h生成所有可能的组合。

以下是一些示例代码:

bool is_chosen(n, false);
fill(is_chosen.begin() + n - k, is_chosen.end(), true); 
do
{
    for(int i = 0; i < n; i++)
    {
        if(is_chosen[i])
            cout << some_array[i] << " ";
    }
    cout << endl;
} while( next_permutation(is_chosen.begin(), is_chosen.end()) );

不要忘记包含算法。

答案 3 :(得分:0)

正如我在评论中所说,目前尚不清楚你真正想要的是什么。

  1. 如果您想为相对较小的值计算( n选择k ),请说 n,k &lt; 100左右,您可能希望使用递归方法,使用Pascals三角形。

  2. 如果 n,k 很大(比如 n = 1000000,k = 500000 ),您可能会对使用Sterling公式的近似结果感到满意factorial:( n选择k )= exp(loggamma(n)-loggamma(k)-loggamma(n-k)),通过Sterling公式计算loggamma(x)

  3. 如果你想要( n选择k )所有或许多 k 但是相同的 n ,你可以简单地迭代 k 并使用( n选择k + 1 )=(( n选择k )*( nk )) /( K + 1 )。