What is the best algorithm to solve this puzzle? - python

So I came across this question:
How many numbers are there from 1 to 1000 which are not divisible by the digits 2, 3 and 5?
It seems pretty easy at first, so I wrote a quick python program to solve it:
count = 0
for number in range(1,1000):
if number % 2 != 0 and number % 3 != 0 and number % 5 != 0:
count += 1
print(count)
I got the correct answer (266), but I thought that doing it that way was a lot of typing if I ever wanted to check more than just 3 values. I also wanted to do a mathematical solution so I came across this:
1000 - ((1000/2 +1000/3 +1000/5) -(1000/2x3 +1000/2x5 + 1000/3x5)+ (1000/2x3x5)) = 1000-((500+333+200) - (166 +100 + 66) + 33) = 1000- 734 = 266
I thought it was a good approach so I implemented it in code:
def foo(ln = 1000), numbers = [2,3,5]:
div = 0
muldiv = 0
totdiv = 1
for n in numbers:
div += ln/n
for i in numbers:
for n in range(numbers.index(i)+1, len(numbers)):
muldiv += ln/(i * numbers[n])
for n in numbers:
totdiv *= n
answer = ln - (div - muldiv + ln/totdiv)
print("answer is ", math.floor(answer))
Now I am pretty sure I messed up somewhere in my second function because it doesn't seem to work for more numbers. For example, if I were to try to find
How many numbers are there from 1 to 1000 which are not divisible by the digits 2, 3, 5 and 7?
the first method returns 228 and foo(numbers = [2,3,5,7]) returns 300... I'm pretty sure 228 is the correct answer since one more number would mean that there are LESS factors instead of more, but where did I go wrong? and is there a better way to solve this problem?

You do not need an algorithm for that, simple mathematics is enough:
Say you want to count the amount of numbers from 1 to N (inclusive) dividable by k, that is simply equivalent to:
floor(N/k).
So the amount of numbers dividable by 3 in this case is 333.
Now you can't however simply use calculate the amount of numbers dividable by 2, 3 and 5; and sum them up, because there are common ones. Indeed: for instance 15 is dividable by both 3 and 5.
You can solve this however using the inclusion-exclusion principle:
the amount of numbers dividable by 2, 3 and 5 is the same as
the amount numbers dividable by 2
plus the amount of numbers dividable by 3
plus the amount of numbers dividable by 5
minus the amount of numbers dividable by 2 and 3
minus the amount of numbers dividable by 2 and 5
minus the amount of numbers dividable by 3 and 5
plus the amount of numbers dividable by 2, 3 and 5.
So in order to solve your first problem, you can simply state:
def n_div(N,k):
return N//k
def n_div235(N):
return n_div(N,2)+n_div(N,3)+n_div(N,5)-n_div(N,2*3)-n_div(N,2*5)-n_div(N,3*5)+n_div(N,2*3*5)
def not_div235(N):
return N-n_div235(N)
As you can see it generates the correct result:
>>> not_div235(1000)
266
As long as N is very large compared to the number of divisors, you better use the inclusion-exclusion approach:
you can do this like:
import itertools
from functools import reduce
import operator
def gcd(a, b):
while b:
a, b = b, a % b
return a
def lcm(a, b):
return a * b // gcd(a, b)
def lcm_list(ks):
res = 1
for k in ks:
res = lcm(res,k)
return res
def n_div_gen(N,ks):
nk = len(ks)
sum = 0
factor = 1
for i in range(1,nk+1):
subsum = 0
for comb in itertools.combinations(ks,i):
subsum += n_div(N,lcm_list(comb))
sum += factor * subsum
factor = -factor
return sum
def not_div_gen(N,ks):
return N-n_div_gen(N,ks)
For small N, this will not pay off, but say to want to calculate the amount of numbers dividable by 3, 5 and 7 from 1 to 1 000 000 000 is:
>>> not_div_gen(1000000000,[3,5,7])
457142857
You can do this with:
>>> sum(i%3!=0 and i%5!=0 and i%7!=0 for i in range(1,1000000001))
457142857
But it takes minutes to calculate that whereas our own approach uses milliseconds. Note that this only works for a huge N.

Use the built-in functions sum and all with a nested generator:
def f(r=1000, nums=(2,3,5)):
return sum(all(x%n for n in nums) for x in range(1, r+1))
This goes through the range of numbers, check whether each of those numbers has a nonzero modulus with each of the specified numbers, and sums those boolean values (False is 0 and True is 1). A nums of (2,3,5,7) produces a result of 228, which is in agreement with your shorter, simpler code (which, reassuringly, doesn't use any floating-point arithmetic, as your second code block does).

The number of integers up to N not divisible by n1,n2,...,nt (assumed to be pairwise-coprime) is
the number of integers up to N minus
( SUMi in 1..t ( the number of integers up to N divisible by ni)) plus
( SUMi,j in 1..t, i<j ( the number of integers up to N divisible by ninj)) minus
( SUMi,j,k in 1..t, i<j<k ( the number of integers up to N divisible by ninjnk)) plus
( SUMi,j,k,l in 1..t, i<j<k<l ( the number of integers up to N divisible by ninjnknl)) minus
... ... ... ...
( SUMi,j,k,l,...q in 1..t, i<j<k<l<...<q ( the number of integers up to N divisible by ninjnknl...nq))
The series continues until the subscript contains all t integers from the original list.
For numbers that are not known to be pairwise-coprime, replace their product by the least common multiple.
This is why your method works only for 3 numbers. You only compute the first four members of the series.

Here's another implementation that uses inclusion-exclusion. It's simpler than the code in Willem Van Onsem's excellent answer (which I didn't see before I wrote this code), but this one only works if the numbers in the list of divisors are all coprime to each other. For the the more general case, you need to use Willem's approach.
from itertools import combinations
from functools import reduce
def prod(seq, mul=int.__mul__):
return reduce(mul, seq, 1)
def count_coprimes(n, divisors):
total = n
sign = -1
for i in range(1, len(divisors) + 1):
for k in combinations(divisors, i):
total += n // prod(k) * sign
sign = -sign
return total
print(count_coprimes(1000, [2, 3, 5]))
output
266
FWIW, here's the same algorithm as a "one-liner" (split over several lines to improve readability). It's a little less efficient due to the (-1)**i in the inner loop.
def count_coprimes(n, divisors):
return n + sum(n // prod(k) * (-1)**i
for i in range(1, len(divisors) + 1)
for k in combinations(divisors, i))
print(count_coprimes(1000000000, [3, 5, 7]))
output
457142857
We can get rid of that (-1)**i by negating the divisors and using a modified integer division function:
def div(p, q):
return p // q if q > 0 else -(p // -q)
def count_coprimes(n, divisors):
return sum(div(n, prod(k))
for i in range(len(divisors) + 1)
for k in combinations([-u for u in divisors], i))

