【问题标题】:Prolog do predicate for all pairs in List?Prolog对List中的所有对做谓词?
【发布时间】:2012-04-27 13:14:37
【问题描述】:

我在 Prolog 脚本中有一组可能性,并想找到最大的集合,其中应用于所有列表对的特定谓词评估为真。

一个简化的例子是一组人,你想找到最大的一组,其中所有人都是共同的朋友。所以,给定:

% Four-way friend CIRCLE
link(tom, bill).
link(bill, dick).
link(dick, joe).
link(joe, tom).

% Four-way friend WEB
link(jill, sally).
link(sally, beth).
link(beth, sue).
link(sue, jill).
link(jill, beth).
link(sally, sue).

% For this example, all friendships are mutual
friend(P1, P2) :- link(P1, P2); link(P2, P1).

可能的匹配项应该是(为清楚起见,按字母顺序显示每一对):

% the two-person parts of both sets :
[bill, tom], [bill, dick], [dick, joe], [joe, tom],
[jill, sally], [beth, sally], [beth, sue], [jill, sue],
  [beth, jill], [sally, sue]

% any three of the web :
[beth, jill, sally], [beth, sally, sue], [beth, jill, sue]

% and the four-person web :
[beth, jill, sally, sue]

我可以找到所有的两人匹配:

% Mutual friends?
friendCircle([Person1, Person2]) :-
    friend(Person1, Person2),
    % Only keep the alphabetical-order set:
    sort([Person1, Person2], [Person1, Person2]).

但后来我在寻找更大的集合时遇到了困难:

friendCircle([Person1|Tail]) :-
    friendWithList(Person1, Tail),
    Tail = [Person2|Tail2],
    % Only keep if in alphabetical order:
    sort([Person1, Person2], [Person1, Person2]),
    friendWithList(Person2, Tail2).

% Check all members of the list for mutual friendship with Person:
friendWithList(Person, [Head|Tail]) :-
    friend(Person, Head), % Check first person in list
    friendWithList(Person, Tail). % Check rest of list

但是当我运行它时,在枚举了两个人的列表之后,Prolog 只是挂起并最终耗尽了堆栈空间。我做错了什么?

我正在尝试做的是浏览网络,对于一个有五个朋友的网络,它会检查这些对中的每一对的朋友状态:

(1,2) (1,3), (1,4), (1,5) % Compare element 1 with the rest of the list
      (2,3), (2,4), (2,5) % Remove element 1 and repeat
             (3,4), (3,5)
                    (4,5)

这就是我认为我在 friendCircle/1 规则中的两个 friendsWithList/2 调用所做的事情。

【问题讨论】:

  • 请注意,sort([Person1, Person2], [Person1, Person2]) 只是表达Person1 @=< Person2 的一种复杂方式。
  • @larsman:确实是一种复杂的方式。但这并不明显,你在说什么。考虑:?- Person1 = john, sort([Person1, Person2], [Person1, Person2]). 给出Person2 = john.

标签: prolog


【解决方案1】:

我相信你正在进入一个循环。您应该在建立朋友圈时检查您是否已经“访问”过朋友。

我对此的看法:

friendCircle(Friends):-
  findall(SFriendCircle,
    (
      friend(Person1, Person2),
      friendCircle([Person1, Person2], FriendCircle),
      sort(FriendCircle, SFriendCircle)
     ),
    LFriends),
  sort(LFriends, SFriends),
  member(Friends, SFriends).

friendCircle([From|Tail], Friends):-
  friend(From, To),
  \+ member(To, Tail),
  forall(member(Friend, Tail), friend(To, Friend)),
  friendCircle([To,From|Tail], Friends).
friendCircle(Friends, Friends).

测试:

?- friendCircle(Friends).
Friends = [ben, tom] ;
Friends = [dick, joe] ;
Friends = [dick, joe, tom] ;
Friends = [dick, tom] ;
Friends = [joe, tom].

