我遇到了一个令人惊讶的挑战性问题,它安排了一个类似矩阵(列表的列表)的值受到以下约束(或决定它是不可能的):
m个随机生成的行的矩阵,最多包含n个不同的值(行内没有重复),排列矩阵,使得以下成员保持(如果可能):
1)矩阵必须是“下三角”;行必须以递增的长度排序,因此唯一的“间隙”位于右上角
2)如果某个值出现在多个行中,则它必须位于同一列中(即允许重新排列行中值的顺序)。
需要在函数式语言(例如Scala)中表达问题/解决方案。
示例1 - 具有解决方案
A B
C E D
C A B
成为(作为一个解决方案)
A B
E D C
A B C
因为A,B和C分别出现在第1,2和3列中。
示例2 - 没有解决方案
A B C
A B D
B C D
没有解决方案,因为约束要求第三行在第三行中具有C和D. 列不可能。
答案 0 :(得分:1)
我认为这是一个有趣的问题,并在MiniZinc(一个非常高级别的约束编程系统)中建立了概念验证版本,这似乎是正确的。我不确定它是否有用,说实话,我不确定它是否适用于最大的问题实例。
第一个问题实例 - 根据这个模型 - 有4个解决方案:
B A _
E D C
B A C
----------
B A _
D E C
B A C
----------
A B _
E D C
A B C
----------
A B _
D E C
A B C
第二个例子被认为是不可满足的(应该如此)。
完整的模型在这里:http://www.hakank.org/minizinc/ordering_a_list_of_lists.mzn
基本方法是使用矩阵,其中较短的行填充空值(此处为0,为零)。问题实例是矩阵“矩阵”;得到的解决方案在矩阵“x”中(决策变量,作为整数,然后转换为输出中的字符串)。然后有一个辅助矩阵,“perms”,用于确保“x”中的每一行是“矩阵”中相应行的排列,用谓词“permutation3”完成。还有一些其他辅助数组/集合简化了约束。
主要的MiniZinc型号(无输出)如下所示。
以下是一些可能会使模型失效的评论/假设:
这只是一个概念验证模型,因为我觉得它很有趣 问题
我假设矩阵中的行(问题数据)已经被排序了 按尺寸(下三角形)。这应该很容易作为预处理步骤 不需要约束编程的地方。
较短的列表用0(零)填充,因此我们可以使用矩阵。
因为MiniZinc是一种强类型语言而且不支持 符号,我们只定义整数1..5来表示字母A..E。 使用传统时,使用整数也很有用 约束编程系统。
% The MiniZinc model (sans output) include "globals.mzn"; int: rows = 3; int: cols = 3; int: A = 1; int: B = 2; int: C = 3; int: D = 4; int: E = 5; int: max_int = E; array[0..max_int] of string: str = array1d(0..max_int, ["_", "A","B","C","D","E"]); % problem A (satifiable) array[1..rows, 1..cols] of int: matrix = array2d(1..rows, 1..cols, [ A,B,0, % fill this shorter array with "0" E,D,C, A,B,C, ]); % the valid values (we skip 0, zero) set of int: values = {A,B,C,D,E}; % identify which rows a specific values are. % E.g. for problem A: % value_rows: [{1, 3}, {1, 3}, 2..3, 2..2, 2..2] array[1..max_int] of set of int: value_rows = [ {i | i in 1..rows, j in 1..cols where matrix[i,j] = v} | v in values]; % decision variables % The resulting matrix array[1..rows, 1..cols] of var 0..max_int: x; % the permutations from matrix to x array[1..rows, 1..cols] of var 0..max_int: perms; % % permutation3(a,p,b) % % get the permutation from a b using the permutation p. % predicate permutation3(array[int] of var int: a, array[int] of var int: p, array[int] of var int: b) = forall(i in index_set(a)) ( b[i] = a[p[i]] ) ; solve satisfy; constraint forall(i in 1..rows) ( % ensure unicity of the values in the rows in x and perms (except for 0) alldifferent_except_0([x[i,j] | j in 1..cols]) /\ alldifferent_except_0([perms[i,j] | j in 1..cols]) /\ permutation3([matrix[i,j] | j in 1..cols], [perms[i,j] | j in 1..cols], [x[i,j] | j in 1..cols]) ) /\ % zeros in x are where there zeros are in matrix forall(i in 1..rows, j in 1..cols) ( if matrix[i,j] = 0 then x[i,j] = 0 else true endif ) /\ % ensure that same values are in the same column: % - for each of the values % - ensure that it is positioned in one column c forall(k in 1..max_int where k in values) ( exists(j in 1..cols) ( forall(i in value_rows[k]) ( x[i,j] = k ) ) ) ; % the output % ...
答案 1 :(得分:0)
我需要一个函数式语言(XQuery)的解决方案,所以我首先在Scala中实现了它,因为它具有表现力,我发布了下面的代码。它使用蛮力,广度优先的方式搜索解决方案。我对单个解决方案(如果存在)感兴趣,因此该算法会抛弃额外的解决方案。
def order[T](listOfLists: List[List[T]]): List[List[T]] = {
def isConsistent(list: List[T], listOfLists: List[List[T]]) = {
def isSafe(list1: List[T], list2: List[T]) =
(for (i <- list1.indices; j <- list2.indices) yield
if (list1(i) == list2(j)) i == j else true
).forall(_ == true)
(for (row <- listOfLists) yield isSafe(list, row)).forall(_ == true)
}
def solve(fixed: List[List[T]], remaining: List[List[T]]): List[List[T]] =
if (remaining.isEmpty)
fixed // Solution found so return it
else
(for {
permutation <- remaining.head.permutations.toList
if isConsistent(permutation, fixed)
ordered = solve(permutation :: fixed, remaining.tail)
if !ordered.isEmpty
} yield ordered) match {
case solution1 :: otherSolutions => // There are one or more solutions so just return one
solution1
case Nil => // There are no solutions
Nil
}
// Ensure each list has unique items (i.e. no dups within the list)
require (listOfLists.forall(list => list == list.distinct))
/*
* The only optimisations applied to an otherwise full walk through all solutions is to sort the list of list so that the lengths
* of the lists are increasing in length and then starting the ordering with the first row fixed i.e. there is one degree of freedom
* in selecting the first row; by having the shortest row first and fixing it we both guarantee that we aren't disabling a solution from being
* found (i.e. by violating the "lower triangular" requirement) and can also avoid searching through the permutations of the first row since
* these would just result in additional (essentially duplicate except for ordering differences) solutions.
*/
//solve(Nil, listOfLists).reverse // This is the unoptimised version
val sorted = listOfLists.sortWith((a, b) => a.length < b.length)
solve(List(sorted.head), sorted.tail).reverse
}