A very small change you can make to roughly halve the amount of time that it takes is rather than generating all numbers from 1 to 1000, generate all odd numbers from 1 to 1000:
count = 0
for number in range(1,1001,2):
if number % 3 != 0 and number % 5 != 0:
count += 1
print(count)
While this is not a huge change and is not a mathematical solution it makes the code no less readable and slightly more efficient.
And to take into account your other code, you can use a list comprehension in the if statement to check other numbers(note that I also use the first number to generate the initial list of numbers, rather than performing the modulo operation for all 1000):
def foo(lst):
count = 0
for number in range(1,1001,lst[0]):
if not any([number % i == 0 for i in lst[1:]]):
count += 1
return count
>>> foo([2,3,5])
266
>>> foo([2,3,5,7])
228

There are many ways to solve this little problem iteratively, all of them with pretty much similar performance, here's few examples:
import timeit
def f1(l, h):
count = 0
for number in range(l, h):
if number % 2 != 0 and number % 3 != 0 and number % 5 != 0:
count += 1
return count
def f2(l, h):
return len(filter(lambda x: x % 2 != 0 and x % 3 != 0 and x % 5 != 0, range(l, h)))
def f3(l, h):
count = 0
for number in range(l, h):
if number % 2 == 0:
continue
if number % 3 == 0:
continue
if number % 5 == 0:
continue
count += 1
return count
def f4(l, h):
return len([x for x in range(l, h) if x % 2 != 0 and x % 3 != 0 and x % 5 != 0])
a, b, N = 1, 1000, 10000
print timeit.timeit('f1(a,b)', setup='from __main__ import f1, a, b', number=N)
print timeit.timeit('f2(a,b)', setup='from __main__ import f2, a, b', number=N)
print timeit.timeit('f3(a,b)', setup='from __main__ import f3, a, b', number=N)
print timeit.timeit('f4(a,b)', setup='from __main__ import f4, a, b', number=N)
Times on a i7-2.6ghz would be:
0.802361558825
1.46568073638
0.91737188946
0.846404330893
Usually these times are good enough to be considered when the lower/upper bounds (1,1000) are relatively small. Now, if we're talking about really high bounds (trillions) where the computation is not feasible you could consider apply the much smarter inclusion-exclusion principle, that way you'd solve the problem analitically and you'd be granted to get constant time with your solution.

input
let n be the interval <0,n> to test and d[]={2,3,5,0}; be null terminated array of divisors
compute LCM of d[]
least common multiple is the period with which the SoE will repeat itself. for 2,3,5 is lcm=30. Use the max(d[]) as increment while computing it to boost speed... If LCM is too big (LCM>=n) then use n instead for speed.
compute SoE for <0,LCM)
simply create array of LCM numbers and set a[i]=1 for non divisible i and a[i]=0 for divisible i.
convert SoE to non divisible number count
simply compute a'[i]=a[0]+a[1]+..a[i]
compute the count
count is simply:
int(n/LCM)*a'[LCM-1] + a'[n%LCM];
Here simple C++ example:
int non_divisibles(int n,const int *d) // SoE
{
int i,j,cnt,lcm,m,*a;
for (m=0;d[m];m++); // compute m
for (cnt=0,i=0;i<m;i++) if (cnt<d[i]) cnt=d[i]; // cnt = max d[] (to speed up LCM)
// LCM d[]
a=new int[m]; if (a==NULL) return -1;
for (i=0;i<m;i++) a[i]=d[i];
for (lcm=cnt;lcm<=n;lcm+=cnt) // no need to test above `n` even if lcm is bigger
{
for (i=0;i<m;i++) for (;a[i]<lcm;) a[i]+=d[i];
for (i=0;i<m;i++) if (a[i]!=lcm) { i=-1; break; }
if (i>=0) break;
}
delete[] a;
// SoE <0,LCM)
a=new int[lcm]; if (a==NULL) return -1;
for (i=0;i<lcm;i++) a[i]=1;
for (j=0;d[j];j++)
for (i=0;i<lcm;i+=d[j])
a[i]=0;
// convert to cnt
for (i=1;i<lcm;i++) a[i]+=a[i-1];
// compute whole count
cnt =(n/lcm)*a[lcm-1];
cnt+=a[n%lcm];
delete[] a;
return cnt;
}
Here some measurements to compare naive,SoE and this SoE(max(n,LCM(d[]))) approaches:
n=1000000 d[]={ 2 3 5 7 11 13 17 19 }
171021 [ 27.514 ms] naive
171021 [ 12.642 ms] SoE
171021 [ 25.438 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 7 11 13 17 }
180524 [ 26.212 ms] naive
180524 [ 11.781 ms] SoE
180524 [ 9.807 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 7 11 13 }
191808 [ 24.690 ms] naive
191808 [ 11.512 ms] SoE
191808 [ 0.702 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 }
266666 [ 16.468 ms] naive
266666 [ 9.744 ms] SoE
266666 [ 0.006 ms] LCM+Soe
n= 1000 d[]={ 2 3 5 }
266 [ 0.012 ms] naive
266 [ 0.012 ms] SoE
266 [ 0.001 ms] LCM+Soe
n=1000000 d[]={ 2 3 5 19 23 61 87 10001 }
237662 [ 26.493 ms] naive
237662 [ 10.180 ms] SoE
237662 [ 19.429 ms] LCM+Soe
As you can see SoE(n) is better if LCM is too big in comparison to n(d[] contains many primes or big numbers) but need O(n) space.

