Вот рекурсия, которая, по моему мнению, тесно связана с алгоритмом Жана-Бернарда Пеллера в Mathematica .
. В качестве входных данных принимается число элементов каждого типа.Вывод в аналогичной форме.Например:
{a,a,b,b,c,d,d,d,d} -> {2,2,1,4}
Функция:
f[k_, {}, c__] := If[+c == k, {{c}}, {}]
f[k_, {x_, r___}, c___] := Join @@ (f[k, {r}, c, #] & /@ 0~Range~Min[x, k - +c])
Использование:
f[4, {2, 2, 1, 4}]
{{0, 0, 0, 4}, {0, 0, 1, 3}, {0, 1, 0, 3}, {0, 1, 1, 2}, {0, 2, 0, 2},
{0, 2, 1, 1}, {1, 0, 0, 3}, {1, 0, 1, 2}, {1, 1, 0, 2}, {1, 1, 1, 1},
{1, 2, 0, 1}, {1, 2, 1, 0}, {2, 0, 0, 2}, {2, 0, 1, 1}, {2, 1, 0, 1},
{2, 1, 1, 0}, {2, 2, 0, 0}}
Запрошено объяснение этого кода.Это рекурсивная функция, которая принимает переменное число аргументов.Первый аргумент k
, длина подмножества.Второй - это список значений каждого типа для выбора.Третий аргумент и далее используется внутри функции для хранения подмножества (комбинации) в том виде, как он создан.
Поэтому это определение используется, когда в наборе выбора больше нет элементов:
f[k_, {}, c__] := If[+c == k, {{c}}, {}]
Если сумма значений комбинации (ее длина) равна k
, вернуть эту комбинацию, в противном случае вернуть пустой набор.(+c
сокращенно для Plus[c]
)
В противном случае:
f[k_, {x_, r___}, c___] := Join @@ (f[k, {r}, c, #] & /@ 0~Range~Min[x, k - +c])
Чтение слева направо:
Join
используетсячтобы сгладить уровень вложенных списков, чтобы результат не становился все более глубоким тензором.
f[k, {r}, c, #] &
вызывает функцию, отбрасывая первую позицию набора выбора (x
) и добавление нового элемента в комбинацию (#
).
/@ 0 ~Range~ Min[x, k - +c])
для каждого целого числа от нуля до меньшего из первого элемента набора выбора, иk
меньше общего количества комбинации, что является максимальным значением, которое можно выбрать, не превышая размер комбинации k
.