Determine if the input list is strictly increasing - python

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

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 M-n-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:
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.
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

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

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

Related

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 solution(sequence):
n = len(sequence)
for i in range(n):
count = 0
trail = sequence[:]
del trail[i]
m = len(trail)
for j in range(m-1):
if trail[j] >= trail[j+1]:
count += 1
if count == 0:
return True
return False
This is not efficient nor optimized but it does the work.
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))
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

finding if an array can be converted to strictly increasing order

I have a porblem that I almost solved it but I'm facing problem with one test case,
giving an array of numbers, it is allowed to choose any number and swap it digits so the array become strictly increasing, for example: [2, 4, 800, 12], we can choose 800 and convert it to 008 then array become [2,4,008,12], so it should return true, and it is allowed to it once, so the task is to check if it possible to convert an array to strictly increasing with at most one swap, here is my code
def solution(numbers):
swapped = 0
for i in range(len(numbers)-1):
if numbers[i] > numbers[i+1]:
if swapped >= 1:
return False
s1= int(''.join(sorted(str(numbers[i]))))
if s1 < numbers[i+1]:
if i > 0 and s1 >= numbers[i-1]:
numbers[i] = s1
swapped += 1
continue
s2 = int(''.join(sorted(str(numbers[i+1]), reverse=True)))
if s2 >= numbers[i]:
numbers[i+1] = s2
swapped += 1
continue
return True
the only test case the code doesn't pass is [13,31,30] (returning True instead of False) and I don't know how to solve this problem.
(apologies for any an un-elegant parts in my question, I'm new to this world and I'm trying my best)
Your logic is largely right, except that I would put the check for the swap at the end of the loop, and I would check all the permutations instead of just the smallest and largest integer created from the digits.
from itertools import permutations, pairwise
def soln(numbers):
swapped = 0
for i in range(len(numbers)):
perms = list(int(''.join(shuffled_digits)) for shuffled_digits in permutations(str(numbers[i])))
if i == 0:
candidate_nums = [shuffled_number for shuffled_number in perms if shuffled_number < numbers[i+1]]
elif i < len(numbers) - 1:
candidate_nums = [shuffled_number for shuffled_number in perms if numbers[i-1] < shuffled_number < numbers[i+1]]
else:
candidate_nums = [shuffled_number for shuffled_number in perms if shuffled_number > numbers[i-1]]
if candidate_nums:
swapped_num = candidate_nums.pop()
numbers[i] = swapped_num
swapped +=1
if all(x < y for x, y in pairwise(numbers)):
return True
return False
Output
In [80]: inlist
Out[80]: [2, 4, 8, 12]
In [81]: soln(inlist)
Out[81]: True
In [82]: inlist = [2, 4, 800, 12]
In [83]: soln(inlist)
Out[83]: True
In [84]: inlist = [21, 21]
In [85]: soln(inlist)
Out[85]: True
In [86]: inlist = [2, 1, 9, 7]
In [87]: soln(inlist)
Out[87]: False
In [88]: inlist = [2, 1, 1]
In [89]: soln(inlist)
Out[89]: False
In [90]: inlist = [100, 2, 3]
In [91]: soln(inlist)
Out[91]: True
In [92]: inlist = [52, 53, 36]
In [93]: soln(inlist)
Out[93]: True
In [94]: inlist = [44,37,74]
In [95]: soln(inlist)
Out[95]: True
When you have a candidate number that needs to change, I would suggest making a directed search in the (virtual) list of permutations to find the permutation whose value is as close as possible to the value you compared with in order to restore the strictly ascending order.
It is not necessary to generate all permutations for this, which would consume too much time and space when the number of digits in the input numbers exceeds 10.
The idea is to count the number of digits you have of each digit, in the number that needs a permutation, and then take the next digit for building a permutation in such a way that you stay as close as possible to the target value. Some backtracking is needed, but for each digit position at most 2 attempts are needed: one for a digit that exactly matches the digit in the target (the value to compare with), and potentially a second attempt with another digit.
Here is an implementation of that idea.
First a helper function to find a permutation of a given value that is as near as possible to a target value (but not equal to it), and must be either less or greater (last argument controls this):
def permute_near(value, target, side=1):
# If side is 1, we will look for the minimum permutation of value that is
# greater than the target.
# If side is -1, we will look for the maximum permutation of value that is
# less than the target.
freedigit = 0 if side == 1 else 9
end = freedigit + 10*side
target += side
# Get a count for each digit of value (could also use Counter module for this)
s = str(value)
n = len(s)
digits = [0]*10
for digit in s:
digits[int(digit)] += 1
# Pad the target value to have the same number of digits
limit = [int(digit) for digit in str(target).rjust(n, "0")]
# Use recursion to find the permutation of value that is nearest to the target
def recur(k, isfree, accumulator):
if k >= n: # All digits were processed, we have the result
return accumulator
limitdigit = limit[k]
for i in range(freedigit if isfree else limitdigit, end, side):
if digits[i]:
digits[i] -= 1 # Select this digit for the permutation
permutation = recur(k + 1, isfree or i != limitdigit, accumulator * 10 + i)
digits[i] += 1 # Backtrack
if permutation > -1 or i != limitdigit:
return permutation
# If it failed for the case where the chosen digit was the same
# as in the target, try with one alternative digit in a
# second and also last iteration of this loop.
return -1
return recur(0, False, 0)
Here is the main function:
def solution(numbers):
try:
# Find the first index where the next value is not greater
i = next(i for i, val in enumerate(numbers) if val >= numbers[i+1])
except:
return numbers # The input is already completely ascending
# Permute numbers[i], so it becomes less than its successor
result = permute_near(numbers[i], numbers[i+1], -1)
# Check that such permutation exists, and is still greater than its predecessor
if result >= 0 and (i == 0 or numbers[i - 1] < result):
numbers[i] = result # OK, we have success
else:
# ... or permute numbers[i+1], so it becomes greater than its predecessor
result = permute_near(numbers[i+1], numbers[i], 1)
# Check that such permutation exists, and is still less than its successor
if result >= 0 and (i + 2 >= len(numbers) or result < numbers[i + 2]):
numbers[i + 1] = result # OK, we have success
if all(a < b for a, b in zip(numbers, numbers[1:])):
return numbers
Some runs:
# A few test cases
print(solution([2,4,800,12])) # [2, 4, 8, 12]
print(solution([44,37,74])) # [44, 73, 74]
print(solution([52,53,36])) # [52, 53, 63]
print(solution([64,52,53])) # [46, 52, 53]
print(solution([89221173000,89221173456,13437127829,89221173477]))
# [89221173000, 89221173456, 89221173473, 89221173477]
The function returns the adjusted list if there is a solution, None otherwise.
The code mentioned in the question also fails for this testcase: [52,31,30]
Here's the solution that returns True if there's atleast one increasing array / list by swapping only one number.
def solution(numbers):
# Converts to str
numbers = [str(i) for i in numbers]
n = len(numbers)
for j in range(n + 1):
if j != n:
# Reverse number
numbers[j] = str(numbers[j])[::-1]
# Checks if the array is in increasing order
if all([int(numbers[i + 1]) > int(numbers[i]) for i in range(n - 1)]):
return True
if j != n:
numbers[j] = str(numbers[j])[::-1]
return False

