How can i know if a list is decreasing? (Python) - python

I am new to python and I have to do an exercise for classes. The exercises asks me to make a function which tells weather a list given is ordered decreasing or not (Giving back True or False)
I tried the following code:
def no_decreasing(list):
for num in len(list):
if list[num] <= list[num+1]:
check = bool(1)
else:
check = bool(0)
break
return check
It gives back an te error "int" object is not iterable in line 2, does anyone know why?

Note: don't use list as the parameter name (it's a builtin type), use something else. I'll use nums as the place of the list parameter rather than list.
The expression for num in len(nums) doesn't work because len(nums) is a single int. What you would want instead is for num in nums (which would iterate over each number in nums, or for index in len(range(nums)) (which would iterate over each valid index into nums).
Other options:
for i, num in enumerate(nums) -- i is the index, num is the value.
for num1, num2 in zip(nums, nums[1:]) -- num1 and num2 are two successive values from nums, obtained by zipping nums with a shifted version of itself.
Additional note: when you need a boolean literal, instead of bool(1) and bool(0) just use True and False!
You could also shortcut the entire problem by sorting the list in decreasing order and seeing if it's the same as the original list:
def is_decreasing(nums):
return nums == sorted(nums, reverse=True)

Well you are trying to iterate over indeces, so
for i in range(len(lst)): # never use "list" as a variable name
or rather
for i in range(len(lst)-1): # to avoid an index error for its right neighbor
would be appropriate. However, a better way would use zip
def non_decreasing(lst):
for a, b in zip(lst, lst[1:]):
if b < a:
return False
return True
A short-hand for that pattern is any or all:
def non_decreasing(lst):
return all(a <= b for a, b in zip(lst, lst[1:]))
# return not any(b < a for a, b in zip(lst, lst[1:]))

You are trying to get the index in the for loop, but you've made a semantic mistake:
for num in len(list):
# This does not work. It can not iterate through an integer.
len() function returns an integer. Your basically saying for num in 10, say if the list has 10 numbers.
What you want is the range function:
for num in range(0, len(list)):
This will loop from num=0 to num=0+len(list)-1.
Be careful though with if list[num] <= list[num+1]:, as the previous approach will make that line search for an index greater them your array size. As such, this is how you could fix your code:
for num in range(0, len(list)-1):
P.S.: There are other ways to solve that issue, but since it is a class exercise, I've focused on solving the issue you've had when iterating through an integer.

Others have pointed out using zip(lst, lst[1:]). This is undesirable for large lists, though, since you first have to make a copy of lst (minus the first element) before zip can produce the pairwise iterator that the for loop uses.
Instead, use two separate iterators, advancing the second one before passing it to zip.
def no_decreasing(lst):
i1 = iter(lst)
i2 = iter(lst)
next(i2)
return all(a >= b for a, b in zip(i1, i2))
# Or you can use map
# return all(map(operator.ge, i1, i2))

Related

Code optimisation ideas

I wrote a code in Python that print a Fibonacci sequence truncated at a given threshold.
m_char=input('threshold: ')
m=int(m_char)
def fibonacci(m):
lst=[0, 1]
while lst[-1] <= m:
a = lst[-2]+lst[-1]
if a <= m:
lst.append(a)
else:
print(lst)
return
fibonacci(m)
I don't like the double check on the variable m in the while and if statement: I'm pretty sure it is redundant, so there is a way to write more efficient code. I would like to preserve the use of lists. Have you got any idea?
def fibonacci(m):
lst=[0, 1]
a = lst[-2]+lst[-1]
while a <= m:
lst.append(a)
a = lst[-2]+lst[-1]
return lst
You can calculate a once per loop, and use it to determine the whether the loop continues
Just use
while True:
it is the check inside the loop which actually determines how often the loop is run.
It would be slightly more efficient to not use list indexing at all but instead maintain the last two Fibonacci numbers with two variables. Furthermore, it is more idiomatic to return the list rather than print it. Let the calling code print the list if it wants:
def fibonacci(m):
lst=[0, 1]
a,b = lst
while True:
a,b = b, a+b
if b <= m:
lst.append(b)
else:
return lst

