具有额外限制的排列

时间:2010-05-07 16:15:59

标签: algorithm math permutation combinatorics

我有一组项目,例如:{1,1,1,2,2,3,3,3}和一组限制,例如{{3},{1,2} ,{1,2,3},{1,2,3},{1,2,3},{1,2,3},{2,3},{2,3}。我正在寻找项目的排列,但第一个元素必须是3,第二个元素必须是1或2,等等。

一种适合的排列是: {3,1,1,1,2,2,3}

是否有算法可以统计此问题的所有排列?这类问题有名称吗?

为了说明,我知道如何为某些类型的“限制集”解决这个问题。 项目集:{1,1,2,2,3},限制{{1,2},{1,2,3},{1,2,3},{1,2},{1,2 }}。这相当于2!/(2-1)!/ 1! * 4!/ 2!/ 2!。首先有效地置换3,因为它是最具限制性的,然后置换有空间的剩余物品。

也是...多项式时间。这可能吗?

更新:这将在以下链接中进一步讨论。上面的问题被称为“计算完美匹配”,上面的每个排列限制都由占用者的矩阵矩阵上的{0,1}表示。

4 个答案:

答案 0 :(得分:1)

您可能会考虑使用数字池的递归解决方案(在您提供的示例中,它将初始化为{1,1,1,2,2,3,3,3}),并决定,作为参数给出的索引,该数字放在该索引处(当然使用您提供的限制)。

如果您愿意,我可以提供伪代码。

答案 1 :(得分:1)

你可以建造一棵树。 级别0:创建根节点。 级别1:将第一个“限制集”中的每个项目作为根的子项附加。 级别2:将第二个限制集中的每个项目作为每个1级节点的子级附加。 级别3:将第三个限制集中的每个项目作为每个级别2节点的子级附加。 ...

排列计数是最终树的叶节点数。

修改

目前还不清楚“项目集”{1,1,1,2,2,3,3,3}是什么意思。如果这意味着约束每个值可以使用多少次(“1”可以使用3次,“2”可以使用两次,等等),那么我们还需要一个步骤:

  • 在将节点附加到树之前,从项集中删除当前路径上使用的值。如果您要追加的值仍然可用(例如,您想要追加“1”,而“1”到目前为止仅使用了两次),则将其附加到树中。

答案 2 :(得分:1)

这里所有其他解决方案都是指数时间 - 即使是他们不需要的情况。这个问题表现出类似的子结构,因此应该用动态规划来解决。

你想要做的是写一个记住子问题解决方案的类:

class Counter {
  struct Problem {
     unordered_multiset<int> s;
     vector<unordered_set<int>> v;
  };

  int Count(Problem const& p) {
    if (m.v.size() == 0)
      return 1;
    if (m.find(p) != m.end())
      return m[p];
    // otherwise, attack the problem choosing either choosing an index 'i' (notes below)
    // or a number 'n'.  This code only illustrates choosing an index 'i'.
    Problem smaller_p = p;
    smaller_p.v.erase(v.begin() + i);
    int retval = 0;
    for (auto it = p.s.begin(); it != p.s.end(); ++it) {
      if (smaller_p.s.find(*it) == smaller_p.s.end())
        continue;
      smaller_p.s.erase(*it);
      retval += Count(smaller_p);
      smaller_p.s.insert(*it);      
    }
    m[p] = retval;
    return retval;
  }

  unordered_map<Problem, int> m;
};

代码说明了选择一个索引i,它应该在v [i] .size()很小的地方选择。另一个选择是选择一个数字n,它应该是一个可以放置的位置v很少的数字。我会说两个决定因素中的最小值应该会赢。

另外,你必须为问题定义一个哈希函数 - 使用boost的哈希函数不应该太难。

该解决方案可以通过用集合&替换矢量并且定义&lt; unordered_set的运算符。这会将更多相同的子问题折叠成一个地图元素,并进一步减少指数的爆发。

通过使问题实例相同,除了将数字重新排列为相同的值并进行比较之外,可以进一步改进此解决方案。

答案 3 :(得分:0)

为了节省空间,您可以构建有向图而不是树。

  • 创建根节点。
  • 为每个项目创建一个节点 首先设置,并从根链接到 新节点。
  • 为每个项目创建一个节点 第二组,并从每个第一组链接 将项目设置为每个第二个设置项目。
  • ...

然后,排列的数量是从根节点到最终集节点的路径数。