【问题标题】:Detecting repetition with infinite input检测具有无限输入的重复
【发布时间】:2010-02-17 09:39:20
【问题描述】:

在无限的整数序列中找到重复的最佳方法是什么?

即如果在无限序列中,数字“5”出现两次,那么我们将第一次返回“false”,第二次返回“true”。

最后,我们需要一个函数,如果整数出现在前面,则返回“true”,如果函数第一次收到整数,则返回“false”。

如果有两个解决方案,一个是空间方面的,第二个是时间方面的,那么都提到。 我会在答案中写下我的解决方案,但我认为这不是最佳解决方案。

编辑:请不要假设微不足道的情况(即没有重复,不断上升的序列)。我感兴趣的是如何降低非平凡案例(具有重复的随机数)的空间复杂度。

【问题讨论】:

  • 数字都是整数吗?哪个范围有数字?

标签: language-agnostic data-structures repeat infinite-sequence


【解决方案1】:

我会使用以下方法:

使用哈希表作为您的数据结构。对于读取的每个数字,将其存储在您的数据结构中。如果在您发现重复之前它已经存储了。

如果n是序列中从开始到重复的元素个数,那么这只需要O(n)的时间和空间。时间复杂度是最佳的,因为您至少需要读取输入序列的元素直到重复点。

我们正在谈论多长时间的序列(在重复发生之前)?甚至可以保证重复吗?对于极端情况,空间复杂性可能会成为问题。但要改进它,您可能需要了解有关序列的更多结构信息。

更新:如果序列如您所说的非常长且很少重复,并且您必须减少空间需求,那么您可能(鉴于序列的足够结构信息)能够减少空间成本。

举个例子:假设您知道您的无限序列一般倾向于返回适合当前见证的最小-最大数字范围内的数字。然后,您最终将拥有已包含在序列中的整个间隔。在这种情况下,您可以通过存储此类间隔而不是其中包含的所有元素来节省空间。

【讨论】:

  • 关于最后一段,这更像是一个思想实验,而不是我遇到的实际问题,所以我没有实际的答案。但真正让我感兴趣的是人们在更成问题的情况下所做的事情——一个真正无限的序列(假设你让系统运行很长时间),很少重复。
【解决方案2】:

用于 int 值(2^32 个数字)的 BitSet 将消耗 512Mb。如果 BitSet 分配不频繁、足够快并且内存可用,这可能没问题。

另一种方法是compressed BitSets,它最适合稀疏位集。

【讨论】:

    【解决方案3】:

    实际上,如果值的最大数量是无限的,您可以对单色位图使用任何无损压缩算法。如果您想象一个正方形的像素数至少与可能值的数量一样多,则可以将每个值映射到一个像素(有一些备用)。然后,您可以将白色表示为出现的像素,将黑色表示为其他像素,如果空间非常宝贵,则可以使用任何压缩算法(这当然是一个已经研究过的问题)

    您还可以存储块。最坏的情况在空间 O(n) 中是相同的,但对于最坏的情况,您需要出现的数字在它们之间恰好有 1。一旦出现更多数字,则存储量将减少: 我会写伪代码,我会使用 List,但你总是可以使用不同的结构

    List changes // global
    
    boolean addNumber(int number):
      boolean appeared = false
      it = changes.begin()
      while it.hasNext():
        if it.get() < number:
          appeared != appeared
          it = it.next()
        else if it.get() == number:
          if !appeared: return true
          if it.next().get() == number + 1
            it.next().remove() // Join 2 blocks 
          else 
            it.insertAfter(number + 1)  // Insert split and create 2 blocks
          it.remove()
            return false
        else: // it.get() > number
          if appeared: return true
          it.insertBefore(number)
          if it.get() == number + 1:
            it.remove() // Extend next block
          else:
            it.insertBefore(number + 1)  
      }
      return false
    }
    

    这段代码如下:它存储了一个块列表。对于您添加的每个数字,它会遍历存储出现的数字块和未出现的数字的列表。让我用一个例子来说明;我将添加 [) 来说明块中的哪些数字,包含第一个数字,最后一个不包含。在伪代码中它被布尔值 appeared 替换。例如,如果你得到 5, 9, 6, 8, 7(按此顺序),你将在每个函数之后有以下序列:

    [5,6)

    [5,6),[9,10)

    [5,7),[9,10)

    [5,7),[8,10)

    [5,10)

    在最后一个值中,您保留一个只有 2 个的 5 个数字块。

    【讨论】:

      【解决方案4】:

      返回真

      如果序列是无限的,那么每个可以想象的模式都会重复。

      如果您想知道的是序列中的第一位,当有一个重复的数字时,那是另一回事,但您的问题和您的示例之间存在一些差异。

      【讨论】:

      • 即使有一组有限的数字,唯一已知的是至少有一个无限序列的重复。
      • 好吧,假设我们第一次遇到一个新号码,那么系统需要做一些事情......
      • @SF 好点,我的意思是数字会以你想提到的任何模式重复。实际上,我真正的意思当然是,OP 的问题对于返回的期望函数是模糊的。
      【解决方案5】:

      嗯,很明显,在任何解决方案中,我们都需要保存已经出现的数字,因此在空间方面,我们将总是遇到 O(N) 的最坏情况,其中 N

      为了保存已经出现的数字,我会使用哈希表,然后在每次收到新数字时检查它。

      【讨论】:

      • 2^32 在 BitSet 中只有 512Mb。 :-)
      • 哈希表在任何情况下都有很多开销。存储桶数组和条目对象的数组。这加起来了。
      • 我认为保存 n 个数字需要 O(n) 空间并不明显。这在很大程度上取决于序列的结构。如果序列由 s[i] = i 给出,即数字稳步增加一,那么您可以通过存储最大的见证数字将所有出现的数字存储在恒定空间中。
      • Thomas,您能否详细说明替代方案?弗兰克,你是对的,但是说在一般情况下(比如说接收随机整数)它必须是 O(N) 是否正确?
      猜你喜欢
      • 2013-03-04
      • 2012-03-06
      • 2011-12-26
      • 2020-08-28
      • 1970-01-01
      • 2013-05-20
      • 2013-10-29
      • 2014-09-29
      • 2012-09-29
      相关资源
      最近更新 更多