最长等距子序列


76

我有一百万个按排序顺序排列的整数,我想找到连续对之间的差相等的最长子序列。例如

1, 4, 5, 7, 8, 12

有一个子序列

   4,       8, 12

我的幼稚方法是贪婪的,只检查从每个点可以扩展子序列多远。这O(n²)似乎需要花费一点时间。

有没有更快的方法来解决这个问题?

更新。我将尽快测试答案中给出的代码(谢谢)。但是,很显然,使用n ^ 2内存将不起作用。到目前为止,还没有代码以输入结尾的代码[random.randint(0,100000) for r in xrange(200000)]

时机。 我在32位系统上使用以下输入数据进行了测试。

a= [random.randint(0,10000) for r in xrange(20000)] 
a.sort()
  • ZelluX的动态编程方法使用1.6G RAM,耗时2分14秒。使用pypy只需9秒!但是,由于大输入而导致的内存错误导致崩溃。
  • Armin的O(nd)时间方法使用pypy花费了9秒,但只有20MB的RAM。当然,如果范围更大的话,情况会更糟。低内存使用率意味着我还可以使用a = [xrange(200000)中r的random.randint(0,100000)进行测试],但是在我给pypy提供的几分钟内,它并没有完成。

为了能够测试Kluev的方法,我重试了

a= [random.randint(0,40000) for r in xrange(28000)] 
a = list(set(a))
a.sort()

大致列出长度20000。pypy的所有时间

  • ZelluX,9秒
  • 克鲁夫20秒
  • 阿明52秒

看来,如果ZelluX方法可以做成线性空间,那将是明显的赢家。


3
您认为完成此操作有多快?
米奇·麦特

44
我不了解所有的否决票,尤其是不公开票(离题,更适合超级用户严重吗?)。我也想看到答案,这是一个有趣的问题。因此,我+1。
蒂姆·皮茨克

7
@ user2179021:您可以通过包含现有代码来改善您的问题。这似乎使一些更关键的SO用户感到平静。现在不用担心投票不足。
2013年

3
@TimPietzcker我和你在一起,我看到了更糟糕的问题,我认为这是个好问题
Roman Pekar

7
在此示例中,是什么决定4, 8, 12了正确的输出1, 4, 7以及相等的长序列?
FThompson

Answers:


11

更新:Armin Rigo的第二个答案已废除了这里描述的第一个算法,该算法更简单,更有效。但是这两种方法都有一个缺点。他们需要许多小时才能找到一百万个整数的结果。因此,我尝试了另外两个变体(请参阅此答案的后半部分),其中假定输入整数的范围是有限的。这种限制允许更快的算法。我也尝试优化Armin Rigo的代码。最后查看我的基准测试结果。


这是使用O(N)内存的算法思想。时间复杂度为O(N 2 log N),但可以降低为O(N 2)。

算法使用以下数据结构:

  1. prev:索引数组,指向(可能不完整)子序列的前一个元素。
  2. hash:具有key的哈希图=子序列中连续对之间的差,而value =另外两个哈希图。对于这些其他哈希图:键=子序列的开始/结束索引,值=对(子序列长度,子序列的结束/开始索引)。
  3. pq:优先级队列,用于存储在prev和中的子序列的所有可能的“差异”值hash

算法:

  1. prev用索引初始化i-1。更新hashpq注册在此步骤中发现的所有(不完整)子序列及其“差异”。
  2. 从中获取(并删除)最小的“差异” pq。从hash第二级哈希图中获取相应的记录并进行扫描。此时,具有给定“差异”的所有子序列均已完成。如果第二级哈希图包含的子序列长度比迄今为止找到的更好,请更新最佳结果。
  3. 在数组中prev:对于在步骤#2中找到的任何序列的每个元素,递减索引,并更新hash和可能pq。更新时hash,我们可以执行以下操作之一:添加长度为1的新子序列,或将现有的子序列增加1,或合并两个现有的子序列。
  4. 删除在步骤2中找到的哈希映射记录。
  5. pq不为空时,从步骤2继续。

