【问题标题】:Size of the largest subsequence of values with consecutive alternative sign?具有连续替代符号的值的最大子序列的大小?
【发布时间】:2021-12-09 03:51:32
【问题描述】:

有没有一种捷径可以检测列表中带有替代符号的最长子列表?

例如:

my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]

返回 4 从 -0.5 到 4?

这是我到目前为止所写的,但我觉得还有更短的内容。

import numpy

my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]

# function that detects whether a list has alternate signs
# https://stackoverflow.com/questions/6451514/detect-alternating-signs
def is_alternating_signs(a):
    return numpy.all(numpy.abs(numpy.diff(numpy.sign(a))) == 2)

# getting all sublists from the main list
sublists = []
for i in range(len(my_list) + 1):
      for j in range(i + 1, len(my_list) + 1):
          sublists.append(my_list[i:j])

# detecting the longest sublist with alternate signs
max_list = 0
for sublist in sublists:
      if is_alternating_signs(sublist) and len(sublist) > max_list:
          max_list = len(sublist)
print(max_list)

【问题讨论】:

  • 那么,您的预期输出是只是长度还是说起始索引/值?
  • 我认为,与其尝试所有子列表,不如从列表的开头迭代并跟踪它交替的时间。这至少应该让它更快。
  • 预期的输出只是长度。

标签: python python-3.x list numpy


【解决方案1】:

使用zip比较当前元素和下一个元素:

maxlen = 1
curlen = 1
for i, j in zip(l, l[1:]):

    # if one conditions match
    # increment curlen by 1
    if (i < 0 and j > 0) or (i > 0 and j < 0):
        curlen += 1

    # break the alternative sign
    # keep the highest value between maxlen and curlen
    # reset curlen to 1
    else:
        maxlen = max(maxlen, curlen)
        curlen = 1
maxlen = max(maxlen, curlen)

输出:

>>> maxlen
4

【讨论】:

  • 如果0 被认为是积极的或没有发生,那么if (i &lt; 0) != (j &lt; 0) 可以逃脱。
  • 问题 - 这个lst = [-1, -5, 1, -3, 4, 5, 5, -1, 9, -4, 5, -6, 7] 的输出是什么,我从这篇文章中不清楚。请确认。谢谢。
  • @DanielHao。我修复了这个错误。我也必须在循环结束时比较 maxlen 和 curlen 。谢谢。现在的答案是 7
  • @user2390182。我让 OP 澄清这一点。
  • 在这里操作。答案是 7。
【解决方案2】:

您可以使用 zip 来检测交替中“中断”的位置。然后将这些中断组合成范围以找到最长的交替值连续:

L = [-1, -0.5, 1, -3, 4, 5, 5, -1]

breaks  = [i for i,(a,b) in enumerate(zip(L,L[1:]),1) if (a<0)==(b<0)]
longest = max((L[s:e] for s,e in zip([0]+breaks,breaks+[None])),key=len)

print(longest)
[-0.5, 1, -3, 4]

如果您只是在寻找条纹的长度,您可以将 zip 结果转换为 1 和 0 的字符串,然后在 0 上拆分并测量最长的子字符串:

max(map(len,"".join("01"[a*b<0] for a,b in zip(L,L[1:])).split('0')))+1 

4

【讨论】:

  • 对于任何后来者,请记住,与单个循环相比,此解决方案的时间和内存效率都很低。
【解决方案3】:

单循环呢?

def max_alt_subseq_size(seq):
    last_x = seq[0]
    size = max_size = 1
    for x in seq[1:]:
        # use the fact that x * y < 0 iff x > 0 and y < 0 or x < 0 and y > 0
        if last_x * x < 0: 
            size += 1 
        else:
            # once the size of the alternating subsequence is found, we need to check if it is the largest
            if size > max_size: 
                max_size = size
            size = 1 
        last_x = x
    # check on the final subsequence to see if it is the largest
    if size > max_size: 
        max_size = size
    return max_size


my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
max_alt_subseq_size(my_list)
# 4

【讨论】:

    【解决方案4】:

    一个人可以有(多个)完全矢量化的方法。

    下面的代码假设一个 NumPy 一维数组作为输入。

    例如,如果计算vectorized fashion 中的run-length encoding (RLE),则可以简单地在某个数组上使用RLE 信息来表示符号变化的位置来计算所需值

    import numpy as np
    
    
    def rle(arr):
        n = len(arr)
        if n == 0:
            values = np.empty(0, dtype=arr.dtype)
            lengths = np.empty(0, dtype=np.int_)
        else:
            positions = np.concatenate(
                [[-1], np.nonzero(arr[1:] != arr[:-1])[0], [n - 1]])
            lengths = positions[1:] - positions[:-1]
            values = arr[positions[1:]]
        return values, lengths
    
    
    def max_alt_rle(arr):
        values, lengths = rle(arr[1:] * arr[:-1] < 0)
        subs_lengths = lengths[values]
        return (1 if len(arr) > 0 else 0) + \
            (np.max(subs_lengths) if len(subs_lengths) > 0 else 0)
    

    或者,可以充分利用字符串/字节可用的更丰富的功能,特别是 str.split() 来制作一个非常短的、矢量化但不是非常有效的解决方案:

    def max_alt_np(arr):
        return (1 if len(arr) > 0 else 0) + \
            len(max((arr[1:] * arr[:-1] < 0).tobytes().split(b'\x00')))
    

    如果追求原始速度,使用 Numba the single loop solution 加速将是最有效和快速的解决方案:

    import numba as nb
    
    
    @nb.jit
    def max_alt_nb(arr):
        if len(arr):
            last_x = arr[0]
            size = max_size = 1
            for x in arr[1:]:
                if last_x * x < 0: 
                    size += 1 
                else:
                    if size > max_size: 
                        max_size = size
                    size = 1 
                last_x = x
            if size > max_size: 
                max_size = size
            return max_size
        else:
            return 0
    

    最后,这里报告了currently accepted answer 的改编版,它既不高效也不快速,但它相对紧凑(但不如max_alt_np 紧凑并且相当慢)并且可以使用列表而无需事先转换为NumPy 数组:

    def max_alt_str(arr):
        return (1 if len(arr) > 0 else 0) + len(max(
            ("".join(
                "01"[1 if a * b < 0 else 0]
                for a, b in zip(arr[:-1], arr[1:])))
            .split("0")))
    

    这里有一些关于不同大小的随机整数数组的基准:

    (完整分析here)。

    【讨论】:

      猜你喜欢
      • 2014-10-31
      • 2022-12-20
      • 1970-01-01
      • 2023-03-04
      • 2017-04-27
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多