You can bail out as soon as you hit a divisible.
def test(tocheck):
count = 0
for number in range(1, 1000):
for div in tocheck:
if not number % div:
break
else:
#else on a loop means it ran to completion w.o. break
count += 1
print("%d not divisible by %s" % (count, tocheck))
test([2,3,5])
test([2,3,5,7])
output:
266 not divisible by [2, 3, 5]
228 not divisible by [2, 3, 5, 7]

def not_divisible(n = 1000, divisors = [2, 3, 5]):
count = 0
for i in range(1, n + 1):
if all(1 if i % d else 0 for d in divisors):
count += 1
return count
Explanation for 4th line:
If number i is not divisible by divisor d, i % d returns a non-zero
integer. Python considers any non-zero number as True.
List comprehension [1 if i % d else 0 for d in divisors] returns a
list of 1s and 0s such that, if number is not divisible, the value is
1 , else 0.
The all() function checks if all values in a collection are True. If
all values in the list are 1 (True), meaning the number is 'not
divisible' by all divisors.
If number is not divisible by both 2 and 3, it cannot be divisible by
6. So no need to check for that here.
Here's a smaller code:
def not_divisible(n = 1000, divisors = [2, 3, 5]):
return sum(1 for i in range(1, n + 1) if all(1 if i % d else 0 for d in divisors))
Here we're generating a list of 1s for every number not divisible by all divisors and sum of that list is the answer.

Related

Egg dropping puzzle

I'm working on this kata on codewars, which is extended version of famous two egg poblem. Goal is to find the critical floor height with given count of eggs and tries, attempts.
I look it for a little bit an find out that critical floor height is equal to sum of binomial coefficients until the given egg and try count:
Sum of Combination (Tries/i) where i is in range between 1 to Eggs + 1.
I use the comb function from scipy.special with exact=True parameter.
For relatively small numbers, like 477 eggs and 500 tries or 477 eggs and 10000 tires, program calculates very fast, under 0.05 secs. But there are very large checks like 4477 eggs and 10000 tries, which takes about 15 secs.
Is my approach wrong or could i done this calculations without getting execution timeout on codewars with this way?
Here is my script:
import scipy.special
# n: Eggs m: Tries
def height(n,m):
ret = sum(scipy.special.comb(m,i, exact=True) for i in range(1,n+1))
return int(ret)
Since C(N,k) = N! / (k!(N-k)!),
We have C(N,1) = N
And for larger k: C(N,k) = C(N,k-1) * (N-k+1) / k
Calculating C(N,k) is much faster and easier if you start with C(N,k-1).
Let's do it by hand on very small numbers, say 5 and 7. And let me consider also the case of 0 items out of 7, whose value is 1 (you can always subtract 1 from the following if you don't want to include it)
The number of combinations for 0 items out of 7 is 7! / (0! (7-0)!) => 1
The number of combinations for 1 item is 7! / (1! (7-1)!) => 7
2 items => 7! / (2! (7-2)!) => 21. Note we have a new factor 6, since (7-2)! * 6 == (7-1)!, and a new divisor 2 since 1! * 2 == 2!. At the previous iteration we had a factor 7 and a divisor 1 of course
3 items => 35. New factor 5, new divisor 3
4 items => 35 again. New factor and new divisor are both 4
5 items => 21 again. Now new factor and new divisor are inverted (3 and 5)
Should we continue we would get
6 items => 7
7 items => 1.
So the total number of combinations up to 5 items out of 7 is 1 + 7 + 21 + 35 + 35 + 21 => 120. Note that if we count up to 7 we get 128, i.e. 2**7 - 1
So the simplest code can be:
def sum_of_combos(n,k):
mult = k
total = 1
currval = 1
for i in range(1,n+1):
currval = (currval * mult) // i
total += currval
mult -= 1
return total
>>> timeit("sum_of_combos(4477,10000)", globals=globals(), number=1000)
17.124391099999997
>>> timeit("sum_of_combos(8477,10000)", globals=globals(), number=1000)
36.44438840000001
Is there room for optimizations? Yes, there is. For instance in the second example above instead of iterating 8477 times we could calculate 2**10000 and then subtract from that the first (10000 - 8477) values.
I found out instead of using scipy, using loop is more efficient. And implemented this:
# n: Eggs m: Tries
def height(n,m):
if n == 0 or m == 0:
return 0
if n > m:
n = m
counter,current,total = 1,1,0
while counter <= n:
d = (m+1-counter) * current // counter
total,current,counter = total+d,d,counter+1
return total
It is very fast compared to my previous attempt. On 4477,10000 this takes 0.02 secs to complete, previous one toke 16.97 secs to complete.

How to find even sum

