简体   繁体   中英

Obtain a strictly increasing sequence python

Question:

"Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.

Note: sequence a0, a1, ..., an is considered to be a strictly increasing if a0 < a1 <... < an. Sequence containing only one element is also considered to be strictly increasing.

Examples:

  • For sequence = [1, 3, 2, 1], the output should be solution(sequence) = false. There is no one element in this array that can be removed in order to get a strictly increasing sequence.

  • For sequence = [1, 3, 2], the output should be solution(sequence) = true. You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3]."

Here's my code:

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

My code covers three cases:

Case 1: when the sequence is just one element long. I caught that in the first if statement.

Case 2: If there's more than one down-step – a case where the neighbour is less than the element being considered – then there is no way to adjust the sequence with just one removal, and so we get false (count > 1). I caught this in the first if statement.

Case 3: There are some cases, however, where there is only one down-step but there is still no way to remove just one element. This happens when the second element along is also less than the element being considered. For example, with [1,4,3,2] even if you removed the 3, you would still get a downstep. Now I covered this case by doing a second check, which checked whether the element two along is less, and if it is, then we add to the count.

Case 4: There is a case my code doesn't cover, which seems to be the only one, and that is when an element's neighbour and the next element along are both smaller than the element under consideration, but we could solve the issue just by getting rid of the element under consideration. So, with [1,4,2,3] both 2 and 3 are smaller than 4, but if we just got rid of the 4, then we're good. This case can occur either when the problem element is the first in the sequence or not. I'm not sure how to capture this properly. I suppose you might add in a conditional which looked at whether i-2 is less than i+1, but this won't work when i indexes the first element and it's quite cumbersome. I'm not sure how to go sort this out.

I'm quite sure I've overcomplicated things and what really is needed is to step back and think of a less piece-meal solution, but I'm stuck. Could anyone help? Note that we don't have to actually obtain the strictly increasing sequence; we just have to see whether we could.

Here is an idea you can a look at (edited after comments)

def distinct(L: list[int]) -> bool:
    return len(L) == len(set(L))

def almost_increasing(L: list[int]) -> bool:

    # some trivial cases
    if len(L) <= 2:                                   return True
    if L[1: ] == sorted(L[1: ]) and distinct(L[1: ]): return True
    if L[:-1] == sorted(L[:-1]) and distinct(L[:-1]): return True
    
    return any(
        L[  :i] == sorted(L[  :i]) and distinct(L[  :i]) and
        L[i+1:] == sorted(L[i+1:]) and distinct(L[i+1:]) and
        L[i-1 ] < L[i+1]
        for i in range(1, len(L)-1)
    )

And here is a nice way you can test it with hypothesis and pytest :

@given(L=st.lists(st.integers(), min_size=2, max_size=6))
def test_almost_increasing(L: list[int]):

    expected = False
    for i in range(len(L)):
        Lcopy = L.copy()
        del Lcopy[i]
        expected |= (Lcopy == sorted(Lcopy) and distinct(Lcopy))
    
    received = almost_increasing(L)
    assert received == expected

Let's split the input into at most two increasing subsequences. If this is not possible, return false.

If there's only one sequence, return true.

If the length of either sequence is 1, the answer is true - we simply remove this element.

Otherwise we can join two sequences by either removing the last item from the first sequence, or the first item of the second one. That is,

 if a[j-2] < a[j]     -> ok, remove a[j - 1]
 if a[j-1] < a[j + 1] -> ok, remove a[j]

where j is the index where the second sequence starts.

Code:

def check(a):
    j = 0

    for i in range(1, len(a)):
        if a[i - 1] >= a[i]:
            if j > 0:
                return None
            j = i

    if j == 0:
        return a

    if j == 1:
        return a[1:]

    if j == len(a) - 1:
        return a[:-1]

    if a[j - 2] < a[j]:
        return a[:(j - 1)] + a[j:]

    if a[j - 1] < a[j + 1]:
        return a[:j] + a[(j + 1):]

    return None


assert check([2, 4, 6, 8]) == [2, 4, 6, 8], 'j == 0'
assert check([9, 4, 6, 8]) == [4, 6, 8], 'j == 1'
assert check([4, 6, 8, 1]) == [4, 6, 8], 'j == len-1'
assert check([2, 4, 9, 6, 8]) == [2, 4, 6, 8], 'j-2 < j'
assert check([2, 4, 1, 6, 8]) == [2, 4, 6, 8], 'j-1 < j+1'

assert check([2, 2, 2, 2]) is None, 'early return'
assert check([2, 8, 9, 6, 1]) is None, 'early return'

assert check([2, 4, 9, 3, 5]) is None, 'last return'

assert check([2]) == [2]

Try this:

def is_solution(list_to_check):
    if len(list_to_check) == 1:
        return True
    for i in range(1, len(list_to_check)):
        if list_to_check[i] <= list_to_check[i - 1]:
            new_list = list_to_check[:i - 1] + list_to_check[i:]
            if (list_to_check[i] > list_to_check[i - 2]
                and new_list == sorted(new_list)):
                return True
            elif (i == len(list_to_check) - 1
                  and list_to_check[:-1] == sorted(list_to_check[:-1])):
                return True
    return False


if __name__ == '__main__':
    list_to_check = [1, 2, 1]
    print(is_solution(list_to_check))

This version can solve 16/19 cases.

def solution(sequence):
count = 0
for i in range(len(sequence)-1):
    print(sequence[i], sequence[i+1])
    if sequence[i] < sequence[i+1]:
        continue
    else:
        count += 1

if count <= 1:
    return True
else:
    return False

This cannot solve cases when sequence: [1, 2, 1, 2] or sequence: [40, 50, 60, 10, 20, 30] or sequence: [1, 2, 3, 4, 5, 3, 5, 6]

I will update here if I was able to solve the other 3 test cases.

def solution(sequence):
    """determine strict increase"""
    sequence_length = len(sequence)
    if (
        sequence_length == 0
        or not len(set(sequence)) + 1 >= sequence_length
    ):
        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.

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