Matrix 如何解决这个ILP/CP矩阵难题
我正在研究算法,最近发现了一个有趣的挑战 它将给我们一些行/列,我们的任务是用整数1~N填充表格,该整数只显示一次,并且它们的行和列总和等于给定的行/列 挑战是一个简单的例子:Matrix 如何解决这个ILP/CP矩阵难题,matrix,puzzle,constraint-programming,integer-programming,Matrix,Puzzle,Constraint Programming,Integer Programming,我正在研究算法,最近发现了一个有趣的挑战 它将给我们一些行/列,我们的任务是用整数1~N填充表格,该整数只显示一次,并且它们的行和列总和等于给定的行/列 挑战是一个简单的例子: [ ] [ ] [ ] 13 [ ] [ ] [ ] 8 [ ] [ ] [ ] 24 14 14 17 answer: [2] [6] [5] 13 [3] [1] [4] 8 [9] [7] [8]
[ ] [ ] [ ] 13
[ ] [ ] [ ] 8
[ ] [ ] [ ] 24
14 14 17
answer:
[2] [6] [5] 13
[3] [1] [4] 8
[9] [7] [8] 24
14 14 17
谢谢我认为回溯算法在这里会非常有效 虽然回溯仍然是一种“蛮力”,但在一般情况下,回溯的速度确实很快。例如,用回溯法求解数独通常只需要1000-10000次迭代(考虑到O复杂度为O(9^n),其中n为空空间,这是非常快的,因此平均数独大约有9^60种可能性,这将需要几年时间在计算机上完成) 这个任务有很多规则(数字的唯一性和行/列的和),这对跟踪非常有用。更多规则=在每个步骤后进行更多检查,并丢弃无法带来解决方案的分支
这可能会有所帮助:据我所知,没有比使用回溯方法更有效地解决这个特定问题的直接算法 但是,与简单地枚举所有可能的解决方案相比,您可以更智能地执行此操作。一种有效的方法是约束编程(CP)(或约束逻辑编程(CLP)等派生范式)。基本上,它归结于对问题施加的约束进行推理,试图缩小变量的范围 在减少域之后,您可以选择以后可以回溯的域。在做出这样的选择后,您再次减少域,并且可能必须做出额外的选择 例如,您可以为此使用ECLiPSe(不是IDE,而是约束逻辑编程工具):
:- lib(ic).
:- import alldifferent/1 from ic_global.
:- import sumlist/2 from ic_global.
solve(Problem) :-
problem(Problem,N,LA,LB),
puzzle(N,LA,LB,Grid),
print_Grid(Grid).
puzzle(N,LA,LB,Grid) :-
N2 is N*N,
dim(Grid,[N,N]),
Grid[1..N,1..N] :: 1..N2,
(for(I,1,N), param(N,Grid,LA,LB) do
Sc is nth1(I,LA),
Lc is Grid[1..N,I],
sumlist(Lc,Sc),
Sr is nth1(I,LB),
Lr is Grid[I,1..N],
sumlist(Lr,Sr)
),
term_variables(Grid,Vars),
alldifferent(Vars),
labeling(Vars).
print_Grid(Grid) :-
dim(Grid,[N,N]),
( for(I,1,N), param(Grid,N) do
( for(J,1,N), param(Grid,I) do
X is Grid[I,J],
( var(X) -> write(" _") ; printf(" %2d", [X]) )
), nl
), nl.
nth1(1,[H|_],H) :- !.
nth1(I,[_|T],H) :-
I1 is I-1,
nth1(I1,T,H).
problem(1,3,[14,14,17],[13,8,24]).
该计划是基于模糊的。现在,您可以使用ECLiPSe解决此问题:
ECLiPSe Constraint Logic Programming System [kernel]
Kernel and basic libraries copyright Cisco Systems, Inc.
and subject to the Cisco-style Mozilla Public Licence 1.1
(see legal/cmpl.txt or http://eclipseclp.org/licence)
Source available at www.sourceforge.org/projects/eclipse-clp
GMP library copyright Free Software Foundation, see legal/lgpl.txt
For other libraries see their individual copyright notices
Version 6.1 #199 (x86_64_linux), Sun Mar 22 09:34 2015
[eclipse 1]: solve(1).
lists.eco loaded in 0.00 seconds
WARNING: module 'ic_global' does not exist, loading library...
queues.eco loaded in 0.00 seconds
ordset.eco loaded in 0.00 seconds
heap_array.eco loaded in 0.00 seconds
graph_algorithms.eco loaded in 0.03 seconds
max_flow.eco loaded in 0.00 seconds
flow_constraints_support.eco loaded in 0.00 seconds
ic_sequence.eco loaded in 0.01 seconds
ic_global.eco loaded in 0.05 seconds
2 5 6
3 1 4
9 8 7
Yes (0.05s cpu, solution 1, maybe more) ? ;
5 2 6
1 3 4
8 9 7
Yes (0.05s cpu, solution 2, maybe more) ? ;
2 6 5
3 1 4
9 7 8
Yes (0.05s cpu, solution 3, maybe more) ? ;
3 6 4
2 1 5
9 7 8
Yes (0.05s cpu, solution 4, maybe more) ? ;
6 2 5
1 3 4
7 9 8
Yes (0.05s cpu, solution 5, maybe more) ? ;
6 3 4
1 2 5
7 9 8
Yes (0.05s cpu, solution 6, maybe more) ? ;
2 6 5
4 1 3
8 7 9
Yes (0.05s cpu, solution 7, maybe more) ? ;
4 6 3
2 1 5
8 7 9
Yes (0.05s cpu, solution 8, maybe more) ?
6 2 5
1 4 3
7 8 9
Yes (0.05s cpu, solution 9, maybe more) ? ;
6 4 3
1 2 5
7 8 9
Yes (0.05s cpu, solution 10, maybe more) ? ;
No (0.06s cpu)
只需查询solve(1)
,约束逻辑编程工具完成其余工作。因此,总共有10个解决方案
请注意,该程序适用于任意的N
,尽管-因为最坏的情况下,该程序执行回溯-显然,该程序只能解决合理的N
问题。哦,当这些小优化问题出现时,我非常喜欢。他们总是让我想起我第一年的一次,当时我做了一个可以解决数独问题的东西,玩得很开心!你可能猜到从那以后我解决了多少数独游戏:)
现在,你的问题是一个问题。甚至在你阅读那篇文章之前,你应该注意到ILP是很难的。将解决方案空间限制为N或Z是严重的限制,通常情况下,这样的解决方案不存在
对于你的问题,手头的任务基本上归结为解决这个问题
Minimise 0 (arbitrary objective function)
根据,
x1 + x2 + x3 = 13
x4 + x5 + x6 = 8
x7 + x8 + x9 = 24
x1 + x4 + x7 = 14
x2 + x5 + x8 = 14
x3 + x6 + x9 = 17
A * x = B
以及
x_i in N, x_i distinct.
以矩阵形式,这些方程变成
|1 1 1 0 0 0 0 0 0|
|0 0 0 1 1 1 0 0 0|
A = |0 0 0 0 0 0 1 1 1|
|1 0 0 1 0 0 1 0 0|
|0 1 0 0 1 0 0 1 0|
|0 0 1 0 0 1 0 0 1|
以及
使约束减少到A*x=B
。所以我们要解决的问题现在可以等价地写成
Minimise 0
根据,
x1 + x2 + x3 = 13
x4 + x5 + x6 = 8
x7 + x8 + x9 = 24
x1 + x4 + x7 = 14
x2 + x5 + x8 = 14
x3 + x6 + x9 = 17
A * x = B
以及
你觉得这很难吗?如果不是,想想看:真正的线是巨大的,在这条线上,每隔一段时间,就会有一个小点。这是一个整数。我们需要一些。我们不知道是哪一个。所以本质上,一个完美的类比就是大海捞针
现在,不要绝望,我们非常擅长发现这些ILP针!我只是想让你们了解这个问题所产生的领域的非平凡的困难
我想为您提供工作代码,但我不知道您首选的语言/工具包。如果这只是一个业余爱好者的方法,那么即使是Excel的解算器也会工作得很好。如果不是的话,我认为我的措辞不可能比Willem Van Onsem已经有的更好,我想让你看看他的实现答案。下面是另一个约束编程模型,使用与Willem Van Onsem解决方案类似的方法,即使用全局约束“所有不同”,这是确保矩阵中的数字只分配一次的有效方法。(在CP中,“全局约束”的概念非常重要,有很多研究发现可以快速实现各种常见约束。)
这是一个迷你锌模型:
以下是前两个(共10个)解决方案:
2 5 6
3 1 4
9 8 7
----------
5 2 6
1 3 4
8 9 7
----------
...
另一条评论:CP的一个有趣之处以及一个重要概念是,可以使用几乎相同的模型生成新的问题实例:
唯一的区别是以下几行,即将“行和”和“列和”更改为决策变量,并对提示进行注释
array[1..rows] of var int: row_sums; % add "var"
array[1..cols] of var int: col_sums; % add "var"
% ...
% row_sums = [13,8,24];
% col_sums = [14,14,17];
以下是三个生成的问题实例(可能为9!=362880):
这个问题转化为9个未知数,但只有6个方程式。因此,我认为没有唯一的解决方案。@TimBiegeleisen有很多隐式约束,变量必须是超出[1..N]范围的唯一整数。“蛮力”是一种算法。你也可以用遗传算法来解决:)试着建立方程组,然后为3个正方形选择种子值。你应该能够解决其他6个问题。你可以迭代,直到在你的范围内找到一个解决方案。说真的,答案不错!我试图在我的文章中添加更多的背景信息,但这是一个很好的答案,因为您还为OP提供了工作代码和开始的基础。穆伊·沃克:)。我没有考虑过ILP,但这确实会奏效。戈德·格达安!;)+1.我担心你的“任意”目标函数。这是行不通的,因为目标不是约束变量的函数。如果添加约束x_i>=0
,则返回值将为0,而不处理任何约束!检查:或任何教科书或维基。@Erobre
array[1..rows] of var int: row_sums; % add "var"
array[1..cols] of var int: col_sums; % add "var"
% ...
% row_sums = [13,8,24];
% col_sums = [14,14,17];
row_sums: [21, 15, 9]
col_sums: [19, 15, 11]
5 9 7
8 4 3
6 2 1
----------
row_sums: [20, 16, 9]
col_sums: [20, 14, 11]
5 8 7
9 4 3
6 2 1
----------
row_sums: [22, 14, 9]
col_sums: [18, 15, 12]
5 9 8
7 4 3
6 2 1
----------