Changing a generator with another function - Python 3.x - python

I want to change the behavior of the generator below so that it only yields even numbers. How can I do this?
I'm aware that there simpler, clever ways to do this. This is a contrived HR challenge, where the
The change_generator function that I wrote does not yield the desired output. I can only change change_generator.
I cannot change positive_integers_generator() nor the for loop below.
Can I solve this with a decorator?
#can't change the body of this function
def positive_integers_generator():
n = 1
while True:
x = yield n
if x is not None:
n = x
else:
n += 1
# can only change this function
def change_generator(generator, n):
for i in generator:
if i%2 == 0:
yield(i)
# can't change this code either
# should print 1, 2, 4, 6, 8
g = positive_integers_generator()
for _ in range(5):
n = next(g)
print(n)
change_generator(g, n)

You can use the built in function filter
even_numbers_generator = filter(lambda n: n % 2 == 0, positive_integers_generator())
Or a generator expression.
even_numbers_generator = (n for n in positive_integers_generator() if n % 2 == 0)
Or itertools.count from the standard library:
even_numbers_generator = itertools.count(start=2, step=2)
But if you only can change the change_generator function, the "correct answer" to the challenge probably involves using generator.send()
# can only change this function
def change_generator(generator, n):
if n % 2 == 0:
generator.send(n + 1)

You don't need the parens on generator in your loop, and you don't seem to be printing the output of the right generator. Updated version that works for me:
def positive_integers_generator():
n = 1
while True:
x = yield n
if x is not None:
n = x
else:
n += 1
def change_generator(generator):
for i in generator:
if i%2 == 0:
yield i
g = positive_integers_generator()
# should print 1, 2, 4
for _ in range(5):
n = next(change_generator(g))
print(n)

In your very specific problem, if you can't change the print(n) part then you are pretty cornered because you can't change the instance of generator g that was created for positive_integers_generator().
In what may be a frowned upon answer, in this particular case you might want to update the global g to be reassigned to a new generator after that:
def change_generator(generator, n):
def even_gen():
n = 2
while True:
if n % 2 == 0:
yield n
else:
yield
n += 1
global g # directly change the g referenced in main code
if not g.__name__ == 'even_gen': # change g if it is not even_gen
g = even_gen()
# output:
# 1
# 2
# None
# 4
# None

Related

generator with conditions in python

which is taken from the predefined interval [a=10000, b=99999]
will fill the list (lst) with a random value, taking into account the following principle: on an even index
The sum of the standing digits is equal to the sum of the standing digits on the odd index
Here is a hit-or-miss generator that works well for numbers in your range:
import random
def even_odd_places(n):
evens = []
odds = []
for i,d in enumerate(str(n),start = 1):
if i % 2 == 0:
evens.append(int(d))
else:
odds.append(int(d))
return evens,odds
def check_condition(n):
e,o = even_odd_places(n)
return sum(e) == sum(o)
def rand_nums(a,b,max_tries = 1000):
tries = 0
while True:
tries += 1
n = random.randint(a,b)
if check_condition(n):
tries = 0
yield n
else:
if tries == max_tries:
return None
For example:
g = rand_nums(10000, 99999)
s = [next(g) for _ in range(10000)]
print(s[:5])
#typical output: [25641, 45980, 38225, 34320, 94380]
Something better than a hit-or-miss approach would require some nontrivial mathematical analysis. There is no easy way to generate all numbers in a given range which satisfy the target condition.

Write recursive pseudocode problem in python code

