【问题标题】:Efficient way to compute unique permutations with restrictions计算具有限制的唯一排列的有效方法
【发布时间】:2021-02-15 08:12:36
【问题描述】:

所以我知道有很多方法可以计算唯一排列。 通常在生成过程中应用限制比之后删除它们要慢。 所以假设我有一个包含 10 个元素的向量。 举个例子:

std::vector<int> v = {7, 5, 16, 8, 5, 8, 1, 7, 3, 25, 109, 8};

我有一个独特的限制,我知道我不允许连续有相同的三个数字,所以在任何地方包含 {8,8,8} 的所有排列都是无效的。 如果我有一个巨大的数组,例如20 个元素我认为如果我跳过所有以 {8,8,8} 开头的排列,我可以节省很多时间。

有没有什么可以有效地做这样的事情?我如何确定在什么时候添加检查每个排列的额外减速是有意义的?

【问题讨论】:

  • 您可以删除任何三倍的值
  • @ti7 不确定你的意思。您如何将其合并到排列生成代码中,而不是简单地遍历每个排列然后检查结果的有效性? IE。如果有 100k 排列,但其中 80k 是“无效的”,你怎么根本不生成那些 80k?
  • 您是要计算排列,还是实际列出它们(生成它们)?
  • 如果你有 20 个元素,你就有大约 quintillion 排列要生成。您要么 a) 没有足够的存储空间来生成它们,要么 b) 没有足够的时间来生成它们。好吧,也许你会:每个 1 纳秒,这就像 70 多年。
  • 您知道“通常在生成期间应用限制比之后删除它们要慢。”但是您立即问“如果我跳过所有以 {8,8,8} 开头的排列,可以(我)节省很多时间。”是什么让您认为这是一个特例?

标签: c++ permutation restriction


【解决方案1】:

根据您的限制,在按顺序迭代时,可以从第一个无效排列块中跳过无效排列块。

表格编号的第一个无效排列

y y y 8 8 x x 8 x

y y y 8 8 8 x1 x2 x3 与 x1

所以你可以直接进入最后一个无效排列

y y y 8 8 8 x3 x2 x1 与 x1

所以只需颠倒最后的数字。

bool is_valid(const std::vector<int>& v)
{
    auto it = std::find(v.begin(), v.end(), 8);
    
    return v.end() - it >= 3 && (*it != *(it + 1) || *it != *(it + 2));
}

void go_to_last_invalid(std::vector<int>& v)
{
    auto it = std::find(v.begin(), v.end(), 8);
    std::reverse(it + 3, v.end());
}

int main()
{
    std::vector<int> v = {1, 7, 7, 8, 8, 8, 9, 10};
    do {
        if (!is_valid(v)) { go_to_last_invalid(v); continue; }
        for (auto e : v) { std::cout << e << " "; } std::cout << std::endl;
    } while (std::next_permutation(v.begin(), v.end()));
}

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多