Performance 如何找到第k个最大和的对?

Performance 如何找到第k个最大和的对?,performance,algorithm,math,language-agnostic,combinatorics,Performance,Algorithm,Math,Language Agnostic,Combinatorics,给定两个已排序的数字数组,我们希望找到具有第k个最大可能和的对。(一对是第一个数组中的一个元素和第二个数组中的一个元素)。例如,使用数组 [2,3,5,8,13] [4,8,12,16] 总和最大的对是 13+16=29 13+12=25 8+16=24 13+8=21 8+12=20 所以第四大和的一对是(13,8)。如何找到第k个最大可能和的对 还有,最快的算法是什么?数组已排序,大小为M和N 我已经知道了使用给定的Max-Heap的O(Klogk)解决方案 这也是谷歌最喜欢的面试

给定两个已排序的数字数组,我们希望找到具有第k个最大可能和的对。(一对是第一个数组中的一个元素和第二个数组中的一个元素)。例如,使用数组

  • [2,3,5,8,13]
  • [4,8,12,16]
总和最大的对是

  • 13+16=29
  • 13+12=25
  • 8+16=24
  • 13+8=21
  • 8+12=20
所以第四大和的一对是(13,8)。如何找到第k个最大可能和的对

还有,最快的算法是什么?数组已排序,大小为M和N


我已经知道了使用给定的Max-Heap的O(Klogk)解决方案

这也是谷歌最喜欢的面试问题之一,他们需要一个O(k)解决方案

我也在某个地方读到,存在一个O(k)解决方案,我无法理解

有人能用伪代码解释正确的解决方案吗

附言。
请不要将链接作为答案/评论发布。它不包含答案。

我从一个简单但不完全线性的时间算法开始。我们在
array1[0]+array2[0]
array1[N-1]+array2[N-1]
之间选择一些值。然后我们确定有多少对和大于这个值,有多少对和小于这个值。这可以通过使用两个指针迭代数组来实现:当总和太大时,指向第一个数组的指针递增;当总和太小时,指向第二个数组的指针递减。对不同的值重复此过程,并使用二进制搜索(或单边二进制搜索),我们可以在O(N log R)时间内找到第k个最大和,其中N是最大数组的大小,R是
array1[N-1]+array2[N-1]
array1[0]+array2[0]
之间的可能值的数量。该算法仅当数组元素是以小常数为界的整数时才具有线性时间复杂度

当二进制搜索范围内的对和数从O(N2)减少到O(N)时,如果我们立即停止二进制搜索,则先前的算法可能会得到改进。然后我们用这些对和填充辅助数组(这可以通过稍微修改的双指针算法完成)。然后我们使用quickselect算法在这个辅助数组中找到第k个最大和。所有这些并没有改善最坏情况的复杂性,因为我们仍然需要O(logr)二进制搜索步骤。如果我们保留此算法的quickselect部分,但(为了获得适当的值范围),我们使用比二进制搜索更好的方法,会怎么样

我们可以使用以下技巧估计值范围:从每个数组中获取每一秒的元素,并尝试为这些半数组找到秩
k/4
的对和(递归使用相同的算法)。显然,这应该为所需的值范围提供一些近似值。事实上,这个技巧稍微改进的变体给出了只包含O(N)元素的范围。这在以下论文中得到了证明:。本文包含了算法的详细解释、证明、复杂度分析以及算法所有部分的伪代码,除了。如果需要线性最坏情况复杂度,Quickselect可以使用算法进行扩充

