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. 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).
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.
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:
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.
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.
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. 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.
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.
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]
. 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
.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.