计算多个列表中项目对的组合而不重复

发布于 2024-07-24 07:47:29 字数 865 浏览 8 评论 0原文

假设我们有多个项目对列表,例如:

  1. {12,13,14,23,24}
  2. {14,15,25}
  3. {16,17,25,26,36}

其中 12 是一对项目“1”和“2”(因此 21 相当于 12),我们想要计算可以从每个列表中选择项目对的方式数量,这样就没有单一项目重复。 您必须从每个列表中选择一对,并且只能选择一对。 每个列表中的项目数和列表数是任意的,但您可以假设至少有两个列表,每个列表至少有一对项目。 这些对由有限字母表中的符号组成,假设为数字 [1-9]。 此外,列表既不能包含重复的对 {12,12} 或 {12,21},也不能包含对称对 {11}。

更具体地说,在上面的示例中,如果我们从第一个列表中选择一对项目 14,那么我们对第二个列表的唯一选择就是 25,因为 14 和 15 包含“1”。 因此,第三个列表中的唯一选择是 36,因为 16 和 17 包含“1”,而 25 和 26 包含“2”。

有谁知道一种有效的方法来计算项目对的总组合,而无需遍历每个选择排列并询问“这是一个有效的选择吗?”,因为每个列表可以包含数百个对的物品?


更新

在花了一些时间之后,我意识到当没有一个列表共享不同的对时,计算组合的数量是微不足道的。 然而,一旦两个或多个列表之间共享不同的对,组合公式就不适用。

到目前为止,我一直在试图弄清楚是否有一种方法(使用组合数学而不是暴力)来计算每个列表具有相同项目对的组合数量。 例如:

  1. {12,23,34,45,67}
  2. {12,23,34,45,67}
  3. {12,23,34,45,67}

Given a scenario where we have multiple lists of pairs of items, for example:

  1. {12,13,14,23,24}
  2. {14,15,25}
  3. {16,17,25,26,36}

where 12 is a pair of items '1' and '2' (and thus 21 is equivalent to 12), we want to count the number of ways that we can choose pairs of items from each of the lists such that no single item is repeated. You must select one, and only one pair, from each list. The number of items in each list and the number of lists is arbitrary, but you can assume there are at least two lists with at least one pair of items per list. And the pairs are made from symbols from a finite alphabet, assume digits [1-9]. Also, a list can neither contain duplicate pairs {12,12} or {12,21} nor can it contain symmetric pairs {11}.

More specifically, in the example above, if we choose the pair of items 14 from the first list, then the only choice we have for the second list is 25 because 14 and 15 contain a '1'. And consequently, the only choice from the third list is 36 because 16 and 17 contain a '1', and 25 and 26 contain a '2'.

Does anyone know of an efficient way to count the total combinations of pairs of items without going through every permutation of choices and asking "is this a valid selection?", as the lists can each contain hundreds of pairs of items?


UPDATE

After spending some time with this, I realized that it is trivial to count the number of combinations when none of the lists share a distinct pair. However, as soon as a distinct pair is shared between two or more lists, the combinatorial formula does not apply.

As of now, I've been trying to figure out if there is a way (using combinatorial math and not brute force) to count the number of combinations in which every list has the same pairs of items. For example:

  1. {12,23,34,45,67}
  2. {12,23,34,45,67}
  3. {12,23,34,45,67}

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

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

发布评论

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

评论(7

静水深流 2024-07-31 07:47:29

问题是#P-完整的。 这比 NP 完全问题更难。 这就像找到 SAT 考试中满意的作业数量一样困难。

减少来自完美匹配。 假设您有图 G = {V, E},其中 E(边集)是顶点对列表(由边连接的顶点对) )。 然后通过使用 E|V|/2 副本对“项目对”的实例进行编码。 换句话说,E 的副本数量等于顶点数量的一半。 现在,您的情况中的“命中”将对应于没有重复顶点的 |V|/2 边,这意味着所有 |V| 顶点都被覆盖。 这就是完美匹配的定义。 每一次完美的匹配都会很成功——这是一一对应的。

The problem is #P-complete. This is even HARDER than NP-complete. It is as hard as finding the number of satisfying assignments to an instance of SAT.

The reduction is from Perfect matching. Suppose you have the graph G = {V, E} where E, the set of edges, is a list of pairs of vertices (those pairs that are connected by an edge). Then encode an instance of "pairs of items" by having |V|/2 copies of E. In other words, have a number of copies of E equal to half of the number of vertices. Now, a "hit" in your case would correspond to |V|/2 edges with no repeated vertices, implying that all |V| vertices were covered. This is the definition of a perfect matching. And every perfect matching would be a hit -- it's a 1-1 correspondence.