【讨论】:

  • 有趣;尽管我对您的逻辑感到困惑(以及它为什么起作用!):您的 bagof 行创建 NFriends 应该是一个列表,其中一个元素代表先前操作的 count (多少From 的朋友尚未在列表 Tail 中),但随后您强制 To(一个人)成为下一行中该列表的成员(计数 /= 人)?
  • 不,bagof 行创建了一组直接连接到“From”的朋友,这些朋友以前没有被访问过。基本上它是在图上设置 DFS(但永远不会返回到已经看到的节点)。
  • 哦,我明白你在做什么了;你正在寻找一条穿过朋友的路,直到你回到你开始的地方,这是一个真正的朋友圈(每个人都是列表中其他两个人的朋友)。请注意,在我最初的问题中,我需要 ALL 对成为朋友(我猜那是“网络”,而不是“圈子”;对不起,如果我的命名让你失望了)。如果添加第四个人 (friend(tom,bill).friend(bill,tom).friend(bill,dick).fiend(dick,bill)),则 [bill, dick, joe, tom] 不是有效集合因为 bill 不是 joe 的朋友,但您的脚本会在结果集中返回它。
  • 我修改了我的答案,以便所有对都成为朋友(forall 的行就是这样做的)
  • 优秀;我现在知道您是如何建立列表的,尽管我认为 findallbagof 调用并不是真正需要的。我不知道forall 电话,它确实简化了事情!如果您的解决方案是我的简化版本,我会发布我自己的答案,但将您标记为正确的功劳!
【解决方案2】:

这是我最终使用的清理版本(使用 cmets 以增加清晰度),它消除了 bagofsortfindall 调用(以及 forall,如果你的序言没有的话):

% Four-way friend CIRCLE
link(tom, bill).
link(bill, dick).
link(dick, joe).
link(joe, tom).

% Four-way friend WEB
link(jill, sally).
link(sally, beth).
link(beth, sue).
link(sue, jill).
link(jill, beth).
link(sally, sue).

% Assume if one is friends with the other, it's reflexive
friend(Person1, Person2) :- (link(Person1, Person2); link(Person2, Person1)).

% Replace a forall/2 call
friendWithList(_, []).
friendWithList(Person, [Friend|Tail]) :-
    friend(Person, Friend),
    friendWithList(Person, Tail).

% Build a friend web
friendCircle(Friends):-
    friend(Person1, Person2), % Start with two random friends...
    Person1 @=< Person2, % ...who are in alphabetical order.
    validCircle([Person1, Person2], Friends). % Build a web with them.


% Given a valid web in the first parameter,
% find a valid web and put it in the second parameter.

% Because the input is a valid web, the simplest output is itself:
validCircle(Friends, Friends).

% The other option is to try and grow the web:
validCircle([Person|Tail], Output):-
    friend(Person, NewGuy), % Grab a friend of the first person in the list... 
    NewGuy @=< Person, % ...who alphabetically comes before that person...
    \+ member(NewGuy, Tail), % ...and we don't have in the list already.

    % Check that the new guy is friends with everyone already on the list
    % If you have the forall/2 predicate,
    % you can swap the comment on the next two lines
    friendWithList(NewGuy, Tail), 
    %forall(member(ExistingFriend, Tail), friend(NewGuy, ExistingFriend)),

    % Build a valid circle with the new inputs,
    % and put that in the output slot.
    validCircle([NewGuy,Person|Tail], Output).

【讨论】:

    【解决方案3】:

    这个观察

    ?- setof(Friend, friend(Person, Friend), Friends).
    Person = beth,
    Friends = [jill, sally, sue] ;
    Person = bill,
    Friends = [dick, tom] ;
    ....
    

    引导我写:

    pair(P1, A, B) :-
        append(_, [A|As], P1),
        append(_, [B|_], As).
    
    circles(Cs) :-
        setof(C, X^P^A^B^(setof(Person, friend(Person, X), P),
                  forall(pair(P, A, B), friend(A, B)),
                  sort([X|P], C)
                 ), Cs).
    

    有了这个结果

    ?- circles(L).
    L = [[beth, jill, sally, sue]].
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      相关资源
      最近更新 更多