Я читал о проблеме подмножества сумм, когда придумал, как представляется, универсальный алгоритм для ее решения:
(defun subset-contains-sum (set sum)
(let ((subsets) (new-subset) (new-sum))
(dolist (element set)
(dolist (subset-sum subsets)
(setf new-subset (cons element (car subset-sum)))
(setf new-sum (+ element (cdr subset-sum)))
(if (= new-sum sum)
(return-from subset-contains-sum new-subset))
(setf subsets (cons (cons new-subset new-sum) subsets)))
(setf subsets (cons (cons element element) subsets)))))
«набор» - это список, не содержащий дубликатов, а «сумма» - это сумма для поиска подмножеств. «подмножества» - это список cons-ячеек, где «car» - это список подмножеств, а «cdr» - это сумма этого подмножества. Новые подмножества создаются из старых за O (1) раз, просто помещая элемент вперед.
Я не уверен, какова его сложность во время выполнения, но похоже, что с каждым элементом «сумма» увеличивается, размер «подмножеств» удваивается плюс один, поэтому мне кажется, что он по крайней мере квадратичен.
Я публикую это, потому что раньше у меня сложилось впечатление, что NP-полные проблемы, как правило, неразрешимы и что лучшее, на что обычно можно надеяться, - это эвристика, но это, похоже, универсальное решение, которое будет, если у вас есть циклы ЦП , всегда даю вам правильный ответ. Сколько еще NP-полных задач можно решить подобным образом?