Thanks to Mathieu Vavrille for his modelling of problem 103 of the Euler project, “Special subset sums: optimum”, using Constraint Programming and Choco.
public class Euler {
public static void main(final String[] args) {
System.out.println(euler103());
}
/** Euler 103, Special subset sums */
private static String euler103() {
int n = 7;
int bound = 40;
List<Set<Integer>> allPartitions = partitions(n-1); // Enumerates all the partitions of {0, ..., n-1}
Model model = new Model();
IntVar[] mainSet = model.intVarArray("set", n, 0, 100); // Numbers in the set
for (int i = 0; i < n-1; i++) {
mainSet[i].lt(mainSet[i+1]).post(); // Order the numbers in the set
}
IntVar[] partitionSums = model.intVarArray("partition", allPartitions.size(), 0, n*bound); // Create a variable representing the sum of each subset
for (int i = 0; i < partitionSums.length; i++)
model.sum(allPartitions.get(i).stream()
.map(id -> mainSet[id])
.toArray(IntVar[]::new), "=", partitionSums[i]).post();
for (int i = 0; i < allPartitions.size(); i++)
for (int j = i+1; j < allPartitions.size(); j++)
if (Collections.disjoint(allPartitions.get(i), allPartitions.get(j))) { // Enforce the constraint for distinct subsets
if (allPartitions.get(i).size() > allPartitions.get(j).size())
partitionSums[i].gt(partitionSums[j]).post();
else if (allPartitions.get(i).size() == allPartitions.get(j).size())
partitionSums[i].ne(partitionSums[j]).post();
else if (allPartitions.get(i).size() < allPartitions.get(j).size())
partitionSums[i].lt(partitionSums[j]).post();
}
model.setObjective(false, partitionSums[partitionSums.length-1]); // Objective is the sum of the variables in the set
Solver solver = model.getSolver();
String result = "";
while (solver.solve()) { // Solve
result = Arrays.stream(mainSet)
.map(var -> Integer.toString(var.getValue()))
.collect(Collectors.joining());
}
return result;
}
private static List<Set<Integer>> partitions(final int n) {
if (n == 0) {
List<Set<Integer>> parts = new ArrayList<Set<Integer>>();
parts.add(new HashSet<Integer>());
parts.add(Set.of(0));
return parts;
}
List<Set<Integer>> smaller = partitions(n-1);
for (int i = 0; i < 1<<n; i++) {
Set<Integer> currentAdded = new HashSet<Integer>(smaller.get(i));
currentAdded.add(n);
smaller.add(currentAdded);
}
return smaller;
}
}