具有额外限制的排列

发布于 2024-08-31 12:30:27 字数 1139 浏览 6 评论 0原文

我有一组项目,例如:{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} 表示。

I have a set of items, for example: {1,1,1,2,2,3,3,3}, and a restricting set of sets, for example {{3},{1,2},{1,2,3},{1,2,3},{1,2,3},{1,2,3},{2,3},{2,3}. I am looking for permutations of items, but the first element must be 3, and the second must be 1 or 2, etc.

One such permutation that fits is:
{3,1,1,1,2,2,3}

Is there an algorithm to count all permutations for this problem in general? Is there a name for this type of problem?

For illustration, I know how to solve this problem for certain types of "restricting sets".
Set of items: {1,1,2,2,3}, Restrictions {{1,2},{1,2,3},{1,2,3},{1,2},{1,2}}. This is equal to 2!/(2-1)!/1! * 4!/2!/2!. Effectively permuting the 3 first, since it is the most restrictive and then permuting the remaining items where there is room.

Also... polynomial time. Is that possible?

UPDATE: This is discussed further at below links. The problem above is called "counting perfect matchings" and each permutation restriction above is represented by a {0,1} on a matrix of slots to occupants.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一袭水袖舞倾城 2024-09-07 12:30:29

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

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

排列的数量就是从根节点到最终集合的节点的路径数量。

To save space, you could build a directed graph instead of a tree.

  • Create a root node.
  • Create a node for each item in the
    first set, and link from the root to
    the new nodes.
  • Create a node for each item in the
    second set, and link from each first
    set item to each second set item.
  • ...

The number of permutations is then the number of paths from the root node to the nodes of the final set.

初懵 2024-09-07 12:30:28

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

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

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 的散列内容应该不会太难。

可以通过用集合<>替换向量并定义<>来改进该解决方案。 unordered_set 的运算符。这会将更多相同的子问题折叠成单个映射元素,并进一步减少缓解指数爆炸。

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

All of the other solutions here are exponential time--even for cases that they don't need to be. This problem exhibits similar substructure, and so it should be solved with dynamic programming.

What you want to do is write a class that memoizes solutions to subproblems:

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

The code illustrates choosing an index i, which should be chosen at a place where there are v[i].size() is small. The other option is to choose a number n, which should be one for which there are few locations v that it can be placed in. I'd say the minimum of the two deciding factors should win.

Also, you'll have to define a hash function for Problem -- that shouldn't be too hard using boost's hash stuff.

This solution can be improved by replacing the vector with a set<>, and defining a < operator for unordered_set. This will collapse many more identical subproblems into a single map element, and further reduce mitigate exponential blow-up.

This solution can be further improved by making Problem instances that are the same except that the numbers are rearranged hash to the same value and compare to be the same.

z祗昰~ 2024-09-07 12:30:28

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

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

You might consider a recursive solution that uses a pool of digits (in the example you provide, it would be initialized to {1,1,1,2,2,3,3,3}), and decides, at the index given as a parameter, which digit to place at this index (using, of course, the restrictions that you supply).

If you like, I can supply pseudo-code.

暖阳 2024-09-07 12:30:28

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

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

编辑

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

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

You could build a tree.
Level 0: Create a root node.
Level 1: Append each item from the first "restricting set" as children of the root.
Level 2: Append each item from the second restricting set as children of each of the Level 1 nodes.
Level 3: Append each item from the third restricting set as children of each of the Level 2 nodes.
...

The permutation count is then the number of leaf nodes of the final tree.

Edit

It's unclear what is meant by the "set of items" {1,1,1,2,2,3,3,3}. If that is meant to constrain how many times each value can be used ("1" can be used 3 times, "2" twice, etc.) then we need one more step:

  • Before appending a node to the tree, remove the values used on the current path from the set of items. If the value you want to append is still available (e.g. you want to append a "1", and "1" has only been used twice so far) then append it to the tree.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文