该算法prev每次更新O(N)次的O(N)个元素。这些更新中的每一个都可能需要为添加新的“差异” pq。如果我们使用的简单堆实现,所有这些都意味着O(N 2 log N)的时间复杂度pq。为了将其减少到O(N 2),我们可以使用更高级的优先级队列实现。此页面列出了一些可能性:优先队列

请参阅Ideone上相应的Python代码。此代码不允许列表中有重复的元素。可以解决此问题,但无论如何还是要删除重复项(并找到重复项之外最长的子序列),这将是一个很好的优化。

相同的代码经过一点优化。只要子序列长度乘以可能的子序列“差异”超过源列表范围,搜索就会终止。


Armin Rigo的代码简单而高效。但是在某些情况下,它会执行一些额外的计算,这些计算可以避免。只要子序列长度乘以可能的子序列“差异”超出源列表范围,搜索就可以终止:

def findLESS(A):
  Aset = set(A)
  lmax = 2
  d = 1
  minStep = 0

  while (lmax - 1) * minStep <= A[-1] - A[0]:
    minStep = A[-1] - A[0] + 1
    for j, b in enumerate(A):
      if j+d < len(A):
        a = A[j+d]
        step = a - b
        minStep = min(minStep, step)
        if a + step in Aset and b - step not in Aset:
          c = a + step
          count = 3
          while c + step in Aset:
            c += step
            count += 1
          if count > lmax:
            lmax = count
    d += 1

  return lmax

print(findLESS([1, 4, 5, 7, 8, 12]))

如果源数据(M)中的整数范围较小,则可以使用O(M 2)时间和O(M)空间的简单算法:

def findLESS(src):
  r = [False for i in range(src[-1]+1)]
  for x in src:
    r[x] = True

  d = 1
  best = 1

  while best * d < len(r):
    for s in range(d):
      l = 0

      for i in range(s, len(r), d):
        if r[i]:
          l += 1
          best = max(best, l)
        else:
          l = 0

    d += 1

  return best


print(findLESS([1, 4, 5, 7, 8, 12]))

它与Armin Rigo的第一种方法类似,但是不使用任何动态数据结构。我想源数据没有重复项。并且(为了使代码简单),我还假设最小输入值是非负的,并且接近零。


如果我们不是使用布尔数组,而是使用位集数据结构和按位运算来并行处理数据,则可能会改进以前的算法。下面显示的代码将位集实现为内置的Python整数。它具有相同的假设:没有重复项,最小输入值是非负值并且接近零。时间复杂度为O(M 2 * log L),其中L为最佳子序列的长度,空间复杂度为O(M):

def findLESS(src):
  r = 0
  for x in src:
    r |= 1 << x

  d = 1
  best = 1

  while best * d < src[-1] + 1:
    c = best
    rr = r

    while c & (c-1):
      cc = c & -c
      rr &= rr >> (cc * d)
      c &= c-1

    while c != 1:
      c = c >> 1
      rr &= rr >> (c * d)

    rr &= rr >> d

    while rr:
      rr &= rr >> d
      best += 1

    d += 1

  return best

基准测试:

输入数据(大约100000整数)通过以下方式生成:

random.seed(42)
s = sorted(list(set([random.randint(0,200000) for r in xrange(140000)])))

对于最快的算法,我还使用了以下数据(大约1000000整数):

s = sorted(list(set([random.randint(0,2000000) for r in xrange(1400000)])))

所有结果以秒为单位显示时间:

Size:                         100000   1000000
Second answer by Armin Rigo:     634         ?
By Armin Rigo, optimized:         64     >5000
O(M^2) algorithm:                 53      2940
O(M^2*L) algorithm:                7       711

2
我必须承认我不了解此解决方案。你能提供一些代码吗?
eleanora

