高效的多队列迭代

时间:2014-01-14 17:00:11

标签: arrays algorithm data-structures

  

我们得到一个子程序,它接受两个正的int参数并返回一个int,假设它是def f(m,n): return (m+n)**2-n**2。输入值必须是正整数。相对于两个输入,返回的值都在增加:f(m,n)<f(m+1,n)f(m,n)<f(m,n+1)适用于所有m和所有n。我们希望以递增的顺序迭代所有可能的mn对,直到它通过测试为止。我们不关心测试:我们知道一对值将通过它,我们想要通过的最小返回值。我们也不知道测试是否会在每个mn的前一百万个值中传递,因此我们不能只构建整个值列表并对其进行排序。

     

我们如何以合理有效的方式以正确的顺序迭代m,n

我把它想象成一个多队列,它不能是正确的名称:它叫什么?

我有一个由nextN[]编制索引的数组m-1,为n存储访问量最大的m。由nextV[]索引的另一个数组m-1存储f(m,nextN[m-1])的返回值,因此我们不会为任何一对调用f()多次(这可以作为预先删除优化,但当f需要很长时间才能运行或有副作用时,这是必要的优化。在每个步骤中,我们采用最小的存储值并对其进行测试,并使用下一个值n更新两个数组中的元素。

问题是:应该使用哪些数据结构和方法来使这个多队列高效且易于理解?我有快速入侵,但我想要一个更好,更易理解和可维护的解决方案。

我用Python编写,但同样的问题适用于Java,C等。用你喜欢的任何语言给出答案。 (我不会选择以其默默无闻的语言选择的答案,但如果我能理解它并且有帮助,我会给它+1。)

以下是一些示例代码:

from array import array
from math import sqrt

def findSmallestV(f,test):
    # initialize with m,n=1,1 filled out
    nextN = array('I', [1])
    nextV = array('I', [f(1,1)])
    while True:
        v = min(nextV)
        m = nextV.index(v)+1
        n = nextN[m-1]
        if test(v):
            return (m,n,v)
        nextN[m-1] += 1
        nextV[m-1] = f(m,n+1)
        # if we've just operated on the last column, put a value into the next column
        if m == len(nextN):
            nextN.append(1)
            nextV.append(f(m+1,1))


# example value function
def g(m,n): return (m+n)**2-n**2

# example test function
def h(v): return len(str(v))>5 and int(sqrt(v))**2 == v

ans = findSmallestV(g,h)
print("Smallest V: m=%d, n=%d -> %d" % ans)

min(nextV)的大小变大时,我觉得这会花费很长时间nextV。什么是更好的方式?

2 个答案:

答案 0 :(得分:1)

您可以做的是将问题分解为两个步骤:

  1. 找一些通过测试的(m,n)。
  2. 使用二进制搜索技术查找通过的最低(m,n)。
  3. 你也可以在第一部分使用二进制搜索技术。

    很难知道“通过测试”意味着什么。你知道返回的值是太大还是太小?如果是这样,您可以通过减半或加倍来调整mn,直到找到解决方案。

    鉴于您的限制(即您描述的关系),对两个变量的二元搜索不应该太困难。

    您可以跟踪优先级队列中的中间传递值。因此,当您找到一个通过时,您可以将其放入队列。这可能是您下一次的起点。您还需要跟踪已找到的最高和最低通过值,以便更轻松地对搜索进行括号。

    而且,我想,你会想要保留某种哈希表,这会阻止你多次生成相同的(m,n)

    如果mn有一定的范围,这会变得更容易。如果它是“所有正整数”,我所描述的技术是可能的,但如果它们被置于括号内则会更容易。

答案 1 :(得分:1)

评论中建议的优先级队列可以提供一个很好的解决方案。

在算法运行期间的任何时候,您都会考虑一些点而不考虑其他点。您将把这个鸿沟的边界上的点放入优先级队列。更具体地说,您维护一个三元组(m,n,v)的优先级队列(可能实现为二进制堆),按v排序,其中(m,n)是此边界上的一个点,v是其值。

在每次迭代中,从队列中提取最低值的点并对其进行测试。如果它通过,那就是答案。如果没有,请将其右侧和上方的点连同它们的值一起放入队列中。

这将按原样运行,但效率低,因为每个点都会被处理多次。为了防止这种情况发生,还要在队列中的每个列中维护最低坐标的可增长数组,并在每行中保留最左边条目的数组。无论何时向队列输入一个点,首先要检查该列中是否已经存在较低点,或者同一行中是否存在更左侧的点。如果是这样,就不要输入它 - 无论如何它会在以后出现。