一个面试题
今天看到一个很有意思的面试题:
一个给定的集合有100万个元素,其中每个元素又是由1~1000万之间的100万个不重复数字组成的集合,如果对这些集合进行和并操作,求最少有哪些集合能构成1..1000W这个全集?(06年底,同学的一道baidu面试题,大致意思是这样的)
这个题目很有意思,不愧为BD的BT面试题。
总的思路来说,这个用穷举计算估计算N天也不一定有结果,如果按照题目的要求,要求“最少的集合”,也就是要求一个最优解,那估计不好 做。不过如果去掉这个要最优解的限制,我想有比较快的近似算法,不一定能得出最佳结果甚至不一定能得出正确结果,但得到一个结果的速度应该是比较快的。
大 致思路如下:对于由1~1000万之间的100万个不重复数字组成的集合,将它映射到X轴上,其中每个数字n映射到X轴上[n,n+1]的那么一小段,那么这个集合就可以被看成是一个中间有很多洞的一个线段。在上面重叠另一个线段,就可能有些洞被填上,形成一个新的线段。因此,这个题目的要求可以看作是,求一个线段的集合,它们重叠起来后,可以得到一个完整的1-1000W的线段。
首先,对每个线段我们可以求得他的起点终点,这样就得到了线段的长度,用线段中点的数量/线段长度表示这个线段的密度。密度值表示了一个线段中洞的数量和大小,显然密度越小线段中的洞就越多越大,当密度=1时,就是一个完全没有洞的普通线段了。也就是说,可以用(最小值,最大值,密度)来描述一个线段的特征。
选择两个线段L1(a1,b1,c1) L2(a2,b2,c2),那么应该有以下几种可能:
如此重复选择线段加入到我们的线段集合中,直到线段集合中的每个线段都首尾相连,从1连接到1000W,而且他们的密度都足够大,大到可以认为中间存在空洞的可能性相当的小,这就可能得到了一个解。
对于这个可能的解,可以有以下几种处理方式:
× 对这个解进行验证,如果他不正确,那么重新再计算再验证,直到得到一个正确的解为止。(这要求在选择线段的过程中有一定的随机性,以保证每次重新计算可以得到一个不同的解)
× 不对解进行验证,把它作为一个结果输出,那么它可能是一个正确的解,也可能其中还存在洞。得到正确结果的概率,取决于我们对算法结束条件的选择:选择多少个线段后就大致可以认为得到一个解了?或者线段密度到达多大就可以认为基本上不可能有空洞了?这些阈值的选择,可以通过在一个小数量(如1-1000而不是1-1000W)的问题上进行一些运算,得到经验值。
大致估计一下,原始线段的平均长度是估计是500W(正态分布原则),平均密度是1/5,保守起见选择1000个线段就可能得到一个解,做1000次的线段合并,这个过程显然是非常的快的。
根据这个算法,也可以得到一个近似的“最优”解,也就是最少的集合数:可以在得到一个解后,尝试将选择的线段数量减半(对半搜索),如果较少的线段选择数无法得到一个解,那么说明需要选择更多一点线段。这样反复几次,就可以得到一个根据这个算法能求出的包括最少集合数的解,这个解很可能是一个最优解,其概率同样取决与尝试次数的选择等参数的选择。
这种随机解法不一定能保证得到最优解,但它得到一个近似解或近似最优解的速度比严格求解的算法要快几个数量级。
F函数的选择:
F函数的含义是两段首尾相同的线段重叠后,新线段的密度的估计值。因此,一个最简单的F函数可以表示 f(c1,c2)=(c1+c2)*k,k为修正系数经验值(0-1)。将原两个线段的密度相加后再乘以一个修正系数,修正系数含义为两个线段的空洞重叠的概率,它的选择可以通过在小数量的问题域上进行多次尝试计算得到经验值。一般来说,k值应该是(c1,c2)的一个函数相关值,(c1,c2)小的时候,k值比较大,直觉上可以理解:两个洞很多的线段重叠,他们的空洞重叠的概率就大,反之,两个洞很少的线段重叠,空洞重叠的概率就小。这样得到的新线段的密度估计可能会大于1,但这并没有关系,它仅仅是个估计值嘛。
选择下一个加入的线段算法:
根据前面的说明,这个选择算法要有一定的随机性,这样才能保证本算法的随机性。但是这个选择也可以有一定的启发性,这样可以保证算法能更快速的收敛。这两个要求有点矛盾,但最佳的选择算法就是在这两个矛盾的要求上取得一个较好的平衡。启发的规则可以根据集合中现有的线段的一个简单的分析,首先选择覆盖空白区域,或空洞较多的线段对应的区域。在这个原则下,再随机从原始线段中挑选符合条件的线段加入到集合中。
一个给定的集合有100万个元素,其中每个元素又是由1~1000万之间的100万个不重复数字组成的集合,如果对这些集合进行和并操作,求最少有哪些集合能构成1..1000W这个全集?(06年底,同学的一道baidu面试题,大致意思是这样的)
这个题目很有意思,不愧为BD的BT面试题。
总的思路来说,这个用穷举计算估计算N天也不一定有结果,如果按照题目的要求,要求“最少的集合”,也就是要求一个最优解,那估计不好 做。不过如果去掉这个要最优解的限制,我想有比较快的近似算法,不一定能得出最佳结果甚至不一定能得出正确结果,但得到一个结果的速度应该是比较快的。
大 致思路如下:对于由1~1000万之间的100万个不重复数字组成的集合,将它映射到X轴上,其中每个数字n映射到X轴上[n,n+1]的那么一小段,那么这个集合就可以被看成是一个中间有很多洞的一个线段。在上面重叠另一个线段,就可能有些洞被填上,形成一个新的线段。因此,这个题目的要求可以看作是,求一个线段的集合,它们重叠起来后,可以得到一个完整的1-1000W的线段。
首先,对每个线段我们可以求得他的起点终点,这样就得到了线段的长度,用线段中点的数量/线段长度表示这个线段的密度。密度值表示了一个线段中洞的数量和大小,显然密度越小线段中的洞就越多越大,当密度=1时,就是一个完全没有洞的普通线段了。也就是说,可以用(最小值,最大值,密度)来描述一个线段的特征。
选择两个线段L1(a1,b1,c1) L2(a2,b2,c2),那么应该有以下几种可能:
× 他们没有重叠部分,那么我们得到包括两个独立线段的集合(L1,L2)继续从原集合中选择一个新的线段加入到上面的线段集合中(选择算法稍后再议),它会和集合中的某些线段重叠或者不重叠等等,根据上面的几种情况分析,我们将得到一个新的线段集合。
× 其中一条范围包含另一条,假设 L1包含L2,那么我们可将重叠后的结果分为三个部分,得到一个包含三个线段的集合(K1,K2,K3)。其中,K1 是 L1中 L2 前面的那一段,对应参数为(a1, a2, c1),K3 是L1中L2后面的那一段(b2,b1,c2),K2 是他们重叠的那一段(a2, b2, c3),他的密度 c3 = f(c1,c2) ,关于 f 函数的选择,稍后再议。
× 他们不包含但有重叠部分,和上面一样,我们同样可以把它们分为 K1,K2,K3 三个新的线段
如此重复选择线段加入到我们的线段集合中,直到线段集合中的每个线段都首尾相连,从1连接到1000W,而且他们的密度都足够大,大到可以认为中间存在空洞的可能性相当的小,这就可能得到了一个解。
对于这个可能的解,可以有以下几种处理方式:
× 对这个解进行验证,如果他不正确,那么重新再计算再验证,直到得到一个正确的解为止。(这要求在选择线段的过程中有一定的随机性,以保证每次重新计算可以得到一个不同的解)
× 不对解进行验证,把它作为一个结果输出,那么它可能是一个正确的解,也可能其中还存在洞。得到正确结果的概率,取决于我们对算法结束条件的选择:选择多少个线段后就大致可以认为得到一个解了?或者线段密度到达多大就可以认为基本上不可能有空洞了?这些阈值的选择,可以通过在一个小数量(如1-1000而不是1-1000W)的问题上进行一些运算,得到经验值。
大致估计一下,原始线段的平均长度是估计是500W(正态分布原则),平均密度是1/5,保守起见选择1000个线段就可能得到一个解,做1000次的线段合并,这个过程显然是非常的快的。
根据这个算法,也可以得到一个近似的“最优”解,也就是最少的集合数:可以在得到一个解后,尝试将选择的线段数量减半(对半搜索),如果较少的线段选择数无法得到一个解,那么说明需要选择更多一点线段。这样反复几次,就可以得到一个根据这个算法能求出的包括最少集合数的解,这个解很可能是一个最优解,其概率同样取决与尝试次数的选择等参数的选择。
这种随机解法不一定能保证得到最优解,但它得到一个近似解或近似最优解的速度比严格求解的算法要快几个数量级。
F函数的选择:
F函数的含义是两段首尾相同的线段重叠后,新线段的密度的估计值。因此,一个最简单的F函数可以表示 f(c1,c2)=(c1+c2)*k,k为修正系数经验值(0-1)。将原两个线段的密度相加后再乘以一个修正系数,修正系数含义为两个线段的空洞重叠的概率,它的选择可以通过在小数量的问题域上进行多次尝试计算得到经验值。一般来说,k值应该是(c1,c2)的一个函数相关值,(c1,c2)小的时候,k值比较大,直觉上可以理解:两个洞很多的线段重叠,他们的空洞重叠的概率就大,反之,两个洞很少的线段重叠,空洞重叠的概率就小。这样得到的新线段的密度估计可能会大于1,但这并没有关系,它仅仅是个估计值嘛。
选择下一个加入的线段算法:
根据前面的说明,这个选择算法要有一定的随机性,这样才能保证本算法的随机性。但是这个选择也可以有一定的启发性,这样可以保证算法能更快速的收敛。这两个要求有点矛盾,但最佳的选择算法就是在这两个矛盾的要求上取得一个较好的平衡。启发的规则可以根据集合中现有的线段的一个简单的分析,首先选择覆盖空白区域,或空洞较多的线段对应的区域。在这个原则下,再随机从原始线段中挑选符合条件的线段加入到集合中。