@ user2179021:不确定会有所帮助。那应该是很多代码。不容易写。太复杂了,难以理解。我仍然希望可以从简短的描述中得到启发。您可能对某个特定步骤有疑问吗?
Evgeny Kluev

甚至第1步也将有所帮助。我们使用问题中的示例1、4、5、7、8、12在第一步中将prev,next,hash和pq设置为什么?
eleanora 2013年

@ user2179021:prev为[Nil,0,1,2,3,4]。next是[1,2,3,4,5,Nil]。pq将包含所有相邻的差异:1、2、3、4。每个子序列的长度为1。此值以及开始/结束位置存储在hash:{1:({1:1,3:1},{2:1,4:1}),2:({2:1 },{3:1}),3:({0:1},{1:1}),4:({4:1},{5:1})}}。
Evgeny Kluev 2013年

1
@EvgenyKluev您的方法至少在以下三个方面更好:) 1.将解决方案并行化以使其真正快速。2.时间复杂度很明显,并且不取决于数字的大小。我不是100%不确定所提出的解决链接问题的方法的时间复杂度。3.如果您对“ d”的可能范围有所了解,那么您的解决方案甚至会更快。
eleanora

19

O(n*m)通过适应您的需求,我们可以及时解决几乎没有内存需求的问题。这n是给定的数字输入序列中的项目数,m是范围,即最高编号减去最低编号。

将所有输入数字的序列称为A(并使用预先计算的set()常数时间回答问题“此数字是否在A中?”)。将d称为我们要查找的子序列的步骤(该子序列的两个数字之间的差)。对于d的每个可能值,对所有输入数字进行以下线性扫描:对于A中的每个数字n(从小到大),如果尚未看到该数字,则在A中查找以n开头的序列的长度,其中a步骤d。然后,按该顺序将所有项目标记为已看到,以便我们避免再次从它们中搜索相同的d。因此,复杂度仅O(n)针对d的每个值。

A = [1, 4, 5, 7, 8, 12]    # in sorted order
Aset = set(A)

for d in range(1, 12):
    already_seen = set()
    for a in A:
        if a not in already_seen:
            b = a
            count = 1
            while b + d in Aset:
                b += d
                count += 1
                already_seen.add(b)
            print "found %d items in %d .. %d" % (count, a, b)
            # collect here the largest 'count'

