Almost increasing sequence in python

Question:

The goal of the code is to see if a sequence is almost increasing, that is, it can be made strictly increasing by removing a single element.

For example: [1, 3, 2, 3] would be strictly increasing if the element at index 1 were removed. [1, 2, 1, 2] is not almost increasing because if you removed the first ‘2’, you would get [1, 1, 2] which is not strictly increasing.

My code has to work in under 4000 ms for a sequence of length 2 <= len <= 10^5. It is likely getting caught up on very long sequences.

def almostIncreasingSequence(sequence):
    length = len(sequence)
    count = 0

    for previous, next in zip(sequence[:-1],sequence[1:]):
        if previous < next:
            count +=1

    if (length-count>2):
        return False
    return True

[1, 2, 1, 2]

[1, 2, 3, 4, 5, 3, 5, 6]

[40, 50, 60, 10, 20, 30]

would not work.. any help please

Please note that this is not a homework question, as most of you guys know, there are lots of answers to this question posted online and i was wondering if i can do it this way

Asked By: David L

||

Answers:

This works:

import timeit
import random


def increasing_sequence_pos(sequence):
    for n, (a, b) in enumerate(zip(sequence[:-1], sequence[1:])):
        if a >= b:
            return False, n + 1
    return True, -1


def almost_increasing_sequence(sequence):
    increasing, n = increasing_sequence_pos(sequence)
    return (
        # either it was already increasing
        increasing or
        # or the problem is with the last element
        n == len(sequence)-1 or
        ((
            # or the first element was the problem 
            n == 1
            # or the element at position n was the problem
            (sequence[n - 1] < sequence[n + 1]) or
            # or the element at position n-1 was the problem
            (sequence[n - 2] < sequence[n] < sequence[n + 1])
        ) and increasing_sequence_pos(sequence[n:])[0])
    )


size = 1000000

# time on simple increasing series
numbers = list(range(size))
print(timeit.timeit(lambda: almost_increasing_sequence(numbers), number=1))
print(f'Result: {almost_increasing_sequence(numbers)}')

# time on single issue
numbers[random.randint(1, size)] = 0
print(timeit.timeit(lambda: almost_increasing_sequence(numbers), number=1))
print(f'Result: {almost_increasing_sequence(numbers)}')

# time on two issues issue
numbers[random.randint(1, size)] = 0
print(timeit.timeit(lambda: almost_increasing_sequence(numbers), number=1))
print(f'Result: {almost_increasing_sequence(numbers)}')

Note how you get varying times and the time of course depends on the hardware you’re executing on, so "4000ms" is rather silly (and very easy to achieve on modern computers, even with really, really bad code).

You’ll notice that the first is fairly steady (0.07 seconds on my workstation), while the second and third typically take a lot shorter to just a bit shorter.

Example output:

0.07000060000000001
Result: True
0.06909959999999998
Result: True
0.06430069999999999
Result: False

Note that an increasing sequence is also considered an almost increasing sequence.

Answered By: Grismar
def seqIncCheck(lst):
    temp = lst[0]
    for i in range(1,len(lst)):
        if lst[i] > temp:
           temp = lst[i]
           continue
        else:
           return False
    return True
Answered By: Z. Cajurao

Your algorithm is not even right. You are just checking if there is one or less element that is smaller that its previous. However it does not mean it is a “almost-increasing” sequence.

In brief, when you meet an element that is smaller than its previous, then sequence is “almost-increasing” if the sequence is “strictly increasing” by removing that element itself, OR by removing the previous element.

e.g.

1, 2, 3, [2*], 4, 5, 6
1, 2, 3, 100*, [4], 5, 6
1, 2, 3, [1], 2, 3

In the first 2 example, number in [] is the element that is smaller than previous. However, number with * is the number need to be removed to form a strictly-increasing sequence. 3rd example is an example that contains only 1 element that is smaller than previous, but it is not a almost-increasing sequence

So the most straight-forward logic (in pseudo-code) is something like:

