I'm studying the theme of generators in python and can't deal with one task.
The point is this: you need to implement a generator that takes 2 non-decline sequences, combines it into 1 non-decline and returns it.
I understood how to write it with a function, but I don't know how to implement it through "yield".
Here's the code for my function:
def merge_lists(lst1, lst2):
res = []
i1, i2 = 0, 0
while i1 < len(lst1) and i2 < len(lst2):
el1, el2 = lst1[i1], lst2[i2]
res.append(el1)
i1 += 1
res.append(el2)
i2 += 1
res.extend(lst1[i1:])
res.extend(lst2[i2:])
return res
I will be glad to get help in writing code and explaining the solution.
Code / Algorithm:
def merge_lists(lst1, lst2):
i = j = 0
while i < len(lst1) and j < len(lst2):
if lst1[i] <= lst2[j]:
yield lst1[i]
i += 1
else:
yield lst2[j]
j += 1
while i < len(lst1):
yield lst1[i]
i += 1
while j < len(lst2):
yield lst2[j]
j += 1
Explanation:
We first initialize variables i, j to zero which represent the indices within the lists lst1 and lst2 respectively.
In the first while loop we consequently checks the smaller element amongst lst1 and lst2 eg. if the smaller one is lst1[i] then we yield the element lst1[i] and increment the index i otherwise we yield the element lst2[j] and increment the index j and rest of the loop is self explanatory.
In the other while loops we check if there are any remaining elements inside lst1 and lst2, and if there are then we yield those elements also.
Example:
lst1 = list(range(0, 10)) # lst1 is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
lst2 = list(range(10, 21)) # lst2 is [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
for num in merge_lists(lst1, lst2): # -- loop through the generator
print(num, end=" ") #--> use the results on demand
Result:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # --> combined non declining sequence
import itertools
itertools.chain.from_iterable([range(10), range(20, 30)])
# Should get your work done.
# Or, in your case
itertools.chain.from_iterable([lst1, lst2])
Please visit https://docs.python.org/3/library/itertools.html for more useful generators.
a = [9,7,5,3]
b = [8,6,4,2]
def merger(l1,l2):
i1=0
i2=0
while i1<len(l1) and i2<len(l2):
if l1[i1]>l2[i2]:
yield l1[i1]
i1+=1
else:
yield l2[i2]
i2+=1
while i1<len(l1):
yield l1[i1]
i1+=1
while i2<len(l2):
yield l2[i2]
i2+=1
for x in merger(a,b):
print(x)
Explanation: merger() is a generator function. Hence every value of x obtained on looping over merger() will return a value. It must be understood that in a generator, the function starts from the same point from which it was left in the last call, hence i1=0,i2=0 would be executed just once.
Related
hello I am struggling with this problem for school and can't get my code to do what it needs to solve this. The question is: Define an element of a list of items to be a dominator if every element to its right (not just the one
element that is immediately to its right) is strictly smaller than that element. It wants me to count how many denominators are in the list.
def extract_increasing(digits):
countDem = 0
#check and see if there is anything in the list
if not digits:
return 0
#compare the first element to the one on the right of it
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
return countDem
The code below should check if a number in the list is a dominator.
def is_dominator(lst, idx):
for i in range(idx + 1, len(lst)):
if lst[i] >= lst[idx]:
return False
return True
digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in digits:
print(is_dominator(digits, i))
The error in your code is that you're adding one for the counter every time the next value meets the condition.
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
Every time digits[x] > digits[y] is met you add one to your counter. You should only add one once you checked that all values to the right meet the condition.
isDem = False
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
isDem = True
else:
isDem = False
#Once you went through all the values to the right you can add one to the counter
if isDem ==True:
countDem += 1
Hope that helps!
You start in the last element, and save always the max_element in every iteration, then you know always if exist some number grater than the current number. This is a little more efficient because it runs through the array only once.
def dominator(li: list):
sol = 0
max_number = -math.inf
for i in range(len(li)-1, -1,-1):
if li[i] > max_number:
sol+=1
max_number = li[i]
return sol
Try list comprehension
lst = [0, 10, 2, 6, 7]
new_lst = [v for k,v in enumerate(lst) if all(v > x for x in lst[k+1:])]
# [10, 7]
Update
def extract_increasing(digits: list) -> int:
countDem = 0
for x, y in enumerate(digits):
if all(y > a for a in digits[x+1:]):
countDem += 1
return countDem
lst = [0, 10, 2, 6, 7]
extract_increasing(lst) # -> 2
I'm trying to create a code which deletes the odd numbers in a user-defined range (for example, between 4 and 10). So far I have this:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
for i in s:
if s[i]%2!=0:
del s[i]
return s
even(4,10)
When I run the code, it returns [4, 5, 6, 7, 8, 10] instead of [4, 6, 8, 10]. Any idea why?
It makes little sense to create a larger collection and then remove the items you don't want.
I suspect it would be better if you just create the list with what you want up front:
def even(lo, hi):
if lo > hi: return None # although [] may make more sense
return [item for item in range(lo, hi + 1) if item % 2 == 0]
The reason why I state that it may be better to return [] for the case of lo > hi is because that's what gets returned for other edge cases, such as even(3,3).
This gives what you desire, as per the following transcript:
>>> def even(lo, hi):
... if lo > hi: return None
... return [item for item in range(lo, hi + 1) if item % 2 == 0]
...
>>> even(4, 10)
[4, 6, 8, 10]
There are 3 things wrong with your code.
Using s[i] accesses the ith item of the list, but i is already holding the list item because you did for i in s::
>>> s = list(range(4, 11))
>>> s
[4, 5, 6, 7, 8, 9, 10]
>>> for i in s:
... print(i)
...
4
5
6
7
8
9
10
What your loop is actually checking with s[i] is this:
>>> for i in s:
... print(s[i])
...
8 # i=4, s[4]
9 # i=5, s[5]
10 # i=6, s[6]
When you do find an odd number (s[5]=9, 9%2 != 0), you immediately break out of the loop because of the return s. So, your loop will only remove the first odd number it finds, then immediately break out of the loop.
Maybe it's just wrongly indented, but the return s should be at the end of the function, not inside the loop.
You are removing items from the list while you are iterating over it. That is never a good idea, because that will mess up the loop.
>>> s = list(range(4, 11))
>>> for idx, i in enumerate(s):
... print(i)
... if i%2 != 0:
... print("removing i")
... del s[idx]
...
4
5
removing i
7 # notice that 6 was now skipped after removing 5
removing i
9 # notice that 8 was now skipped after removing 7
removing i
With that said, the correct way is to iterate over the input list but the result/output should be on a different list. That way, the loop does not get messed up. The simplest (and most "pythonic") way is by using list comprehension:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
return [d for d in s if d % 2 == 0]
Or, you can manually loop using while and then track the correct list index:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
idx = 0
while idx < len(s):
if s[idx]%2!=0:
del s[idx]
# after this, s[idx] now points to the next item
else:
idx += 1
# move to the next item
return s
This is the right code. you can remove from list by items and del by index in python:
def even(x,y):
if x > y:
return None
else:
s = list(range(x, y+1))
for i in s:
if i%2 != 0:
s.remove(i)
return s
even(4,10)
I am having trouble finding information on using sum to take from a list. I know how to use sum with range, for example:
sum = 0
for i in range(50):
sum=sum + i
print (sum)
But I can't get my code to work when I am using a list such as [1, 2, 6, 7, 8, 10] and taking the even numbers using sum. Can anyone point me in the right direction?
You can filter out odd-values:
def is_even(x):
# if the remainder (modulo) is 0 then it's evenly divisible by 2 => even
return x % 2 == 0
def sum_of_evens(it):
return sum(filter(is_even, it))
>>> sum_of_evens([1,2,3,4,5])
6
Or if you prefer a conditional generator expression:
>>> lst = [1,2,3,4,5]
>>> sum(item for item in lst if item % 2 == 0)
6
Or the explicit (long) approach:
lst = [1,2,3,4,5]
sum_ = 0
for item in lst:
if item % 2 == 0:
sum_ += item
print(sum_) # 6
I need to add a counter of total comparisons for my Insertion Sort program but I don't know why I'm getting a total of 0 comparisons!
I know that the comparisons output should be 15 (for my specific array) not 0.
This is my code so far:
def insertionSort(values):
k = 0
n = len(values) - 1
comparisons = 0
while k+1 <= n:
i = k
while values[i] > values[i+1]:
temp = values[i]
values[i] = values[i+1]
values[i+1] = temp
comparisons += 1 #I think this is wrong
k = k + 1
return comparisons, values
What am I doing wrong?
I just checked your code and its not serving the purpose for sorting [7,5,4,6].
Here's a modified version -
def insertionSort_mod(values):
k = 0
n = len(values) - 1
comparisons = 0
while k+1 <= n:
i = k+1
curr_val = values[i]
comparisons += 1
while i>0 and values[i-1] > curr_val:
values[i] = values[i-1]
i=i-1
comparisons += 1
values[i] = curr_val
k = k + 1
return comparisons, values
print insertionSort_mod( [1, 2, 3, 55, 5, 6, 8, 7, 9, 111])
Outputs this:
(15, [1, 2, 3, 5, 6, 7, 8, 9, 55, 111])
In your context k+1 should be the current index so i should be k+1 and should be compared to the previous value i-1
Hope this works
def insertion(a,length):
count=0
for i in range(1,length):
key=a[i]
jj=i
while(jj>0 and a[jj-1]>key):
a[jj]=a[jj-1]
jj=jj-1
count += 1
a[jj]=key
print count
The no. of swaps would be equal to the number of elements for which you shift the elements using the while loop. So, you could use a flag variable inside the while loop, to check if the loop runs for each element, and increase the counter variable by 1, every time the flag variable shows swapping.
I want to write a function called find_integer_with_most_divisors that accepts a list of integers and returns the integer from the list that has the most divisors. In case of a tie, return the first item that has the most divisors.
For example:
if the list is:
[8, 12, 18, 6]
In this list, 8 has four divisors which are: [1,2,4,8] ; 12 has six divisors which are: [1,2,3,4,6,12]; 18 has six divisors which are: [1,2,3,6,9,18] ; and 6 has four divisors which are: [1,2,3,6]. Notice that both 12 and 18 are tied for maximum number of divisors (both have 6 divisors). My function should return the first item with maximum number of divisors; so it should return:
12
Now I wrote bellow code for find division of each number, that are in list. Can any body help me to continue this function.
Thanks.
def find_integer_with_most_divisors(input_list):
for i in input_list:
my_list = []
for x in range(1,i+1):
if i % x == 0:
my_list.append(i)
You can create a list of the number of divisors in your function and then match the highest number in that list to your original list:
def find_integer_with_most_divisors(input_list):
nr_divisors = []
for i in input_list:
my_list = []
for x in range(1, i+1):
if i % x == 0:
my_list.append(x)
nr_divisors.append(len(my_list))
return input_list[nr_divisors.index(max(nr_divisors))]
a = [8, 12, 18, 6]
print find_integer_with_most_divisors(a)
returns
12
Why not just use max with the number of divisors as key?
>>> max([8, 12, 18, 6], key=lambda n: sum(n%d == 0 for d in range(1, n+1)))
12
All you need to do is keep track of the number with the most so far. I also changed your creation of my_list to something a little simpler:
def find_integer_with_most_divisors(input_list):
winning_list = []
winning_number = None
for i in input_list:
my_list = [x for x in range(1, i + 1) if i % x == 0]
if len(my_list) > len(winning_list):
winning_list = my_list
winning_number = i
return winning_number
Yet another flavor of solution that uses sorted:
def find_integer_with_most_divisors(input_list):
def divisor_length(num):
length = 0
for i in range(1,num+1):
if num % i == 0:
length += 1
return length
return sorted(input_list, key=lambda x: divisor_length(x), reverse=True)[0]
print(find_integer_with_most_divisors([8,12,18,6]))
12