更新:

  • 如果您只对d相对较小的值感兴趣,那么此解决方案可能就足够了。例如,如果获得最佳结果d <= 1000就足够了。然后,复杂度下降到O(n*1000)。这使算法近似,但实际上可用于n=1000000。(使用CPython在400-500秒处进行测量,使用PyPy在80-90秒处进行测量,数字的随机子集介于0到10,000'000之间。)

  • 如果仍要搜索整个范围,并且通常的情况是存在长序列,则显着的改进是,如果d太大而无法找到更长的序列,则立即停止。


如果您只对d相对较小的值感兴趣,那么此解决方案可能就足够了。例如,如果获得d <= 1000的最佳结果就足够了。然后,复杂度下降到O(n*1000),对于n = 1000000,它可能在不到一分钟的时间内运行(也尝试PyPy)。
Armin Rigo

好主意,如果范围非常有限
2013年

但是,如果d <= 1000您只是删除重复项,则最多将有1000个元素并以O(1000 ^ 2)的形式解决,这我最多只能花几秒钟的时间。
RiaD

不,如果A具有一百万个介于0到10'000'000之间的数字,则m = 10'000'000。但是,如果我们限制自己,d <= 1000我们正在寻找整个A中最多具有1000个步骤的序列。
Armin Rigo

啊,我明白你的意思
RiaD

12

更新:我已经找到有关此问题的论文,您可以在此处下载。

这是基于动态编程的解决方案。它需要O(n ^ 2)时间复杂度和O(n ^ 2)空间复杂度,并且不使用哈希。

我们假设所有数字都a按升序保存在数组中,并n保存其长度。2D数组l[i][j]定义了最长的等距子序列的长度,该子序列以a[i]和结束,如果- = - (i <j <k)a[j],则l[j][k]= l[i][j]+ 1 。a[j]a[i]a[k]a[j]

lmax = 2
l = [[2 for i in xrange(n)] for j in xrange(n)]
for mid in xrange(n - 1):
    prev = mid - 1
    succ = mid + 1
    while (prev >= 0 and succ < n):
        if a[prev] + a[succ] < a[mid] * 2:
            succ += 1
        elif a[prev] + a[succ] > a[mid] * 2:
            prev -= 1
        else:
            l[mid][succ] = l[prev][mid] + 1
            lmax = max(lmax, l[mid][succ])
            prev -= 1
            succ += 1

print lmax

很好很干净。可以使其在线性空间中运行吗?
eleanora

@ user2179021行lmax = max(lmax,l [mid] [succ])更新lmax,如果您需要最佳子序列,也可以在此处保存此序列。
ZelluX

您认为它可以做成线性空间吗?
eleanora

@ user2179021我无法弄清楚。但是我发现了另一个在某些情况下运行速度更快的解决方案。请查看更新后的答案中的链接。
ZelluX

谢谢。但是,我不相信本文中的方法在实践中会更快。我认为空间确实是主要问题(以及能够使用有关可能的间隙大小的知识来加快速度)。
eleanora

3

算法

  • 主循环遍历列表
  • 如果在预计算列表中找到数字,则该数字属于该列表中的所有序列,请重新计算count + 1的所有序列
  • 删除所有为当前元素预先计算的
  • 重新计算新序列,其中第一个元素的范围是从0到当前,第二个元素是遍历的当前元素(实际上,不是从0到当前,我们可以使用这样的事实:新元素的取值不应大于max(a)和new列表应该有可能变得更长,已经找到的一个)

因此,列表[1, 2, 4, 5, 7]输出将是(有点混乱,请自己尝试编写代码,看看)

  • 索引0,元素1
    • 如果1在预计算?否-不执行任何操作
    • 没做什么
  • 索引1,元素2
    • 如果2在预计算?否-不执行任何操作
    • 检查我们的集合中是否3 = 1+(2- 1)* 2?否-不执行任何操作
  • 索引2,元素4
    • 如果4在预计算?否-不执行任何操作
      • 检查我们的集合中是否6 = 2+(4- 2)* 2?没有
      • 检查我们的集合中是否7 = 1+(4- 1)* 2?是-添加新元素{7: {3: {'count': 2, 'start': 1}}} 7-列表中的元素,第3步。
  • 索引3,元素5
    • 如果5在预计算?否-不执行任何操作
      • 不要检查,4因为6 = 4 +(5- 4)* 2小于计算的元素7
      • 检查我们的集合中是否8 = 2+(5- 2)* 2?没有
      • 检查10 = 2+(5- 1)* 2-大于max(a)== 7
  • 索引4,元素7
    • 如果precalc中有7个?是的-付诸实践
      • 不要检查,5因为9 = 5 +(7- 5)* 2大于max(a)== 7

result =(3,{'count':3,'start':1})#步骤3,计数3,开始1,将其转换为序列

复杂

它不应该大于O(N ^ 2),并且我认为它不大是因为较早地终止了搜索新序列的过程,稍后我将尝试提供详细的分析

def add_precalc(precalc, start, step, count, res, N):
    if step == 0: return True
    if start + step * res[1]["count"] > N: return False

    x = start + step * count
    if x > N or x < 0: return False

    if precalc[x] is None: return True

    if step not in precalc[x]:
        precalc[x][step] = {"start":start, "count":count}

    return True

def work(a):
    precalc = [None] * (max(a) + 1)
    for x in a: precalc[x] = {}
    N, m = max(a), 0
    ind = {x:i for i, x in enumerate(a)}

    res = (0, {"start":0, "count":0})
    for i, x in enumerate(a):
        for el in precalc[x].iteritems():
            el[1]["count"] += 1
            if el[1]["count"] > res[1]["count"]: res = el
            add_precalc(precalc, el[1]["start"], el[0], el[1]["count"], res, N)
            t = el[1]["start"] + el[0] * el[1]["count"]
            if t in ind and ind[t] > m:
                m = ind[t]
        precalc[x] = None

        for y in a[i - m - 1::-1]:
            if not add_precalc(precalc, y, x - y, 2, res, N): break

    return [x * res[0] + res[1]["start"] for x in range(res[1]["count"])]

1
寻找大多数时间出现的差异,对吗?它将在“ 1 2 5 6 100 101 1000 1001 1e5 1e5 + 2 1e5 + 4”中找到很多1,而最佳答案是diff = 2?
RiaD

实际上,我的解决方案不是高效的内存,无论如何
都要

你有改变吗?我没有注意到差异
RiaD

还没有,必须去做些运动,跑步时我的脑袋实际上会更好地工作,所以也许这会有所帮助:)如果我找到解决方法,那我就把它放在这里。我当前的内容非常幼稚,感谢您的关注
Roman Pekar

