简体   繁体   English

如何使用 clp(fd) 绑定所有组合 prolog 搜索?

[英]How to bound all combinations prolog search with clp(fd)?

The thing I wanted to do was generate all combinations of elements from a given list.我想做的是从给定列表中生成元素的所有组合。 Eg: From [a,b,c], I might want:例如:从 [a,b,c],我可能想要:

[]
[a]
[b]
[c]
[a,a]
[a,b]
[a,c]
[b,a]
...

And so on.等等。 Perhaps there is a magical prolog one-liner that does this.也许有一个神奇的 prolog one-liner 可以做到这一点。 If so, I would love to hear it.如果是这样,我很想听听。

However, my question is less about solving this particular problem and more of a request that someone explain some subtleties of Prolog's search algorithm for me.但是,我的问题不是解决这个特定问题,而是要求有人为我解释 Prolog 搜索算法的一些微妙之处。

So here's what I did first to solve the above problem:所以这是我首先为解决上述问题所做的:

members([], _).
members([X|Xs], List) :-
  member(X,List),
  members(Xs, List).

This works great but returns all possible results, and not in a great order:这很好用,但会返回所有可能的结果,而且顺序不是很好:

[]
[a]
[a,a]
[a,a,a]

Okay, that's no problem.好的,那没问题。 I really just want all combinations up to a certain length.我真的只想要达到一定长度的所有组合。 So I decided to first get the ones that have exactly a particular length:所以我决定首先得到那些具有特定长度的:

membersWithLength(Members, List, Bound) :-
  L = Bound,
  length(Members, L),  members(Members, List).

This works great, eg for length 2:这很好用,例如长度为 2:

[a,a]
[a,b]
[a,c]
...

And so on.等等。 Now my attempt to use clpfd to leverage the above function to get all lists up to a certain length went awry:现在,我尝试使用 clpfd 来利用上述函数使所有列表达到一定长度时出错了:

:- use_module(library(clpfd)).


membersLessThan(Members, List, Bound) :-
  L in 0..Bound,  % I also tried L #=< Bound
  membersWithLength(Members, List, L).

Kind of works.种作品。 Finds the right results (lists with length less than Bound).找到正确的结果(长度小于 Bound 的列表)。 But after it finds them, it loops continuously searching for more results.但是在找到它们之后,它会不断循环搜索更多结果。 Eg for length 2:例如对于长度 2:

[]
[a]
[b]
[c]
[a,a]
[a,b]
...
[c,c]
Hangs looking for more solutions.

I guess this is the heart of my question.我想这是我问题的核心。 Can someone explain why (according to the trace) prolog continues to check larger and larger lists as possible solutions, even though they are all doomed to failure?有人可以解释为什么(根据跟踪)序言继续检查越来越大的列表作为可能的解决方案,即使它们都注定要失败? And can someone tell me if there's a way to help prolog avoid this doomed journey?有人能告诉我是否有办法帮助序言避免这个注定的旅程吗?

I ultimately used the following code to solve the problem, but I was disappointed that I couldn't figure out how to use clpfd's integer constraints to constrain the size of the lists.最终我用下面的代码解决了这个问题,但很失望,我无法弄清楚如何使用clpfd的整数约束来约束列表的大小。

membersLessThan_(Members, List, Bound) :-
  numlist(0,Bound,ZeroToBound),
  member(L, ZeroToBound),
  membersWithLength(Members, List, L).

Here is all the relevant code on SWISH: http://swish.swi-prolog.org/p/allcombos.pl这是 SWISH 上的所有相关代码: http ://swish.swi-prolog.org/p/allcombos.pl

With you original implementation of members, if you want to enumerate all the answers you can do:使用您最初的成员​​实现,如果您想枚举您可以执行的所有答案:

length(L, _), members(L, [a,b,c]).

which gives you the answers:这给了你答案:

L = [] ;
L = [a] ;
L = [b] ;
L = [c] ;
L = [a, a] ;
L = [a, b] ;
L = [a, c] ;
L = [b, a] ;
L = [b, b] ;
L = [b, c] ;
L = [c, a] ;
L = [c, b] ;
L = [c, c] ;
L = [a, a, a] ;
L = [a, a, b] ;
L = [a, a, c] ;
L = [a, b, a]

This is a common idiom for iterative deepening, which allows you to list all the answers fairly.这是迭代深化的常用习语,它允许您公平地列出所有答案。 I don't think clpfd can help you in this case.在这种情况下,我认为 clpfd 无法帮助您。

EDIT编辑

I see that in the title you explicitly ask about CLPFD.我在标题中看到您明确询问 CLPFD。 The reason your code doesn't work is that when you do您的代码不起作用的原因是当您这样做时

L in 0..Bound

you are not actually enumerating those values.您实际上并没有枚举这些值。 For the next predicates, L is still unbound and carries a constraint.对于下一个谓词,L 仍然是未绑定的并带有约束。 So membersWithLength will keep looping trying new lengths, and once the length it's instantiated, it will see that the constraint fails and try again.所以 membersWithLength 将继续循环尝试新的长度,一旦长度被实例化,它将看到约束失败并重试。 You can see it in these examples:您可以在这些示例中看到它:

L in 0..2, length(X, L)

loops like in your code, because length keeps trying.像在您的代码中一样循环,因为长度一直在尝试。 If you want to limit it, L has to be instantiated before calling length.如果你想限制它,L必须在调用length之前实例化。 You can use label for that.您可以为此使用标签。 This next example doesn't loop:下一个示例不会循环:

L in 0..2, label([L]), length(X, L)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM