【编程之美】游戏之乐——快速找出故障机器

快读找出故障机器

问题描述:

为了保证搜索引擎的服务质量,需要保证每份数据都有多个备份。简单起见,假设一个机器仅存储一个标号为ID的记录(假设ID是小于10亿的整数),假设每份数据保存两个备份,这样就有两个机器存储了同样的数据。

要求

  • 在某个时间,如果得到一个数据文件ID的列表,是否能够快速地找出这个表中仅出现一次的ID?
  • 如果已经知道只有一台机器司机(也就是说只有一个备份丢失)呢?如果有两台机器死机呢(假设同一个数据的两个备份不会同时丢失)?

问题分析

解法一:

问题转化为:有很多的ID,其中只有一个ID出现的次数小于2,其他正常ID出现的次数都等于2,问如何找到这个次数为1的ID。
直接的办法是遍历列表,利用一个数组记下每个ID的出现次数,便利完毕后,出现次数小于2的ID就是我们想要的结果。
时间复杂度为O(N),空间复杂度为O(N)

解法二:

ID出现次数等于2的机器肯定不是故障的机器,可以不予考虑。因此把解法1数组中等于2的元素清空,然后用来存储下一个机器ID的出现次数,减少需要的空间。
具体方法是:遍历列表,利用哈希表记下每个ID出现的次数,每次遇到一个ID,就向哈希表中增加一个元素;如果这个ID出现的次数为2,那么就行哈希表中删除这个ID,最后剩下的ID就是结果。
最好情况下的空间复杂度为O(1),最坏情况下的空间复杂度为O(N)

解法三:

列表中仅有一个ID出现了一次,那么,可以考虑使用异或关系。因为X⊕X=0且X⊕0=X,因此所有ID的异或值就等于这个仅出现一次的ID(因为异或运算满足交换律和结合律,其他出现两次的ID异或完都为0).这样只使用一次遍历运算就得到了只有一台故障机器情况下故障机器的ID。
时间复杂度为O(N),空间复杂度为O(1)

对于第二问有两个ID仅出现一次,设这两个ID为A和B,那么所有ID的异或值为A⊕B,但是无法确定A和B的值。因此分类讨论:

  • 如果A=B,则A⊕B=0,即丢失的同一份数据的两个拷贝。可以通过求和的方式得到A和B(A=B=(所有ID之和减去所有正常工作机器ID之和)/2)。。
  • 如果A≠B,则A⊕B≠0,那么这个异或值的二进制中某一位为1,显然,A和B中有且仅有一个数的相同为上也为1。

因此把所有ID分为两类,一类在这个数位上为1,另一类在这个数位上为0。那么对于这两类ID,每一类分别含有A和B中的一个。那么使用两个变量,在遍历列表时,分别计算这两类ID的异或和,即可得到A和B的值。

解法四:

问题抽象:在一个事先预定的整数(ID)集合当中,怎么样找出其中丢失的一个数(ID)或两个数(ID)?
思考:如果只有一台机器死机,相当于我们这个ID集合里面少了一个ID,也就是说把剩下的ID求和,和所有ID的求和(“不变量”)的差就是当前缺少的ID的数值。
解法过程:预先计算并保存好所有ID的求和(不变量),顺序列举当前所有剩下的ID,对它们求和,然后用所有的ID的求和(不变量)减去当前剩下所有ID的和,结果就是死机的机器ID值。由于所有的ID的求和(不变量)可以预先算好,而且在所有的检测中只算一次,当前算法的时间复杂度为O(N),空间复杂度为O(1)。
如果两个ID可以相同也可以不相同,如果ID集合中丢失了两个ID,则利用求和的方法可以计算两个ID的和,即X+Y=M,并不能区分X和Y的值。因此,可以考虑用一个所有ID的乘积的不变量,预先计算并保存好所有ID的乘积(“不变量”),顺序列举当前所有剩下的ID,计算它们的乘积,然后用所有ID的乘积除以当前剩下所有ID的乘积,结果就是两台死机的机器ID的乘积,即XY=N,联立两个方程,可以计算出X和Y的值。
计算时间复杂度为O(N),空间复杂度为O(1)

扩展问题

如果所有的机器都有三个备份,也就是说同一台ID的机器有3台,而且同时又有三台机器死机,还能用上面解法四的思路解决么?如果有N个备份,而且同时又有N台机器死机,是否还能解决?

方法一:我们需要建立三/N个方程,求出这些都是的数。此时,当方程为N时,要求N个方程难度比较大。
方法二:使用Map/哈希表,计数值达到N时从Map/哈希表中丢弃。这时,最终可以得到这几个数。

相关问题

这个问题本质上是从一堆数字中找到丢失的一个数字的问题。有这样的一个扑克牌抽牌问题:给你一副杂乱的扑克牌(没有大小王),任意从其中抽出一张牌丢弃。请问怎样用最简单的方法分析出抽出的是1~13中的哪一张(忽略花色)?

方法利用不变量

  • 事先算好所有牌的和(1+…+13) x 4 = 364
  • 然后分别减去留下的牌点数,最终得到的就是抽出的那一张
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

镰刀韭菜

看在我不断努力的份上,支持我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值