3
将来,我强烈建议不要删除或重新发布答案(如果偶然将其标记为Community Wiki)。就像我在这里所做的那样,我们可以轻松地撤销该状态。
布拉德·拉尔森

3

这是另一个答案,可以及时工作,O(n^2)并且没有将列表变成集合的任何显着内存需求。

这个想法很幼稚:就像原始的海报一样,它很贪心,只是检查可以从每对点延伸一个子序列多远---但是,首先检查我们是否处于子序列的开始。换句话说,从点ab你检查多远,你可以扩展到b + (b-a)b + 2*(b-a)......但前提是a - (b-a)尚未在所有点的集合。如果是,那么您已经看到了相同的子序列。

诀窍是使自己相信,这种简单的优化足以将复杂度降低到O(n^2)原始水平O(n^3)。留给读者练习:-)在O(n^2)这里,时间与其他解决方案竞争。

A = [1, 4, 5, 7, 8, 12]    # in sorted order
Aset = set(A)

lmax = 2
for j, b in enumerate(A):
    for i in range(j):
        a = A[i]
        step = b - a
        if b + step in Aset and a - step not in Aset:
            c = b + step
            count = 3
            while c + step in Aset:
                c += step
                count += 1
            #print "found %d items in %d .. %d" % (count, a, c)
            if count > lmax:
                lmax = count

print lmax

2

您的解决方案O(N^3)现在是(您说过O(N^2) per index)。这是O(N^2)时间和O(N^2)存储解决方案。

理念

如果我们知道子,通过指数去i[0]i[1]i[2]i[3]我们不应该试图子与启动i[1]i[2]i[2]i[3]

注意,我对代码进行了编辑,以使其更易于使用a排序,但不适用于相等的元素。您可以O(N)轻松检查等号中的最大数量

伪码

我只在寻找最大长度,但这并没有改变任何东西

whereInA = {}
for i in range(n):
   whereInA[a[i]] = i; // It doesn't matter which of same elements it points to

boolean usedPairs[n][n];

for i in range(n):
    for j in range(i + 1, n):
       if usedPair[i][j]:
          continue; // do not do anything. It was in one of prev sequences.

    usedPair[i][j] = true;

    //here quite stupid solution:
    diff = a[j] - a[i];
    if diff == 0:
       continue; // we can't work with that
    lastIndex = j
    currentLen = 2
    while whereInA contains index a[lastIndex] + diff :
        nextIndex = whereInA[a[lastIndex] + diff]
        usedPair[lastIndex][nextIndex] = true
        ++currentLen
        lastIndex = nextIndex

    // you may store all indicies here
    maxLen = max(maxLen, currentLen)

关于内存使用的想法