I am having trouble to solve the problem below:
I am supposed to implement the pseudocode in python. h is just some given list. I have tried all kinds of stuff, most recently for example:
def _p_recursion(n,i):
if n == 0:
return h[n+i]
for i in range(1,n+1):
s = 0
s = h[i] + _p_recursion(n-i,i)
v.append(s)
return s
v=[]
h =[0,3,11,56,4]
_p_recursion(2,0)
I know why it does not work but I am not able to come up with a solution. It feels like a pretty simple problem but I have been stuck with it for hours. I am not very experienced with recursive functions only really basic ones. I can't come up with one. Feels like the simplest way to come up with a solution must be to append all possible outputs of p(n) into an array and then one can easily find the maximum. For the values in the code above 11 is missing from the list v. Can anybody give me a hint how to fix this problem using python statements for, if, while...
Thanks in advance!
Code
def p(n):
" Implements function shown in image "
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p(n-i) for i in range(1, n+1))
More explicit recursive function
def p(n):
" Implements function shown in image "
if n == 0:
return 0
# Stores previous results in an array for formula
# then computes max
previous = []
for i in range(1, n+1):
previous.add(h[i] + p(n-i))
return max(previous)
Test
h = range(10)
for i in range(len(h)):
print(i, p(i))
Output
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
Performance
darrylg solution
def p_dg(n):
" Implements function shown in image "
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p_dg(n-i) for i in range(1, n+1))
Poster (Karl) solution
def p(n,m):
if n == 0:
return 0
for i in range(1,n+1):
s = h[i] + p(n-i,m)
m[n-1].append(s)
return max(m[n-1])
def p_caller(n):
if type(n) != int:
return
m =[]
for g in range(n):
m.append([])
return p(n,m)
darrylg solution with caching (memorization)
def p_cache(n, cache = {}):
if n in cache:
return cache[n]
if n == 0:
return 0
cache[n] = max(h[i] + p_cache(n-i) for i in range(1, n+1))
return cache[n]
Timing (seconds)
darrylg method
time taken: 0.20136669999965306
Poster method (Karl)
time taken: 26.77383000000009
darrylg with memoization
time taken: 0.00013790000002700253
Thus Caching greatly improves performance.
Timing Code
import time
import random
# timing software allows timing recursive functions
# Source: https://stackoverflow.com/questions/29560643/python-counting-executing-time-of-a-recursion-function-with-decorator
def profile(f):
is_evaluating = False
def g(x):
nonlocal is_evaluating
if is_evaluating:
return f(x)
else:
start_time = time.perf_counter()
is_evaluating = True
try:
value = f(x)
finally:
is_evaluating = False
end_time = time.perf_counter()
print('time taken: {time}'.format(time=end_time-start_time))
return
return g
# darrylg method
#profile
def p_dg(n):
" Implements function shown in image "
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p_dg(n-i) for i in range(1, n+1))
# Poster (Karl) method
def p(n,m):
if n == 0:
return 0
for i in range(1,n+1):
s = h[i] + p(n-i,m)
m[n-1].append(s)
return max(m[n-1])
#profile
def p_caller(n):
if type(n) != int:
return
m =[]
for g in range(n):
m.append([])
return p(n,m)
# darrylg with caching (Memoization)
#profile
def p_cache(n, cache = {}):
if n in cache:
return cache[n]
if n == 0:
return 0
cache[n] = max(h[i] + p_cache(n-i) for i in range(1, n+1))
return cache[n]
h = [random.randint(0, 100) for _ in range(18)]
l = 17
print('darrylg method')
p_dg(l)
print('Poster method (Karl)')
p_caller(l)
print('darrylg with memoization')
p_cache(l)
I was not able to comment my code in the previous post so I am writing it here instead:
(my Solution to problem)
def p(n,m):
if n == 0:
return 0
for i in range(1,n+1):
s = h[i] + p(n-i,m)
m[n-1].append(s)
return max(m[n-1])
def p_caller(n):
if type(n) != int:
return
m =[]
for g in range(n):
m.append([])
return p(n,m)
I would never actually call p() only p_caller()
DarrylG solution to problem:
def p(n):
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p(n-i) for i in range(1, n+1))
What would the difference in worst case time complexity be between these methods and why?

Trying to print the factorial of natural numbers using generators .Getting the output as 1 1 1 1 1 instead of 1 1 2 6 24 120 ......?

def my_gen():
number = 1
fact = 1
fact = fact * number //Trying to calculate factorial
yield fact
number = number + 1
a = my_gen()
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
Trying to print the output for the first five natural numbers.
Expected output: 1 1 2 6 24
obtained output: 1 1 1 1 1
How can i do this??.Any help is appreciated
If you want to write this as a generator, you might want to look up how that works. The generator somehow has to to repeatedly yield a value, your function just provides a single yield, i.e. there is no "next". A fix, working for arbitrarily sized factorials, could involve itertools.count like so:
from itertools import count
def factorial():
res = 1
for x in count(1):
res = x*res
yield res
This starts with the value 1, after each iteration multiplying with the next higher number, and yielding that result.
Should you want to get the first value, 1, twice, then insert another yield res before entering the for-loop.
This may help you to solve your problem.
def my_gen(num):
if num < 0:
return 0
elif num == 0:
return 1
else:
for i in range(1,num + 1):
factorial = factorial*i
return factorial
a = 1
print(my_gen(a))
a = a+1
print(my_gen(a))
a = a+1
print(my_gen(a))
a = a+1
print(my_gen(a))
a = a+1
print(my_gen(a))
Here is the tested code
from itertools import count
def factorial():
res = 1
for x in count(1):
yield res
res = x*res
fs = factorial()
for item in range(10):
print(next(fs))
def factorial(x):
start = 0
fact = 1
while start <= x:
yield fact
start = start + 1
fact = fact * start
fact = factorial(5)
print(next(fact))
print(next(fact))
print(next(fact))
print(next(fact))

Python: while loop inside else

