【问题标题】:Fast algorithm for generating all combinations (n choose k) based on an initial input [duplicate]基于初始输入生成所有组合(n选择k)的快速算法[重复]
【发布时间】:2018-05-09 20:33:50
【问题描述】:

我正在研究一个应用集覆盖问题。在这项研究中,我想生成所有可能的组合。 IE。 n = 5 和 k = 3 产量

0 0 1
0 0 2 
0 0 3
etc..

这对于较小的问题没有问题,但是当 n 和 k 增加时,比如 n = 250 和 k = 6,组合的数量是 3.1920e+11。所有组合都不能存储在一个矩阵中,因此我需要一种算法,它可以计算 x 个组合,然后在给定第一个矩阵的端点的情况下计算 x 个下一个组合。有谁知道任何可以在 C/C++/CUDA 或 Matlab 中快速执行此操作的算法?

谢谢。

【问题讨论】:

  • 我认为您打算如何处理所有这些组合非常重要。或者您只想生成它们并存储到文件中?
  • 这个问题的主要目的是将它们存储在文件或数组/矩阵中。我稍后会使用这些组合进行一些计算,但我已经拥有了所有需要的代码。
  • 为什么0 0 0 不见了?
  • ^ 应该在问题中说明...
  • 所以 2 个数字相等是可以的,但不是吗?还有什么我们不知道的?

标签: c++ matlab combinations combinatorics


【解决方案1】:

我认为您将遇到的最大问题不是计算,而是磁盘写入速度或内存大小。顺便说一句,您似乎错误地确定了n = 250k = 6 的组合数量。你用uint64_t了吗?我的号码是244 140 625 000 000

所以对于这个数字,您需要~1.4 Petabyte (~1400 Tb) 的内存。这是你的主要问题。如果你有那么大的硬盘,写的时候最好使用memory mapping。您可以考虑使用多个线程进行写入:每个线程都会写入自己的内存块。

所以,我认为您应该考虑其他方法来提供组合以解决您的实际目标。

一个幼稚的解决方案。用内存映射对象更改std::ofstream

int main()
{
    const constexpr uint8_t N = 250;
    const constexpr uint8_t K = 6;
    const constexpr uint64_t CombinationsCount = std::pow(N, K);
    using TCombination = std::array<uint8_t, K>;

    std::cout << CombinationsCount << std::endl;

    std::ofstream file("output.txt");
    TCombination c;
    for (uint64_t i = 0; i < CombinationsCount; ++i)
    {
        auto I = i;
        for (auto j = 0; j < K; ++j)
        {
            c[j] = I % N;
            I /= N;
            file << (int)c[j];
        }
        file << std::endl;
    }

}

如果你想使用线程,只需将CombinationsCount 与核心数相除,然后给每个线程一个任务,从内存的特定地址(偏移量)写入。

您要求提供类似函数的解决方案。您可以传递不同的文件名称并使用不同的线程。买了还需要用到内存映射。

const constexpr uint8_t N = 250;
const constexpr uint8_t K = 6;
const constexpr uint64_t CombinationsCount = std::pow(N, K);
using TCombination = std::array<uint8_t, K>;

void Generate(uint64_t start, uint64_t size, const char* fileName)
{
    std::ofstream file(fileName);
    TCombination c;
    for (uint64_t i = start; i < start + size; ++i)
    {
        auto I = i;
        for (auto j = 0; j < K; ++j)
        {
            c[j] = I % N;
            I /= N;
            file << (int)c[j];
        }
        file << std::endl;
    }
}

int main()
{
    std::cout << CombinationsCount << std::endl;

    unsigned int threadsNum = std::thread::hardware_concurrency();

    std::vector<std::thread> workers;
    for (size_t i = 0; i < threadsNum; ++i)
        workers.emplace_back(
            Generate, 
            i * CombinationsCount / threadsNum,
            CombinationsCount / threadsNum,
            (std::string("output") + std::to_string(i)).c_str());

    for (size_t i = 0; i < threadsNum; ++i)
        workers[i].join();
}

【讨论】:

  • 我使用 Matlab 快速解决,但我可能超出了最大限制?正如我在另一条评论中所说,我使用一种方法将问题分割成更小的文件(每次生成 5GB 的组合)。这对于 k = 5 是可行的,但对于 k = 6 实际上是不可能的,因为每个 5GB 需要大约 70 分钟(在一台非常糟糕的计算机上)来生成和写入文件。因此,我希望存在能够在更短的时间内评估组合的 C/C++ 实现。
  • 检查文档的最大值。 32位是不够的。 64 还可以。
  • @proczell C++ 对于这个目的要好得多。正如我所说,使用内存映射进行磁盘写入,并且可能是线程。我将用简单的解决方案更新我的答案。您应该使用内存映射对象更改ofstream。我仍然鼓励你考虑一些其他的方法来结合你的实际目标。还是你真的有这么多内存?
  • 是的,这看起来很像我可以使用的东西。我的下一个问题是,如果我想把它分成几个函数调用。您的代码是否可以更改为从组合开始:2 13 54 110 214 并计算以下 1e7 组合?
  • @proczell 当然。将 for 循环移动到函数中,该函数需要开始 (i) 和结束 (CombinationsCount) 以及写入位置的指针。修改我的示例没有多大意义。无论如何,您应该处理内存映射。
【解决方案2】:

我正在研究一个应用集覆盖问题。在这项研究中,我想生成所有可能的组合。 ... 有谁知道任何可以在 C/C++/CUDA 或 Matlab 中快速执行此操作的算法?

没有“快速”生成所有可能的组合这样的事情。根据定义,随着 n 和 k 的增加,这非常慢: n!/((n-k)!k!) 上升得快于 (k/e)^n ,渐近地作为 n 的函数;因此,通过使用 GPU 使您的组合生成速度更快,只会让您将 n 和/或 k 增加一点点。

很抱歉听起来很说教,但您可能需要做一些事情,而不是尝试生成所有组合。

【讨论】:

  • 当然这对于too large的问题是不可行的,但是如果我可以增加too large的限制,至少这个方法可以申请了更多的问题。
  • 正如您自己观察到的,n 和 k 很快就会使这变得不可行。您不太可能从生成所有这些配置中获得很大的吸引力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 2012-09-10
  • 1970-01-01
  • 2021-01-10
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多