How to check if N can be expressed as sum of two other numbers in specific list

I have a list:
l = [1,3,4,6,7,8,9,11,13,...]
and a number n.
How do I efficiently check if the number n can be expressed as the sum of two numbers (repeats are allowed) within the list l.
If the number is in the list, it does not count unless it can be expressed as two numbers (e.g for l = [2,3,4] 3 would not count, but 4 would.
This, embarrassingly, is what I've tried:
def is_sum_of_2num_inlist(n, num_list):
num_list = filter(lambda x: x < n, num_list)
for num1 in num_list:
for num2 in num_list:
if num1+num2 == n:
return True
return False
Thanks
def summable(n, l):
for v in l:
l_no_v = l[:]
l_no_v.remove(v)
if n - v in l_no_v:
return True
return False
EDIT: Explanation...
The itertools.cominations is a nice way to get all possible answers, but it's ~4x slower than this version since this is a single loop that bails out once it gets to a possible solution.
This loops over the values in l, makes a copy of l removing v so that we don't add v to itself (i.e. no false positive if n = 4; l = [2, 1]). Then subtract v from n and if that value is in l then there are two numbers that sum up to n. If you want to return those numbers instead of returning True just return n, n - v.
Although you can check this by running through the list twice, I would recommend for performance converting the list to a set, since x in set() searches in linear time.
Since n can be the sum of the same number, all you have to do is iterate through the set once and check if n - i occurs elsewhere in the set.
Something like the following should work.
>>> def is_sum_of_numbers(n, numbers):
... for i in numbers:
... if n - i in numbers:
... return True
... return False
...
>>>
>>>
>>> numbers = {2,7,8,9}
>>> is_sum_of_numbers(9, numbers) # 2 + 7
True
>>> is_sum_of_numbers(5, numbers)
False
>>> is_sum_of_numbers(18, numbers) # 9 + 9
True
If the list is ordered you could use two variables to go through the list, one starting at the beginning of the list and one at the end, if the sum of the two variables is greater than N you assign to the variable at the end the values that precedes it, if the sum is less than N you assign to the variable at the beginning the following value in the list. If the sum is N you've found the two values. You can stop when the two variables meet eachother.
If the list is not ordered you start from the beginning of the list and use a variable x to go through the list. You'll need another structure like an hashset or another structure. At every step you'll look up in the second hashset if the value N-x is in there. If there is, you've found the two numbers that add up to N. If there isn't you'll add N-x in the hashset and assign to x the following value. I recommend using an hashset because both the operations of looking up and inserting are O(1).
Both algorithms are linear
I'm sorry I couldn't write directly the code in python because I don't use it.
As I said in the comment HERE there's a video in wich your problem is solved
If I got the OP's concern then-
As the question says repeats are allowed within the list l this process i think is good though a bit slower.So if you need to count the occurances along with the existence of a condition then go with this answer but if you want a bolean esixtence check the go with the others for the mere performance issue nothing else.
You can use itertools.combinations. It will give you all the combinations, not permutations. Now you can just use the sum function to get the sum.
from itertools import combinations
l = [1,3,4,6,7,8,9,11,13]
checks = [4,6] #these are the numbers to check
for chk in checks:
for sm in combinations(l,2):
if chk == sum(sm): #sum(sm) means sum(1,3) for the first pass of the loop
#Do something

IndexError: "pop index out of range" with a for loop

How can I resolve this IndexError? I tried by using a while loop, but nothing changed.
Here is my code, it should check the length of the object of two lists (la, lb) and remove the string from la if the string is shorter than the lb string and vice versa. Plus it has to remove both of the strings if their length is the same.
def change(l1, l2):
la1 = l1[:]
la2 = l2[:]
i = 0
for i in range(len(la1)):
if la1[i] == la2[i]:
l1.pop(i)
l2.pop(i)
elif la1[i] > la2[i]:
l2.pop(i)
elif la2[i] > la1[i]:
l1.pop(i)
Assuming your lists are of equal lengths
As has been pointed out in the comments, the IndexError happens due to your lists' length changing when you pop() an item.
Since you're iterating over your list using a range(len(l)) in a for loop, which isn't updated after every completed loop, you'll eventually hit an index that's out of range.
An example, which you can try easily enough yourself:
l = [1,2,3,4,5,6,7,8,9,10]
for i in range(len(l)):
l.pop(i)
print("Length of list", len(l))
Do not confuse yourself by calling print(range(len(l)) in the for loop - this will give you an updated range, but is misleading. The range in the for loop is only called once, hence never updates while iterating.
A different approach
Instead of working with indices, try using zip() and building new lists, instead of changing existing ones.
def change(l1, l2):
new_l1 = []
new_l2 = []
for a, b in zip(l1, l2):
if len(a) == len(b):
continue # do nothing
elif len(a)<len(b):
new_l2.append(b)
elif len(a)>len(b):
new_l1.append(a)
return new_l1, new_l2
This approach, essentially, generates the same list you create using pop(), while avoiding usage of indices.
Note that zip() will stop once it reaches the end of the smaller of both iterables. If your lists may not be of equal length, and you'd like to iterate until the longest of both iterables is iterated over entirely, use zip_longest(). But I do not think this is what you need in this case.
Additional Notes
You would also run into a problem if you were to iterate over your list using the following code:
l = [i for i in range(10)]
for item in l:
l.remove(item)
>>>[1, 3, 5, 7, 9]
Essentially, it's not advisable to iterate over any iterable while changing it. This can result in anything from an Exception being thrown, to silent unexpected behaviour.
I'm aware you were avoiding this by looping over the copies, I just wanted to add this for posterity.
You can traverse the lists backwards, so that when you remove an item from the list the indices of the elements that you have not examined yet won't be affected
def f(a, b):
l = len(a) if len(a)<len(b) else len(b)
for i in range(l):
j = l-i-1
la, lb = len(a[j]), len(b[j])
if la<lb: a.pop(j)
elif lb<la: b.pop(j)
else: a.pop(j), b.pop(j)
return a, b
ps I staid faithful to your problem statement and not to your implementation re the comparison based on strings' lengths.
if you want to iterate over a list and want to empty it
but don't want pop index error use this:
lst = [ 1, 4, 56, 2, 4 , 12, 6, 89 ,11, 0]
i =0
while len(lst) != 0:
lst.pop(0)
i+=1
print(lst)

Test if all N variables are different

I want to make a condition where all selected variables are not equal.
My solution so far is to compare every pair which doesn't scale well:
if A!=B and A!=C and B!=C:
I want to do the same check for multiple variables, say five or more, and it gets quite confusing with that many. What can I do to make it simpler?
Create a set and check whether the number of elements in the set is the same as the number of variables in the list that you passed into it:
>>> variables = [a, b, c, d, e]
>>> if len(set(variables)) == len(variables):
... print("All variables are different")
A set doesn't have duplicate elements so if you create a set and it has the same number of elements as the number of elements in the original list then you know all elements are different from each other.
If you can hash your variables (and, uh, your variables have a meaningful __hash__), use a set.
def check_all_unique(li):
unique = set()
for i in li:
if i in unique: return False #hey I've seen you before...
unique.add(i)
return True #nope, saw no one twice.
O(n) worst case. (And yes, I'm aware that you can also len(li) == len(set(li)), but this variant returns early if a match is found)
If you can't hash your values (for whatever reason) but can meaningfully compare them:
def check_all_unique(li):
li.sort()
for i in range(1,len(li)):
if li[i-1] == li[i]: return False
return True
O(nlogn), because sorting. Basically, sort everything, and compare pairwise. If two things are equal, they should have sorted next to each other. (If, for some reason, your __cmp__ doesn't sort things that are the same next to each other, 1. wut and 2. please continue to the next method.)
And if ne is the only operator you have....
import operator
import itertools
li = #a list containing all the variables I must check
if all(operator.ne(*i) for i in itertools.combinations(li,2)):
#do something
I'm basically using itertools.combinations to pair off all the variables, and then using operator.ne to check for not-equalness. This has a worst-case time complexity of O(n^2), although it should still short-circuit (because generators, and all is lazy). If you are absolutely sure that ne and eq are opposites, you can use operator.eq and any instead.
Addendum: Vincent wrote a much more readable version of the itertools variant that looks like
import itertools
lst = #a list containing all the variables I must check
if all(a!=b for a,b in itertools.combinations(lst,2)):
#do something
Addendum 2: Uh, for sufficiently large datasets, the sorting variant should possibly use heapq. Still would be O(nlogn) worst case, but O(n) best case. It'd be something like
import heapq
def check_all_unique(li):
heapq.heapify(li) #O(n), compared to sorting's O(nlogn)
prev = heapq.heappop(li)
for _ in range(len(li)): #O(n)
current = heapq.heappop(li) #O(logn)
if current == prev: return False
prev = current
return True
Put the values into a container type. Then just loop trough the container, comparing each value. It would take about O(n^2).
pseudo code:
a[0] = A; a[1] = B ... a[n];
for i = 0 to n do
for j = i + 1 to n do
if a[i] == a[j]
condition failed
You can enumerate a list and check that all values are the first occurrence of that value in the list:
a = [5, 15, 20, 65, 48]
if all(a.index(v) == i for i, v in enumerate(a)):
print "all elements are unique"
This allows for short-circuiting once the first duplicate is detected due to the behaviour of Python's all() function.
Or equivalently, enumerate a list and check if there are any values which are not the first occurrence of that value in the list:
a = [5, 15, 20, 65, 48]
if not any(a.index(v) != i for i, v in enumerate(a)):
print "all elements are unique"

Python Recursion List Sum of Pairs

I am supposed to write two functions that do the exact same thing but their implementation is different.
The function takes as input a list of positive integers and a positive integer n, and returns True if two of the numbers in list equal to n. Otherwise, it returns False.
The first function is supposed to use a nested a loop, which I was able to get.
The second functions is not supposed to use a nested loop. However, you are supposed to sort the list out and then solve the problem.
Here is what I have for the second function.
def pairs2(lst, n):
lst.sort()
if len(lst) == 2:
if lst[0] + lst[1] == n:
return True
else:
return False
elif len(lst) >= 3:
for i in range(len(lst) - 1):
if lst[0] + lst[i + 1] == n:
return True
lst.remove(lst[0])
pairs2(lst, n)
The function works until the last two lines are implemented. After that, it doesn't return anything. What is wrong with my function?
Also, are they any other alternatives to that I do not use recursion? I just came up with using recursion since it was the first idea that I got.
A recursive algorithm that eliminates the largest number at each recursive step:
def pairs2(lst, n, s=False):
if len(lst) < 2: return False
if not s: lst = sorted(lst)
for item in lst:
if item + lst[-1] > n:
return pairs2(lst[:-1], n, True)
if item + lst[-1] == n:
print item, lst[-1]
return True
return False
The s parameter indicates whether the list is already sorted or not.
def pairs2(lst, n):
[pair for pair in itertools.combinations(lst,2) if sum(pair) == n]
Instead of using recursion, you could use the brute-force approach to find the pairs using the itertools.combinations.
Read more about itertools: https://docs.python.org/2/library/itertools.html

Categories

Resources