繁体   English   中英

Prolog:比较列表列表中的列表

[英]Prolog: Comparing Lists from Lists of Lists

我现在花了很长时间试图弄清楚我的错误是什么,但我做不到。

任务:我们必须弄清楚如何以列表列表的形式找到包含 9 个元素的列表的三个排列。 每个列表列表应包含三个子列表,每个子列表包含三个元素。 但是不允许任何元素与两个不同子列表中的另一个元素在一起。

以下 output 对于三个排列 A、B、C 和给定的 List= [1,2,3,4,5,6,7,8,9] 可以是:

predicate(A, B, C , [1,2,3,4,5,6,7,8,9]).

A = [[1,2,3],[4,5,6],[7,8,9]],
B = [[1,4,7],[2,5,8],[3,6,9]],
C = [[1,5,9],[2,6,7],[3,4,8]].

到目前为止我的代码(首先是我的辅助谓词):

将列表拆分为列表列表( N 始终为 3 ):

split_list(List, N, Splitted_List) :-
    split_helper(List, N, [], Splitted_List).

split_helper([], _, Acc, Acc).

split_helper(List, N, Acc, Splitted_List) :-
    my_append(H, T, List),
    my_length(H, N),
    split_helper(T, N, [H|Acc], Splitted_List).

一个可能的查询:

split_list([1,2,3,4,5,6,7,8,9], 3, X).

X = [[1,2,3],[4,5,6],[7,8,9]].

检查列表列表的所有子列表是否最多包含一个相同的元素:

max_one_common_element(List1, List2) :-
    max_one_common_element(List1, List2, 0).

max_one_common_element([], _, Count) :-
    Count =< 1.
max_one_common_element([H|T], List2, Count) :-
    (my_member(H, List2) ->
        NewCount is Count + 1,
        max_one_common_element(T, List2, NewCount)
    ;
        max_one_common_element(T, List2, Count)
    ).

一个可能的查询:

max_one_common_element([[1,2,3],[4,5,6],[7,8,9]], [[1,4,7],[2,5,8],[3,6,9]]).

True.

要更改子列表的顺序,以便进行比较(稍后很重要):

swap_lists(List, Result):-
    select(Selected, List, Rest),
    append(Rest, [Selected], Result).

一个可能的查询:

swap_list([[1,2,3],[4,5,6],[7,8,9]], X).

X =  [[4,5,6],[7,8,9],[1,2,3]].

我的主要谓词,它实例化了 A、B 和 C。让我产生问题的是 C,A 和 B 已正确实例化。

我正在考虑采用输入列表的所有排列并检查 max_one_common_element/2 每个子列表是否最多有一个公共元素。 由于 max_one_common_element/2 只能检查当前索引处的两个列表(例如 [[1,2],[3,4]], [[3,4],[1,2]] 会返回 True,即使这是错误的)我的想法是两次更改 A 和 B 的子列表的顺序,并在第一次和第二次更改后再次检查 C,因此应覆盖 A 和 B 的所有 3 个子列表。

main_predicate(A, B, C, List):- 

    /* instantiates A as the input list but seqmented */

    split_list(List, 3 , A),

    /* instantiates B as a permutation of A, taking every nth element in a sublist*/

    %This part is unimportant since it works properly

    /* instantiates C as a permutation from the input list, test that each Sub-List contains at most one same element */

    permutation(List, Permuted),
    split_list(Permuted, Size, Dessert),
    max_one_common_element(A, C),
    max_one_common_element(A, C),

    /* first swap A and B two times */

    swap_lists(A, A1),
    swap_lists(A1, A2),
    swap_lists(B, B1),
    swap_lists(B1, B2),

    /* Check again with C */

    max_one_common_element(A1, C),
    max_one_common_element(A2, C),
    max_one_common_element(B1, C),
    max_one_common_element(B2, C).

当我查询:

predicate(A, B ,C, [1,2,3,4,5,6,7,8,9] ).

My output is:
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ,
B = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] ,
C = [[7, 8, 9], [4, 5, 6], [1, 2, 3]] .

Prolog 只是似乎没有考虑 max_one_common_element/2 的每一次调用。 由于删除一些似乎改变了 output,但在我看来,我已经考虑了所有情况,一切都应该没问题。 我也考虑过更改 max_one_common_element/2,但没有任何效果。 非常感谢您的提前帮助。

控制回溯很有趣(对所有解决方案子列表执行member_count_inc_le ):

:- dynamic used/2.

list_perm3(L, P) :-
    retractall(used(_, _)),
    list_perm3_loop(L, P).

list_perm3_loop(L, P) :-
    % Keeping backtracking to this top-level
    (list_perm3_(L, P) -> true ; !, fail).
list_perm3_loop(L, P) :-
    list_perm3_loop(L, P).

list_perm3_(L, P) :-
    length(P, 3),
    perm3_lists(P, L),
    assert_used(P).

% Add the new solution via assert
assert_used([]).
assert_used([H|T]) :-
    % Assert the used pairs, to prevent reuse
    forall(
        (   select(E1, H, H0),
            member(E2, H0)
        ),
        assert(used(E1, E2))
    ),
    assert_used(T).

perm3_lists([], []).
perm3_lists([H|T], L) :-
    perm3(L, H, R),
    perm3_lists(T, R).

perm3(L, P, R) :-
    length(P, 3),
    perm3_(P, L, [], R).

perm3_([], R, _, R).
perm3_([H|T], L, P, R) :-
    select(H, L, L0),
    comb_available(P, H),
    perm3_(T, L0, [H|P], R).

comb_available([], _).
comb_available([H|T], E) :-
    \+ used(E, H),
    comb_available(T, E).

swi-prolog 中的结果:

?- list_perm3([1,2,3,4,5,6,7,8,9], P).
P = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ;
P = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] ;
P = [[1, 5, 9], [2, 6, 7], [3, 4, 8]] ;
P = [[1, 6, 8], [2, 4, 9], [3, 5, 7]] ;
false.

我设法想出了自己的解决方案:

make_dessert(Starter, Main, Dessert, List_of_Persons, Size):-
    permutation(List_of_Persons, Permuted),
    split_list(Permuted, Size, Dessert),
    at_most_one_common(Starter, Dessert),
    at_most_one_common(Main, Dessert).

split_list(List, N, Splitted_List) :-
    split_helper(List, N, [], Splitted_List).

split_helper([], _, Acc, Acc).

split_helper(List, N, Acc, Splitted_List) :-
    append(H, T, List),
    length(H, N),
    split_helper(T, N, [H|Acc], Splitted_List).

at_most_one_common([], _).
at_most_one_common([H|T], List2) :-
    check_list(H, List2),
    at_most_one_common(T, List2).

check_list(_, []).
check_list(X, [H|T]) :-
    intersection(X, H, Z),
    length(Z, L),
    L =< 1,
    check_list(X, T).

我忘了说,我会因为将推理保持在最低水平而获得加分。 由于我的程序不如@brebs 的程序高效,我非常感谢您提供一些建议来降低这些。 我可能也在考虑稍后开始关于这个案例的新问题。

暂无
暂无

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

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