Meet in the middle

译文转自

原文:

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)。


       代码看起来这样:

[python]  view plain  copy
  1. def 4sum(A):  
  2.   sums = {}  
  3.   for a in A:  
  4.     for b in A:  
  5.       sums[a + b] = (a, b)  
  6.   
  7.   for c in A:  
  8.     for d in A:  
  9.       if -(c + d) in sums:  
  10.         print (sums[-(c + d)][0], sums[-(c + d)][1], c, d)  
  11.         return  
  12.   
  13.   print "No solution."   

 

       这个算法的时间复杂度和空间复杂度都是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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值