热鲨 2024-07-31 07:47:29

可以说列表中的每个元素都是图中的一个节点。 如果两个节点可以一起选择,则它们之间有一条边(它们没有公共符号)。 同一列表的两个节点之间没有边。 如果我们有 n 个列表,问题就是找到图中大小为 n 的派系的数量。 不存在大于 n 个元素的团。 鉴于找出至少一个这样的派系是否存在是 np 完全的,我认为这个问题是 np 完全的。 请参阅:http://en.wikipedia.org/wiki/Clique_problem

正如所指出的,我们有证明解决这个问题可以解决Clique问题,证明这是NP完全的。 如果我们可以计算所需集合的数量,即 n 大小的派系的数量,那么我们就知道是否至少存在一个大小为 n 的派系。 不幸的是,如果没有大小为 n 的派系,那么我们不知道是否存在大小为 k < 的派系。 名词

另一个问题是我们是否可以表示这个问题中的任何图。 我想是的,但我不确定。

我仍然觉得这是 NP 完全的

Lets says that every element in the lists is a node in a graph. There is an edge between two nodes if they can be selected together (they have no common symbol). There is no edge between two nodes of the same list. If we have n lists the problem is to find the number of cliques of size n in this graph. There is no clique which is bigger than n elements. Given that finding out whether at least one such clique exists is np-complete I think this problem is np-complete. See: http://en.wikipedia.org/wiki/Clique_problem

As pointed out we have to prove that solving this problem can solve the Clique problem to show that this is NP-complete. If we can count the number of required sets ie the number of n size cliques then we know whether there is at least one clique with size n. Unfortunatelly if there is no clique of size n then we don't know whether there are cliques with size k < n.

Another question is whether we can represent any graph in this problem. I guess yes but I am not sure about it.

I still feel this is NP-Complete

惟欲睡 2024-07-31 07:47:29

虽然问题看起来很简单,但它可能与 NP 完全设置覆盖问题有关。 因此,可能没有有效的方法来检测有效组合,因此也没有有效的方法来计算它们。

更新

我认为列表项是成对的,因为这似乎使问题更难攻击 - 您必须检查一项的两个属性。 所以我寻找一种方法将对减少为标量项并找到了一种方法。

n 个符号的集合映射到前 n 个素数的集合 - 我将调用此函数 M。 对于符号 09 的情况,我们获得以下映射,例如 M(4) = 11

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} => {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}

现在我们可以使用映射 X 将一对 (n, m) 映射到 nm 映射的乘积。 这会将 (2, 5) 对转换为 X((2, 5)) = M(2) * M(5) = 5 * 13 = 65

X((n, m)) = M(n) * M(m)

为什么这一切? 如果我们有来自两个列表的两对 (a, b)(c, d),请使用映射 X 将它们映射到 xy 相乘,我们得到乘积 x * y = M(a) * M(b) * M(c) * M(d)< /code> - 四个素数的乘积。 如果我们有 w 列表,我们可以通过从每个列表中选择一对来扩展乘积,并获得 2w 个素数的乘积。 最后一个问题是,这个乘积告诉我们关于我们选择和相乘的对的什么信息? 如果所选对形成有效选择,我们永远不会选择一个符号两次,因此该乘积不包含两次质数,并且是 广场免费。 如果选择无效,则乘积至少包含两次素数并且不是无平方的。 这是最后一个例子。

X((2, 5)) = 5 * 13 = 65
X((3, 6)) = 7 * 17 = 119
X((3, 4)) = 7 * 11 = 77

选择 2536 得到 65 * 119 = 7735 = 5 * 7 * 13 * 17 并且是无平方的,因此有效。 选择 3634 得到 119 * 77 = 9163 = 7² * 11 * 17 并且不是无平方的,因此无效。

另请注意,这很好地保留了对称性 - X((m, n)) = X((n, m)) - 并禁止对称对,因为 X((m, m)) = M(m) * M (m) 不是无平方的。

我不知道这是否有任何帮助,但现在您知道了并且可以思考它...^^


这是将 3-SAT 问题简化为该问题的第一部分。 3-SET 问题如下。

(!A | B | C) & (B | !C | !D) & (A | !B)