I need to find the sum of all even numbers below the inserted number. For example if I insert 8 then the sum would be 2+4+6+8=20. If I insert 9 then it also needs to be 20. And it needs to be based on recursion.
This is what I have so far:
def even(a):
if a == 0:
else:
even(a - 1)
even(8)
I cannot figure out what to change under the "if" part for it to give the right outcome
If the function is called with an odd number, n, then you can immediately call again with the number below (an even).
Then if the function is called with an even number return that even number plus the result of summing all the even numbers below this number by calling again with n - 2.
Finally, your base case occurs when n = 0. In this case just return 0.
So we have
def even_sum(n):
if n % 2 == 1: # n is odd
return even_sum(n - 1)
if n == 0:
return 0
return n + even_sum(n - 2)
which works as expected
>>> even_sum(8)
20
>>> even_sum(9)
20
>>> even_sum(0)
0
To design a recursive algorithm, the first thing to wonder is "In what cases can my algorithm return an answer trivially?". In your case, the answer is "If it is called with 0, the algorithm answers 0". Hence, you can write:
def even(n):
if n == 0:
return 0
Now the next question is "Given a particular input, how can I reduce the size of this input, so that it will eventually reach the trivial condition?"
If you have an even number, you want to have this even number + the sum of even numbers below it, which is the result of even(n-2). If you have an odd number, you want to return the sum of even numbers below it. Hence the final version of your function is:
def even(n):
if n == 0 or n == 1:
return 0
if n % 2 == 0:
return n + even(n - 2)
return even(n - 1)
Both with o(n) time complexity
With For loop
num = int(input("Enter a number: ")) # given number to find sum
my_sum = 0
for n in range(num + 1):
if n % 2 == 0:
my_sum += n
print(my_sum)
With recursion
def my_sum(num):
if num == 0:
return 0
if num % 2 == 1:
return my_sum(num - 1)
return num + my_sum(num - 2)
always avoid O(n^2) and greater time complexity
For a recursive solution:
def evenSum(N): return 0 if N < 2 else N - N%2 + evenSum(N-2)
If you were always given an even number as input, you could simply recurse using N + f(N-2).
For example: 8 + ( 6 + (4 + ( 2 + 0 ) ) )
But the odd numbers will require that you strip the odd bit in the calculation (e.g. subtracting 1 at each recursion)
For example: 9-1 + ( 7-1 + ( 5-1 + ( 3-1 + 0 ) ) )
You can achieve this stripping of odd bits by subtracting the modulo 2 of the input value. This subtracts zero for even numbers and one for odd numbers.
adjusting your code
Your approach is recursing by 1, so it will go through both the even and odd numbers down to zero (at which point it must stop recursing and simply return zero).
Here's how you can adjust it:
Return a value of zero when you are given zero as input
Make sure to return the computed value that comes from the next level of recursion (you are missing return in front of your call to even(a-1)
Add the parameter value when it is even but don't add it when it is odd
...
def even(a):
if a == 0 : return 0 # base case, no further recusion
if a%2 == 1 : return even(a-1) # odd number: skip to even number
return a + even(a-1) # even number: add with recursion
# a+even(a-2) would be better
A trick to create a recursive function
An easy way to come up with the structure of a recursive function is to be very optimistic and imagine that you already have one that works. Then determine how you would use the result of that imaginary function to produce the next result. That will be the recursive part of the function.
Finally, find a case where you would know the answer without using the function. That will be your exit condition.
In this case (sum of even numbers), imagine you already have a function magic(x) that gives you the answer for x. How would you use it to find a solution for n given the result of magic(n-1) ?
If n is even, add it to magic(n-1). If n is odd, use magic(n-1) directly.
Now, to find a smaller n where we know the answer without using magic(). Well if n is less than 2 (or zero) we know that magic(n) will return zero so we can give that result without calling it.
So our recursion is "n+magic(n-1) if n is even, else magic(n-1)"
and our stop condition is "zero if n < 2"
Now substitute magic with the name of your function and the magic is done.
For an O(1) solution:
Given that the sum of numbers from 1 to N can be calculated with N*(N+1)//2, you can get half of the sum of even numbers if you use N//2 in the formula. Then multiply the result by 2 to obtain the sum of even numbers.
so (N//2)*(N//2+1) will give the answer directly in O(1) time:
N = 8
print((N//2)*(N//2+1))
# 20
# other examples:
for N in range(10):
print(N,N//2*(N//2+1))
# 0 0
# 1 0
# 2 2
# 3 2
# 4 6
# 5 6
# 6 12
# 7 12
# 8 20
# 9 20
Visually, you can see the progression like this:
1..n : 1 2 3 4 5 6 7 8
∑n : 1 3 6 10 15 21 28 36 n(n+1)/2
n/2 : 0 1 1 2 2 3 3 4
1..n/2 : 1 2 3 4
∑n/2 : 1 3 5 10 half of the result
2∑n/2 : 2 6 10 20 sum of even numbers
So we simply replace N with N//2 in the formula and multiply the result by 2:
N*(N+1)//2 --> replace N with N//2 --> N//2*(N//2+1)//2
N//2*(N//2+1)//2 --> multiply by 2 --> N//2*(N//2+1)
Another way to see it is using Gauss's visualisation of the sum of numbers but using even numbers:
ascending 2 4 6 8 ... N-6 N-4 N-2 N (where N is even)
descending N N-2 N-4 N-6 ... 8 6 4 2
--- --- --- --- --- --- --- ---
totals N+2 N+2 N+2 N+2 ... N+2 N+2 N+2 N+2 (N/2 times N+2)
Because we added the even numbers twice, once in ascending order and once in descending order, the sum of all the totals will be twice the sum of even numbers (we need to divide that sum by 2 to get what we are looking for).
sum of evens: N/2*(N+2)/2 --> N/2*(N/2+1)
The N/2(N/2+1) formulation allows us to supply the formula with an odd number and get the right result by using integer division which absorbs the 'odd bit': N//2(N//2+1)
Recursive O(1) solution
Instead of using the integer division to absorb the odd bit, you could use recursion with the polynomial form of N/2*(N+2)/2: N^2/4 + N/2
def sumEven(n):
if n%2 == 0 : return n**2/4 + n/2 # exit condition
return sumEven(n-1) # recursion
Technically this is recursive although in practice it will never go deeper than 1 level
Try out this.
>>> n = 5
>>> sum(range(0, n+1, 2))
with minimum complexity
# include <stdio.h>
void main()
{
int num, sum, i;
printf("Number: ");
scanf("%d", &num);
i = num;
if (num % 2 != 0)
num = num -1;
sum = (num * (num + 2)) / 4;
printf("The sum of even numbers upto %d is %d\n\n", i, sum);
}
It is a C program and could be used in any language with respective syntax.
And it needs to be based on recursion.
Though you want a recursion one, I still want to share this dp solution with detailed steps to solve this problem.
Dynamic Programming
dp[i] represents the even sum among [0, i] which I denote as nums.
Case1: When i is 0, there is one number 0 in nums. dp[0] is 0.
Case2: When i is 1, there are two numbers 0 and 1 in nums. dp[1] is still 0.
Case3: When i is 2, there are three numbers 0, 1 and 2 in nums. dp[2] is 2.
Case4: When i is greater than 2, there are two more cases
If i is odd, dp[i] = dp[i-1]. Since i is odd, it is the same with [0, i-1].
If i is even, dp[i] = dp[i-2] + i by adding the current even number to the even sum among [0, i-2] (i-1 is odd, so won't be added).
PS. dp[i] = dp[i-1] + i is also ok. The difference is how you initialize dp.
Since we want the even sum among [0, n], we return dp[n]. You can conclude this from the first three cases.
def even_sum(n):
dp = []
# Init
dp.append(0) # dp[0] = 0
dp.append(0) # dp[1] = 0
# DP
for i in range(2, n+1): # n+1 because range(i, j) results [i, j) and you take n into account
if i % 2 == 1: # n is odd
dp.append(dp[i-1]) # dp[i] = dp[i-1]
else: # n is even
dp.append(dp[i-2] + i) # dp[i] = dp[i-2] + i
return dp[-1]

Python - why is method 2 (nested while loops) faster than method 1 (for, while loops)?

I'm working my way through the Project Euler questions and am testing out two approaches to computing the prime factors of a number. Repetition within the list of factors is fine.
Both approaches are very similar but the second (with nested while loops) is much, much faster than the first. Does anyone know why this is?
Thanks in advance
def prime_factors_1(n):
"""Returns all the prime factors of a positive integer"""
factors = []
for d in range(2, n):
while n % d == 0:
factors.append(d)
n /= d
return factors
def prime_factors_2(n):
"""Returns all the prime factors of a positive integer"""
factors = []
d = 2
while n > 1:
while n % d == 0:
factors.append(d)
n /= d
d += 1
return factors
Count the number of steps either method would take:
the range() is precomputed so you will loop over n - 2 divisor candidates. Reducing n in the loop (via the /= augmented division operator) will not influence the number of iterations.
the while loop will loop over far fever candidate divisors, as n is lowered with each divisor found, resulting in fewer iterations.
Say n is 100. For the for loop that means there will be 98 iterations. But for the while loop, you get:
while 100 > 1 is true, 100 % 2 == 0 is true, so n /= 2 reduces this to 50. Then to 25, then d is incremented to 3 and the next iteration starts.
while 25 > 1, is true, 25 % 3 == 0 is false, so d is incremented to 4
while 25 > 1, is true, 25 % 4 == 0 is false, so d is incremented to 5
while 25 > 1, is true, 25 % 5 == 0 is true, so n /= 5 reduces n to 5, then 1.
In just 4 steps of the outer loop you have determined the divisors. 4 << 98.
If you use python2, the following line makes the difference (in the 1st method):
for d in range(2, n):
range() in Python2 creates a whole list of numbers and for large values of n this can represent a significant overhead. It is thus better to use xrange() which returns an iterator - an object that knows how to yield values one by one as needed, without having to create them all in advance and holding them in memory.

Sum of all the multiples of 3 or 5 below 1000

Beginner here- trying to make a simple python program that will compute/answer this problem:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
Currently this is what I have:
a = 0
b = 0
while a < 1000:
a = a + 3
print (a)
while b < 1000
b = b + 5
print (b)
This will print all the numbers being considered. I just need to add them together and that's my answer.
I would like one of two things to happen, instead of the code that I have written:
I would like all of this to happen internally, and therefore not have to use the "print" function. The just print the sum of all of those multiples.
I would like all of this stuff to print, but then I want to be able to print the sum of all of them too. Is there a way to make the computer take the value of everything it has printed?
Actually this problem can be solved in O(1) instead of O(N) without using any loops or lists:
The required sum is the sum of all multiples of 3 plus sum of all multiples of 5 minus the sum of multiples of (3*5=15) below the given number 1000 (LIMIT=999). The sums are calculated as a sum of arithmetic series.
It can be calculated in following way:
LIMIT=999
# Get the upper bounds for the arithmetic series
upper_for_three = LIMIT // 3
upper_for_five = LIMIT // 5
upper_for_fifteen = LIMIT // 15
# calculate sums
sum_three = 3*upper_for_three*(1 + upper_for_three) / 2
sum_five = 5*upper_for_five*(1 + upper_for_five) / 2
sum_fifteen = 15*upper_for_fifteen*(1 + upper_for_fifteen) / 2
# calculate total
total = sum_three + sum_five - sum_fifteen
# print result with Python 3
print(int(total))
The result is:
>>>
233168
It is possible to do this in one line in Python using a generator expression:
print(sum(x for x in range(1000) if x % 3 == 0 or x % 5 == 0))
The range(1000) produces all the integers from 0 to 999 inclusive. For each one of those integers, if it is divisible by 3 or divisible by 5, then it is included in the result. The sum(...) function adds up all those numbers, and finally print(...) prints the result.
I would use a for loop to iterate over each number in your selected range. Then you can check if the modulus % is equal to 0, meaning it has no remainder when divided by those values, if so, add it to the total.
total = 0
for num in range(1000):
if num % 3 == 0 or num % 5 == 0:
print(num)
total += num
>>> total
233168
While a for loop would work, you could also use a generator expression and sum:
sum(n for n in range(1000) if n % 3 == 0 or n % 5 == 0)
def sum_multiply (n):
data = []
for num in range (1, n):
if num % 3 == 0 or num % 5 == 0:
data.append(num)
return sum(data)
sum_multiply(1000)
total=0
i=0
while i<1000:
if i%3==0 or i%5==0:
print(num)
total+=i
i+=1
print("Total is: ")
**with python**
print(sum([i for i in range(1,1000) if i%3==0 or i%5==0 ]))

Project Euler 5 in Python - How can I optimize my solution?

I've recently been working on Project Euler problems in Python. I am fairly new to Python, and still somewhat new as a programmer.
In any case, I've ran into a speed-related issue coding a solution for problem #5. The problem is,
"2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?"
I've checked around some, and I haven't been able to find anything on this problem pertaining to Python specifically. There were some completed scripts, but I want to avoid looking at other's code in full, if possible, instead wanting to improve my own.
The code I have written runs successfully for the example of 2520 and the range 1 to 10, and should be directly modifiable to work with the question. However, upon running it, I do not get an answer. Presumably, it is a very high number, and the code is not fast enough. Printing the current number being checked seems to support this, reaching several million without getting an answer.
The code, in it's current implementation is as follows:
rangemax = 20
def div_check(n):
for i in xrange(11,rangemax+1):
if n % i == 0:
continue
else:
return False
return True
if __name__ == '__main__':
num = 2
while not div_check(num):
print num
num += 2
print num
I have already made a couple changes which I think should help the speed. For one, for a number to be divisible by all numbers 1 to 20, it must be even, as only even numbers are divisible by 2. Hence, I can increment by 2 instead of 1. Also, although I didn't think of it myself, I found someone point out that a number divisible by 11 to 20 is divisible by 1 to 10. (Haven't checked that one, but it seems reasonable)
The code still, however is not fast enough. What optimisations, either programmatic, or mathematics, can I make to make this code run faster?
Thanks in advance to any who can help.
Taking the advice of Michael Mior and poke, I wrote a solution. I tried to use a few tricks to make it fast.
Since we need a relatively short list of numbers tested, then we can pre-build the list of numbers rather than repeatedly calling xrange() or range().
Also, while it would work to just put the numbers [1, 2, 3, ..., 20] in the list, we can think a little bit, and pull numbers out:
Just take the 1 out. Every integer is evenly divisible by 1.
If we leave the 20 in, there is no need to leave the 2 in. Any integer evenly divisible by 20 is evenly divisible by 2 (but the reverse might not be true). So we leave the 20 and take out the 2, the 4, and the 5. Leave the 19, as it's prime. Leave the 18, but now we can take out the 3 and the 6. If you repeat this process, you wind up with a much shorter list of numbers to try.
We start at 20 and step numbers by 20, as Michael Mior suggested. We use a generator expression inside of all(), as poke suggested.
Instead of a while loop, I used a for loop with xrange(); I think this is slightly faster.
The result:
check_list = [11, 13, 14, 16, 17, 18, 19, 20]
def find_solution(step):
for num in xrange(step, 999999999, step):
if all(num % n == 0 for n in check_list):
return num
return None
if __name__ == '__main__':
solution = find_solution(20)
if solution is None:
print "No answer found"
else:
print "found an answer:", solution
On my computer, this finds an answer in under nine seconds.
EDIT:
And, if we take advice from David Zaslavsky, we realize we can start the loop at 2520, and step by 2520. If I do that, then on my computer I get the correct answer in about a tenth of a second.
I made find_solution() take an argument. Try calling find_solution(2520).
My first answer sped up the original calculation from the question.
Here's another answer that solves it a different way: just find all the prime factors of each number, then multiply them together to go straight to the answer. In other words, this automates the process recommended by poke in a comment.
It finishes in a fraction of a second. I don't think there is a faster way to do this.
I did a Google search on "find prime factors Python" and found this:
http://www.stealthcopter.com/blog/2009/11/python-factors-of-a-number/
From that I found a link to factor.py (written by Mike Hansen) with some useful functions:
https://gist.github.com/weakish/986782#file-factor-py
His functions didn't do quite what I wanted, so I wrote a new one but used his pull_prime_factors() to do the hard work. The result was find_prime_factors() which returns a list of tuples: a prime number, and a count. For example, find_prime_factors(400) returns [(2,4), (5,2)] because the prime factors of 400 are: (2*2*2*2)*(5*5)
Then I use a simple defaultdict() to keep track of how many we have seen so far of each prime factor.
Finally, a loop multiplies everything together.
from collections import defaultdict
from factor import pull_off_factors
pf = defaultdict(int)
_primes = [2,3,5,7,11,13,17,19,23,29]
def find_prime_factors(n):
lst = []
for p in _primes:
n = pull_off_factors(n, p, lst)
return lst
def find_solution(low, high):
for num in xrange(low, high+1):
lst = find_prime_factors(num)
for n, count in lst:
pf[n] = max(pf[n], count)
print "prime factors:", pf
solution = 1
for n, count in pf.items():
solution *= n**count
return solution
if __name__ == '__main__':
solution = find_solution(1, 20)
print "answer:", solution
EDIT: Oh wow, I just took a look at #J.F. Sebastian's answer to a related question. His answer does essentially the same thing as the above code, only far more simply and elegantly. And it is in fact faster than the above code.
Least common multiple for 3 or more numbers
I'll leave the above up, because I think the functions might have other uses in Project Euler. But here's the J.F. Sebastian solution:
def gcd(a, b):
"""Return greatest common divisor using Euclid's Algorithm."""
while b:
a, b = b, a % b
return a
def lcm(a, b):
"""Return lowest common multiple."""
return a * b // gcd(a, b)
def lcmm(*args):
"""Return lcm of args."""
return reduce(lcm, args)
def lcm_seq(seq):
"""Return lcm of sequence."""
return reduce(lcm, seq)
solution = lcm_seq(xrange(1,21))
print "lcm_seq():", solution
I added lcm_seq() but you could also call:
lcmm(*range(1, 21))
Since your answer must be divisible by 20, you can start at 20 and increment by 20 instead of by two. In general, you can start at rangemax and increment by rangemax. This reduces the number of times div_check is called by an order of magnitude.
Break down the number as a prime factorization.
All primes less than 20 are:
2,3,5,7,11,13,17,19
So the bare minimum number that can be divided by these numbers is:
2*3*5*7*11*13*17*19
Composites:
4,6,8,9,10,12,14,15,16,18,20 = 2^2, 2*3, 2^3, 3^2, 2*5, 2^2*3, 2*7, 3*5, 2*3^2, 2^2*5
Starting from the left to see which factors needed:
2^3 to build 4, 8, and 16
3 to build 9
Prime factorization: 2^4 * 3^2 * 5 * 7 * 11 * 13 * 17 * 19 = 232,792,560
I got the solution in 0.066 milliseconds (only 74 spins through a loop) using the following procedure:
Start with smallest multiple for 1, which = 1. Then find the smallest multiple for the next_number_up. Do this by adding the previous smallest multiple to itself (smallest_multiple = smallest_multiple + prev_prod) until next_number_up % smallest_multiple == 0. At this point smallest_multiple is the correct smallest multiple for next_number_up. Then increment next_number_up and repeat until you reach the desired smallest_multiple (in this case 20 times). I believe this finds the solution in roughly n*log(n) time (though, given the way numbers seem to work, it seems to complete much faster than that usually).
For example:
1 is the smallest multiple for 1
Find smallest multiple for 2
Check if previous smallest multiple works 1/2 = .5, so no
previous smallest multiple + previous smallest multiple == 2.
Check if 2 is divisible by 2 - yes, so 2 is the smallest multiple for 2
Find smallest multiple for 3
Check if previous smallest multiple works 2/3 = .667, so no
previous smallest multiple + previous smallest multiple == 4
Check if 4 is divisible by 3 - no
4 + previous smallest multiple == 6
Check if 6 is divisible by 3 - yes, so 6 is the smallest multiple for 3
Find smallest multiple for 4
Check if previous smallest multiple works 6/4 = 1.5, so no
previous smallest multiple + previous smallest multiple == 12
Check if 12 is divisble by 4 - yes, so 12 is the smallest multiple for 4
repeat until 20..
Below is code in ruby implementing this approach:
def smallestMultiple(top)
prod = 1
counter = 0
top.times do
counter += 1
prevprod = prod
while prod % counter != 0
prod = prod + prevprod
end
end
return prod
end
List comprehensions are faster than for loops.
Do something like this to check a number:
def get_divs(n):
divs = [x for x in range(1,20) if n % x == 0]
return divs
You can then check the length of the divs array to see if all the numbers are present.
Two different types of solutions have been posted here. One type uses gcd calculations; the other uses prime factorization. I'll propose a third type, which is based on the prime factorization approach, but is likely to be much faster than prime factorization itself. It relies on a few simple observations about prime powers -- prime numbers raised to some integral exponent. In short, it turns out that the least common multiple of all numbers below some number n is equal to the product of all maximal prime powers below n.
To prove this, we begin by thinking about the properties that x, the least common multiple of all numbers below n, must have, and expressing them in terms of prime powers.
x must be a multiple of all prime powers below n. This is obvious; say n = 20. 2, 2 * 2, 2 * 2 * 2, and 2 * 2 * 2 * 2 are all below 20, so they all must divide x. Likewise, 3 and 3 * 3 are both below n and so both must divide x.
If some number a is a multiple of the prime power p ** e, and p ** e is the maximal power of p below n, then a is also a multiple of all smaller prime powers of p. This is also quite obvious; if a == p * p * p, then a == (p * p) * p.
By the unique factorization theorem, any number m can be expressed as a multiple of prime powers less than m. If m is less than n, then m can be expressed as a multiple of prime powers less than n.
Taken together, the second two observations show that any number x that is a multiple of all maximal prime powers below n must be a common multiple of all numbers below n. By (2), if x is a multiple of all maximal prime powers below n, it is also a multiple of all prime powers below n. So by (3), it is also a multiple of all other numbers below n, since they can all be expressed as multiples of prime powers below n.
Finally, given (1), we can prove that x is also the least common multiple of all numbers below n, because any number less than x could not be a multiple of all maximal prime powers below n, and so could not satisfy (1).
The upshot of all this is that we don't need to factorize anything. We can just generate primes less than n!
Given a nicely optimized sieve of eratosthenes, one can do that very quickly for n below one million. Then all you have to do is find the maximal prime power below n for each prime, and multiply them together.
prime_powers = [get_max_prime_power(p, n) for p in sieve(n)]
result = reduce(operator.mul, prime_powers)
I'll leave writing get_max_prime_power as an exercise. A fast version, combined with the above, can generate the lcm of all numbers below 200000 in 3 seconds on my machine.
The result is a 86871-digit number!
This solution ran pretty quickly for me (imports numpy).
t0 = time.time()
import numpy
ints = numpy.array(range(1,21))
primes = [2,3,5,7,11,13,17,19] # under 20
facts = []
for p in primes:
counter = 0
nums = ints
while any(nums % p == 0):
nums = nums / float(p)
counter += 1
facts.append(counter)
facts = numpy.array(facts)
mults = primes**facts
ans = 1
for m in mults:
ans = m * ans
t1 =time.time()
perf = t1 - t0
print "Problem 5\nAnswer:",ans, "runtime:", perf, "seconds"
"""Problem 5
Answer: 232792560 runtime: 0.00505399703979 seconds"""
Here i have also done using prime factorization way.
#!/usr/bin/env python
import math
def is_prime(num):
if num > 1:
if num == 2:
return True
if num%2 == 0:
return False
for i in range(3, int(math.sqrt(num))+1, 2):
if num%i == 0:
return False
return True
return False
def lcm(number):
prime = []
lcm_value = 1
for i in range(2,number+1):
if is_prime(i):
prime.append(i)
final_value = []
for i in prime:
x = 1
while i**x < number:
x = x + 1
final_value.append(i**(x-1))
for j in final_value:
lcm_value = j * lcm_value
return lcm_value
if __name__ == '__main__':
print lcm(20)
After checking how much time it has taken, it was not bad at all.
root#l-g6z6152:~/learn/project_euler# time python lcm.py
232792560
real 0m0.019s
user 0m0.008s
sys 0m0.004s
I wrote a solution to euler5 that:
Is orders of magnitude faster than most of the solutions here when n=20 (though not all respondents report their time) because it uses no imports (other than to measure time for this answer) and only basic data structures in python.
Scales much better than most other solutions. It will give the answer for n=20 in 6e-05 seconds, or for n=100 in 1 millisec, faster than most of the responses for n=20 listed here.
import time
a=time.clock() # set timer
j=1
factorlist=[]
mydict={}
# change second number to desired number +1 if the question were changed.
for i in range(2,21,1):
numberfactors=[]
num=i
j=2
# build a list of the prime factors
for j in range(j,num+1,1):
counter=0
if i%j==0:
while i%j==0:
counter+=1
numberfactors.append(j)
i=i/j
# add a list of factors to a dictionary, with each prime factor as a key
if j not in mydict:
mydict[j] = counter
# now, if a factor is already present n times, including n times the factor
# won't increase the LCM. So replace the dictionary key with the max number of
# unique factors if and only if the number of times it appears is greater than
# the number of times it has already appeared.
# for example, the prime factors of 8 are 2,2, and 2. This would be replaced
# in the dictionary once 16 were found (prime factors 2,2,2, and 2).
elif mydict[j] < counter:
mydict[j]=counter
total=1
for key, value in mydict.iteritems():
key=int(key)
value=int(value)
total=total*(key**value)
b=time.clock()
elapsed_time=b-a
print total, "calculated in", elapsed_time, "seconds"
returns:
232792560 calculated in 6e-05 seconds
# does not rely on heuristics unknown to all users, for instance the idea that
# we only need to include numbers above 10, etc.
# For all numbers evenly divisible by 1 through 100:
69720375229712477164533808935312303556800 calculated in 0.001335 seconds
Here is program in C language. Cheers
#include <stdio.h>
#include <stdlib.h>
//2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
//What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
bez_ost(int q)
{
register br=0;
for( register i=1;i<=20;i++)
if(q%i==0)
br++;
if(br==20)
return 1;
return 0;
}
int main()
{
register j=20;
register ind=0;
while(ind!=1)
{
j++;
if(bez_ost(j))
break;
}
fprintf(stdout,"\nSmallest positive number that is evenlu divisible by all of the numbers from 1 to 20 is: %d\n\a",j);
system("Pause");
}
I've had the same problem. The algorithm seems to be quite slow, but it does work nonetheless.
result = list()
xyz = [x for x in range(11, 21)]
number = [2520]
count = 0
while len(result) == 0:
for n in number:
print n
for x in xyz:
if n % x == 0:
count += 1
elif n % x != 0:
count = 0
break
if count == 10:
result.append(number[0])
elif count != 10:
number[0] += 1
print result
This was the algorithm I made.
How about this? The required number is, after all, the LCM of the given numbers.
def lcm(a,b):
lcm1 = 0
if a == b:
lcm1 = a
else:
if a > b:
greater = a
else:
greater = b
while True:
if greater % a == 0 and greater % b == 0:
lcm1 = greater
break
greater += 1
return lcm1
import time
start_time = time.time()
list_numbers = list(range(2,21))
lcm1 = lcm(list_numbers[0],list_numbers[1])
for i in range(2,len(list_numbers)):
lcm1 = lcm(lcm1,list_numbers[i])
print(lcm1)
print('%0.5f'%(time.time()-start_time))
This code took a full 45 s to get the answer to the actual question! Hope it helps.
import time
primes = [11,13,17,19]
composites = [12,14,15,16,18,20]
def evenlyDivisible(target):
evenly = True
for n in composites:
if target % n > 0:
evenly = False
break
return evenly
step = 1
for p in primes:
step *= p
end = False
number = 0
t1 = time.time()
while not end:
number += step
if evenlyDivisible(number):
end = True
print("Smallest positive evenly divisible number is",number)
t2 = time.time()
print("Time taken =",t2-t1)
Executed in 0.06 seconds
Here is my Python solution, it has 12 iteration so compiled quite fast:
smallest_num = 1
for i in range (1,21):
if smallest_num % i > 0: # If the number is not divisible by i
for k in range (1,21):
if (smallest_num * k) % i == 0: # Find the smallest number divisible by i
smallest_num = smallest_num * k
break
print (smallest_num)
Here's an observation on this problem. Ultimately, it takes 48 iterations to find the solution.
Any number that is divisible by all of the numbers from 1..20 must be divisible by the product of the primes in that range, namely 2, 3, 5, 7, 11, 13, 17, and 19. It cannot be smaller than the product of these primes, so let's use that number, 232,792,560, as the increment, rather than 20, or 2,520, or some other number.
As it turns out, 48 * 232,792,560 is divisible by all numbers 1..20. By the way, the product of all of the non-primes between 1..20 is 66. I haven't quite figured out the relationship between 48 and 66 in this context.
up = int(input('Upper limit: '))
number = list(range(1, up + 1))
n = 1
for i in range(1, up):
n = n * number[i]
for j in range(i):
if number[i] % number[j] == 0:
n = n / number[j]
number[i] = number[i] / number[j]
print(n)
How I can reduce the complexity of this
num = 1
found = False
while not found:
count =0
for i in range(1, 21):
if num %i == 0:
count+=1
if count ==10:
print(num)
found = True
num+=1
Here is the code in C++ to find the solution for this question.
What we have to do is to run a loop from 1 until we got that number so just iterate through the loop and once the number get evenly divisble(remainder 0) flag value dont get change and flag remains 1 and we got that number and break through outer loop and print the answer
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int i,j,flag=1;
for(i=1;;i++) //iterate until we got the number
{
flag=1;
for(j=2;j<=20;j++) //check form 1 to 20 for that number
{
if(i%j!=0) //if the number is not evenly divisible we break loop and
{
flag=0;break; // initilize flag as 0 i.e. that number is not what we want
}
}
if(flag==1) //if any number we got that is evenly divisible i.e. flag value doesnt change we got that number we break through the loop and print the answer
break;
} // after ending of the loop as we jump to next number make flag also 1 again so that again inner loop conditions apply on it
cout<<i;
return 0;
}
A typescript variant that seems to be relatively quick, leveraging recursion and known facts.
describe(`2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?`,
() => {
it("prooves the example: 10", () => smallestWithoutRemainder(10).should.be.equal(2520));
it("prooves 1", () => smallestWithoutRemainder(1).should.be.equal(1));
it("prooves 2", () => smallestWithoutRemainder(2).should.be.equal(2));
it("prooves 3", () => smallestWithoutRemainder(3).should.be.equal(6));
it("prooves 4", () => smallestWithoutRemainder(4).should.be.equal(12));
it("prooves 5", () => smallestWithoutRemainder(5).should.be.equal(60));
it("prooves 6", () => smallestWithoutRemainder(6).should.be.equal(60));
it("prooves 7", () => smallestWithoutRemainder(7).should.be.equal(420));
it("prooves 8", () => smallestWithoutRemainder(8).should.be.equal(840));
it("prooves 9", () => smallestWithoutRemainder(9).should.be.equal(2520));
it("prooves 12", () => smallestWithoutRemainder(12).should.be.equal(27720));
it("prooves 20", () => smallestWithoutRemainder(20).should.be.equal(232792560));
it("prooves 30", () => smallestWithoutRemainder(30).should.be.equal(2329089562800));
it("prooves 40", () => smallestWithoutRemainder(40).should.be.equal(5342931457063200));
});
let smallestWithoutRemainder = (end: number, interval?: number) => {
// What do we know?
// - at 10, the answer is 2520
// - can't be smaller than the lower multiple of 10
// - must be an interval of the lower multiple of 10
// so:
// - the interval and the start should at least be divisable by 'end'
// - we can recurse and build on the results before it.
if (!interval) interval = end;
let count = Math.floor(end / 10);
if (count == 1) interval = 2520;
else if (count > 1) interval = smallestWithoutRemainder((count - 1) * 10, interval);
for (let i = interval; true; i += interval) {
let failed = false;
for (let j = end; j > 1; j--) {
if (i % j != 0) {
failed = true;
break;
}
}
if (!failed) return i;
}
}
I think this the answer:
primes = [11, 13, 17, 19]
result = 2520
for i in primes:
result *= i
print (result * 2)

Categories

Resources