def is_prime(x):
count = 1
my_list = []
while count > 0 and count < x:
if x % count == 0:
my_list.append(x/count)
count += 1
return my_list
my_list = is_prime(18)
def prime(x):
my_list2 = []
for number in my_list:
if number <= 2:
my_list2.append(number)
else:
count = 2
while count < number:
if number % count == 0:
break
else:
my_list2.append(number)
count += 1
return my_list2
print prime(18)
Just started out with Python. I have a very simple question.
This prints: [9, 3, 2].
Can someone please tell me why the loop inside my else stops at count = 2? In other words, the loop inside my loop doesn't seem to loop. If I can get my loop to work, hopefully this should print [2, 3]. Any insight is appreciated!
Assuming that my_list2 (not a very nice name for a list) is supposed to contain only the primes from my_list, you need to change your logic a little bit. At the moment, 9 is being added to the list because 9 % 2 != 0. Then 9 % 3 is tested and the loop breaks but 9 has already been added to the list.
You need to ensure that each number has no factors before adding it to the list.
There are much neater ways to do this but they involve things that you may potentially find confusing if you're new to python. This way is pretty close to your original attempt. Note that I've changed your variable names! I have also made use of the x that you are passing to get_prime_factors (in your question you were passing it to the function but not using it). Instead of using the global my_list I have called the function get_factors from within get_prime_factors. Alternatively you could pass in a list - I have shown the changes this would require in comments.
def get_factors(x):
count = 1
my_list = []
while count > 0 and count < x:
if x % count == 0:
my_list.append(x/count)
count += 1
return my_list
# Passing in the number # Passing in a list instead
def get_prime_factors(x): # get_prime_factors(factors):
prime_factors = []
for number in get_factors(x): # for number in factors:
if number <= 2:
prime_factors.append(number)
else:
count = 2
prime = True
while count < number:
if number % count == 0:
prime = False
count += 1
if prime:
prime_factors.append(number)
return prime_factors
print get_prime_factors(18)
output:
[3, 2]
Just to give you a taste of some of the more advanced ways you could go about doing this, get_prime_factors could be reduced to something like this:
def get_prime_factors(x):
prime_factors = []
for n in get_factors(x):
if n <= 2 or all(n % count != 0 for count in xrange(2, n)):
prime_factors.append(n)
return prime_factors
all is a built-in function which would be very useful here. It returns true if everything it iterates through is true. xrange (range on python 3) allows you to iterate through a list of values without manually specifying a counter. You could go further than this too:
def get_prime_factors(x):
return [n for n in get_factors(x) if n <= 2 or all(n % c != 0 for c in xrange(2, n))]

Python summing itertools.count?

I'm having some trouble with the itertools.count function, and I don't quite understand what it does. I expect the code below to accomplish Project Euler problem 2.
I know that I could write this with a simple while loop, but is there a way to do it with a list comprehension? This code just freezes as I guess it's going to go infinity with count(). I would have hoped it would stop after x > MAX, but I know that won't happen. Is there a way to stop count in a generator expression like below?
def fib(n):
if (n <= 1): return 1
else: return fib(n-1) + fib(n-2)
MAX = 4000000
infiniteFib = (fib(x) for x in count())
s = (x for x in infiniteFib if x < MAX and x % 2 == 0)
print sum(s)
You could use takewhile:
>>> from itertools import count, takewhile, imap
>>> sum(x for x in takewhile(lambda x: x < 4000000, imap(fib, count())) if x % 2 == 0)
4613732
We just need to tell the infiniteFib generator when to stop yielding elements. itertools provides a number of useful methods to help with this:
less_than_max = itertools.takewhile(lambda x: x<MAX, infiniteFib))
even = itertools.ifilter(lambda x: x%2==0, less_than_max)
print sum(even)
We get a generator for all the numbers yielded by infiniteFib, until one returned is greater than MAX. Then we filter that generator, choosing only the even numbers. And finally we can sum the result.
How about:
def fib():
a, b = 1, 1
while True:
yield b
a, b = b, a+b
sum(f for f in itertools.takewhile(functools.partial(operator.ge, 4000000), fib()) if f % 2 == 0)
Or, pushing the parity check into the generator:
def even_fib():
a, b = 1, 1
while True:
if b % 2 == 0: yield b
a, b = b, a+b
sum(itertools.takewhile(functools.partial(operator.ge, 4000000), even_fib()))
Yeah, count() just keeps going, which isn't what you want. List comprehensions / iterator expressions don't have flexible exit conditions (but see #DSM's solution using takewhile).
I prefer just using while.
Here's my old answer to Euler 2:
def SumEvenFibonacci(limit):
x = y = 1
sum = 0
while (sum <= limit):
sum += (x + y)
x, y = x + 2 * y, 2 * x + 3 * y
return sum
ce = SumEvenFibonacci(4000000)
print ce
Here's another solution using takewhile, but non-recursively. Since the recursive solution requires calculating all the fibs less than n for each n, it's horrible slow.
def fib_gen(only_even=False):
one = 1
if not only_even:
yield one
two = 1
if not only_even:
yield two
while True:
next = one + two
one = two
two = next
if only_even:
if next % 2 == 0:
yield next
else:
yield next
list(itertools.takewhile(lambda x: x < 4000000, fib_gen()))

Categories