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()))
Related
I wanted to know if there is a way to find the smallest number in the list which is closest to a given number.
Foe e.x.
my_list=[1,10,15,20,45,56]
my_no=9
Output should be equal to 1
Explanation: since 9 comes between 1 and 10 and I want the smaller number, i.e. 1.
Similarly if my_no=3, output is equal to 1
I am new to python, so any help will be much appreciated. Thanks!
List comprehension is another elegant way to do this.
my_list=[1,10,15,20,45,56]
my_no = 9
output = max([x for x in my_list if x<=my_no])
print(output) #1
You can reduce from functools:
from functools import reduce
i = reduce(lambda l, r: l if l <= my_no <= r else r, my_list)
print(i)
# Output
1
Test
# my_no = 18
>>> reduce(lambda l, r: l if l <= my_no <= r else r, my_list)
15
# my_no = 59
>>> reduce(lambda l, r: l if l <= my_no <= r else r, my_list)
56
Note: this code doesn't work if my_no < my_list[0]. You have to check it before.
If you want to stick to basics and want to approach it using for loop and if statement then it can be achieved as follows:
my_list=[1,10,15,20,45,56]
given_number = 9
output=my_list[0]
for x in my_list:
if(x < given_number and x > output):
output= x
print(output)
# Output
1
I'm trying to make a number based Mastermind game. Trying to figure out a function that takes two lists as paramenters and if indexes in the lists are the same it should return the amount of indexes that are the same.
PS. hope you understand what i mean ;P
generated_number_as_list = [1,1,1,1]
guess_as_list = [1,2,1,2]
correct = right_inrightplace(guess_as_list, generated_number_as_list)
print(correct)
output >> 2
You can use zip to compare values with corresponding indexes and then sum True which will be cast to 1
print(sum(x==y for x,y in zip(generated_number_as_list, guess_as_list))) #2
I wrote it outside of a function. Just copy the for loop in your function and return the ans value as the output.
generated_number_as_list = [1,1,1,1]
guess_as_list = [1,2,1,2]
ans = 0
for i in range(len(generated_number_as_list)):
if guess_as_list[i] == generated_number_as_list[i]:
ans = ans + 1
print(ans)
You can try this also:
a=[1,1,1,1]
b=[1,2,1,2]
print(min(len([x for x in a if x in b]),len([x for x in b if x in a])))
You can use the sum and map with operator.eq:
def right_inrightplace(a, b):
return sum(map(eq, a, b))
Or without using additional libraries:
def right_inrightplace(a, b):
return sum(x == y for x, y in zip(a, b))
Just for fun here's a solution using recursion:
def right_inrightplace(a, b):
if len(a) == 0 or len(b) == 0:
return 0
current = 0
if a[0] == b[0]:
current = 1
return current+right_inrightplace(a[1:],b[1:])
I need to write a function that returns the number of ways of reaching a certain number by adding numbers of a list. For example:
print(p([3,5,8,9,11,12,20], 20))
should return:5
The code I wrote is:
def pow(lis):
power = [[]]
for lst in lis:
for po in power:
power = power + [list(po)+[lst]]
return power
def p(lst, n):
counter1 = 0
counter2 = 0
power_list = pow(lst)
print(power_list)
for p in power_list:
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
counter1 == 0
else:
counter1 == 0
return counter2
pow() is a function that returns all of the subsets of the list and p should return the number of ways to reach the number n. I keep getting an output of zero and I don't understand why. I would love to hear your input for this.
Thanks in advance.
There are two typos in your code: counter1 == 0 is a boolean, it does not reset anything.
This version should work:
def p(lst, n):
counter2 = 0
power_list = pow(lst)
for p in power_list:
counter1 = 0 #reset the counter for every new subset
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
return counter2
As tobias_k and Faibbus mentioned, you have a typo: counter1 == 0 instead of counter1 = 0, in two places. The counter1 == 0 produces a boolean object of True or False, but since you don't assign the result of that expression the result gets thrown away. It doesn't raise a SyntaxError, since an expression that isn't assigned is legal Python.
As John Coleman and B. M. mention it's not efficient to create the full powerset and then test each subset to see if it has the correct sum. This approach is ok if the input sequence is small, but it's very slow for even moderately sized sequences, and if you actually create a list containing the subsets rather than using a generator and testing the subsets as they're yielded you'll soon run out of RAM.
B. M.'s first solution is quite efficient since it doesn't produce subsets that are larger than the target sum. (I'm not sure what B. M. is doing with that dict-based solution...).
But we can enhance that approach by sorting the list of sums. That way we can break out of the inner for loop as soon as we detect a sum that's too high. True, we need to sort the sums list on each iteration of the outer for loop, but fortunately Python's TimSort is very efficient, and it's optimized to handle sorting a list that contains sorted sub-sequences, so it's ideal for this application.
def subset_sums(seq, goal):
sums = [0]
for x in seq:
subgoal = goal - x
temp = []
for y in sums:
if y > subgoal:
break
temp.append(y + x)
sums.extend(temp)
sums.sort()
return sum(1 for y in sums if y == goal)
# test
lst = [3, 5, 8, 9, 11, 12, 20]
total = 20
print(subset_sums(lst, total))
lst = range(1, 41)
total = 70
print(subset_sums(lst, total))
output
5
28188
With lst = range(1, 41) and total = 70, this code is around 3 times faster than the B.M. lists version.
A one pass solution with one counter, which minimize additions.
def one_pass_sum(L,target):
sums = [0]
cnt = 0
for x in L:
for y in sums[:]:
z = x+y
if z <= target :
sums.append(z)
if z == target : cnt += 1
return cnt
This way if n=len(L), you make less than 2^n additions against n/2 * 2^n by calculating all the sums.
EDIT :
A more efficient solution, that just counts ways. The idea is to see that if there is k ways to make z-x, there is k more way to do z when x arise.
def enhanced_sum_with_lists(L,target):
cnt=[1]+[0]*target # 1 way to make 0
for x in L:
for z in range(target,x-1,-1): # [target, ..., x+1, x]
cnt[z] += cnt[z-x]
return cnt[target]
But order is important : z must be considered descendant here, to have the good counts (Thanks to PM 2Ring).
This can be very fast (n*target additions) for big lists.
For example :
>>> enhanced_sum_with_lists(range(1,100),2500)
875274644371694133420180815
is obtained in 61 ms. It will take the age of the universe to compute it by the first method.
from itertools import chain, combinations
def powerset_generator(i):
for subset in chain.from_iterable(combinations(i, r) for r in range(len(i)+1)):
yield set(subset)
def count_sum(s, cnt):
return sum(1 for i in powerset_generator(s) if sum(k for k in i) == cnt)
print(count_sum(set([3,5,8,9,11,12,20]), 20))
I am trying to solve Project Euler problem 2 in Python, and decided on a strategy based on iterables.
Here is the generator for the Fibonacci sequence,
def fnFibonacci():
fibNumPrev, fibNumCurrent = 0, 1
while True:
yield fibNumCurrent
fibNumPrev, fibNumCurrent = fibNumCurrent, fibNumCurrent + fibNumPrev
When I try to filter out the Fibonacci numbers that are less than 4 million and divisible by 2, it doesn't work, filtering everything out:
sum(list(itertools.takewhile(lambda x: x < 4e6 and x % 2 == 0 , fnFibonacci())))
However, both this (which ignores the evenness condition):
sum(list(itertools.takewhile(lambda x: x < 4e6, fnFibonacci())))
and this list comprehension:
sum([fibNum for fibNum in list(itertools.takewhile(lambda x: x < 4e6, fnFibonacci())) if fibNum % 2 == 0])
work. Can't really tell what's going on.
itertools.takewhile stops when it finds the first value that does not match the criterion. Since the first number is 1 and not divisible by 2 it stops immediately.
You can write this:
sum(x for x in itertools.takewhile(lambda n: n < 4e6, fnFibonacci())
if x % 2 == 0)
Can somebody tell me why this should be wrong?
#Each new term in the Fibonacci sequence is generated
#by adding the previous two terms. By starting with 1 and 2,
#the first 10 terms will be:
#1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
#Find the sum of all the even-valued terms in the sequence
#which do not exceed four million.
sum=2
list = [1,2]
for x in range(2,100):
a = list[x-2]+list[x-1]
print(a)
list.append(a)
if a % 2 == 0:
sum += a
print('sum', sum)
if sum >= 4000000:
break
Here's a completely different way to solve the problem using a generator and itertools:
def fib():
a = b = 1
while 1:
yield a
a, b = b, a + b
import itertools
print sum(n for n in itertools.takewhile(
lambda x: x <= 4000000, fib()) if n % 2 == 0)
Output:
4613732
So your code, even though it is wrong (see other answers), happens to give the correct answer.
replace
sum += a
print('sum', sum)
if sum >= 4000000:
break
with
if a > 4000000:
break
sum += a
print('sum', sum)
You should compare "a" with 4000000, not "sum", like Daniel Roseman said.
The question asked for the sum of even terms which do not exceed four million. You're checking if the sum doesn't exceed 4m.
I'm trying to solve the same problem - although I understand the logic to do it, I don't understand why this works (outputs the right sum)
limit = 4000000
s = 0
l = [1,2]
while l[-1]<limit:
n = l[-1]+l[-2]
l.append(n)
print n
And then then moment I put in the modulo function, it doesn't output anything at all anymore.
limit = 4000000
s = 0
l = [1,2]
while l[-1]<limit:
n = l[-1]+l[-2]
if n % 2 == 0 :
l.append(n)
print n
I'm sure this is fairly simple...thanks!
This is the code I used. It is very helpful and teaches you about generators.
def fib():
x,y = 0,1
while True:
yield x
x,y = y, x+y
def even(seq):
for number in seq:
if not number % 2:
yield number
def under_a_million(seq):
for number in seq:
if number > 4000000:
break
yield number
print sum(even(under_a_million(fib())))
-M1K3
Keep it simple and it should take you less than 0.1 seconds.
from datetime import datetime
x, y = 1, 1
total = 0
for i in xrange (1, 100):
x = x + y
if x % 2 == 0 and x <= 4000000:
total += x
y = y + x
if y % 2 == 0 and x <= 4000000:
total += y
print total
starttime = datetime.now()
print datetime.now() - starttime