Все комбинации деления целого числа на несколько "групп"
Допустим, у меня есть 5 конфет, и я хочу найти все возможные комбинации, чтобы поделиться ими с моими 3 детьми.
Это будет примерно так:
5 for kid_A, 0 for kid_B, 0 for kid_3
0 for kid_A, 5 for kid_B, 0 for kid_3
....
4 for kid_A, 1 for kid_B, 0 for kid_3
4 for kid_A, 0 for kid_B, 1 for kid_3
....
and so on
Есть ли алгоритм для поиска этой комбинации?
До сих пор мне удалось вычислить первые 3 комбинации, где я отдаю все свои сладости одному из моих детей, а остальные получают 0; но я потерян на том, как разделить, как только я закончу с этой первой итерацией.
2 ответа
Есть три аспекта этой проблемы:
- Сколько способов я могу дать одному ребенку до N конфет? (перестановок на ребенка)
- Сколько способов я могу дать другому ребенку оставшиеся N сладостей для каждого из них? (рекурсия, может быть)
- Когда я прекращаю делить сладости? (Окончание)
Сколько способов вы можете дать ребенку n сладостей, просто: вы получаете одну перестановку для каждого из 0 -> n, например, первому ребенку можно дать 0, 1, 2, 3, 4 или 5 сладостей.
Для второго ребенка вы можете повторить это после того, как вычтете количество сладостей первого ребенка. Итак, для 0 -> m, где m = 5 - n.
Для каждой из этих перестановок n и m третий потомок просто получает остаток: t = 5 - (n + m).
Исходя из этого, вы можете сформулировать два цикла для генерации ваших перестановок. Существует более общий случай для любого количества детей или конфет, но вы должны быть осторожны с повторением (проблемы с размером стека) и помните, что для больших чисел количество комбинаций может быть огромным.
Код
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Example {
public static void main(final String... args) {
for (final List<Integer> option : divide(5, 3)) {
System.out.printf("%d for kid_A, %d for kid_B, %d for kid_3%n", option.toArray());
}
}
private static List<List<Integer>> divide(final int count, final int groups) {
if (groups == 1) {
final List<Integer> inner = new ArrayList<>(1);
inner.add(count);
final List<List<Integer>> outer = new ArrayList<>(1);
outer.add(inner);
return outer;
}
return IntStream.range(0, count + 1)
.mapToObj(Integer::valueOf)
.flatMap(p -> {
return divide(count - p, groups - 1).stream()
.map(q -> {
q.add(p);
return q;
});
}).collect(Collectors.toList());
}
}
Как это устроено
Это начинается с базового предположения, что если у вас есть 1 ребенок, вы даете им все сладости.
Если у вас есть более одного ребенка, он определяет, сколько дать первого, а затем проверяет, сколько других может быть.
FlatMap получает все параметры и добавляет количество конфет, которые получает текущий ребенок.