【问题标题】:Find a shortest distance between two buckets of numbers找到两个数字桶之间的最短距离
【发布时间】:2016-11-29 16:26:49
【问题描述】:

我有两个数字桶(无序的一维数据结构),我想计算两个桶的任何元素之间的最小距离。有没有办法在O(1) 中找到来自不同桶的任意数字之间的最短距离?我最好的选择是什么?

Input
[B1] 1, 5, 2, 347, 50
[B2] 21, 17, 345

Output
2 // abs(347 - 345)

编辑

  • 我希望查找比插入更多
  • 任何桶中最小和最大元素之间的距离小于 10^5
  • 任意桶的元素个数小于10^5
  • 桶中的数字“几乎”排序 - 这些是事件的时间戳。桶中乱序的元素可能不到 1%
  • bucket 中的元素数量很少,但我需要以 2k/sec 的平均速率查找,并定期丢弃陈旧的 bucket 并用新的 bucket 替换它们,因此我希望我的查找在 O(1)

了解我为什么需要这个以及我在previous question edition 中的想法。

【问题讨论】:

  • O(1)?当然不是!
  • 这些元素必须以某种方式到达桶中,如果在添加它们的同时将每个桶中的所有元素添加到单个平衡树中,您可以获得 O(log n) 查询时间到桶(尽管如果还没有的话,这会将桶插入成本增加到 O(log n)):只需在插入它们时从另一个桶中查找下一个较小和下一个较大的元素,并保持最小的-到目前为止。
  • O(1) 肯定 - 但它会惩罚你的插入性能
  • 对每个桶进行排序,然后对它们进行合并排序,跟踪沿途的最小距离:O(n+n/2.ln(n/2)) = O(n.ln(n))
  • 你的价值观有多大?具体来说,(largest_value - minimum_value) / window_size 有多大?如果这小于 10000000,则只需创建一个该大小的数组,每个 window_size-block 时间块有 2 位。然后在每次将 x 插入存储桶时,让 y = (x - minimum_value) / window_size,并更新 array[y] 和 array[y+1] 以反映添加了来自该存储桶的元素。任何以 window_size 或更少重叠的不同桶项对必须命中 array[] 的相同元素。

标签: c++ algorithm distance intersection euclidean-distance


【解决方案1】:

总共有n 个数字。
1. 将所有数字写成二进制。 ==> O(n)
2. 根据是从 B1 还是 B2,在每个数字中附加 0 或 1。 ==> O(n)
3. 快速排序,忽略第一位。 ==> O(n log n) 平均
4.对于整个列表,遍历排序顺序。对于每两个相邻的数字uv,如果它们同时来自B1或B2,则忽略。
否则,每当tmp > abs(u-v) 时设置tmp <-- abs(u-v)。 因此,tmp 是迄今为止在相邻数字内的最小距离。
最后的tmp 是答案。 ==> O(n)

总计:平均==> O(n log n)