这是我所得到的减少量。

  • mn 代表一对,
  • 一条线代表一个列表,
  • 一个星号代表一个任意唯一符号

A1-A1'    !A1-!A1'     => Select A true or false
B1-B1'    !B1-!B1'     => Select B true or false
C1-C1'    !C1-!C1'     => Select C true or false
D1-D1'    !D1-!D1'     => Select D true or false

A1-*   !B1-*   !C1-*   => !A | B | C

A2-!A1'   !A2-A1'      => Create a copy of A
B2-!B1'   !B2-B1'      => Create a copy of B
C2-!C1'   !C2-C1'      => Create a copy of C
D2-!D1'   !D2-D1'      => Create a copy of D

!B2-*  C2-*    D2-*    => B | !C | !D

(How to perform a second copy of the four variables???)

!A3-*  B3-*

如果我(或其他人)可以完成这个简化并展示如何在一般情况下做到这一点,这将证明问题是 NP 完全的。 我只是坚持第二次复制变量。

While the problem looks quite simple it could be related to the NP-complete Set Cover Problem. So it could be possible that there is no efficent way to detect valid combinations, hence no efficent way to count them.

UPDATE

I thought about the list items beeing pairs because it seems to make the problem harder to attack - you have to check two properties for one item. So I looked for a way to reduce the pair to a scalar item and found a way.

Map the set of the n symbols to the set of the first n primes - I will call this function M. In the case of the symbols 0 to 9 we obtain the following mapping and M(4) = 11 for example.

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} => {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}

Now we can map a pair (n, m) using the mapping X to the product of the mappings of n and m. This will turn the pair (2, 5) into X((2, 5)) = M(2) * M(5) = 5 * 13 = 65.

X((n, m)) = M(n) * M(m)

Why all this? If we have two pairs (a, b) and (c, d) from two lists, map them using the mapping X to x and y and multiply them, we obtain the product x * y = M(a) * M(b) * M(c) * M(d) - a product of four primes. We can extend the product by more factors by selecting a pair from each list and obtain a product of 2w primes if we have w lists. The final question is what does this product tell us about the pairs we selected and multiplied? If the selected pairs form a valid selection, we never choose one symbol twice, hence the product contains no prime twice and is square free. If the selection is invalid the product contains at least one prime twice and is not square free. And here a final example.

X((2, 5)) = 5 * 13 = 65
X((3, 6)) = 7 * 17 = 119
X((3, 4)) = 7 * 11 = 77

Selecting 25 and 36 yields 65 * 119 = 7735 = 5 * 7 * 13 * 17 and is square free, hence valid. Selecting 36 and 34 yields 119 * 77 = 9163 = 7² * 11 * 17 and is not square free, hence not valid.

Also note how nicely this preserves the symmetrie - X((m, n)) = X((n, m)) - and prohibites symmetric pairs because X((m, m)) = M(m) * M(m) is not square free.

I don't know if this will be any help, but now you know it and can think about it...^^


This is the first part of an reduction of a 3-SAT problem to this problem. The 3-SET problem is the following.

(!A | B | C) & (B | !C | !D) & (A | !B)

And here is the reduction as far as I got.

  • m-n represents a pair
  • a line reprresents a list
  • an asterisk represents an abitrary unique symbol

A1-A1'    !A1-!A1'     => Select A true or false
B1-B1'    !B1-!B1'     => Select B true or false
C1-C1'    !C1-!C1'     => Select C true or false
D1-D1'    !D1-!D1'     => Select D true or false

A1-*   !B1-*   !C1-*   => !A | B | C

A2-!A1'   !A2-A1'      => Create a copy of A
B2-!B1'   !B2-B1'      => Create a copy of B
C2-!C1'   !C2-C1'      => Create a copy of C
D2-!D1'   !D2-D1'      => Create a copy of D

!B2-*  C2-*    D2-*    => B | !C | !D

(How to perform a second copy of the four variables???)

!A3-*  B3-*

If I (or somebody else) can complete this reduction and show how to do it in the general case, this will proof the problem NP-complete. I am just stuck with copying the variables a second time.

长伴 2024-07-31 07:47:29

我想说的是,除了蛮力之外,你没有其他计算可以做,因为必须评估一个函数来决定是否可以使用集合 B 中的某个项目(给定集合 A 中选择的项目)。简单的组合数学不会工作。

您可以使用记忆和散列将计算速度加快 1 到 2 个数量级。

