首页 > 代码库 > 快速找出故障机器
快速找出故障机器
题目描述
关心数据挖掘和搜索引擎的程序员都知道,我们需要很多的计算机来存储和处理海量数据。
然而,计算机难免出现硬件故障而导致网络联系失败或死机。为了保证搜索引擎的服务质量,我们需要保证每份数据都有多个备份。
简单起见,假设每个机器存储一个标号为ID的记录(ID是小于十亿的整数),假设每份数据都保存两个备份,这样就有两个机器储存了同样的数据。
1.在某个时间,如果得到一个数据文件ID的列表,是否能够快速地找出这个表中仅出现一次的ID?
2.如果已经知道只有一台机器死机(也就是说只有一个备份丢失)呢?如果有两台机器死机呢(假设同一个数据的两个备份不会同时丢失)?
问题分析
这个题目肯定要考虑大量数据的处理,即需要重点考虑效率问题。
首先,ID是小于十亿的整数,这说明用一个long型(4个字节)可以表示ID,不会超出表示范围,所以这点不用担心。
根据题目特点,得到ID列表后,可以考虑一种像是玩扑克牌游戏的做法,找一个数组来盛放遍历的数(想象成摸牌),找到成对的就丢弃(或者你也可以把这个配对过程想象成连连看)。
最后将所有数字遍历完之后还留下的就是单独的ID。
这样时间复杂度是O(n * n),因为需要摸n张牌,并且每摸一张牌都需要和手中的牌进行查询配对。
下面是书中的解法,看完之后才知道我的解法真是弱爆了,为什么没想到哈希表呢。
解法一
直接遍历列表,利用一个数组记录下每个ID出现的次数,遍历完毕之后,出现次数小于2的ID就是我们想要的结果。
假设有N个ID,且ID的取值在0~N-1之间,这个解法占用的时间复杂度为O(N),空间复杂度为O(N)。
时间复杂度已经相当理想,但是空间复杂度不够理想,如果ID的数量多达几个G甚至几十G,这样的空间复杂度在实际的运算中就会带来效率问题。
解法二
考虑到大部分ID的出现次数都等于2,这些ID的信息并不是必要的,所以不必存储。
因此,可以把解法一数组中等于2的元素清空,然后用来存储下一个机器ID的出现次数,这样就可以减少需要的空间。
具体方法如下:遍历列表,利用哈希表记下每个ID出现的次数,每次遇见一个新的ID,就向哈希表中增加一个元素;如果这个ID出现的次数为2,那么就从哈希表中删除这个元素,最后剩下的ID就是我们想要的结果。
这个算法的空间复杂度在最好情况下可以达到O(1),在最坏的情况下仍然是O(N)。
解法三
对于第一问,假设列表中仅有一个ID出现了一次,那么可以考虑用异或关系来帮忙找到结果。
因为异或运算的定义是相同为假相异为真,异或运算满足交换律和结合律。
所以,所有ID的异或值就等于这个仅出现一次的ID。(两两相同的都得到0,0和任何值异或等于原来的任何值)
这种情况下,时间复杂度为O(N),空间复杂度为O(1)。
对于第二问,由于有两个ID仅出现了一次,设它们为A和B,那么所有ID异或的结果是A异或B,但还是无法确定A和B的值。
可以进行分类讨论:
(1)A == B
这时A异或B等于零,丢失的是同一份数据的两个拷贝,可以通过求和的方法求得A和B,即,所有ID值的和减去所有正常的ID之和,除以2得到A和B。
这种方法需要在系统初始化时计算所有的完整的数据的和sum(原始数据)并保存,发生丢失后,计算sum(当前数据),则:
A=B=(sum(原始数据)-sum(当前数据))/2
(2)A !=B
这时A异或B不等于零,那么这个异或值的二进制位中某一位为1,此时A和B中有且仅有一个数的相同位上也为1。
我们就把所有的ID分成两类,一类在这位上为1,另一类在这位上为0。A和B分别位于这两类中。
我们分别计算这两类的异或和,即可得到A和B的值。问题分解成了两个解法三的第一问情况(卧槽,太巧妙了!!)
解法四
对于第一问,缺失一个ID:
预先计算并保存好所有ID的求和(“不变量”),顺序列举当前所有剩下的ID,对它们求和,然后用所有ID的求和减去当前剩下所有ID的和,结果就是死机的机器的ID值。
时间复杂度为O(N),空间复杂度为O(1),和解法三一样是计算复杂度最优的算法。
对于第二问,我们考虑所有的情况,即两个ID可以相同也可以不同。
用上面的方法可以得到这两个ID的和。我们构造出一个方程: x + y = a; a已知。
第二个方程有很多构造方法,比如可以用所有ID的乘积计算出另一个不变量,除以所有剩下的ID,结果得到两台死机机器的ID的乘积,即x * y = b。
这样联立两个方程之后,可以解出x和y的值。
时间复杂度为O(N),空间复杂度为O(1)。
第二个方程构造也可以考虑平方和的方法(使用乘积作为不变量,有可能在实际应用中出现算数溢出问题)。
扩展问题
方法一:我们需要建立三/N个方程,求出这些都是的数
此时,当方程为N时,要求N个方程难度比较大
方法二:使用Map/哈希表,计数值达到N时从Map/哈希表中丢弃
这时,最终可以得到这几个数
相关问题
方法:利用不变量
事先算好所有牌的和(1+...+13) x 4 = 364
然后分别减去留下的牌点数,最终得到的就是抽出的那一张