O(n^2)1000000个元素的时间非常慢。但是,如果要在如此多的元素上运行此代码,最大的问题将是内存使用情况。
可以做些什么来减少它?

  • 将布尔数组更改为位域,以每位存储更多布尔值。
  • 使每个下一个布尔数组更短,因为我们仅使用 usedPairs[i][j]ifi < j

少数启发式方法:

  • 仅存储成对使用的索引。(与第一个想法冲突)
  • 删除将永远不再使用的usedPairs(用于such ij已在循环中选择)

n是一百万,因此boolean usedPairs[n][n]至少需要一个TB的内存。
Michael Butscher 2013年

哦,没有注意到确切的约束,也没有考虑内存。(顺便说一下,它可能会被2除,因为我们只usedPairs[i][j]用于i <j)
RiaD

@MichaelButscher,以任何方式O(n^2)获取100万个数字都非常慢。
RiaD

是的,我感觉它无法更快地完成(但无法证明)。
Michael Butscher

2
@MichaelButscher,也许我们可以以某种方式使用它的排序方式。
RiaD

1

这是我的2美分。

如果您有一个称为输入的列表:

input = [1, 4, 5, 7, 8, 12]

您可以构建一个数据结构,对于每个要点(不包括第一个要点),它都将告诉您该点距其任何前任有多远:

[1, 4, 5, 7, 8, 12]
 x  3  4  6  7  11   # distance from point i to point 0
 x  x  1  3  4   8   # distance from point i to point 1
 x  x  x  2  3   7   # distance from point i to point 2
 x  x  x  x  1   5   # distance from point i to point 3
 x  x  x  x  x   4   # distance from point i to point 4

现在已经有了列,您可以考虑i-th输入项(是input[i]n及其列中的每个数字。

属于一系列等距数字,包括数字input[i],是那些具有n * ji-th其列,其中的位置j是移动列时,从左至右已经找到匹配的数量,加上k-th前身input[i],这里k是指数n在的列input[i]

例如:如果我们考虑i = 1input[i] = 4n = 3,然后,我们可以识别的序列理解4input[i]), 7(因为它有一个3在位置1其列的)和1,因为k是0,因此,我们采取的第一个前身i

可能的实现(很抱歉,如果代码未使用与说明相同的符号):

def build_columns(l):
    columns = {}
    for x in l[1:]:
        col = []
        for y in l[:l.index(x)]:
            col.append(x - y)
        columns[x] = col
    return columns

def algo(input, columns):
    seqs = []
    for index1, number in enumerate(input[1:]):
        index1 += 1 #first item was sliced
        for index2, distance in enumerate(columns[number]):
            seq = []
            seq.append(input[index2]) # k-th pred
            seq.append(number)
            matches = 1
            for successor in input[index1 + 1 :]:
                column = columns[successor]
                if column[index1] == distance * matches:
                    matches += 1
                    seq.append(successor)
            if (len(seq) > 2):
                seqs.append(seq)
    return seqs

最长的一个:

print max(sequences, key=len)

您的算法在300分上工作了2秒,我的算法工作了0.03秒。我已经在5000上尝试过您的,但时间太长了:(我在5000上工作了18秒,所以我们仍然无法以1000000的速度做到这一点
Roman Pekar

是的,但是可能是我们遗漏了一点,并且有可能按原样做:)或至少NlogN ..
Roman Pekar

0

遍历数组,记录最佳结果/秒和一张表,该表具有

(1)索引-序列中的元素差异,
(2)计数-到目前为止序列中的元素数,以及
(3)最后记录的元素 元件。

对于每个数组元素,请查看与先前每个数组元素的区别;如果该元素在表中索引的序列中最后一个,则调整表中的序列,并在适用时更新最佳序列,否则,开始一个新序列,除非当前最大值大于可能序列的长度。

向后扫描可以在d大于数组范围的中间值时停止扫描。或当当前最大值大于可能序列的长度时,d大于最大索引差。s[j]大于序列中最后一个元素的序列将被删除。

我将代码从JavaScript转换为Python(我的第一个python代码):

