【问题标题】:N choose k for large n and kN 为大的 n 和 k 选择 k
【发布时间】:2013-10-11 21:54:02
【问题描述】:

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

我已经编写了一个递归算法(在 C++ 中),它工作得很好,但是对于大量它会崩溃并超出堆空间。

我该如何解决这个问题?如何计算所有 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;
}

【问题讨论】:

标签: c++ arrays algorithm combinations


【解决方案1】:

这里是非递归算法:

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))) );

【讨论】:

  • 你的算法有效,但是:(10 5) 1 秒后正确结束,(10 6) 崩溃
  • @Nadir 你复制错了,它适用于任何 N/K,K 高达 2^30(32 位)和 2^62(64 位)
  • 是的,我写错了,我的意思是 (100 6) 不是 (10 6)。在我的电脑上(100 6)不工作,在你的电脑上工作吗?
  • 是的,你是对的,它有效,这是 Netbeans 的问题!真的谢谢你!
  • int currentCombination[k]; C++ 中不允许使用 VLA
【解决方案2】:

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

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

如果你确实需要检查所有这些组合,那么也许你应该考虑使用像genetic algorithmsimulated annealing 这样的搜索技术。这两种爬山搜索技术都提供了在相对较短的时间内搜索大空间以获得接近最优解的能力,但都不能保证找到最优解。

【讨论】:

  • 我有一个困难的算法,它使用矩阵的一行的布尔数据作为数组,并在取值 1 的位置的 k 值的增加值处找到子集。在正常情况下,行中的 1很少,但我不能依赖这个。
【解决方案3】:

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

下面是一些示例代码:

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()) );

不要忘记包含算法。

【讨论】:

【解决方案4】:

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

  1. 如果您想计算 (n 选择 k) 相对较小的值,例如 n,k

  2. 如果 n,k 很大(例如 n=1000000, k=500000),您可能会对使用 Sterlings 公式的近似结果感到满意阶乘:(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).

【讨论】:

  • 真的很抱歉,但是:“我必须在长度为 n 的数组中找到 k 个元素的所有可能组合,并且对于每个集合(长度为 k),对所选元素进行一些计算。”对我来说,它已经足够清晰了。我需要具有 n 个元素的集合中 k 个元素的所有子集。我需要所有的集合,而不是数字。正如您在第 1 点中指出的那样:n,k
猜你喜欢
  • 2012-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-24
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
相关资源
最近更新 更多