记忆是记住类似暴力路径的先前结果。 如果您在列表 n 中并且已经使用了符号 x、y、z,并且之前遇到过这种情况,那么您将从剩余列表中添加相同数量的可能组合。 如何使用 x,y,z 列出 n 并不重要。 因此,如果有缓存结果,请使用缓存结果,或者继续计算到下一个列表并检查那里。 如果您使用强力递归算法来计算结果,但缓存结果,则效果很好。

保存结果的关键是:当前列表,以及已经使用过的符号。 对符号进行排序以制作你的钥匙。 我认为字典或字典数组在这里是有意义的。

使用散列来减少每个列表中需要搜索的对的数量。 对于每个列表,在已经消耗了一定数量的符号的情况下,对可用的对进行哈希。 根据您想要使用的内存量以及您想要花费的预计算时间,选择您想要在哈希中使用的已消耗符号的数量。 我认为使用 1-2 个符号会很好。 按这些哈希值中的项目数对这些哈希值进行升序排序,然后保留前 n 个。 我说扔掉其余的,因为如果哈希值只减少了你的少量工作,那么它可能不值得保留(如果哈希值更多,则需要更长的时间才能找到哈希值)。 因此,当您浏览列表时,您可以快速扫描列表的散列以查看散列中是否使用了符号。 如果有,则使用出现的第一个哈希来扫描列表。 第一个哈希将包含要扫描的最少对。 如果您真的很方便,您也许可以随时构建这些哈希值,而不必预先浪费时间。

您也许可以扔掉散列并使用树,但我的猜测是填充树将需要很长时间。

I am going to say there is no calculation that you can do other than brute force becuse there is a function that has to be evaluated to decide whether an item from set B can be used given the item chosen in set A. Simple combinatorial math wont work.

You can speed up the calculation by 1 to 2 magnitudes using memoization and hashing.

Memoization is remembering previous results of similar brute force paths. If you are at list n and you have already consumed symbols x,y,z and previously you have encountered this situation, then you will be adding in the same number of possible combinations from the remaining lists. It does not matter how you got to list n using x,y,z. So, use a cached result if there is one, or continue the calc to the next list and check there. If you make a brute force recursive algorithm to calculate the result, but cache results, this works great.

The key to the saved result is: the current list, and the symbols that have been used. Sort the symbols to make your key. I think a dictionary or an array of dictionaries makes sense here.

Use hashing to reduce the number of pairs that need to be searched in each list. For each list, make a hash of the pairs that would be available given that a certain number of symbols are already consumed. Choose the number of consumed symbols you want to use in your hash based on how much memory you want to use and the time you want to spend pre-calculating. I think using 1-2 symbols would be good. Sort these hashes by the number of items in them...ascending, and then keep the top n. I say throw out the rest, becasue if the hash only reduces your work a small amount, its probably not worth keeping (it will take longer to find the hash if there are more of them). So as you are going through the lists, you can do a quick scan the list's hash to see if you have used a symbol in the hash. If you have, then use the first hash that comes up to scan the list. The first hash would contain the fewest pairs to scan. If you are really handy, you might be able to build these hashes as you go and not waste time up front to do it.

You might be able to toss the hash and use a tree, but my guess is that filling the tree will take a long time.

眼波传意 2024-07-31 07:47:29

如果您想生成所有组合,约束编程是一个不错的方法。 为了尝试一下,我使用 Gecode (版本 3.2.2)编写了一个模型来解决您的问题。 给出的两个例子很容易解决,但其他例子可能更难。 无论如何,它应该比生成和测试更好。

/*
 *  Main authors:
 *     Mikael Zayenz Lagerkvist <[email protected]>
 *
 *  Copyright:
 *     Mikael Zayenz Lagerkvist, 2009
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <gecode/driver.hh>
#include <gecode/int.hh>
#include <gecode/minimodel.hh>

using namespace Gecode;

namespace {
  /// List of specifications
  extern const int* specs[];
  /// Number of specifications
  extern const unsigned int n_specs;
}


/**
 * \brief Selecting pairs
 *
 * Given a set of lists of pairs of values, select a pair from each
 * list so that no value is selected more than once.
 *
 */