import random
import timeit
import sys

#s = [1,4,5,7,8,12]
#s = [2, 6, 7, 10, 13, 14, 17, 18, 21, 22, 23, 25, 28, 32, 39, 40, 41, 44, 45, 46, 49, 50, 51, 52, 53, 63, 66, 67, 68, 69, 71, 72, 74, 75, 76, 79, 80, 82, 86, 95, 97, 101, 110, 111, 112, 114, 115, 120, 124, 125, 129, 131, 132, 136, 137, 138, 139, 140, 144, 145, 147, 151, 153, 157, 159, 161, 163, 165, 169, 172, 173, 175, 178, 179, 182, 185, 186, 188, 195]
#s = [0, 6, 7, 10, 11, 12, 16, 18, 19]

m = [random.randint(1,40000) for r in xrange(20000)]
s = list(set(m))
s.sort()

lenS = len(s)
halfRange = (s[lenS-1] - s[0]) // 2

while s[lenS-1] - s[lenS-2] > halfRange:
    s.pop()
    lenS -= 1
    halfRange = (s[lenS-1] - s[0]) // 2

while s[1] - s[0] > halfRange:
    s.pop(0)
    lenS -=1
    halfRange = (s[lenS-1] - s[0]) // 2

n = lenS

largest = (s[n-1] - s[0]) // 2
#largest = 1000 #set the maximum size of d searched

maxS = s[n-1]
maxD = 0
maxSeq = 0
hCount = [None]*(largest + 1)
hLast = [None]*(largest + 1)
best = {}

start = timeit.default_timer()

for i in range(1,n):

    sys.stdout.write(repr(i)+"\r")

    for j in range(i-1,-1,-1):
        d = s[i] - s[j]
        numLeft = n - i
        if d != 0:
            maxPossible = (maxS - s[i]) // d + 2
        else:
            maxPossible = numLeft + 2
        ok = numLeft + 2 > maxSeq and maxPossible > maxSeq

        if d > largest or (d > maxD and not ok):
            break

        if hLast[d] != None:
            found = False
            for k in range (len(hLast[d])-1,-1,-1):
                tmpLast = hLast[d][k]
                if tmpLast == j:
                    found = True
                    hLast[d][k] = i
                    hCount[d][k] += 1
                    tmpCount = hCount[d][k]
                    if tmpCount > maxSeq:
                        maxSeq = tmpCount
                        best = {'len': tmpCount, 'd': d, 'last': i}
                elif s[tmpLast] < s[j]:
                    del hLast[d][k]
                    del hCount[d][k]
            if not found and ok:
                hLast[d].append(i)
                hCount[d].append(2)
        elif ok:
            if d > maxD: 
                maxD = d
            hLast[d] = [i]
            hCount[d] = [2]


end = timeit.default_timer()
seconds = (end - start)

#print (hCount)
#print (hLast)
print(best)
print(seconds)

所以您仍然需要O(N ^ 2)记忆
罗马·佩卡尔

@RomanPekar谢谢您的评论。那么,尽早终止反向扫描的附加优化又如何呢?
2013年

@RomanPeckar不,只是考虑算法;我也不懂Python。
2013年

@RomanPeckar在我的答案中添加了jsfiddle示例。jsfiddle.net/groovy/b6zkR
2013年

好吧,我认为有很多O(N ^ 2)解决方案,今天我已经编写了一个这样的示例。我仍然不知道是否有一个好的解决方案可以运行1000000个数字……
Roman Pekar

0

对于这里描述的更一般的问题,这是一个特例:发现K = 1且固定的长模式。在那里证明可以在O(N ^ 2)中求解。Runnig在我的C算法实现中提出,在我的32位计算机中,需要3秒钟才能找到N = 20000和M = 28000的解决方案。


0

贪婪方法
1。仅生成一个决策序列。
2.产生许多决策。动态编程1.它不能保证始终提供最佳解决方案。
2.绝对可以提供最佳解决方案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.