Prolog 手动或自定义标签
Posted
技术标签:
【中文标题】Prolog 手动或自定义标签【英文标题】:Prolog manual or custom labeling 【发布时间】:2016-07-15 14:24:12 【问题描述】:我目前正在为 Prolog 中的平面规划问题编写求解器,并且在标记部分存在一些问题。
当前的问题是我的约束已发布,但是当我启动标签时,需要很长时间才能找到解决方案。我想引入一些启发式方法。
我的问题是,如何手动标记我的变量?恐怕在像这样定义一个 clpfd 变量之后:
X in Xinf..Xsup
并限制它,如果我做类似的事情:
fd_sup(X, Xmax),
X = Xmax,
...
在我的自定义标签中,我不会使用 Prolog 的回溯能力来测试 X 域的其他值。我错了吗 ?
另外,有没有比编写自定义标记程序更聪明的方法来标记我的变量?我对启发式的想法是交替尝试变量域的极值(如 max(X)、min(X)、max(X-1)、min(X-1) 等......)
希望你能帮助我:)
【问题讨论】:
手动标注?只需将实际选定的值添加到约束存储... 对不起,我是初学者。通过将值添加到约束存储,您的意思是写“X #= Xmax”吗?如果是这样,这将行不通,我想要的是或者尝试变量域的最大值和最小值,如果它们不起作用,则从域中删除这些值这将是一种标签([min(X) , max(X)], [X]) 除非我不知道这是否可行,而且我有一个完整的变量列表,我想做同样的事情 要获得最佳建议,您确实应该更多地展示您的具体问题。 【参考方案1】:编写自定义标记程序并不难,而且对于大多数实际问题,您最终还是需要一个,以便结合特定问题的启发式方法。
标签过程的两个主要组成部分是
-
变量选择:从所有剩余的(即尚未实例化的)问题变量中,选择一个进行下一步考虑。
值选择或分支:通过以(通常)互补的方式减少所选变量的域,通过回溯探索两个或多个替代子问题。
使用这个方案,默认的标注过程可以写成
label(Xs) :-
( select_variable(X, Xs, Xs1) ->
branch(X),
label(Xs1)
;
true % done, no variables left
).
select_variable(X, [X|Xs], Xs). % 'leftmost' strategy
branch(X) :- indomain(X).
您现在可以重新定义 select_variable/3
以实现“first-fail”等技术,并重新定义 branch/1
以尝试不同顺序的域值。只要您确保branch/1
在回溯时枚举了X
的所有域值,您的搜索就会保持完整。
有时您只想先尝试一个域值(例如,启发式建议的一个),但如果不好,不要立即提交另一个值。 假设,就像在您的示例中一样,您想首先尝试最大域值。你可以这样写
branch(X) :-
fd_sup(X, Xmax),
(
X = Xmax % try the maximum
;
X #\= Xmax % otherwise exclude the maximum
).
因为这两种情况是互补的,并且涵盖了 X 的所有可能值,所以您的搜索仍然是完整的。但是,由于第二种选择,branch/1
现在可以使用未实例化的X
成功,这意味着您必须确保在标记过程中不会从列表中丢失此变量。一种可能性是:
label(Xs) :-
( select_variable(X, Xs, Xs1) ->
branch(X),
( var(X) -> append(Xs1, [X], Xs2) ; Xs2=Xs1 ),
label(Xs2)
;
true % done, no variables left
).
【讨论】:
感谢您的详细解释。我知道分支需要涵盖域的所有值,但是标签程序是否需要处理每个变量?我的意思是,我可以对某些变量使用自定义标记程序,而对其他变量使用通常的标记程序吗?我会说不,但想确定 是的,您可以结合使用标签例程,例如labeling(Xs), labeling(Ys)
。但是您应该知道,X 始终位于搜索树的顶部,而 Y 位于底部。您也可以让my_labeling(Xs), labeling(Xs)
让my_labeling
只处理一些变量,以便由labeling
处理剩余的变量。标记例程应该是健壮的并且忽略已经实例化的变量。【参考方案2】:
首先,始终尝试内置启发式方法。 ff
通常是个好策略。
对于自定义标签策略,通常最简单的方法是先将域转换为列表,然后重新排序列表,然后简单地使用member/2
使用新顺序分配域的值。
一个好的建筑黑色是dom_integers/2
,将一个有限 CLP(FD)域与一个整数列表联系起来:
:- use_module(library(clpfd)).
dom_integers(D, Is) :- phrase(dom_integers_(D), Is).
dom_integers_(I) --> integer(I) , [I].
dom_integers_(L..U) --> numlist(L, U, Is) , Is.
dom_integers_(D1\/D2) --> dom_integers_(D1), dom_integers_(D2).
您的特定策略很容易在此类有序整数列表上表达,将这些整数与第二个列表相关联,其中值按您描述的顺序出现:
outside_in([]) --> [].
outside_in([I]) --> [I].
outside_in([First|Rest0]) --> [First,Last],
append(Rest, [Last], Rest0) ,
outside_in(Rest).
示例查询和结果:
?- 短语(outside_in([1,2,3,4]),是)。 是 = [1, 4, 2, 3] ; 错误的。将其与fd_dom/2
和dom_integers/2
结合,我们得到(省略了X
以外的变量的绑定):
?- X in 10..20,
fd_dom(X, Dom),
dom_integers(Dom, Is0),
phrase(outside_in(Is0), Is),
member(X, Is).
X = 10 ;
X = 20 ;
X = 11 ;
X = 19 ;
X = 12 ;
X = 18 ;
etc.
member/2
保留了不确定性。
【讨论】:
感谢您的回答!因此,您将通过 member/2 过程分配值,但我不太理解您提供的“outside_in”,尤其是语法,我们是否在 swi prolog 中使用“ ”?否则它似乎完全符合我的要求我正在寻找! 是的,这正是您所要求的,我给出的其他答案与您绝对应该考虑的其他方面对这个答案进行了补充,并解决了您的一些隐含的问题题。请参阅dcg 了解我在这里用来描述列表的语法。您使用phrase/2
接口谓词来调用DCG。还要确保查看@jschimpf 的答案:与其简单地使用member/2
,不如在两个分支中发布由(#=)/2
和(\#=)/2
组成的析取,以增强替代方案中的传播。
【参考方案3】:
确保将标记策略与其他传播区分开来。这两个方面目前在您的问题中有点混杂。
在 SWI-Prolog 中,有一个谓词 clpfd:contracting/1
。它按照您的描述进行:它尝试来自域边界的值,并删除可以被视为不一致的值,即已知不存在解决方案的值。
因此,如果您有变量列表Vs
,您可以尝试:clpfd:contracting(Vs)
,看看是否有帮助。
请注意,这也可能会显着减慢搜索速度,但另一方面也有助于在尝试任何标记之前显着减少搜索空间!
【讨论】:
感谢您的精确!收缩是在每次为变量赋值时动态发生还是在一开始就以静态方式发生一次?因为我建立我的域的方式不应该有静态方式不一致的值。无论如何,我将在旧版本的求解器上尝试这个,使用没有任何自定义标签的 ff 策略!我会及时通知你! 收缩只发生在你调用它的时候,但你当然也可以多次调用它,例如,为某些变量调用clpfd:contracting/1
during a自定义标签。
但是收缩的效果会随着回溯而消失吗?我的意思是,如果另一个变量的值发生变化,它会恢复变量的不一致值吗?
请为此打开一个新问题。我们需要单独讨论出现的问题。
好的没问题,你可以在这里看到:***.com/questions/36272417/…【参考方案4】:
为了补充其他答案(一个对比标记和传播,一个显示专用标记方法),我现在解决这个问题的另一个非常重要的方面:
很多时候,当初学者抱怨他们的代码速度时,他们的代码实际上甚至没有终止!在这种情况下,提高效率无济于事。
因此,这个答案指向您首先确保您的关系实际终止。
确保终止 CLP(FD) 程序的最佳方法是将它们分成两部分:
-
第一个称为核心关系,只是发布所有约束。
第二个使用
labeling/2
执行实际的搜索。
你在你的程序中做过这个吗?如果没有,请这样做。完成此操作后,请确保核心关系,例如 solution/2
(其中参数是:表示任务实例的术语,以及要标记的变量列表)通过查询普遍终止:
?- solution(Instance, Vs), false.
如果终止,则以下也终止:
?- solution(Instance, Vs), label(Vs), false.
当然,在较大的任务中,你没有机会实际见证后一个查询的终止,但有很好的机会见证第一个查询的终止,因为设置约束通常比实际获得偶数要快得多一个单一的解决方案。
因此,测试您的核心关系是否终止!
【讨论】:
只有在SWI的`clpfd中,标签总是终止 是的!我的程序的构建方式是,我有一个主程序调用其他人发布约束,然后是一个标记程序。我尝试解决问题的小实例并打印了一个解决方案,我想我应该没有核心关系终止问题吗? 看来您对此没有任何问题,所以没关系。您可以通过像?- core_relation, false.
这样的简单查询来确保,即在查询末尾附加, false
,这样只有终止属性仍然是可观察的。在 SWI 和 Yap 中,如果 that 终止,那么 this plus 标记也将终止。
好吧,如果我这样调用我的主程序:main(X), false.
我有同样的“无限”标签问题,但对于小实例,我确信它会结束,因为我在代码中到处都有 writeln; )
在这个测试中不要使用main/1
,而只使用核心关系,即只说明约束,没有标签!如果您对此策略有任何疑问,请为此打开一个新问题。【参考方案5】:
这将跟进this previous answer by @mat。
如果您还有更多 CPU 周期需要消耗,请尝试 shave_zs/1
,如 this previous answer 中所定义。
shave_zs/1
类似于辅助库谓词clpfd:contracting/1
。然而,与contracting/1
不同的是,所有 值都是“可供抓取”——而不仅仅是边界处的值。 YMMV!
【讨论】:
以上是关于Prolog 手动或自定义标签的主要内容,如果未能解决你的问题,请参考以下文章
用于在许多 ViewControllers 上显示标签的自定义 UIView?
AttributeError:'NoneType'对象没有属性'legendHandles',而在自定义图例中为每个标签手动分配颜色时