【问题标题】:k-greatest double selectionk-最大双重选择
【发布时间】:2014-10-06 12:15:58
【问题描述】:

想象一下,你有两个麻袋(A 和 B),里面分别装有 N 和 M 球。每个球都有一个已知的数值(利润)。您被要求提取(有替换)具有最大总利润的一对球(由所选球的乘积得出)。

最佳提取是显而易见的:从A 和B 中选择价值最高的球。

当您被要求给出第二或第 k 个最佳选择时,问题就来了。按照前面的方法,您应该从A 和B 中选择价值最高的球,而无需重复选择。

这可以很笨拙地解决计算每个可能选择的值,对其进行排序和排序(python中的示例):

def solution(A,B,K):
    if K < 1:
        return 0
    pool = []
    for a in A:
        for b in B:
            pool.append(a*b)
    pool.sort(reverse=True)
    if K>len(pool):
        return 0
    return pool[K-1]

这可行,但它最差的时间复杂度是O(N*M*Log(M*M)),我敢打赌会有更好的解决方案。

我基于一个表找到了一个解决方案,其中A 和B 元素从高值到低值排序,并且这些值中的每一个都与一个索引相关联,该索引表示要从另一列测试的下一个值。最初,此表如下所示:

A 中的第一个元素是 25,它必须针对 20 进行测试 (index 2 select from b = 0),因此 25*20=500 是第一个最佳选择,并且在增加要检查的索引后,表会发生变化到:

使用这些索引,我们可以快速获得最佳候选者:

25 * 20 = 500 #first from A and second from B
20 * 20 = 400 #second from A and first from B

我尝试编写此解决方案:

def solution(A,B,K):
    if K < 1:
        return 0
    sa = sorted(A,reverse=true)
    sb = sorted(B,reverse=true)

    for k in xrange(K):
        i = xfrom
        j = yfrom
        if i >= n and j >= n:
                ret = 0
                break
        best = None
        while i < n and j < n:
                selected = False
                #From left
                nexti = i
                nextj = sa[i][1]
                a = sa[nexti][0]
                b = sb[nextj][0]
                if best is None or best[2]<a*b:
                        selected = True
                        best = [nexti,nextj,a*b,'l']
                #From right
                nexti = sb[j][1]
                nextj = j
                a = sa[nexti][0]
                b = sb[nextj][0]
                if best is None or best[2]<a*b:
                        selected = True
                        best = [nexti,nextj,a*b,'r']
                #Keep looking?
                if not selected or abs(best[0]-best[1])<2:
                        break
                i = min(best[:2])+1
                j = i
                print("Continue with: ", best, selected,i,j)
        #go,go,go
        print(best)
        if best[3] == 'l':
            dx[best[0]][1] = best[1]+1
            dy[best[1]][1] += 1
        else:
            dx[best[0]][1] += 1
            dy[best[1]][1] = best[0]+1
        if dx[best[0]][1]>= n:
            xfrom = best[0]+1
        if dy[best[1]][1]>= n:
            yfrom = best[1]+1
        ret = best[2]

    return ret

但它不适用于在线 Codility 评委(我是否提到这是解决已经过期的 Codility 挑战的部分?Sillicium 2014)

我的问题是:

  • 第二种方法是未完成的好解决方案吗?如果是这样的话,有什么线索可以说明我可能遗漏了什么吗?
  • 您知道解决此问题的更好方法吗?

【问题讨论】:

  • 你读过这篇文章了吗:"How to find pair with kth largest sum?"。我认为它回答了您的第二个问题。
  • 问题陈述有点不清楚。你能澄清你所说的第k个选择是什么意思吗?由于提取是有放回的,我们可以继续提取相同的最大值球。
  • @user1990169 认为它不是第 k 个选择,而是第 k 个最佳选择
  • 好的,那么你想要第 k 个最大的对积吗?
  • 取对数,然后在链接的帖子中应用解决方案。

标签: python performance algorithm math


【解决方案1】:

您需要维护一个优先级队列。

您从(sa[0], sb[0]) 开始,然后转到(sa[0], sb[1]) 和(sa[1], sb[0])。如果(sa[0] * sb[1]) &gt; (sa[1] * sb[0]),我们能说一下(sa[0], sb[2])和(sa[1], sb[0])的比较大小吗?

答案是否定的。因此我们必须维护一个优先级队列,并且在删除每个(sa[i], sb[j])(使得sa[i] * sb[j]是队列中最大的)之后,我们必须将(sa[i - 1], sb[j])和(sa[i], sb[j - 1])添加到优先级队列中,并重复此k次。

顺便说一下,我将此算法命名为answer to a different question。该算法起初可能看起来不同,但本质上它解决的是同一个问题。

【讨论】:

    【解决方案2】:

    我不确定我是否理解“替换”位......

    ...但假设这实际上与"How to find pair with kth largest sum?" 相同,那么解决方案的关键是考虑由 A 和 B (一旦它们被排序)——这个paper(由@EvgenyKluev引用)给出了这个线索。

    (你想要 A*B 而不是 A+B...但答案是一样的——虽然负数很复杂,但(我认为)不会使方法无效。)

    一个例子说明了发生了什么:

      for A = (2, 3, 5, 8, 13)
      and B = (4, 8, 12, 16)
    

    我们有(名义上的)数组 S,其中 S[r, c] = A[r] + B[c],在这种情况下:

       6 ( 2+4),  10 ( 2+8),  14 ( 2+12),  18 ( 2+16)
       7 ( 3+4),  11 ( 3+8),  15 ( 3+12),  19 ( 3+16)
       9 ( 5+4),  13 ( 5+8),  17 ( 5+12),  21 ( 5+16)
      12 ( 8+4),  16 ( 8+8),  20 ( 8+12),  14 ( 8+16)
      17 (13+4),  21 (13+8),  25 (13+12),  29 (13+16)
    

    (正如参考论文指出的那样,我们不需要构造数组 S,我们可以在需要时或在需要时生成 S 中项目的值。)

    真正有趣的是 S 的每一列都包含升序的值(当然),所以我们可以通过合并列来从 S 中提取值降序(阅读从底部)。

    当然,合并列可以使用优先级队列(堆)来完成——因此是max-heap 解决方案。最简单的方法是从 S 的底行开始堆,用它来自的列标记每个堆项。然后弹出堆的顶部,并从与刚刚弹出的列相同的列中推送下一项,直到弹出第 k 项。 (由于底行是排序好的,用它来做堆种子是一件小事。)

    这个复杂度是 O(k log n) - 其中“n”是列数。如果您处理行,该过程同样有效......因此,如果有“m”行和“n”列,您可以选择两者中较小的一个!

    NB:复杂度不是 O(k log k)...并且因为对于给定的一对A和B,'n'是常数,O (k log n) 真的是 O(k) !!

    如果您想对不同的“k”进行许多个探测,那么技巧可能是不时缓存进程的状态,以便将来的“k”可以通过重新启动来完成从最近的检查站。在极限情况下,将运行合并以完成并存储所有可能的值,以进行 O(1) 查找!

    【讨论】:

    猜你喜欢
    • 2018-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多