class SelectPairs : public Script {
protected:
  /// Specification
  const int* spec;
  /// The values from all selected pairs
  IntVarArray s;
public:
  /// The actual problem
  SelectPairs(const SizeOptions& opt)
    : spec(specs[opt.size()]),
      s(*this,spec[0] * 2,Int::Limits::min, Int::Limits::max) {

    int pos = 1; // Position read from spec
    // For all lists
    for (int i = 0; i < spec[0]; ++i) {
      int npairs = spec[pos++];
      // Construct TupleSet for pairs from list i
      TupleSet ts;
      for (int p = 0; p < npairs; ++p) {
        IntArgs tuple(2);
        tuple[0] = spec[pos++];
        tuple[1] = spec[pos++];
        ts.add(tuple);
      }
      ts.finalize();

      // <s[2i],s[2i+1]> must be from list i
      IntVarArgs pair(2);
      pair[0] = s[2*i]; pair[1] = s[2*i + 1];
      extensional(*this, pair, ts);
    }

    // All values must be pairwise distinct
    distinct(*this, s, opt.icl());

    // Select values for the variables
    branch(*this, s, INT_VAR_SIZE_MIN, INT_VAL_MIN);
  }

  /// Constructor for cloning \a s
  SelectPairs(bool share, SelectPairs& sp) 
    : Script(share,sp), spec(sp.spec) {
    s.update(*this, share, sp.s);
  }

  /// Perform copying during cloning
  virtual Space*
  copy(bool share) {
    return new SelectPairs(share,*this);
  }

  /// Print solution
  virtual void
  print(std::ostream& os) const {
    os << "\t";
    for (int i = 0; i < spec[0]; ++i) {
      os << "(" << s[2*i] << "," << s[2*i+1] << ") ";
      if ((i+1) % 10 == 0)
        os << std::endl << "\t";
    }
    if (spec[0] % 10)
      os << std::endl;
  }
};

/** \brief Main-function
 *  \relates SelectPairs
 */
int
main(int argc, char* argv[]) {
  SizeOptions opt("SelectPairs");
  opt.iterations(500);
  opt.size(0);
  opt.parse(argc,argv);
  if (opt.size() >= n_specs) {
    std::cerr << "Error: size must be between 0 and "
              << n_specs-1 << std::endl;
    return 1;
  }
  Script::run<SelectPairs,DFS,SizeOptions>(opt);
  return 0;
}

namespace {
  const int s0[] = {
    // Number of lists
    3,
    // Lists (number of pairs, pair0, pair1, ...)
    5,  1,2,  1,3,  1,4,  2,3,  2,4,
    3,  1,4,  1,5,  2,5,
    5,  1,6,  1,7,  2,5,  2,6,  3,6
  };

  const int s1[] = {
    // Number of lists
    3,
    // Lists (number of pairs, pair0, pair1, ...)
    5,  1,2,  2,3,  3,4,  4,5,  6,7,
    5,  1,2,  2,3,  3,4,  4,5,  6,7,
    5,  1,2,  2,3,  3,4,  4,5,  6,7
  };

  const int *specs[] = {s0, s1};
  const unsigned n_specs = sizeof(specs)/sizeof(int*);
}

Constraint programming is a nice approach if you want to generate all the combinations. Just to try it out, I wrote a model using Gecode (version 3.2.2) to solve your problem. The two examples given are very easy to solve, but other instances might be harder. It should be better than generate and test in any case.

/*
 *  Main authors:
 *     Mikael Zayenz Lagerkvist <[email protected]>
 *
 *  Copyright:
 *     Mikael Zayenz Lagerkvist, 2009
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <gecode/driver.hh>
#include <gecode/int.hh>
#include <gecode/minimodel.hh>

using namespace Gecode;

namespace {
  /// List of specifications
  extern const int* specs[];
  /// Number of specifications
  extern const unsigned int n_specs;
}


/**
 * \brief Selecting pairs
 *
 * Given a set of lists of pairs of values, select a pair from each
 * list so that no value is selected more than once.
 *
 */
class SelectPairs : public Script {
protected:
  /// Specification
  const int* spec;
  /// The values from all selected pairs
  IntVarArray s;
public:
  /// The actual problem
  SelectPairs(const SizeOptions& opt)
    : spec(specs[opt.size()]),
      s(*this,spec[0] * 2,Int::Limits::min, Int::Limits::max) {

    int pos = 1; // Position read from spec
    // For all lists
    for (int i = 0; i < spec[0]; ++i) {
      int npairs = spec[pos++];
      // Construct TupleSet for pairs from list i
      TupleSet ts;
      for (int p = 0; p < npairs; ++p) {
        IntArgs tuple(2);
        tuple[0] = spec[pos++];
        tuple[1] = spec[pos++];
        ts.add(tuple);
      }
      ts.finalize();

      // <s[2i],s[2i+1]> must be from list i
      IntVarArgs pair(2);
      pair[0] = s[2*i]; pair[1] = s[2*i + 1];
      extensional(*this, pair, ts);
    }

    // All values must be pairwise distinct
    distinct(*this, s, opt.icl());

    // Select values for the variables
    branch(*this, s, INT_VAR_SIZE_MIN, INT_VAL_MIN);
  }