def is_almost_increasing(seq):
    for i in 1 to len(seq):
        if seq[i] <= seq[i-1]:
            return (  is_strictly_increasing(seq[:i-1] + seq[i:] 
                     or is_strictly_increasing(seq[:i] + seq[i+1:] )
    return true

(is_strictly_increasing is simple, will just leave it to you)

There are some tricks to make this even faster (e.g. you don’t really need to re-check the part that you have already confirmed increasing), but this should be a good starting point for you.

Answered By: Adrian Shum

The simplest thing, I think for me, was to check pairs and remove the element that disturbs the sequence. after checking the adjecent element to i, I also check the surrounding elements to i. if by removing i I still have a disturbed sequence, then I remove i+1 instead, and take a step back to start checking again. Ive tested this on 38 different test cases. Let me know if there are any mistakes.

def almostIncreasingSequence(sequence):
count = 0 
i =0
size = len(sequence)
if(sequence[0]>sequence[1]): #separate case for the first element
    sequence.pop(0)
    count+=1 #update the count everytime I remove an element
    size-=1  # I change the size everytime i remove an element
while(i<size-1):
    if sequence[i]>=sequence[i+1]:
        count+=1
        if(sequence[i+1]<=sequence[i-1]):
            sequence.pop(i+1)
        else:
            sequence.pop(i)
        size = len(sequence)
        if i>0:
            i-=1

    else:
        i+=1
if count>1:
    return False
else:
    return True
Answered By: RJSD3V

Well the way i figured out was using another array of ignore index which matched the false condition so that the loop would not be using those indexes and if the condition matches more than one time than it is not an almost increasing sequece.
Here I used ignoreindex list and added those index which matched the condition. The loop would ignore the index number using the list so that it would not end up checking same number twice. I tried to do it simply by removing the element from the list itself but somehow `def almostIncreasingSequence(sequence):

count = 0
ignoreindex = []
for i in range(0,len(sequence)-1):
    for j in range(i+1,len(sequence)):    
        if i not in ignoreindex:
            if j not in ignoreindex:
                print("The index are %d,%d"%(i,j))
                if sequence[j] <= sequence[i]:
                    print(i,j)
                    count += 1
                    print(count)
                    # if the same number are at two different location ignore the later index
                    if sequence[j] == sequence[i] and j - i > 1:
                        ignoreindex.append(j)
                    # else ignore the earlier index
                    else:
                        ignoreindex.append(i)
                    print(sequence)
                    print(ignoreindex)
                if count > 1:
                    return False

return True`
Answered By: Dhiraz Gazurel

Wow, this has been a hard challenge.

I think I have written a solution that will be understandable to a newbie also.

def solution(sequence):
    
    if len(sequence) < 3: # since list with only 1 element is strictly increasing and 
                           # with 2 elements we can always remove 1 to make it strictly increasing
        return True 
    
    original_sequence = sequence.copy()
    # original_sequence = sequence
    # there is one problem with copying lists in this way. If you modify
#new_list, old_list is also modified. It is because the new list is
#referencing or pointing to the same old_list object.

    
    position_to_pop = 0 #this will be used to remember the position where the strictly increasing does not hold
    # for example 11,31,21,11 will give position_to_pop as index 2(21) in the for loop below...if it doesn't work then we remove the index 1 (31) to check whether it makes the list strictly increasing
    
    if is_sequence_increasing(sequence):
        return True
    else:
        for index, num in enumerate(sequence):
            if index != len(sequence) -1: # if statment since we want to loop until second last item
                if sequence[index+1] <= num:
                    position_to_pop = index + 1
                    break #breaking since we do not want to check further in the for loop
    
    sequence.pop(position_to_pop) #first removing right integer(index+1) where the problem occured
    
    if is_sequence_increasing(sequence):
        return True
    else: # if the first remove of the right integer doesn't work then we need to remove the left integer and see whether it fixes the issue 
        
        original_sequence.pop(position_to_pop-1)
        
        if is_sequence_increasing(original_sequence):
            return True
    
    return False

def is_sequence_increasing(sequence_to_check):
    
    for index, num in enumerate(sequence_to_check):
        if index != len(sequence_to_check) -1:
            if sequence_to_check[index+1] <= num:
                return False
    return True
Answered By: raj
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.