使用prolog将集合划分为n个子集

时间:2018-01-11 18:31:10

标签: prolog set

我正在努力解决以下问题,使用prolog将一个集合划分为n个子集。

所以例如,我给程序输入:X = [1,2,3,4],N = 3,我得到

Res = [[1,2], [3], [4]]
Res = [[1,3], [2], [4]]
Res = [[1,4], [2], [3]]
Res = [[2,3], [1], [4]]
Res = [[2,4], [1], [3]]
Res = [[3,4], [1], [2]]

或者我作为输入:X = [1,2,3,4],N = 2,我得到

Res = [[1,2], [3,4]]
Res = [[1,3], [2,4]]
Res = [[1,4], [2,3]]
Res = [[1,2,3], [4]]
Res = [[1,2,4], [3]]
Res = [[1,3,4], [2]]
Res = [[2,3,4], [1]]

2 个答案:

答案 0 :(得分:4)

这个答案延伸了 @lurker's previous answer具有其他(冗余)约束。

使用我们定义了以下辅助非终端:

same_length([]) --> [].                         % DCG-style same_length/2
same_length([_|Es]) --> [_], same_length(Es).

same_length1([_|Es]) --> [_], same_length(Es).

same_lengths1([]) --> [].
same_lengths1([Es|Ess]) --> same_length1(Es), same_lengths1(Ess).

我们通过预先添加phrase/2目标来利用这些DCG:

list_partitionedNU(Es, Xss) :-
   phrase(same_lengths1(Xss), Es),
   list_partitioned(Es, Xss).

对于一些普通的测试案例,我们仍然可以得到合理的答案吗?

?- list_partitionedNU([a,b,c], Xss).
   Xss = [[a],[b],[c]]
;  Xss = [[a],[b,c]]
;  Xss = [[a,b],[c]]
;  Xss = [[a,c],[b]]
;  Xss = [[a,b,c]]
;  false.

当然看起来对我好。

接下来,让我们谈谈全面终止。像list_partitioned(Es, [[a,b,c]])这样的目标不会普遍终止 - 即使它们是微不足道的。 list_partitionedNU/2解决了这个问题:

?- list_partitioned(Es, [[a,b,c]]).
   Es = [a,b,c]
;  NONTERMINATION

?- list_partitionedNU(Es, [[a,b,c]]).
   Es = [a,b,c]
;  false.                                          % terminates universally

这些额外的约束可以大大加快一些查询。

使用SICStus Prolog 4.4.0:

| ?- use_module(library(between), [numlist/3]).
yes

| ?- numlist(1, 14, _Es),
     length(_Xss, 10),
     member(P_2, [list_partitioned,list_partitionedNU]),
     call_time((call(P_2,_Es,_Xss), false ; true), T_msec).
P_2 = list_partitioned  , T_msec = 29632 ? ;
P_2 = list_partitionedNU, T_msec =   600 ? ;       % 40x faster
no

好的!当然,加速取决于所用列表的实际长度...... YMMV:)

答案 1 :(得分:2)

问题已在这个问题中得到解决:All Partitions of a List In Prolog。只需在“Prolog分区集”上进行Google搜索即可轻松找到。

然后你可以用length/2

来限制它
partitions_of_length(List, N, Partition) :-
    length(Partition, N), list_partitioned(List, Partition).

| ?- partitions_of_length([a,b,c,d], 2, L).

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

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

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

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

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

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

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

no
| ?-

我们通过首先限制长度来优化性能。下面在SWI Prolog中说明了约束之后与之前的长度之间的差异:

:- use_module(library(statistics)).

6 ?- time((list_partitioned([a,b,c,d], P), length(P, 2))).
% 18 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 1580195 Lips)
P = [[a, b, c], [d]] ;
% 12 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 1059696 Lips)
P = [[a, b, d], [c]] ;
% 10 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 900414 Lips)
P = [[a, b], [c, d]] ;
% 19 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 1624070 Lips)
P = [[a, c, d], [b]] ;
% 10 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 1021555 Lips)
P = [[a, c], [b, d]] ;
% 19 inferences, 0.000 CPU in 0.000 seconds (87% CPU, 1665060 Lips)
P = [[a, d], [b, c]] ;
% 19 inferences, 0.000 CPU in 0.000 seconds (87% CPU, 1661420 Lips)
P = [[a], [b, c, d]] ;
% 37 inferences, 0.000 CPU in 0.000 seconds (90% CPU, 2382639 Lips)
false.

7 ?- time((length(P, 2), list_partitioned([a,b,c,d], P))).
% 13 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 1175832 Lips)
P = [[a, b, c], [d]] ;
% 6 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 742023 Lips)
P = [[a, b, d], [c]] ;
% 6 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 848896 Lips)
P = [[a, b], [c, d]] ;
% 9 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 1210328 Lips)
P = [[a, c, d], [b]] ;
% 6 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 828386 Lips)
P = [[a, c], [b, d]] ;
% 9 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 1215723 Lips)
P = [[a, d], [b, c]] ;
% 9 inferences, 0.000 CPU in 0.000 seconds (90% CPU, 697999 Lips)
P = [[a], [b, c, d]] ;
% 10 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 991277 Lips)
false.

如果要修改上面链接中的代码来约束列表的长度,最好的方法可能是在执行任何其他操作之前将length/2调用放在谓词中,但行为是相同的以上。