简体   繁体   English

确定输入列表是否严格增加

[英]Determine if the input list is strictly increasing

I am trying to figure out if an input list is a strictly increasing list. 我想弄清楚输入列表是否是严格增加的列表。 Moreover, If removing only one element from the list results in a strictly increasing list, we still consider the list true. 此外,如果从列表中只删除一个元素会导致严格增加的列表,我们仍然认为列表为true。 Here is my code. 这是我的代码。 It seems to have an index error, but I do not understand why. 它似乎有一个索引错误,但我不明白为什么。

 def almostIncreasingSequence(sequence):
    n=len(sequence)
    count=0
    if n<=2:
        return True

    for i in range (n-1):
        #test if the i-th element is bigger or equal to the elements after it. If it is, remove that element, and add one to count 
        for j in range (i+1,n):
            if sequence[i]>=sequence[j]:
                sequence.pop(i)
                count+=1
    #if there is more than one element that has to be taken out, it's false           
    if count>1:
        return False

    return True
def almost_increasing_sequence(sequence):
    if len(sequence) < 3:
        return True

    a, b, *sequence = sequence
    skipped = 0
    for c in sequence:
        if a < b < c:  # XXX
            a, b = b, c
            continue
        elif b < c:    # !XX
            a, b = b, c
        elif a < c:    # X!X
            a, b = a, c
        skipped += 1
        if skipped == 2:
            return False
    return a < b


if __name__ == '__main__':
    assert almost_increasing_sequence([]) is True
    assert almost_increasing_sequence([1]) is True
    assert almost_increasing_sequence([1, 2]) is True
    assert almost_increasing_sequence([1, 2, 3]) is True
    assert almost_increasing_sequence([3, 1, 2]) is True
    assert almost_increasing_sequence([1, 2, 3, 0, 4, 5, 6]) is True
    assert almost_increasing_sequence([1, 2, 3, 0]) is True
    assert almost_increasing_sequence([1, 2, 0, 3]) is True
    assert almost_increasing_sequence([10, 1, 2, 3, 4, 5]) is True
    assert almost_increasing_sequence([1, 2, 10, 3, 4]) is True
    assert almost_increasing_sequence([1, 2, 3, 12, 4, 5]) is True

    assert almost_increasing_sequence([3, 2, 1]) is False
    assert almost_increasing_sequence([1, 2, 0, -1]) is False
    assert almost_increasing_sequence([5, 6, 1, 2]) is False
    assert almost_increasing_sequence([1, 2, 3, 0, -1]) is False
    assert almost_increasing_sequence([10, 11, 12, 2, 3, 4, 5]) is False

Alright, so it turns out this problem is not that easy. 好吧,事实证明这个问题并不那么容易。

If you want an efficient solution, I think your best bet may be an algorithm similar to the longest increasing subsequence problem . 如果你想要一个有效的解决方案,我认为你最好的选择可能是一个类似于增长最长的子序列问题的算法。

But here, we don't care about the actual longest increasing subsequence - we just need it's length. 但在这里,我们并不关心实际上增长最长的子序列 - 我们只需要它的长度。 Also, we can short-circuit when maintaining our ordered list if we have had to perform n insertions already (where n is our restriction on the number of "out of order" elements). 此外,如果我们必须执行n插入,我们可以在维护有序列表时进行短路(其中n是我们对“无序”元素数量的限制)。

This also generalizes very well to the n element "almost increasing" case, and in the worst case performs n-1 binary searches on lists of size Mn-1 to M , where M is the size of the list. 这也很好地概括了n元素“几乎增加”的情况,并且在最坏的情况下在大小为Mn-1M列表上执行n-1二进制搜索,其中M是列表的大小。

import bisect

def almost_increasing(li, n=1):
    if len(li) < 2:
        return True
    ordered_li = [li[0]]
    violator_count = 0
    for ele in li[1:]:
        if ele < ordered_li[0]:
            violator_count += 1
            ordered_li[0] = ele
        elif ele > ordered_li[-1]:
            ordered_li.append(ele)
        else:
            violator_count += 1
            insertion_pos = bisect.bisect_right(ordered_li, ele)
            ordered_li[insertion_pos] = ele
        if violator_count > n: return False
    return True

