找到一组排列,有一个约束

Posted

技术标签:

【中文标题】找到一组排列,有一个约束【英文标题】:Finding a set of permutations, with a constraint 【发布时间】:2009-08-21 23:15:35 【问题描述】:

我有一组 N^2 个数字和 N 个 bin。每个 bin 应该具有分配给它的集合中的 N 个数字。我面临的问题是找到一组将数字映射到 bin 的分布,满足约束条件,即每对数字只能共享同一个 bin 一次。

分布可以很好地表示为 NxN 矩阵,其中每一行代表一个 bin。然后问题是找到矩阵元素的一组排列,其中每对数字仅共享同一行一次。它是哪一行无关紧要,只是两个数字都分配给了同一个数字。

满足 N=8 约束的 3 个排列示例集:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 0 8 16 24 32 40 48 56 1 9 17 25 33 41 49 57 2 10 18 26 34 42 50 58 3 11 19 27 35 43 51 59 4 12 20 28 36 44 52 60 5 13 21 29 37 45 53 61 6 14 22 30 38 46 54 62 7 15 23 31 39 47 55 63 0 9 18 27 36 45 54 63 1 10 19 28 37 46 55 56 2 11 20 29 38 47 48 57 3 12 21 30 39 40 49 58 4 13 22 31 32 41 50 59 5 14 23 24 33 42 51 60 6 15 16 25 34 43 52 61 7 8 17 26 35 44 53 62

不属于上述集合的排列:

0 10 20 30 32 42 52 62 1 11 21 31 33 43 53 63 2 12 22 24 34 44 54 56 3 13 23 25 35 45 55 57 4 14 16 26 36 46 48 58 5 15 17 27 37 47 49 59 6 8 18 28 38 40 50 60 7 9 19 29 39 41 51 61

由于与第二个排列发生多次冲突,例如,因为它们都将数字 0 和 32 配对在一行中。


枚举三个很容易,它由 1 个任意排列、它的转置和一个矩阵组成,其中行由前一个矩阵的对角线组成。

我无法找到一种方法来制作包含更多内容的集合。这似乎是一个非常复杂的问题,或者是一个没有明显解决方案的简单问题。无论哪种方式,如果有人对如何在合理的时间内解决 N=8 案例有任何想法,或者确定问题的正确学术名称,我将不胜感激,这样我就可以谷歌搜索它。

如果您想知道它有什么用处,我正在寻找一种用于具有 8 个缓冲区的交叉开关的调度算法,该算法为 64 个目的地的流量提供服务。这部分调度算法与输入流量无关,并在多个硬连线目标缓冲区映射之间循环切换。目标是让每对目标地址在循环周期内只竞争一次相同的缓冲区,并最大化该周期的长度。换句话说,这样每对地址就尽可能少地竞争同一个缓冲区。


编辑:

这是我的一些代码。 CODE

它是贪婪的,它通常在找到第三个排列后终止。但应该存在一组至少 N 个排列满足该问题。

替代方案需要选择置换 I 涉及寻找置换 (I+1..N),以检查置换 I 是否是由最大置换数组成的解决方案的一部分。这需要枚举所有排列以在每一步检查,这非常昂贵。

【问题讨论】:

整个问题有点罗嗦。目前尚不清楚您所说的配对是什么意思。您所说的“约束,即每对数字只能共享同一个 bin 一次”是什么意思? 我无法理解您的约束“每对数字只能共享同一个 bin 一次”。对于 N^2 个唯一数字的任何排列,这不是微不足道的吗?你能展示一个不符合约束的安排吗? 整个排列集都需要满足约束。因此,如果一个排列将数字 1 和 2 放在同一行,则集合中的任何其他排列都不允许再将 1 和 2 放在同一行。 形式上,设 P(a,b,i) 为谓词“a 和 b 在排列 i 中出现在同一行”,并假设有 n 个排列。那么约束是“不存在 a, b P(a,b,i) 本身可以表示为“R(a,i) == R(b,i)”,其中 R 是映射对 (a,i) 的函数) 到排列 i 中出现项目 a 的行号。 【参考方案1】:

你想要的是combinatorial block design。使用链接页面上的命名法,您需要尺寸为 (n^2, n, 1) 的设计以获得最大 k。使用您的命名法,这将为您提供 n(n+1) 个排列。这是计数参数理论上可能的最大值(参见文章中关于从 v、k 和 lambda 推导 b 的解释)。对于某些素数 p 和整数 k,使用仿射平面的 n = p^k 存在此类设计。据推测,唯一存在的仿射平面就是这种大小。因此,如果可以选择n,或许这个答案就足够了。

但是,如果不是理论上可能的最大排列数,而是只想找到一个大数(对于给定的 n^2,您可以找到的最大数),我不确定这些对象的研究叫什么。

【讨论】:

非常非常感谢!在块设计的***页面上,我找到了一个包含我感兴趣的 (64, 8, 1) 解决方案的数据库的链接。【参考方案2】:

制作一个 64 x 64 x 8 的数组: bool disabled[i][j][k] 表示 (i,j) 对是否出现在第 k 行。每次使用第 k 行中的对 (i, j) 时,都会将此数组中的关联值设置为 1。请注意,您将只使用该数组中 i 的一半

要构造一个新的排列,首先尝试成员 0,并验证至少有七个未设置的禁止 [0][j][0]。如果没有剩下七个,请增加并重试。重复以填写该行的其余部分。重复整个过程以填充整个 NxN 排列。

在您实现此功能时,您可能应该能够进行一些优化,但这应该做得很好。

【讨论】:

+1,但我认为约束更强:一旦一对出现在同一行中,它们就不能以另一种排列方式一起出现在 any 行中。那么也许“禁止”数组应该是 64x64,没有最终维度? 像这样的贪心方法在终止之前只产生少量排列。这是我尝试的第一件事。【参考方案3】:

也许您可以将您的问题重新表述为图论。例如,您从具有 N×N 个顶点的完整图开始。在每个步骤中,您将图划分为 N 个 N-clique,然后删除所有使用的边。

对于这种 N=8 的情况,K64 有 64×63/2 = 2016 条边,而 64 批 K8 有 1792 条边,所以您的问题可能不是不可能的 :-)

【讨论】:

听起来很对!并且很有见地——因为众所周知,集团发现问题通常是 NP 完全的。我怀疑前几次迭代(虽然 NxN 图仍然相对密集)可能很容易通过蛮力找到,但随着边缘被移除,寻找团的时间越来越长。 嗯,找到最大团是NP完全的。我不确定这个问题。我认为如果存在派系会很容易找到,因为每个顶点都必须是一个顶点的成员,并且您只对大小 N 感兴趣。问题是贪婪算法可能会选择错误的派系并且无法找到所有 N,假设 N 存在(嗯,这就是我的直觉)。 是的,基本上我实现的是找到所有 8 派系。这是快速的具有 2 个起始排列(找到 40320 个 8-cliques),并且具有 1 个起始排列(找到 1600 万个 8-cliques)是可行的。但问题是: 1. 枚举所有合法排列,即 8 个 8 派系的集合,是一个 40320^8 或 (1600 万)^8 事件。 2. 8-cliques 的数量和下一步可能的排列取决于这一步的排列选择,贪心确实行不通。一个完整的搜索需要在寻找排列 I 时评估所有后来的排列 (I+1..N) :/【参考方案4】:

是的,贪婪的风格不起作用,因为你的数字用完了。

很容易看出,在您违反约束之前,排列不能超过 63 个。在 64 日,您必须将至少一个号码与另一个已配对的号码配对。鸽巢原理。

事实上,如果你使用我之前建议的禁止对表,你会发现在你用完之前最多只有 N+1 = 9 个可能的排列。该表有 N^2 x (N^2-1)/2 = 2016 个非冗余约束,每个新排列将创建 N x (N choose 2) = 28 个新配对。所以所有的配对将在 2016/28 = 9 排列之后用完。似乎意识到排列如此之少是解决问题的关键。

您可以生成编号为 n = 0 ... N-1 的 N 个排列的列表

A_ij = (i * N + j + j * n * N) mod N^2

通过移动每个排列中的列来生成新的排列。第 n 个排列的顶行是第 n-1 个排列的对角线。编辑:哎呀......这似乎只在 N 是素数时才有效。

这错过了最后一个排列,您可以通过转置矩阵来获得:

A_ij = j * N + i

【讨论】:

您还可以通过检查给定值的 N-1 个邻居来获得 N+1 上限,例如1. 剩余的 N^2-1 个数字中的每一个只能在一行中出现一次,其中 1,这意味着最多有 (N^2-1)/(N-1)=N+1 个唯一行,因此矩阵,包含 1.

以上是关于找到一组排列,有一个约束的主要内容,如果未能解决你的问题,请参考以下文章

Android练习使用约束布局构建简单计算器效果

差分约束

使用 LINQ 生成排列

使用 LINQ 生成排列

在 Prolog 中使用约束和排列解决难题

使用一个查询递增具有唯一约束的字段中的一组值,Postgres