题目:假设一个机器只存储一个标号为ID的记录,假设每份数据保存2个备份,这样就有2个机器存储了相同的数据。其中ID是小于10亿的整数
问题1、在某个时间,如果得到一个数据文件ID的列表。是否能够快速的找到这个表中仅出现一次的ID?即快速找出出现故障的机器存储的数据ID。
问题2、如果有两台机器死机呢?(假设同一个数据的俩个备份不会同时丢失,即列表中缺少的是两个不等的ID)
扩展题、如果所有的机子都有三个备份,也就是说同一ID的机子有三台。而且同时又有三台机子死机,还能用上面的方法解决吗?
如果有N台备份,又同时有N台机器死机呢?
本来以为自己能够想到一个很好的另外的方法,后来想了半天还是没想到,就只看看书上的解法把!
解法一:
最原始的方法,直接扫描整个列表,把每个ID出现的次数给记录下来,那么最后我们得出,出现一次的就是那个故障机器的ID。
注意我们直接建立一个长度为N的数组,每个元素初始化为0,扫描列表的时候,直接根据ID作为下标去把数组中的元素+1.
解法二:
思考一下解法一其实就是解法一的变更,就多了一个哈希表,我们在解法一中,直接使用了一个长度为N的数组,其实我们可以使用一个哈希表,我们使用的是动态存储的方式,没扫描到一个ID,使用散列函数哈希出一个值,然后根据这个值去寻找其存储的ID是不是已经有了,已经有了,就直接把存储这个ID的元素删除了,没有的话就存储下来。
这样最好的情况就是只使用了一个存储单元,最好的情况是什么呢?就是列表张这个样子:1,1,2,2,3,3,4,4,5,5..........
但是最坏的的情况是使用了N个单元:1,2,3,4,5..........N,1,2,3,4,5.......N.
解法三:
此解法是一个很精妙的算法,很佩服想到这个算法的人。
它用到了亦或运算:⊕,我们知道亦或运算满足这样的性质:X⊕X=0,X⊕0=X。
对于我们这个运算真是太实用了。
为什么呢?
我们假设这样的坏掉的两个机器的ID分别是X和Y。
正常如果没坏的机器的话,我们把列表的所有的ID进行求亦或运算结果一定是0,原因是ID都是成双成对的。
那么在坏掉之后我们再把列表的ID进行亦或,如果亦或的结果是0,这代表什么呢?
这当然代表列表中的ID也是成双成对的啊,那么坏掉的那两台机器的ID就是一样的喽。接下来我们把原来的所有的ID的和再减去现在的ID列表的ID的和,不就是X+Y了吗?而我们又知道X=Y,不就可以求出X了吗?
如果是亦或的结果不等于0,那其实亦或的结果就是X⊕Y的结果,原因自己想。那么这个结果肯定就是其二进制中数的某一位为1,显然就是X和Y中有且仅有一个数的相同位上也为1。那么我们就把列表中的ID分为两类,一类是这个位上的数是1的,一类是这个位上的数是0的,然后我们分别计算着两类ID的亦或值,这样分别计算出来了X和Y。
解法四:
仔细瞅瞅解法三中的亦或值等于0的情况,其实它就是对于两个变量X和Y,找到了两个方程,解方程而已。
根据此解法,我们可以这样想,对啊,我直接根据X,Y两个变量,再去找他们的相关的两个方程不就得了。
怎么找呢?解法三中的把原来的ID和减去之后的ID和得出X+Y的值,这已经算是一个方程,根据这个方程,我们其实可以再去构造一个方程,什么呢?平方和嘛。我们把原来的列表的每个ID的平方和求出来,再把现在的列表的ID的平方和求出来,减一下,不是就得出X的平方+Y的平方了吗?这不就又一个方程了吗?两个方程,两个未知数。
自己的想法:
对于书上的扩展问题,我们很容易的会想到解法四,既然我们用了平方构造出来了第二个方程,为什么不能用三次方构造出第三个方程,用四次方构造出第四个方程。
对于这个算法,我是这样想的,虽然三次方之后,要求的解很难算,但是电脑是可以解出来的。
比如X= 3,Y= 4,Z= 5;
给你一个方程组,让你求三个未知数
X+Y+Z=12;
X^2 + Y^2 +Z^2 = 50;
X^3 + Y^3 + Z^3 =216
这个很多数学软件是都可以求解出来的。