The idea behind this algorithm is as follows: 该算法背后的想法如下:

  • We move through the list, and maintain an ordered subsequence of our list all the while. 我们遍历列表,并始终保持列表的有序子序列。
  • When we reach a new element 当我们达到一个新的元素

    • if that element cannot be appended onto our ordered subsequence, it is a "violator" of the increasing property. 如果该元素不能附加到我们的有序子序列上,则它是增加属性的“违反者”。 We subsequently insert it into the ordered subsequence in the correct position, using bisect for binary search. 我们随后使用bisect进行二进制搜索,将其插入到正确位置的有序子序列中。

    • otherwise, we just append it to our ordered subsequence and continue on. 否则,我们只是将它附加到我们的有序子序列并继续。

  • At the end of each iteration, if we have too many violators already we can short-circuit out. 在每次迭代结束时,如果我们有太多的违规者,我们就可以短路了。 Otherwise, after the loop is done we are guaranteed to have an increasing subsequence that has length within n of the length of our original list. 否则,在循环完成后,我们保证有一个增加的子序列,其长度在原始列表长度的n之内。


Demo 演示

>>> almost_increasing([5, 1, 2, 3, 4])
True
>>> almost_increasing([1, 2, 5, 2, 15, 0, 176])
False
>>> almost_increasing([1, 2, 5, 2, 15, 0, 176], 2)
True

If you've written for i in range(len(some_list)) in Python, you've probably done the wrong thing. 如果你在Python for i in range(len(some_list))编写for i in range(len(some_list)) ,你可能做错了。 This is indeed why this is failing. 这确实是失败的原因。 n is the length of the sequence as it stands before any processing, but that length can change as you pop items from the list. n是在任何处理之前的序列长度,但是当您从列表中pop项目时,该长度可能会发生变化。

Better instead is to compare each mark which indices need to be removed and do them all at once, or better yet -- don't remove them at all!! 更好的是比较每个标记需要删除的标记,并一次完成它们,或者更好 - 不要删除它们! It's a side-effect that's not well-explained. 这是一个副作用,没有得到很好的解释。

You can build this by building a list of all sequences that might be strictly increasing using itertools.combinations , comparing each pair with itertools 's pairwise recipe, then short-circuiting as long as at least one is. 您可以通过使用itertools.combinations构建可能严格增加的所有序列的列表来构建它,将每对与itertoolspairwise配方进行比较,然后只要至少有一个就进行短路。

import itertools

def pairwise(iterable):
    (a, b) = itertools.tee(iterable)
    next(b, None)  # advance b
    return zip(a, b)

def almostIncreasingSequence(sequence):
    if not sequence:
        return True
        # in case of empty list
    combos = itertools.combinations(sequence, len(sequence)-1)
    # combos is each ordered combination that's missing one element
    # it is processed as an iterator, so will do no extra work if we can
    # exit early.

    def strictly_increasing(cs):
        return all(a < b for (a, b) in pairwise(cs))

    return any(strictly_increasing(c) for c in combos)

The only thing you need to do is walk the list, counting the number of times sequence[i] > sequence[i+1] . 您唯一需要做的就是遍历列表,计算sequence[i] > sequence[i+1] If it happens at most once, your list is almost monotonically increasing. 如果它最多发生一次,你的列表几乎单调增加。

def almostIncreasingSequence(sequence):
    count = 0
    for i in range(0, len(sequence) - 1):
        if sequence[i] > sequence[i+1]:
            count += 1
    return count < 2

You can also avoid counting, since the number of exceptions is small. 您也可以避免计数,因为例外的数量很少。 Just return False as soon as you find the second exception, as tracked by the value of a Boolean variable initialized to True . 只要找到第二个异常,就会返回False ,这是由初始化为True的布尔变量的值跟踪的。

def almostIncreasingSequence(sequence):
    increasing = True
    for i in range(0, len(sequence) - 1):
        if sequence[i] > sequence[i+1]:
            if increasing:
                increasing = False
            else:
                return False
    return True

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM