• 快速找出故障机器


    题目描述

      关心数据挖掘和搜索引擎的程序员都知道,我们需要很多的计算机来存储和处理海量数据。

      然而,计算机难免出现硬件故障而导致网络联系失败或死机。为了保证搜索引擎的服务质量,我们需要保证每份数据都有多个备份。

      简单起见,假设每个机器存储一个标号为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)。

      第二个方程构造也可以考虑平方和的方法(使用乘积作为不变量,有可能在实际应用中出现算数溢出问题)。

    扩展问题

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

    方法一:我们需要建立三/N个方程,求出这些都是的数

    此时,当方程为N时,要求N个方程难度比较大

    方法二:使用Map/哈希表,计数值达到N时从Map/哈希表中丢弃

    这时,最终可以得到这几个数

    相关问题

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

    方法:利用不变量

    事先算好所有牌的和(1+...+13) x 4 = 364

    然后分别减去留下的牌点数,最终得到的就是抽出的那一张

  • 相关阅读:
    使用阿里云服务器的总结二-----目录权限
    使用阿里云服务器的总结一----修改配置
    thinkphp框架开启页面gzip压缩
    内容页分页代码
    js禁止中文输入 最简洁的【禁止输入中文】
    JS中setTimeout()的用法详解
    面向对象的5条基本设计原则
    C#_面试题1
    问题 E: C语言11.8
    问题 D: C语言11.7
  • 原文地址:https://www.cnblogs.com/ChrisLi/p/3796146.html
Copyright © 2020-2023  润新知