Lisp 子集和问题与NP完全问题的可解性

Lisp 子集和问题与NP完全问题的可解性,lisp,np-complete,subset-sum,Lisp,Np Complete,Subset Sum,我在阅读有关子集和问题的文章时,提出了一种解决该问题的通用算法: (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)))

我在阅读有关子集和问题的文章时,提出了一种解决该问题的通用算法:

(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)))))
“set”是不包含重复项的列表,“sum”是要搜索子集的总和。“subsets”是cons单元格列表,其中“car”是子集列表,“cdr”是该子集的总和。新的子集是在O(1)时间内从旧子集创建的,只需将元素连接到前面即可

我不确定它的运行时复杂性是什么,但似乎随着每个元素“和”的增长,“子集”的大小增加一倍,再加上一倍,所以在我看来它至少是二次的


我之所以发布这篇文章,是因为我以前的印象是NP完全问题往往很难解决,人们通常希望的最佳解决方案是启发式的,但这似乎是一个通用解决方案,假设你有CPU周期,它总是能给你正确的答案。有多少其他的NP完全问题可以像这样解决?

NP完全问题是可以解决的,只是不能在多项式时间内解决(据我们所知)。也就是说,一个NP完全问题可能有一个
O(n*2^n)
算法可以解决它,但它不会有一个
O(n^3)
算法来解决它

有趣的是,如果为任何NP完全问题找到一个快速(多项式)算法,那么NP中的每个问题都可以在多项式时间内求解。这就是P=NP的意义所在

如果我正确理解了您的算法(这更多是基于您的评论而不是代码),那么它就相当于
O(n*2^n)
算法。有
2^n
子集,由于还需要对每个子集求和,因此算法是
O(n*2^n)


关于复杂性,还有一件事,
O(无论什么)
只表明特定算法的可伸缩性。基于此,你不能比较两个算法并说一个比另一个更快。Big-O表示法不关心实现细节和优化——可以编写同一算法的两个实现,其中一个比另一个快得多,即使它们可能都是
O(n^2)
。一个女人生孩子是一种
O(n)
操作,但很可能这比你执行的大多数
O(n*log(n))
排序要花费更长的时间。基于此,您可以说,对于n上的非常大的值,排序将较慢。

所有NP完全问题都有解决方案。只要你愿意花时间来计算答案,那就是。仅仅因为没有一个有效的算法,并不意味着没有一个。例如,您可以迭代每个可能的解决方案,最终得到一个。这些问题在现实世界的计算中随处可见。如果你需要指数级的时间(或者更糟的时间!)来解决问题,你只需要小心你给自己设置了一个多大的问题

我不确定运行时是什么 它的复杂性是,但似乎 随着每个元素“总和”的增长 “子集”的大小加倍,加上1, 所以在我看来至少是这样 二次型

如果N的每增加一次,运行时间就会加倍,那么您将看到一个O(2^N)算法。这也是访问一个集合的所有子集(或集合的所有powerset成员)所期望的结果,因为这正好是2^N个成员(如果包括空集)


将元素添加到或不添加到所有到目前为止看到的集合是快速的这一事实并不意味着整个处理是快速的。

这里发生的事情可以更简单地用递归表示:

(defun subset-sum (set sum &optional subset) (when set (destructuring-bind (head . tail) set (or (and (= head sum) (cons head subset)) (subset-sum tail sum subset) (subset-sum tail (- sum head) (cons head subset)))))) (定义子集和(集合和和和可选子集) (设定时) (解构绑定(head.tail)集合 (或(和(=总合)(cons总分) (子集和尾和子集) (子集和尾(-和头)(cons头子集(()())))
最后的两个递归调用清楚地表明我们正在遍历深度为n的二叉树,即给定集合的大小。二叉树中的节点数是O(2^n),正如预期的那样。

这是多项式时间。使用堆或二进制搜索将Karp约简为决策问题O(nM),上界为log(M*2^M)=logM+log(2^M)=logM+Mlog2 Ergo Time:O(nM)

是的,我的解决方案基本上是彻底搜索“set”的所有可能子集,直到找到一个具有正确和的子集,所以我想这不是一个有效的算法,它是对所有子集的穷举搜索,直到找到一个和正确的子集为止。子集列表和子集本身都是链表,因此它们可以相互创建,并在O(1)时间内添加到“子集”中。更正:“如果为任何NP完全问题找到了快速(多项式)算法,则每个NP完全问题都可以在多项式时间内求解”应为“那么NP中的每一个问题都可以在多项式时间内得到解决”+1对于一个生孩子的女人来说
O(n)
类比,这有助于向一个非CS人解释这个论点。