Why does this generator not work in python - python

Why does if not work in the below generator
def mygen(m):
n = 0
if n < m:
n = n + 1
yield n
counter = mygen(5)
next(counter)
1
next(counter)
StopIteration
but while does?
def mygen(m):
n = 0
while n < m:
n = n + 1
yield n

The while loop compares n and m repeatedly (until the condition is false) whereas the if statement compares them once and then finishes. The if statement is working, just not in the way you are expecting.

Related

Is there a more efficient way to compare two lists in python than O(m*n)?

I am trying to find a method for comparing two lists in python in a more efficient way than what I think is the current O(m*n) runtime. Right now I have a brute force approach of iterating each item in m and comparing it to n but is anything else possible? I have tried maybe sorting the lists first for maybe something faster but I am kind of stuck on whether anything else could work here.
In my function i take each item in m and compare it to n and count the number of times the item in m is greater than the item in n.
n = [1,3,7]
m = [2,9]
def comparison(n,m):
counter = 0
for i in m:
for j in n:
if i >= j:
counter += 1
return counter
Here's how you could use a binary search approach after sorting the target list:
from bisect import bisect_right
n = [1,3,7,2]
m = [2,9]
n.sort()
counter = sum(bisect_right(n,value) for value in m)
print(counter) # 6
This should correspond to O((n+m) x log(n)) if n is not known to be sorted. If n is always provided in sorted order, then you don't need your function to sort it and you will get O(m x log(n)) time complexity.
I wrote a code for you to test which one runs faster using the built-in "timeit" library. You can test others' advice using the same structure. There is the code:
import timeit
import numpy as np
n = [1,3,7]
m = [9,2]
my_code = '''
def comparison(n,m):
counter = 0
for i in n:
for j in m:
if i >= j:
counter += 1
return counter
'''
mysetup = "import numpy as np"
my_code2 = '''
def comparison_with_numpy(n,m):
x = np.array(n)
y = np.array(m)
smaller = np.array([x[i] > y[:] for i in range(x.shape[0])]).astype('int')
return sum(smaller)[0]
'''
my_code3 = '''
def sort_first(n,m):
sorted(n)
sorted(m)
count = 0
if len(n) > len(m):
iteration = len(n)
else:
iteration = len(m)
for _ in range(iteration):
if n != []:
y = n.pop(0)
if m != []:
x = m.pop(0)
if y > x:
count += 1
return count
'''
def comparison(n,m):
counter = 0
for i in n:
for j in m:
if i >= j:
counter += 1
print(counter)
return counter
def comparison_with_numpy(n,m):
x = np.array(n)
y = np.array(m)
smaller = np.array([x[i] > y[:] for i in range(x.shape[0])]).astype('int')
return sum(smaller)[0]
def sort_first(n,m):
sorted(n)
sorted(m)
count = 0
if len(n) > len(m):
iteration = len(n)
else:
iteration = len(m)
for _ in range(iteration):
if n != []:
y = n.pop(0)
if m != []:
x = m.pop(0)
if y > x:
count += 1
return count
def main():
print('comparison /w sort\t\t',timeit.timeit(stmt = my_code3,number=10000))
print('comparison\t\t',timeit.timeit(stmt = my_code,number=10000))
print('comparison with numpy\t\t',timeit.timeit(setup = mysetup
,stmt = my_code2
,number=10000))
if __name__ == "__main__":
main()

Python number in range

I can't seem to understand why the function fix_teen returns 13 and not 0.
As we have r in the range of 13-19, we check if n in r, it even returns "True" when outside the if/else function.
I also tried it this way if 13 <= n <= 19: and the issue still persists.
def fix_teen(n):
r = range(13,19)
if n in r :
return n == 0
elif n == 15 or n ==16:
return n
else :
return n
def no_teen_sum(a, b, c):
print(fix_teen(a))
print(fix_teen(b))
print(fix_teen(c))
print(a+b+c)
no_teen_sum(2, 13, 1)
The range() function returns a sequence of numbers, starting from 0 by default, and increments by 1 (by default), and stops before the specified number.
range(start, stop)
for example to create a sequence of numbers from 0 to 5, and print each item in the sequence:
x = range(6)
for n in x:
print(n)
so r = range(13,19) has to be replaced with r = range(13,20)
The bit of code below, returns the result of an equality operator (n == 0).
if n in r :
return n == 0
If you want to return 0 if the n is in the range, simply return 0:
if n in r:
return 0

Modifying a generator using send

I want to modify a generator by only changing the manipulate_generator function below. This is a HR challenge that has had me scratching my head throughout the day. I got the idea of using send from a previous SO question on even numbers. I cannot get to work using a primality checker. The algorithm should print the first 10 non-primes. It's currently printing 3.
from math import sqrt
from itertools import count, islice
def is_prime(n):
if n < 2:
return False
for number in islice(count(2), int(sqrt(n) - 1)):
if n % number == 0:
return False
return True
# this is the only portion of the code that can be changed
def manipulate_generator(generator, n):
if not is_prime(n):
generator.send(n+1)
#all the code below cannot be changed
def positive_integers_generator():
n = 1
while True:
x = yield n
if x is not None:
n = x
else:
n += 1
k = int(input())
g = positive_integers_generator()
for _ in range(k):
n = next(g)
print(n)
manipulate_generator(g, n)

Print the collatz sequence of a positive int, n, one value per line stoping at 1 using python

I've done:
def collatz(n):
seq = n
if n == 1:
n = n
while n > 1:
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
print(seq)
The corrct output for calling this function, while n = 10:
collatz(10)
10
5
16
8
4
2
1
But the only number printed is n itself.
The issue is that you are only printing seq which was set to n at start of the function, after the while loop has executed. Hence you only get the value printed once.
You should print the value inside the while loop as well as at start (for the first 10 print). Example -
def collatz(n):
print(n)
while n > 1:
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
print(n)
Demo -
>>> def collatz(n):
... print(n)
... while n > 1:
... if n % 2 == 0:
... n = n // 2
... else:
... n = 3 * n + 1
... print(n)
...
>>> collatz(10)
10
5
16
8
4
2
1
You need to print once for every step within your loop. Your print statement is outside your while loop hence it only fires once.
Additionally, you want to print the value that is changing n not seq which never chances in this function.
On that note, you don't even need seq as you never use it!
The two lines if n == 1: n = n don't do anything. Even if n==1, setting n to itself doesn't change the value.

Can I pick a specific item from generator?

With:
def merge(a, b):
i = j = 0
total = len(a) + len(b)
while i + j < total:
if j == len(b) or (i < len(a) and a[i] <= b[j]):
yield a[i]
i += 1
else:
yield b[j]
j += 1
Can I just pick the third element from the generator or I have to iterate by next() three times?
You can use itertools.islice in combination with next. However this will consume steps in your generator so it's effectively the same as calling next three times and picking up the third value. It's just a more abstract way of doing it.
>>> from itertools import islice
>>> g = (i for i in range(10))
>>> next(islice(g, 2, 2 + 1))
2
>>> next(g)
3

Categories

Resources