  /// Constructor for cloning \a s
  SelectPairs(bool share, SelectPairs& sp) 
    : Script(share,sp), spec(sp.spec) {
    s.update(*this, share, sp.s);
  }

  /// Perform copying during cloning
  virtual Space*
  copy(bool share) {
    return new SelectPairs(share,*this);
  }

  /// Print solution
  virtual void
  print(std::ostream& os) const {
    os << "\t";
    for (int i = 0; i < spec[0]; ++i) {
      os << "(" << s[2*i] << "," << s[2*i+1] << ") ";
      if ((i+1) % 10 == 0)
        os << std::endl << "\t";
    }
    if (spec[0] % 10)
      os << std::endl;
  }
};

/** \brief Main-function
 *  \relates SelectPairs
 */
int
main(int argc, char* argv[]) {
  SizeOptions opt("SelectPairs");
  opt.iterations(500);
  opt.size(0);
  opt.parse(argc,argv);
  if (opt.size() >= n_specs) {
    std::cerr << "Error: size must be between 0 and "
              << n_specs-1 << std::endl;
    return 1;
  }
  Script::run<SelectPairs,DFS,SizeOptions>(opt);
  return 0;
}

namespace {
  const int s0[] = {
    // Number of lists
    3,
    // Lists (number of pairs, pair0, pair1, ...)
    5,  1,2,  1,3,  1,4,  2,3,  2,4,
    3,  1,4,  1,5,  2,5,
    5,  1,6,  1,7,  2,5,  2,6,  3,6
  };

  const int s1[] = {
    // Number of lists
    3,
    // Lists (number of pairs, pair0, pair1, ...)
    5,  1,2,  2,3,  3,4,  4,5,  6,7,
    5,  1,2,  2,3,  3,4,  4,5,  6,7,
    5,  1,2,  2,3,  3,4,  4,5,  6,7
  };

  const int *specs[] = {s0, s1};
  const unsigned n_specs = sizeof(specs)/sizeof(int*);
}
榆西 2024-07-31 07:47:29

第一次尝试..这是一种比蛮力算法具有改进的平均复杂度降低的算法。 本质上,您在每次迭代中创建长度不断增加的字符串。 这可能不是最好的解决方案,但我们会等待最好的解决方案的出现...:)

从列表 1 开始。该列表中的所有条目都是长度为 2 (#=5) 的有效解决方案
接下来,当您引入列表 2 时,记录长度为 4 的所有有效解,最终为 {1425, 2314, 2315, 2415} (#=4)。

当您将第三个列表添加到混合中时,请重复该过程。 您最终将得到 {142536, 241536} (#=2)。

复杂性的降低已经到位,因为您在每次迭代中都会丢弃坏字符串。 最坏的情况碰巧仍然是相同的——所有对都是不同的。

First try.. Here is an algorithm with an improved reduced average complexity than brute force. Essentially you create strings with increasing lengths in each iteration. This may not be the best solution but we will wait for the best one to come by... :)

Start with list 1. All entries in that list are valid solutions of length 2 (#=5)
Next, when you introduce list 2. keep a record of all valid solutions of length 4, which end up being {1425, 2314, 2315, 2415} (#=4).

When you add the third list to the mix, repeat the process. You will end up with {142536, 241536} (#=2).

The complexity reduction comes in place because you are throwing away bad strings in each iteration. The worst case scenario happens to be still the same -- in the case that all pairs are distinct.

西瓜 2024-07-31 07:47:29

这感觉像是一个应用约束编程方法的好问题。 对于 Wikipedia 提供的软件包列表,我将补充一点,我过去使用 Gecode 拥有良好的经验; Gecode 示例还提供了约束编程的基本教程。 如果您想深入了解,约束处理是一本关于该主题的好书算法如何工作。

This feels like a good problem to which to apply a constraint programming approach. To the list of packages provided by Wikipedia I'll add that I've had good experience using Gecode in the past; the Gecode examples also provide a basic tutorial to constraint programming. Constraint Processing is a good book on the subject if you want to dig deeper into how the algorithms work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文