Prolog ECLiPSe CLP中的自定义启发式

Prolog ECLiPSe CLP中的自定义启发式,prolog,clpfd,eclipse-clp,Prolog,Clpfd,Eclipse Clp,考虑以下难题: 单元格有标记或无标记。拼图右侧和底部的数字表示某一行或某一列的总和。单元格对其行和列中的和作出贡献(如果标记):位于(i,j)位置的单元格对列和贡献i,对行和贡献j。例如,在上图的第一行中,标记了第一、第二和第五个单元格。它们对行和贡献1+2+5(因此总计8),对列和贡献1 我在EclipseCLP中有一个用于解决这个难题的解算器,我正准备为它编写一个自定义的启发式程序。 我认为,最容易从列和行提示尽可能低的单元格开始。一般来说,N越低,将N作为介于1和N之间的自然数之和写入的

考虑以下难题:

单元格有标记或无标记。拼图右侧和底部的数字表示某一行或某一列的总和。单元格对其行和列中的和作出贡献(如果标记):位于(i,j)位置的单元格对列和贡献i,对行和贡献j。例如,在上图的第一行中,标记了第一、第二和第五个单元格。它们对行和贡献1+2+5(因此总计8),对列和贡献1

我在EclipseCLP中有一个用于解决这个难题的解算器,我正准备为它编写一个自定义的启发式程序。

我认为,最容易从列和行提示尽可能低的单元格开始。一般来说,
N
越低,将
N
作为介于1和
N
之间的自然数之和写入的可能性就越小。在这个谜题的上下文中,这意味着
列提示+行提示最低的单元格出错几率最低,因此回溯较少

在实现中,我有一个表示电路板的
NxN
数组,以及两个表示提示的大小为N的列表。(数字在侧面和底部。)

我看到两种选择:

  • 为编写自定义选择谓词。然而,如果我理解正确,我只能给它两个参数。因此,无法计算给定变量的行+列和,因为我需要能够将其传递给谓词。我需要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].
最低提示优先(板、行、列、出):-
尺寸(板[N,N]),
尺寸(外侧,[N,N]),
(multifor([I,J],[1,1],[N,N]),foreach(Term,Terms),param(RowArr,ColArr)do
RowHint是ColArr[I],
ColHint是RowArr[J],
TotalSum是RowHint+ColHint,
术语=字段(I,J,总和)
),

排序(3,我看到你已经对约阿希姆提出的改进感到满意),但是,当你要求进一步改进你的启发式时,考虑只有一个方法得到0作为一个总和,以及只有一个方法得到15。 只有一种方法可以得到1和14,2和13;有两种方法可以得到3和12。 一般来说,如果你有K种方法得到和N,你也有K种方法得到15-N


所以困难的总和不是大的,它们是中间的。

我看到你已经对约阿希姆提出的改进感到满意;然而,当你要求进一步改进你的启发式时,考虑只有一个方法可以得到0作为一个和,并且只有一个方法可以得到15。 只有一种方法可以得到1和14,2和13;有两种方法可以得到3和12。 一般来说,如果你有K种方法得到和N,你也有K种方法得到15-N


因此,困难的和不是大的,而是中间的。

要评估您的启发式,首先要做的是比较回溯的数量。使用
search/6
时,您可以使用
回溯(计数)
选项获取此数字。使用您自己的搜索策略,请参阅如何计算等效计数。这是一个好主意。我将在自己的搜索中实现它,尽管使用
search/6
并将其传递给我自己的搜索谓词要容易得多。当您传递输入t时,您有什么办法使此工作正常吗o所说的谓词?我不能让它只处理
search/6
需要的两个参数谓词。您可以调用
search/6
来代替代码中的
foreach(Var,Out)
循环。顺便说一句,您的排序应该类似于
sort(3,=非常感谢您的帮助。我实现了您链接的教程,它一直为高达250x250的谜题提供0个回溯,我认为这非常奇怪。事实证明,其他变量选择启发式也使用0个回溯。这是搜索/6,所以它不可能是回溯实现。这让我感到惊讶在这种情况下,分页非常强大。要评估您的启发式,首先要做的是比较回溯的数量。使用
search/6
时,您可以使用
回溯(计数)
选项获取此数字。使用您自己的搜索策略,请参阅如何计算等效计数。这是一个好主意。我将在自己的搜索中实现它,尽管使用
search/6
并将其传递给我自己的搜索谓词要容易得多。当您传递输入t时,您有什么办法使此工作正常吗o所说的谓词?我不能让它只处理
search/6
需要的两个参数谓词。您可以调用
search/6
来代替代码中的
foreach(Var,Out)
循环。顺便说一句,您的排序应该类似于
sort(3,=非常感谢您的帮助。我实现了您链接的教程,它一直为高达250x250的谜题提供0个回溯,我认为这非常奇怪。事实证明,其他变量选择启发式也使用0个回溯。这是搜索/6,所以它不可能是回溯实现。这让我感到惊讶在这种情况下,分页非常强大。你完全正确。我忘了考虑列的数量限制了选择。谢谢!你完全正确。我忘了考虑c的数量