I'm struggling with the following problem, partition a set into n subsets using prolog.
So for example, I give as input to program: X = [1,2,3,4], N=3 and I get
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]]
or I give as input: X = [1,2,3,4], N=2 and I get
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]]
The problem is already mostly solved in this question: All Partitions of a List In Prolog. This was easy to find just doing a Google search on "Prolog partition set".
Then you can just constrain it with
length/2
:We optimize performance in this case by constraining the length first. Below illustrates, in SWI Prolog, the difference between constraining the length after versus before:
If you were to modify the code in the link above to constrain the length of the list, the best way is probably to put the
length/2
call inside the predicate before doing anything else, but the behavior is identical then to the above.This answer extends @lurker's previous answer with additional (redundant) constraints.
Using dcg we define the following auxiliary non-terminals:
We utilize these DCGs by adding a
phrase/2
goal upfront:Do we still get reasonable answers for some vanilla test case?
Sure looks okay to me.
Next, let's talk about universal termination. Goals like
list_partitioned(Es, [[a,b,c]])
do not terminate universally—even though they are trivial.list_partitionedNU/2
fixes this:These additional constraints can speedup some queries considerably.
Using SICStus Prolog 4.4.0:
Alright! Of course, the speedup depends on the actual lengths of the lists used... YMMV:)