考虑以下难题:
单元格已标记或未标记。拼图右侧和底侧的数字表示某行或列的总和。单元格对其行和列中的总和做出贡献(如果已标记):位置(i,j)中的单元格对列值求和,j为行和。例如,在上图中的第一行中,标记了第1,第2和第5个单元格。它们为行总和贡献了1 + 2 + 5(因此总计为8),并且每列的总和为1。
我在ECLiPSe CLP中有一个解决这个难题的解决方案,我正在为它编写一个自定义启发式。
我认为,最简单的单元格是那些列和行提示尽可能低的单元格。通常,N
越低,将N
写为1和N
之间的自然数之和的可能性就越小。在这个难题的背景下,它意味着具有最低column hint + row hint
的单元具有最低的错误几率,因此回溯较少。
在实现中,我有一个代表董事会的NxN
数组,以及两个代表提示的大小为N的列表。 (侧面和底部的数字。)
我看到两个选项:
为search/6编写自定义选择谓词。但是,如果我理解正确,我只能给它2个参数。然后,无法计算给定变量的行+列总和,因为我需要能够将它传递给谓词。我需要4个参数。
忽略搜索/ 6并编写自己的标签方法。这就是我的方式 它现在,请参阅下面的代码。
它需要董事会(包含所有决策变量的NxN
数组),两个提示列表并返回包含所有变量的列表,现在根据行+列总和进行排序。
但是,正如您所看到的,这可能不会变得更加繁琐。为了能够排序,我需要将和附加到每个变量,但为了做到这一点,我首先需要将它转换为一个也包含所述变量坐标的术语,以便我转换回变量为排序完成后......
lowest_hints_first(Board,RowArr,ColArr,Out) :-
dim(Board,[N,N]),
dim(OutBoard,[N,N]),
( multifor([I,J],[1,1],[N,N]), foreach(Term,Terms), param(RowArr,ColArr) do
RowHint is ColArr[I],
ColHint is RowArr[J],
TotalSum is RowHint + ColHint,
Term = field(I,J,TotalSum)
),
sort(3,<,Terms,SortedTerms), % Sort based on TotalSum
terms_to_vars(SortedTerms,Board,Out), % Convert fields back to vars...
( foreach(Var,Out) do
indomain(Var,max)
).
terms_to_vars([],_,[]).
terms_to_vars([field(I,J,TotalSum)|RestTerms],Vars,[Out|RestOut]) :-
terms_to_vars(RestTerms,Vars,RestOut),
Out is Vars[I,J].
最后,这种启发式方法几乎比input_order快。我怀疑它是由于它实施的可怕方式。关于如何做得更好的任何想法?或者我觉得这种启发式应该是一个巨大的改进不正确?
答案 0 :(得分:2)
我看到你对Joachim提出的改进感到满意;但是,当您要求进一步改进您的启发式时,请考虑只有一种方法可以将0作为总和,并且只有一种方法可以获得15。 获得1和14,2和13只有一种方法;得到3和12的两种方法。 一般来说,如果你有K方法得到和N,你也有K方法得到15-N。
所以困难的数字不是大的,它们是中间的。