译文转自☞
原文:☞
Meet in the middle(有时候也叫作split and merge)是一种用以获取足够高效解决方案的灵巧的思想。和分治思想非常类似,它将问题分割成两个部分,然后试着合并这两个子问题的结果。好处在于通过使用一点额外的空间,你可以解决两倍规模的原来可以解决的问题。
在我们继续之前,我必须要指出,附加问题是这篇文章中最好的部分。现在,我们来看这个技巧的一些应用。
4和问题(流行的面试问题)
给定一个整数数组A,问数组中是否存在4个数,使得这4个数的和是0(同一个元素可以被多次使用)。例如:数组A = [2, 3, 1, 0, -4, -1],一种可能的方案是3 + 1 + 0 - 4 = 0 或 0 + 0 + 0 + 0 = 0。
朴素的算法是判断所有可能的4个数的组合,这种方案需要计算O(N^4)次。
一个些微改进的算法是暴力搜索所有的可能的n^3个3个数的组合,并且用hash表来判断-(a+b+c)是否在原始的数组中。这个算法的复杂度是O(n^3)
到目前为止,你可能想知道meet in the middle在这里要怎么应用,最关键的洞察来自于改写a + b + c + d = 0 成 a + b = -(c + d)。现在我们可以存储n^2个a+b的和在一个hash表S中,然后可以枚举所有可能c和d的n^2种组合并且判断S中是否包括-(c+d)。
代码看起来这样:
这个算法的时间复杂度和空间复杂度都是O(n^2),这个问题没有已知的更快的算法。
双向搜索
在一个很大的图中找到两个点的最短路径,比如说Facebook的朋友关系图(友谊图)。
宽度优先搜索算法是这个问题的标准方法。假设这两个节点之间之间的距离是k,并且网络中点的平均度数是p,那么BFS算法需要扩展O(p^k)个节点。
一个更好的方案是同时从两个节点开始搜索,并且看什么时候这两个搜索的边界相遇。这个可以将需要扩展的节点降低到O(p^(k/2))。这个方案在寻路问题表现很好,包括明确的图以及游戏中遇到的不明确的图。
破解2DES加密
DES是一种使用56bit密钥的加密标准。今天计算机可以使用暴力搜索的方法来破解加密。一种简单的使得加密算法更安全的方法是应用两次加密,使用两个不同的密钥。这种方法容易受到Diffile-Hellman发明的中间者攻击。3DES工作的原理是通过用2个密钥对明文进行3次加密。
我们来看看为什么2DES是容易受到攻击的。假设Ek是密钥为k的加密函数,Dk是密钥为k的解密函数。2DES算法使用两个密钥k和K,Ek(EK(p)) = s是一个加密过程,并且DK(Dk(s)) = p是一个解密。
Diffie-Hellman的中间相遇攻击用空间换取时间来找到这两个密钥。对于p,尝试所有可能的密钥可以取得对应的多个Ek(p)的集合。同样地,对于s可以用所有可能的密钥来解密,取得所有可能Dk(s)的集合。如果我们能够在这两个集合中找到匹配的一对,也就是说Eki(p) = Dkj(s),那么密钥就是ki和kj。
朴素的暴力算法需要2^56*2^56次迭代来遍历所有可能的k1和k2的值,而这个算法使用2^56*56的空间来存储所有的Eki(p),并且2^56个操作来找到匹配。
这个是相当多的空间和相当多的计算时间。但对于一个足够大的公司或者国家,它就变得非常可能的范畴。
2001的国际奥赛的问题DOUBLE要求破解24密钥长度的2DES就是非常可行的。
离散对数算法
给定一个素数n和p,q两个数,0 <= p,q <= n-1,找到k使得p^k = q(mod n)。
朴素的算法是遍历所有可能的k的值,复杂度为O(n)。
baby-step, giant-step算法使用了MITM,更高效地解决了这个问题。
记k = i[sqrt(n)] + j,i <= sqrt(n)且j <= sqrt(n)。替换等式中的k,我们有p^(i*[sqrt(n)] + j) = q(mod n),然后两边同除以p^j有:p^(i*sqrt(n)) = q*p^(-j) (mod n)
在这个时候,我们可以暴力搜索等式两边并且找到一个碰撞。
这个算法需要O(sqrt(n))的空间和O(sqrt(n))的时间
警告
与分治不同,MITM不能用于递归,因为子问题和原问题没有相同的结构。双向搜索在很多时候可以用其他的启发式搜索算法来代替。
附加问题
1.朋友的朋友(面试问题)
给定社交网络中的两个名字,设计一个有效的方法,判断其中一个是否是另外一个的朋友的朋友
2.6度空间(6度间隔/6度分割)
给定社交网络中的两个名字,设计一个有效的方法,判断这两个人是否最多相隔6个朋友
3.等分
给定一个40个实数的集合A,判断是否有方法将集合A分成两个子集,使得两个子集的元素和相同。(提示:复杂度O(2^(n/2))
4.最小点覆盖
给定一个有n(n <= 30)个节点的图,找到一个最小的点集,使得图中的每一条边至少有一个顶点在这个点集中。(提示:复杂度O(3^(n/2))
5.正方形栅栏
给定一个数组L,代表n个木板的长度。请回答是否存在一种方法,使用所有的木板构成一个正方形栅栏且不损坏也不重叠。(提示:复杂度O(4^n/2))
6.8拼图
8拼图是在一个有8个板子和1个空位置的3*3拼板上滑动的游戏,在每一步你只能移动一个与空的位置正交相邻的板子到空的位置上。游戏开始时板子和空位置随机配置,目标是用最少的移动将拼版移动成指定的配置。找到一个有效的算法来解决(提示:每一个位置最多31次移动可解决)。下图我们让可以看到解决8拼图的一系列的移动。
7. 4-反转问题
给定两个相同长度的字符串S和T。请指出是否能够从S得到T,使用4种子串翻转操作。(提示:复杂度O(n^5))
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.双向BFS,深度为1,第一遍记录起点能走到的所有点,第二遍找终点是否能到这些点
2.也是双向BFS,深度到3,
3.实数范围很大,如果数很小可以用背包,想到数大可不可以把包的体积离散化,后来想想数很大时,重复的部分应该很少,所以不可行
暴搜O(2^n), 没想到别的好方法。。。。看了原文下面的评论
设所有数总和为sum,把这些数按个数分两半,然后O(2^(n/2)) 搜出这一半所有的组合并记录(hash)假设为A,然后在对另一半进行O(2^(n/2))搜索,得到值B,如果找到A=sum/2-B,说明就是可以等分的
正确性,因为把原来这些数分成S1,S2两个等大部分(不等大也是可以的,不过等大时速度最优),而符合要求的和为sum/2的那些数,肯定一部分属于S1一部分属于S2