'in' for two sorted lists with the lowest complexity

I have two sorted lists, e.g.
a = [1, 4, 7, 8]
b = [1, 2, 3, 4, 5, 6]
I want to know for each item in a if it is in b. For the above example, I want to find
a_in_b = [True, True, False, False]
(or having the indices where a_in_b is True would be fine too).
Now, both a and b are very large, so complexity is an issue. If M = len(a) and N = len(b). How can I do this with a complexity lower than M * O(N) by making use of the fact that both lists are sorted?
You can iterate over your b list manually within a loop over a. You'll want to advance the b iteration when the latest value you've seen from it is less than the current value from a.
from math import inf
result = []
b_iter = iter(b) # create an iterator over b
b_val = -inf
for a_val in a:
while b_val < a_val:
b_val = next(b_iter, inf) # manually iterate on it
result.append(a_val == b_val)
This should have a running time of O(M+N), since each list item gets iterated over at most once (b may not even need to be fully iterated).
You could avoid using floating point infinities if you want to, but you'd need to do a bit of extra work to handle some edge cases (e.g. if b is empty).
Exploiting sorted'ness is a red-herring for time complexity: The ideal case is to iterate both in lockstep for O(n+m) complexity. This is the same as converting b to a set for O(m), then searching the elements of a in the set for O(n).
>>> a = [1, 4, 7, 8]
>>> b = [1, 2, 3, 4, 5, 6]
>>> bs = set(b) # create set for O(len(b))
>>> [item in bs for item in a] # check O(len(a)) items "in set of b" for O(1) each
[True, True, False, False]
Since most of these operations are builtin, the only costly operation is the iteration over a which is needed in all solutions.
However, this will duplicate the references to the items in b. If b is treated as external to the algorithm, the space complexity is O(m+n) instead of the ideal case O(n) for just the answer.
Late answer, but a different approach to the problem using set() uniqueness and O(1) speed of len(), i. e. :
a_in_b = []
a = [1,4,7,8]
b = [1,2,3,4,5,6]
b_set = set(b)
for v in a:
l1 = len(b_set)
b_set.add(v)
a_in_b.append(l1 == len(b_set))
Unfortunately, my approach isn't the fastest:
mistermiyagi: 0.387 ms
tomerikoo: 0.442 ms
blckknght: 0.729 ms
lobito: 1.043 ms
semisecure: 1.87 ms
notnotparas: too long
lucky6qi: too long
Benchmark
Use Binary Search here:
def bs(b,aele,start,end):
if start > end:
return False
mid = (start + end) // 2
if ale == b[mid]:
return True
if ale < b[mid]:
return bs(b, aele, start, mid-1)
else:
return bs(b, aele, mid+1, end)
For each element in a check if it exists in b using this method.
Time Complexity: O(m*log(n))
Using sets the order doesn't even matter.
Turn b to a set (O(N)). Then iterate a (O(M)), and for each element check if it's in set_b (O(1)). This will give a time complexity of O(max(M, N)):
a = [1, 4, 7, 8]
b = [1, 2, 3, 4, 5, 6]
set_b = set(b)
res = []
for elem in a:
res.append(elem in set_b)
This can of-course be shortened to a nifty list-comp:
res = [elem in set_b for elem in a]
Both give:
[True, True, False, False]
For your parenthesized request, simply iterate with enumerate instead:
for i, elem in enumerate(a):
if elem in set_b:
res.append(i)
Which will give [0, 1].
You should use binary search algorithm(read about it if you don't know what it is).
The modified bin_search function has to return position right for which b[right] >= elem - the first element in b that is greater or equal than searched element from a. This position will be used as the left position for next bin_search call. Also bin_search returns True as a second argument if it have found elem in b
def bin_search(arr, elem, left):
right = len(arr)
while left < right:
mid = (left+right)//2
if arr[mid] == elem:
return (mid, True)
if arr[mid] < elem:
left = mid + 1
else:
right = mid
return (right, False)
def find_a_in_b(a, b):
new_left = 0
a_in_b = [False] * len(a)
# we could have used enumerate but size of a is too large
index = 0
for i in a:
new_left, a_in_b[index] = bin_search(b, i, new_left)
index += 1
return a_in_b
It's probably the best time
P.S. Forget it, i'm stupid and forgot about linear algorithm used in merge sort, so it's not the best
Go through a and b once:
a_in_b = []
bstart = 0
for ai in a:
print (ai,bstart)
if bstart == len(b):
a_in_b.append(False)
else:
for bi in b[bstart:]:
print (ai, bi, bstart)
if ai == bi:
a_in_b.append(True)
break
elif ai > bi:
if bstart < len(b):
bstart+=1
if bstart == len(b):
a_in_b.append(False)
continue
The obvious solution is actually O(M + N):
a = [1, 1, 4, 7, 8]
b = [1, 2, 3, 4, 5, 6]
c = [0] * len(a) # Or use a dict to stash hits ..
j = 0
for i in range(0, len(a)):
while j < len(b) - 1 and b[j] < a[i]:
j += 1
if b[j] == a[i]:
c[i] = 1
print(c)
For each i in 0 ... N where N is length of a, only a unique partition / sub-sequence of b plus one border number is checked, making it O(M + N) all together.
for el in a:
try:
b = b[b.index(el):]
a_in_b.append("True")
except:
a_in_b.append("False")
A simple solution is to convert the lists to a data frame and do an inner merge
The inner join matches like values on a specific column

how to determine whether it is possible to obtain a strictly increasing sequence?

I need to determine a function that for a given a sequence of integers (as an array) and that can determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
the arrays can be in range
2 ≤ sequence.length ≤ 10^5,
-10^5 ≤ sequence[i] ≤ 10^5
for example:
[1, 3, 2, 1] we can not drop any one number in order to get increasing sequence, so the function should return False. And True for [0, -2, 5, 6], [1, 1], [1, 3, 2].
I wanted to write function that deletes one number from the list and examines whether the sequence is increasing. If for all iterations we become False than the function should return False.
def almostIncreasingSequence(sequence):
def order(A):
n = len(A)
for i in range(n):
if A[i] >= A[i+1]:
result = True
else:
result = False
for i in range(len(s0)-1):
s_i = list(sequence)
s_i.remove(s_i.index(s_i[i]))
if order(s_i) == True:
return True
But I have got a mistake:
ValueError: list.remove(x): x not in list
What is the reason? How I can finish my code?
as mentioned in the comments, you can use the numpy.diff for this, and thus avoiding the need to check every combination of removing 1 element from the list
import numpy
def almostIncreasingSequence(sequence):
diff = numpy.diff(sequence) < 1
return diff.sum() < 2
here first we get the difference between adjacent elements, then we ask where those differences are less than 1 and count it, and depending on the amount we get our answer.
Without numpy, we can use a similar technique
import itertools
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a,b = itertools.tee(iterable)
next(b,None)
return zip(a,b) # use itertools.izip in python 2
def almostIncreasingSequence(sequence):
return sum( a>=b for a,b in pairwise(sequence)) < 2
and this one have the advantage that use less memory than the numpy version
And here are some test for it
assert almostIncreasingSequence([0, -2, 5, 6])
assert almostIncreasingSequence([1,1])
assert almostIncreasingSequence([1,3,2])
assert almostIncreasingSequence([1,2,3,0])
assert almostIncreasingSequence([1,2,3,0,10])
assert almostIncreasingSequence([10,1,2,3,4])
assert almostIncreasingSequence([1,10,2,3,4])
assert not almostIncreasingSequence([1,2,3,0,0])
assert not almostIncreasingSequence([1,2,0,3,0,10])
assert not almostIncreasingSequence([1,2,0,3,10,0])
assert not almostIncreasingSequence([1,2,3,0,10,0])
assert not almostIncreasingSequence([10,1,2,3,10,0])
assert not almostIncreasingSequence([1, 3, 2, 1])
in response to the comments, here is an early stop version
def almostIncreasingSequence(sequence):
diff = 0
for a,b in pairwise(sequence):
if a>=b:
diff += 1
if diff >= 2:
return False
return diff < 2
and an accompanying test
assert not almostIncreasingSequence(itertools.chain([1, 3, 2, 1],range(10**100))) # use xrange with python 2
which will take ages with any of the other version
def almostIncreasingSequence(sequence):
for i in range(len(sequence)-1):
s_i = list(sequence)
# Remove the item by index
del s_i[i]
if order(s_i) == True:
return True
# Need to return false at the end if we never find a solution
return False
# You should have tested this function separately first.
# It did not work, so obviously almostIncreasingSequence wouldn't work.
def order(A):
n = len(A)
# Should have been n-1, not n, because you compare against n+1
# which doesn't exist when you hit the tail
for i in range(n - 1):
if A[i] > A[i + 1]:
# Need to actually return the value, not just set it to a variable
return False
# Only tell us it's in order if ALL the values don't fail
return True
print almostIncreasingSequence([1, 3, 2])

Recursively sum a list and compare it to a key in python

I need to write a recursive function sums_to(nums, k) that takes a list of integers and returns True if the sum of all the elements in the list is equal to k and returns False otherwise.
I can't use sum function in any form, nor sum the list and then at the end check whether it equals k. In addition, I have to write sums_to as a single recursive function.
So far I have this:
def sums_to(nums, k):
if nums == []:
if k == 0
return True
return 0
else:
return nums[0] + sums_to(nums[1:], k) == k
Your approach doesn't seem correct, in particular the recursive step isn't right - you have to decrease the expected k value until we reach the end of the list (and only test k at this point!), instead of adding/comparing at each point of the list. Also, the base case is missing something - what happens if k is not zero? you should return False, not 0. Better try this:
def sums_to(nums, k):
if nums == []:
return k == 0
else:
return sums_to(nums[1:], k - nums[0])
It works as expected:
sums_to([1, 2, 3, 4, 5], 14)
=> False
sums_to([1, 2, 3, 4, 5], 16)
=> False
sums_to([1, 2, 3, 4, 5], 15)
=> True

Categories