该算法的复杂度为O(N)。如果其中一个数组比另一个数组短(M 如果kN(N-1),我们最好解决相反的问题:第k个最小和

我将简单的C++11实现上传到。代码没有经过优化,也没有经过彻底测试。我试图使它尽可能接近链接文件中的伪代码。此实现使用
std::nth_元素
,它只允许平均线性复杂度(而不是最坏情况)


另一种完全不同的方法是基于优先级队列(PQ)来寻找线性时间内的第K个和。一种变体是向PQ插入最大的对,然后重复移除PQ的顶部,而是最多插入两对(一对在一个数组中索引递减,另一对在另一个数组中索引递减)。并采取一些措施防止插入重复对。另一种方法是插入包含第一个数组中最大元素的所有可能对,然后重复移除PQ的顶部,而是在第一个数组中插入索引递减的对,在第二个数组中插入索引相同的对。在这种情况下,没有必要担心重复的问题

OP提到了O(K log K)解决方案,其中PQ被实现为max heap。但在某些情况下(当数组元素是范围有限的均匀分布整数且仅需要平均线性复杂度,而不是最坏情况),我们可以使用O(1)时间优先级队列,例如,如本文所述:。这允许O(K)预期的时间复杂度


这种方法的优点是可以按排序顺序提供前K个元素。缺点是数组元素类型的选择有限,算法更复杂、更慢,渐进复杂性更差:O(K)>O(N)。

编辑:这不起作用。我留下答案,因为显然我不是唯一一个有这种想法的人;见下面的讨论。 反例是x=(2,3,6),y=(1,4,5)和k=3,其中算法给出7(3+4)而不是8(3+5)


x
y
为两个数组,按降序排序;我们要构造第K个最大的和

变量是:
i
索引
def kth(a,b,k):
    M = len(a)
    N = len(b)
    if k > M*N:
       raise ValueError("There are only %s possible pairs; you asked for the %sth largest, which is impossible" % M*N,k)
    (ia,ib) = M-1,N-1 #0 based arrays
    # we need this for lookback
    nottakenindices = (0,0) # could be any value
    nottakensum = float('-inf')
    for i in range(k-1):
        optionone = a[ia]+b[ib-1]
        optiontwo = a[ia-1]+b[ib]
        biggest = max((optionone,optiontwo))
        #first deal with look behind
        if nottakensum > biggest:
           if optionone == biggest:
               newnottakenindices = (ia,ib-1)
           else: newnottakenindices = (ia-1,ib)
           ia,ib = nottakenindices
           nottakensum = biggest
           nottakenindices = newnottakenindices
        #deal with case where indices hit 0
        elif ia <= 0 and ib <= 0:
             ia = ib = 0
        elif ia <= 0:
            ib-=1
            ia = 0
            nottakensum = float('-inf')
        elif ib <= 0:
            ia-=1
            ib = 0
            nottakensum = float('-inf')
        #lookahead cases
        elif optionone > optiontwo: 
           #then choose the first option as our next pair
           nottakensum,nottakenindices = optiontwo,(ia-1,ib)
           ib-=1
        elif optionone < optiontwo: # choose the second
           nottakensum,nottakenindices = optionone,(ia,ib-1)
           ia-=1
        #next two cases apply if options are equal
        elif a[ia] > b[ib]:# drop the smallest
           nottakensum,nottakenindices = optiontwo,(ia-1,ib)
           ib-=1
        else: # might be equal or not - we can choose arbitrarily if equal
           nottakensum,nottakenindices = optionone,(ia,ib-1)
           ia-=1
        #+2 - one for zero-based, one for skipping the 1st largest 
        data = (i+2,a[ia],b[ib],a[ia]+b[ib],ia,ib)
        narrative = "%sth largest pair is %s+%s=%s, with indices (%s,%s)" % data
        print (narrative) #this will work in both versions of python
        if ia <= 0 and ib <= 0:
           raise ValueError("Both arrays exhausted before Kth (%sth) pair reached"%data[0])
    return data, narrative
import itertools
def refkth(a,b,k):
    (rightia,righta),(rightib,rightb) = sorted(itertools.product(enumerate(a),enumerate(b)), key=lamba((ia,ea),(ib,eb):ea+eb)[k-1]
    data = k,righta,rightb,righta+rightb,rightia,rightib
    narrative = "%sth largest pair is %s+%s=%s, with indices (%s,%s)" % data
    print (narrative) #this will work in both versions of python
    return data, narrative
[2, 3, 5, 8, 13]
[4, 8, 12, 16]