【问题标题】:Take first 10 element smallest sum combinations from two sorted arrays从两个排序数组中取前 10 个元素的最小和组合
【发布时间】:2020-02-20 01:52:53
【问题描述】:

没能google到这个问题的名字,希望这个问题对社区有所贡献。

假设我们有两个排序的数字数组,例如:

 2  8
12 18
45 35
85 48
87 49
97 59

我们希望首先从两个数组中有效地获取k (10) 个最小的数字总和组合。在我们的例子中是:

 2 +  8 = 10 
 2 + 18 = 20
12 +  8 = 20
12 + 18 = 30
 2 + 35 = 37
12 + 35 = 47
 2 + 48 = 50
 2 + 49 = 51
45 +  8 = 53
12 + 48 = 60

什么是正确的方法?我编写了一个幼稚的实现(由@sanyash 改进),但它没有利用数组已排序的事实,并且问题在线性时间内感觉可行......

def smallest_product(k, arr1, arr2):
    product_iter = itertools.product(
        itertools.islice(arr1, k),
        itertools.islice(arr2, k),
    )
    product_sorted = sorted(product_iter, key=sum)
    product_sliced = itertools.islice(product_sorted, k);
    return list(product_sliced)

print(smallest_product(10, 
    [ 2, 12, 45, 85, 87, 98], 
    [ 8, 18, 35, 48, 49, 59]))

类似问题:efficient sorted Cartesian product of 2 sorted array of integers(但它处理创建一个完整的结果数组,而在我的情况下,我只需要前几个值)

附:我添加了python 标签,因为它是一道数学题,但我很乐意使用任何语言的解决方案,或者只是解释,或者维基百科的链接......

【问题讨论】:

标签: python algorithm sorting iteration cartesian-product


【解决方案1】:

你可以使用堆:

import heapq


def smallest_product(k, a, b):
    k = min(k, len(a) * len(b))
    l = [(a[0] + b[0], 0, 0)]
    heapq.heapify(l)

    seen = set()
    for _ in range(k):
        s, i, j = heapq.heappop(l)

        if i + 1 < len(a) and (i + 1, j) not in seen:
            heapq.heappush(l, (a[i + 1] + b[j], i + 1, j))
            seen.add((i + 1, j))
        if j + 1 < len(b) and (i, j + 1) not in seen:
            heapq.heappush(l, (a[i] + b[j + 1], i, j + 1))
            seen.add((i, j + 1))
        yield (a[i], b[j])

result = list(smallest_product(10, [ 2, 12, 45, 85, 87, 98], [ 8, 18, 35, 48, 49, 59]))

print(result)

输出

[(2, 8), (2, 18), (12, 8), (12, 18), (2, 35), (12, 35), (2, 48), (2, 49), (45, 8), (12, 48)]

上面的代码是从here 中的代码翻译过来的。该方法的时间复杂度为O(k*log k)

输出 (对于 k = 11)

[(2, 8), (2, 18), (12, 8), (12, 18), (2, 35), (12, 35), (2, 48), (2, 49), (45, 8), (12, 48), (2, 59)]

【讨论】:

  • 感谢您的解决方案!我无法一目了然,但由于某种原因,k 的值大于10(如11)会失败。 IndexError: list index out of rangeheapq.heappush(l, (a[i] + b[j + 1], i, j + 1))
  • @ArturKlesun 立即查看。
【解决方案2】:

这个问题可以分三步解决。

  1. 构造一个长度-m 排序对的可迭代列表,其中m = min(len(list1), k);
  2. m-way merging(更多信息请参见this)应用于可迭代对象以获得排序对的单个可迭代对象,使用每对的总和作为键;
  3. 从可迭代对象中获取第一个 k 元素。

m-way 合并有不同的算法。以下是基于堆的实现。复杂度为 O(k*logm)。

from itertools import islice
from heapq import merge

def smallest_pairs(k, list1, list2):
    pairs = map(lambda x:((x, y) for y in list2), islice(list1, k))
    return list(islice(merge(*pairs, key=sum), k))

print(smallest_pairs(10, 
      [ 2, 12, 45, 85, 87, 98],
      [ 8, 18, 35, 48, 49, 59]))

【讨论】:

    【解决方案3】:

    假设我们使用两个数组创建一个表:

    for arr in [[i + j for j in arr2] for i in arr1]: print(arr)
    

    我们得到这样的输出:

    [10, 20, 37, 50, 51, 61]
    [20, 30, 47, 60, 61, 71]
    [53, 63, 80, 93, 94, 104]
    [93, 103, 120, 133, 134, 144]
    [95, 105, 122, 135, 136, 146]
    [106, 116, 133, 146, 147, 157]
    

    请注意,在此矩阵中,matrix[i][j] == arr1[i] + arr2[j]。所以我们可以计算O(1)中矩阵任意位置的元素的值。请注意,这是一个排序矩阵,其中所有行和列都是单调递增的,我们试图在其中找到k 最小的元素。

    在这个阶段,O(KlogN) 堆方法变得相当简单。取第一行并将其变成最小堆。每次弹出最小的元素并将其添加到您的结果中。每次弹出时,都会将下一行的相应列中的元素添加到堆中。重复k 次,您就有了k 最小的总和。

    这与情况并不完全相关,但是确实存在鞍式搜索的变体,可以让您在O(N) 的排序矩阵中找到kth 最小元素,而不是O(KlogN),就像上面的方法一样.可能有一种方法可以将this paper 中采用的方法修改为O(K),但对于这种情况,它很可能是矫枉过正。

    【讨论】:

      【解决方案4】:

      首先将两个数组截断为 len k。之后使用您以前的实现。这将是 O(k^2) 难度:

      import itertools
      
      def smallest_product(k, arr1, arr2):
          product_iter = itertools.product(
              itertools.islice(arr1, k),
              itertools.islice(arr2, k),
          )
          product_sorted = sorted(product_iter, key=sum)[:k]
          return list(product_sorted)
      
      
      print(smallest_product(
          10, 
          [ 2, 12, 45, 85, 87, 98], 
          [ 8, 18, 35, 48, 49, 59])
      )
      

      【讨论】:

      • 谢谢,那肯定会是一次升级,不过我想知道是否有办法获得比O(k^2) 更好的结果...我会更新我的问题。
      猜你喜欢
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      相关资源
      最近更新 更多