【讨论】:

    【解决方案2】:

    为每个桶创建一个包含 10^5 个元素的位向量。跟踪最小距离(最初是 10^5,直到两个桶都非空)。

    现在,假设您要向其中一个桶添加元素 x。执行以下操作:

    1. Set the bit x of the same bucket.
    2. Check whether the other bitvector has any set elements within min_distance-1 of x
    3. Update min_distance as appropriate
    

    运行时间:插入时为 O(min_distance),技术上为 O(1),因为 min_distance 有上限。在轮询时它是 O(1),因为你只是返回 min_distance。

    edit 如果元素的上限不是 10^5,而只是最小值和最大值之间的距离,则需要对其进行修改,但仍然可以使用。如果这很重要,我可以详细说明必要的更改。

    【讨论】:

    • 元素数量上限为 ~ 10^5。你能解释一下你会怎么做数字[2]?是abs (bv1 - bv2) 之类的吗?
    • @oleksii 假设您将元素 x 放入 bucket_A,然后:for(i = x - minsize + 1, i
    • 你可以在上面例子的开头使用一个效率:如果 |B|
    • 您必须为每对存储桶存储该位向量并使其保持最新。这将输入复杂度增加到 O(k),其中 k 是桶的数量。内存复杂度当然是 O(l * k^2),其中 l 是位向量的大小。另一种方法是按需构建位向量。然后,您必须在知道 min_distance 之前插入两个存储桶的所有元素。当然是 O(n)。
    • @cdonat 他有两个桶,所以一对。
    【解决方案3】:

    将您的存储桶插入两次 Y-fast 尝试 (https://en.wikipedia.org/wiki/Y-fast_trie)。搜索最近的继任者或前任者是 O(log log M),其中 M 是范围(实际上是最大元素,但我们可以偏移),在您的情况下,这将限制在大约四个操作。

    由于您将存储最接近的差异,因此查找将是 O(1)(除非您每次都获得完整的存储桶而不是不断更新),而每个元素的插入、删除和更新将是 O(log log M)

    【讨论】:

    • 建树是 O(n log log M)
    • @Ripi2 是正确的。由于Mn 的上限为10^5log n ≈ 16,而log log M ≈ 4,这将是一个比n log n 更快的对数因子。
    【解决方案4】:

    这是我的尝试:对每个桶进行排序,然后对它们进行合并排序,跟踪沿途的最小距离:O(n+2.n/2.ln(n/2)) = O(n.ln(n)):

    sort buk1
    sort buk2
    min = INT_MAX
    last = some value
    do
        if top(buk1) > top(buk2)
            min = min(min, abs(top(buk1) - last))
            last = top(buk1)
            pop(buk1)
        else
            min = min(min, abs(top(buk2) - last))
            last = top(buk2)
            pop(buk2)
    while !empty(buk1) and !empty(buk2)
    

    【讨论】:

      【解决方案5】:

      O(1) 当然是不可能的。

      一些伪代码,我将用作起点:

      sort(B1)
      sort(B2)
      
      i1 = 0
      i2 = 0
      
      mindist = MAX_INT
      
      // when one of the buckets is empty, we'll simply return MAX_INT.
      while(i1 < B1.size() && i2 < B2.size())
          t = B1[i1] - B2[i2]
          mindist = min(mindist, abs(t))
          if t > 0 
              i2 ++
          else
              i1 ++
      
      return mindist
      

      至少那是O(n log n),因为它是由一开始的排序支配的。如果你的桶已经排序,你可以有 O(n)。

      编辑:

      在新信息之后,元素几乎已排序,我建议在插入时对它们进行实际排序。带有二进制搜索的插入排序对于这种情况并不是最好的。只需附加新元素并将其向前交换,直到它适合。通常不会有掉期,而对于需要掉期的 1%,99% 的情况下只有一个。最坏情况复杂度为 O(n),但平均值几乎为 O(1)。

      如果您考虑为所有存储桶对预先计算 mindist,则必须存储 i1i2mindist。假设B1 是存储桶,您可以在其中添加一个新元素。您将其排序并减少i2,直到它是0B2[i2] &lt; B1[i1]。由于元素是时间戳,因此大多数情况下最多只有一步。然后你再次运行 while 循环,这通常也只有一个步骤。所以k个桶的计算复杂度是O(k),内存复杂度是O(k^2)。

      【讨论】:

        【解决方案6】:

        我喜欢 Dave Galvin 的想法,稍作修改:

        设 maxV 为最大元素个数 maxV=max(bucket1.size, bucket2.size)

        1。构建两个数组,每个数组的大小为 maxV。填写:

        for (j=0 to bucket1.size)
            array1(bucket1(j)) = bucket1(j)
        for (j=0 to bucket2.size)
                array2(bucket2(j)) = bucket1(j)
        

        数组现在已排序。数组中的其余元素为 0。

        2。现在使用两个迭代器,每个数组一个:

        it1 = array1.begin
        it2 = array2.begin
        while (it1 == 0)
           ++it1
        while (it2 == 0)
           ++it2
        minDist = abs(it1-it2)
        while (it1 != array1.end && it2 != array2.end)
        {   //advance until overpass the other
            while (it1 <= it2 && it1 != array1.end)
                ++it1
                if (it1 > 0)
                    check minDist between it1, it2
            while (it2 <= it1 && it2 != array2.end)
                ++it2
                if (it2 > 0)
                    check minDist between it1, it2
            if (it1 = it2)
                //well, minDist = 0
                return now
        }
        

        步骤 1 是 O(n)。步骤 2 也是 O(n)。我不知道这是否比对大桶或短桶进行分类更有效。

        【讨论】:

        • 1. mavV 必须是值的最大值,而不是大小。否则 `array1(bucket1(j)) 将失败。您需要另一个 O(n) 循环来确定这一点。 2. 桶中可能包含 0。您如何将它们与数组的空元素区分开来? 3.最后一步是O(maxV),它不依赖于n(元素个数),而是依赖于最大值。
        【解决方案7】:

        考虑预先计算两个列表中每个数字的答案并将它们存储为一个数组。使用列表中每个数字的下标,并使用它来下标数组中包含差异的位置。

        这给出了 O(1) 查找。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-17
          • 2018-03-01
          • 1970-01-01
          • 2011-02-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多