【问题标题】:How to find peaks of minimal length efficiently?如何有效地找到最小长度的峰?
【发布时间】:2019-02-13 15:16:53
【问题描述】:

我有整数列表/数组,如果子数组上升然后下降,则将其称为 peak。例如:

[5,5,4,5,4]

包含

[4,5,4]

这是一个高峰。

同时考虑

[6,5,4,4,4,4,4,5,6,7,7,7,7,7,6]

其中包含

[6,7,7,7,7,7,6]

这是一个高峰。

问题

给定一个输入列表,我想找到其中包含的所有最小长度的峰并报告它们。在上面的示例中,[5,6,7,7,7,7,7,6] 也是一个峰值,但我们删除了第一个元素,它仍然是一个峰值,因此我们不报告它。

所以对于输入列表:

L = [5,5,5,5,4,5,4,5,6,7,8,8,8,8,8,9,9,8]

我们会回来

[4,5,4] and [8,9,9,8] only.

我在为此设计一个很好的算法时遇到问题。任何帮助将不胜感激。

【问题讨论】:

  • 所以 [4, 5, 5, 4] 也被认为是一个峰值?
  • @OlivierMelançon 是的
  • 像 [4, 5, 5, 2] 这样的东西呢?还是保证元素只改变 1。
  • @IFunball 这也是一个高峰。但是当它们只变化 1 时,你有更简单的方法吗?
  • @IFunball 我建议的解决方案是否适合您的需求?

标签: python algorithm


【解决方案1】:

使用itertools

这是使用itertools.groupby 检测峰值的简短解决方案。然后将识别峰的组解包以产生实际序列。

from itertools import groupby, islice

l = [1, 2, 1, 2, 2, 0, 0]

fst, mid, nxt = groupby(l), islice(groupby(l), 1, None), islice(groupby(l), 2, None)
peaks = [[f[0], *m[1], n[0]] for f, m, n in zip(fst, mid, nxt) if f[0] < m[0] > n[0]]

print(peaks)

输出

[[1, 2, 1], [1, 2, 2, 0]]

使用循环(更快)

上面的解决方案很优雅,但是由于创建了三个groupby 实例,所以列表被遍历了三次。

这是一个使用单次遍历的解决方案。

def peaks(lst):
    first = 0
    last = 1
    while last < len(lst) - 1:
        if lst[first] < lst[last] == lst[last+1]:
            last += 1
        elif lst[first] < lst[last] > lst[last+1]:
            yield lst[first:last+2]
            first = last + 1
            last += 2
        else:
            first = last
            last += 1

l = [1, 2, 1, 2, 2, 0, 0]
print(list(peaks(l)))

输出

[[1, 2, 1], [1, 2, 2, 0]]

基准注释

在使用 timeit 进行基准测试时,我注意到使用循环的解决方案的性能提高了约 20%。对于简短列表,groupby 的开销可能会使该数字达到 40%。基准测试是在 Python 3.6 上完成的。

【讨论】:

  • 函数好像缺少return语句?
  • @Anush 这是一个发电机,它yields 峰。当列表可能相当长时,这是一个很好的做法。无论如何,生成器总是可以通过演员表制作成列表。
  • 谢谢。你的回答很棒。
猜你喜欢
  • 2020-12-15
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
  • 2017-06-26
  • 2023-03-09
  • 2020-11-27
  • 2012-10-22
  • 2022-01-05
相关资源
最近更新 更多