Trouble Finding Least Common Multiple of Numbers 1-20 - python

"""5. 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?"""
list=[]
possibility_list=[]
base=1
numberlist=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
for number in numberlist:
for iteration in range(1000):
list.append(iteration*number)
for possibility in list:
if list.count(possibility)==20:
print("Found LCM of [1:21] -->", str(possibility))
possibility_list.append(possibility)
else: continue
print(min(possibility_list))
I am currently trying to solve Euler Problem #5, which wants to find the LCM of numbers 1-20. The code above is brute force, but for some reason it doesn't work. Can someone point me in the right direction?

As of python 3.9 you can compute the least common multiple directly:
from math import lcm
print(lcm(*range(1, 11)))
If that's "cheating", start with a loop like this:
from math import lcm
result = 1
for i in range (1, 11):
result = lcm(result, i)
Then replace lcm with gcd. The two-arg version of gcd (greatest common divisor) has been around since python 3.5:
from math import gcd
result = 1
for i in range (1, 11):
result *= i // gcd(result, i)
Now gcd is something that's relatively easy to implement using Euclid's algorithm. The idea is that if the GCD of two numbers x and y is g, then x = a * g and y = b * g, with a, b relatively prime. If a and b weren't relatively prime, you could divide them by their common multiple and multiply g by that amount. Let's say a >= b. If x is a multiple of y, b must be 1 (again, a and b are relatively prime) and therefore y = g. Otherwise, c = a - b must be relatively prime to both a and b (else they all have a common factor). We can therefore repeat the same reasoning for y and z = x - y until the two numbers become multiples of each other. The sequence must converge because the numbers decrease with every subtraction:
def gcd(x, y):
if x < y:
x, y = y, x
if x % y == 0:
return y
return gcd(x - y, y)
result = 1
for i in range (1, 11):
result *= i // gcd(result, i)
You can probably make this more efficient, but this should be sufficient to form an understanding of how to solve the problem.
A non-recursive GCD might be advisable in the general case, since python supports arbitrary sized integers. You could implemented it as
def gcd(x, y):
while True:
if x < y:
x, y = y, x
if x % y == 0:
return y
x -= y
Even if you're going to use brute force, I suggest that you do it with a little more intelligence. For example, you know that 10 < lcm <= 1 * 2 * ... * 9 * 10 you can therefore write your check like this:
numbers = range(1, 11)
product = 1
for i in numbers:
product *= i
for n in range(max(numbers) + 1, product + 1):
for i in numbers:
if n % i != 0:
break
else:
break
print(n)
The inner loop checks the current possibility against all the numbers. If the loop terminates without breaking, n is a multiple of all the numbers. This triggers the else clause, which breaks out of the outer loop. The result is guaranteed to be correct because if nothing breaks the loop, n will be the multiple of all the numbers.
In all the above cases, it's trivial to include the range [1, 20] in place of [1, 10].

numberlist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
def is_prime(num: int) -> bool:
for factor in range(2, num):
if num % factor == 0:
return False
return True
prime_list = list(filter(is_prime, numberlist))
additional_factor_prime = []
for number in numberlist:
temp_factor_list = [*prime_list] + additional_factor_prime
temp_number = number
for index in range(len(temp_factor_list) - 1, -1, -1):
if temp_number in prime_list:
break
cur_factor = temp_factor_list[index]
if temp_number % cur_factor == 0:
temp_factor_list.pop(index)
temp_number //= cur_factor
if temp_number not in temp_factor_list and temp_number != 0 and temp_number != 1:
additional_factor_prime.append(temp_number)
factor_list = [*prime_list] + additional_factor_prime
LCM = 1
for number in factor_list:
LCM *= number
print(LCM)

Related

How can i make my code run faster for large values(upto 100 million)?

Okay so background: I'm solving a question which requires me to find a number 'n' such that n-9, n-3, n+3, n+9 are consecutive prime numbers and n-8, n-4, n+4, n+8 are practical numbers and then I have to add the first four n that satisfy this condition.
The problem: Whether the logic of the code is correct or incorrect is irrelevant here because my code crashes before it reaches 100 million. I can't even check the output of the code, it works fine for 1million but doesn't scale to well for larger numbers.
What i did:
I used the sieve of erath... to get the prime numbers up to 100 million which we will call M. And since practical numbers are divisible by 6 or by 4, I created another set to store those numbers and from that list i then created a set that contained the numbers that satisfy this condition: 'n-8, n-4, n+4, n+8 are practical numbers' which we will call N. Finally I iterate through each element, a, in N and check whether a - 9, a - 3, a + 3, a + 9 are part of the prime number set.
If anyone has any tips on how i can speed this up or any better algorithms it would be greatly appreciated
code
def SieveOfEratosthenes(n):
m = set()
prime = [True for i in range(n + 1)]
p = 2
while (p * p <= n):
if (prime[p] == True):
for i in range(p * 2, n + 1, p):
prime[i] = False
p += 1
prime[0]= False
prime[1]= False
for p in range(n + 1):
if prime[p]:
m.add(p)
return m
#creates set that stores multiples of 4 and 6
def ps1(n):
s = set()
for i in range(1, n+1):
if i%4 == 0 and i%6 == 0:
s.add(i)
return s
#checks whether any number satisfies n -8, n-4, n+4, n+8 must be practical and stores it in a set
def ps2(l):
q = set()
for i in l:
if ((i-8) in l) and ((i-4) in l) and ((i+4) in l) and ((i+8) in l):
q.add(i)
return q
#using the numbers stored in the prev set, i check the last condition n-9, n-3, n+3, n+9 must be in the
prime list
def TotalSieve(k, l):
q = set()
inc = 0
for i in k:
if inc != 4:
if ((i-9) in l) and ((i-3) in l) and ((i+3) in l) and ((i+9) in l):
inc = inc + 1
q.add(i)
else:
print("Found 4")
return q
# driver program
if __name__=='__main__':
n = 1000000000
m = SieveOfEratosthenes(n)
p = ps1(n)
p = ps2(p)
f = TotalSieve(p, m)
elem1 = f.pop()
elem2 = f.pop()
elem3 = f.pop()
elem4 = f.pop()
#add the first four numbers that satisfy the conditions
tot = elem1 + elem2 + elem3 + elem4
print(tot)
First, ps1 is wrong. The test should say or, not and.
Next, if n is divisible by 4, all n-8, n-4, n+4, n+8 are also divisible by 4. If n is not divisible by 4, none of them are divisible by 4, and some of them are also not divisible by 4. Which means you are only interested in n being a multiple of 4.
Finally, I know that this problem implies some serious number-theoretical homework. Brute force wouldn't do it.

How to use recursion to deal with variable numbers of nested for loop?

I am dealing with the following question:
Create a function that accepts two arguments, the number of dice rolled, and the outcome of the roll. The function returns the number of possible combinations that could produce that outcome. The number of dice can vary from 1 to 6.
And below is my code for 4 dice (x=4). But I am not able to extend this to any number of dices (x).
def dice_roll(x, y):
all_comb = []
for i in range(1, 7):
for j in range(1, 7):
for q in range(1, 7):
for r in range(1, 7):
total = i+j+q+r
all_comb.append(total)
return all_comb.count(y)
Is there a way to use recursion to deal with variable numbers of nested loops? And are there other more elegant ways apart from recursion for this question?
As mentioned in the comments you should use itertools.product like this:
import itertools
def dice_roll(dices: int, result: int) -> int:
combos = [x for x in itertools.product(range(1, 7), repeat=dices) if sum(x) == result]
return len(combos)
# combos = [(1, 3), (2, 2), (3, 1)]
if __name__ == '__main__':
print(dice_roll(2, 4))
# returns 3
With itertools.product you get all possible combinations of the given amount of dices. with the list comprehension we filter the values by the correct sum.
Here's a version that calculates the number of rolls for each total in O(n*s) time where n is the number of dice, and s the number of sides. It uses O(n) storage space.
If R[k, t] is the number of rolls of k dice that total t (keeping the number of sides fixed), then the recurrence relations are:
R[0, t] = 1 if t=0, 0 otherwise
R[k, t] = 0 if t < 0
R[k, t] = R[k-1, t-1] + R[k-1, t-2] + ... + R[k-1, t-s]
Then we solving this with dynamic programming.
def dice_roll(n, sides):
A = [1] + [0] * (sides * n)
for k in range(n):
T = sum(A[k] for k in range(max(0, sides*k), sides*(k+1)))
for i in range(sides*(k+1), -1, -1):
A[i] = T
if i > 0:
T -= A[i-1]
if i - sides - 1 >= 0:
T += A[i - sides - 1]
return A
print(dice_roll(9, 4))
The program returns an array A with A[i] storing the number of ways of rolling n dice with s sides that sum to i.
a more mathematical approach using sympy.
for larger numbers of dice this will scale way better than iterating over all possibilities; for small numbers of dice the start-up time of sympy will probably not be worth the trouble.
from sympy.utilities.iterables import partitions, multiset_permutations
def dice_roll(n_dice, total):
ret = 0
for item in partitions(total, k=6, m=n_dice):
if sum(item.values()) != n_dice:
continue
print(f"{item}")
# this for loop is only needed if you want the combinations explicitly
for comb in multiset_permutations(item):
print(f" {comb}")
ret += sum(1 for _ in multiset_permutations(item))
return ret
i get the number of partitions of total first (limiting the maximum value with k=6 as needed for a dice) and then count the number of possible multiset partitions.
the example:
r = dice_roll(n_dice=3, total=5)
print(r)
outputs:
{3: 1, 1: 2}
[1, 1, 3]
[1, 3, 1]
[3, 1, 1]
{2: 2, 1: 1}
[1, 2, 2]
[2, 1, 2]
[2, 2, 1]
6
meaning there are 6 ways to get to 5 with 3 dice. the combinations are shown.
in order to speed up things you could calculate the number of multiset combinations directly (you loose the explicit representation of the possibilities, though):
from sympy.utilities.iterables import partitions, multiset_permutations
from math import comb
def number_multiset_comb(dct):
ret = 1
n = sum(dct.values()) # number of slots
for v in dct.values():
ret *= comb(n, v)
n -= v
return ret
def dice_roll(n_dice, total):
ret = 0
for item in partitions(total, k=6, m=n_dice):
if sum(item.values()) != n_dice:
continue
ret += number_multiset_comb(dct=item)
return ret
def combin_list(n):
# n is number of dice
if n == 1:
return [1,2,3,4,5,6]
else:
temp = combin_list(n-1)
resualt = []
for i in range(1,7):
for item in temp:
resualt.append(i+item)
return resualt
def dice_roll(x, y):
list = combin_list(x)
return list.count(y)

Python: Reduce runtime?

I recently started to learn python and i'm using CodeWars to train. The task is to return a list [p, p + 4, p + 6, p + 10, p + 12, p + 16] where all of them are primes. The sum of them should be higher than sum_limit. For low values it is working, but at high values (about 2 million) the runtime is high. How can I reduce the runtime?
from math import sqrt; from itertools import count, islice
def find_primes_sextuplet(sum_limit):
for x in range(sum_limit):
if isPrime(x) and isPrime(x+4) and isPrime(x+6) and isPrime(x+10) and isPrime(x+12) and isPrime(x+16):
possible = [x, x+4, x+6, x+10, x+12, x+16]
if sum(possible) > sum_limit:
return possible
def isPrime(n):
return n > 1 and all(n%i for i in islice(count(2), int(sqrt(n)-1)))
print(find_primes_sextuplet(2000000))
For non-negative integer values of n, you can use this:
def isPrime(n):
if n == 1 or n % 2 == 0 or n % 3 == 0:
return False
end = int(sqrt(n)+1)
for start in [5, 7]:
for k in range(start, end, 6):
if n % k == 0:
return False
return True
It won't change the theoretical complexity, but it will reduce the practical running-time.
And if you change the outer loop to for x in range(5, sum_limit), then you can also get rid of the initial check if n == 1 or n % 2 == 0 or n % 3 == 0.
Here's my thinking about reducing complexity and run time.
You can write a sieve in O(n log log n). Here's a reasonable implementation:
def sieve(n):
grid = [None for _ in range(n+1)]
i = 2
while i < n+1:
if grid[i] is None:
grid[i] = True
for p in range(2*i, n+1, i):
grid[p] = False
else:
i += 1
return (index for index, b in enumerate(grid) if b)
There are 6 numbers, and the total amount added to the first number is 48. So the minimum possible value for the first number is (n - 48) / 6. In my sieve we can iterate the generator until number is greater than that.
def get_remaining_sieve(n):
g = sieve(n)
current = next(g)
min_value = (n - 48) / 6
while current < min_value:
current = next(g)
return [current] + list(g)
Now just iterate through every slice of length 6, and check if the separation matches the desired separation (4, 2, 4, 2, 4).
remaining = get_remaining_sieve(n)
for start in range(len(remaining) - 5):
slice = remaining[start:start+6]
differences = [slice[j] - slice[j-1] for j in range(1, 6)]
if differences == [4, 2, 4, 2, 4]:
print(slice)
Summary
Based on those principles, I've come up with this solution:
from itertools import dropwhile, islice
def get_solutions(n):
grid = [None for _ in range(n+1)]
i = 2
while i < n+1:
if grid[i] is None:
grid[i] = True
for p in range(2*i, n+1, i):
grid[p] = False
else:
i += 1
sieve = (index for index, b in enumerate(grid) if b)
min_value = (n - 48) / 6
reduced_sieve = dropwhile(lambda v: v < min_value, sieve)
reference_slice = list(islice(reduced_sieve, 6))
while True:
try:
ref = reference_slice[0]
differences = [v - ref for v in reference_slice[1:]]
if differences == [4, 6, 10, 12, 16]:
yield reference_slice
reference_slice = reference_slice[1:] + [next(reduced_sieve)]
except StopIteration:
break
n = 2000000
print(next(get_solutions(n))) # 695ms
# or for all solutions
for solution in get_solutions(n): # 755ms
print(solution)
This runs in less than a second on my computer.
There are various ways to improve the runtime of your code. For example a lot of numbers are checked for being prime numbers even though their sum is not eligible as a result. Calculating the sum of 6 numbers is faster than checking if they are prime. You could move the sum check above the prime check and only check the numbers for primes if their sum would be eligible.
To improve this further you could skip numbers which will not result in an eligible sum by starting the range at the floor of possible numbers.
x + x + 4 + x + 6 + x + 10 + x + 12 + x + 16 = 6x + 48
which is supposed to be above your sum_limit
6x + 48 >= sum_limit
x >=(sum_limit - 48) / 6
So if your range starts at x you will skip all numbers which would not result in an eligible sum anyway.
You would also be able to improve runtime by skipping even numbers in your loop(via range(y,x,2)).
Further improving the runtime would require you to adjust the isPrime function.

Amicable Numbers

I am struggling with optimizing these functions that I have used to calculate the sum of the amicable pairs under 10000. An amicable pair is a pair (a, b) where the sum of the divisors of "a" excluding "a" itself equals b and the sum of the divisors of "b" excluding "b" itself equals "a".
I.e. divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110: The sum of which is 284. And the sum of the divisors of 284 (1, 2, 4, 71 and 142) equals 220.
My code is:
import math
def Divisorsbaritself(x):
divList = [1]
y = 2
while y <= math.sqrt(x):
if x % y == 0:
divList.append(y)
divList.append(int(x / y))
y += 1
return sum(divList)
def amicable():
solution = []
for i in range(10000):
if Divisorsbaritself(Divisorsbaritself(i)) == i:
solution.append(i)
return sum(solution)
print amicable()
I need help with understanding why the amicable function is not working. To me it makes logical sense that the if Divisorsbaritself(Divisorsbaritself(i)) == i: condition is the right condition to include i in the list, but it is giving me 40285, rather than 31626, the answer.
If Divisorsbaritself(i)==i you shouldn't count i.
def amicable():
solution = []
for i in range(10000):
if Divisorsbaritself(i)!=i and Divisorsbaritself(Divisorsbaritself(i)) == i:
solution.append(i)
return sum(solution)
But you should also fix the bug that would be an issue if i is a perfect square and in an amicable pair.
You can improve this with list comprehensions.
def amicable():
solution = [i for i in xrange(10000) if Divisorsbaritself(i)!=i and Divisorsbaritself(Divisorsbaritself(i)) == i]
return sum(solution)
They're amicable numbers only if they're different. So if divsum(i) is equal to i, then that's not included, despite the fact that means that divsum(divsum(i)) also equals i.
In addition, your current check counts the square root of a perfect square twice, even though it's only one factor.
And, on top of that, I wouldn't be using a list then summing it at the end when you can simply use an accumulator. And it's usually faster to do multiplication than square roots so you can change the while loop to take that into account.
Finally, for the love of whatever deities you believe in, comment your code! It'll make it so much easier to understand what's going on, both for others and for yourself six months down the track.
Incorporating those changes gives you the following DivisorsBarItself function:
def DivisorsBarItself(num):
# Maintain sum of factors.
divSum = 1
# Go through every integer up to but excluding sqrt(num).
testnum = 2
while testnum * testnum < num:
# If factor, add it and the complement (guaranteed integer).
if num % testnum == 0:
divSum += testnum + num/testnum
testnum += 1
# If perfect square, add the square root once.
if testnum * testnum == num:
divSum += testnum
# Return the sum.
return divSum
Fixing the logic for detecting amicable numbers and using a sum rather than a list gives you:
def AmicableSum():
# Set sum to zero and process all numbers below 10,000.
solution = 0
for num in range(10000):
# Get the "friend", add only if different and f(f(x)) = x.
numFriend = DivisorsBarItself(num)
if numFriend != num and DivisorsBarItself(numFriend) == num:
solution += num
return solution
print AmicableSum()
which gives the correct result of 31626.
I have fixed the bug now by going:
def Divisorsbaritself(x):
divList = [1]
y = 2
while y <= math.sqrt(x):
if x % y == 0:
if y is not int(x/y):
divList.append(y)
divList.append(int(x / y))
else:
divList.append(y)
y += 1
return sum(divList)
I have written the whole thing you said as a function
def devisor(a):
listOfFactors=[]
for possibleFactor in range(1,a):
if a%x==0:
listOfFactors.append(possibleFactor)
sumOfFactors=0
for item in z:
sumOfFactors+=item
factorsOfNewSumAddedUp=0
for x in range(1,sumOfFactors):
if temp%x==0:
factorsOfNewSumAddedUp+=x
if a==factorsOfNewSumAddedUp:
print("this is a divisor")

Python: isPrime function much slower after adding lookup of pregenerated list?

I am solving Project Euler problems with Python. I have seen some solvers use isPrime() functions that simply test whether x % y == 0 for all y from 2 to x ** 0.5. That is not efficient and I want to write a better isPrime() function, based on the num % 30 test. This is what I came up with:
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
primality = [1, 7, 11, 13, 17, 19, 23, 29]
def isPrime(num):
if not type(num) in (int, long):
raise ValueError
if num in primes:
return True
elif num < 2:
return False
elif num % 30 not in primality:
return False
else:
for prime in primes[3:]:
if num % prime == 0:
return False
seed, sqrt, tryfactor = 1, getIntSquareRoot(num), 1
while tryfactor < sqrt:
for trymod in primality:
tryfactor = seed * 30 + trymod
if num % tryfactor == 0 and not(num == tryfactor):
return False
seed += 1
return True
Problem 7 is to find the 10001st prime. So I decided to make the code append all these primes to a list which subsequent problems can refer to. I thought that given a 5-digit number num, num in primes would be a much faster operation that repeated num % tryfactor. For parameters where primes[-1] < num < (primes[-1] ** 0.2) it should still be faster to get tryfactor values from the list than repeatedly generating them through tryfactor = seed * 30 + trymod.
So I came up with the following:
def problem7():
count, seed = len(primes), 1
while True:
for modulus in primality:
num = seed * 30 + modulus
if isPrime(num):
count += 1
primes.append(num)
if count > 10000:
return num
seed += 1
def isPrimeB(num):
if not type(num) in (int, long):
raise ValueError
if num in primes:
return True
elif num < 2:
return False
elif num % 30 not in primality:
return False
else:
for prime in primes[3:]:
if num % prime == 0:
return False
seed, sqrt, tryfactor = 1, getIntSquareRoot(num), 1
while tryfactor < sqrt:
for trymod in primality:
tryfactor = seed * 30 + trymod
if num % tryfactor == 0 and not(num == tryfactor):
return False
seed += 1
return True
Of course I expect the code for problem 7 to be much slower, because generating the list of primes a few seconds. I also expect the code for later problems calling isPrime() (such as 10, 27, 35, 41 and 58) to run much faster.
However, I got a shock when the code for problems 27, 35, 41 and 58 became much slower. Could someone explain why looking up values in a list is much slower than calculating them? Or is there a mistake in my code? What else can I do to make the isPrime() function more efficient?
The reason it is slower is because the list lookup is O(n). Instead of using lists use sets:
primes = set()
primes.add(num)
The num in primes check will be now O(1).
Also forget about this "optimization": primes[3:]. It actually slows down your code, since it recreates the list (note that it won't work anyway if you switch to sets).
Finally you can implement the Sieve of Eratosthenes (although there are more sophisticated sieves) to generate primes fast.
#Freakish answered your question about why isPrimeB is slow. Let me propose a couple of alternatives to what you have written.
Here is my version of primality checking with a 2,3,5-wheel, which is the same algorithm as your isPrime function but stated rather differently:
def isPrime(n):
d, w, wheel = 2, 0, [1,2,2,4,2,4,2,4,6,2,6]
while d*d <= n:
if n%d == 0: return False
d = d + wheel[w]
w = 3 if w == 10 else w+1
return True
There are a couple of ways to compute the nth prime. One way uses a Sieve of Eratosthenes. Number theory tells us that the nth prime is always less than n(log n + log log n) with logarithms to base e, so you could sieve up to the limit and discard the excess primes. Here's the sieve:
def primes(n):
m = (n-1)//2; b = [True] * m
i, p, ps = 0, 3, [2]
while i < m:
if b[i]:
ps.append(p)
for j in range(2*i*i+6*i+3, m, p):
b[j] = False
i, p = i+1, p+2
return ps
So, to get the 10001st prime:
>>> 10000 * ( log(10000) + log(log(10000)) )
114306.67178344031
>>> (primes(114306))[10000]
104743
Another alternative generates candidate primes using a 2,3,5,7-wheel and confirms their primality by a Miller-Rabin test to the three bases 2, 7, 61, which is sufficient for primes less then 2^32 (actually, a little bit bigger):
def genPrimes(): # valid to 2^32
def isPrime(n):
def isSpsp(n, a):
d, s = n-1, 0
while d%2 == 0:
d /= 2; s += 1
t = pow(a,d,n)
if t == 1: return True
while s > 0:
if t == n-1: return True
t = (t*t) % n; s -= 1
return False
for p in [2, 7, 61]:
if n % p == 0: return n == p
if not isSpsp(n, p): return False
return True
w, wheel = 0, [1,2,2,4,2,4,2,4,6,2,6,4,2,4,\
6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,\
2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]
p = 2; yield p
while True:
p = p + wheel[w]
w = 4 if w == 51 else w + 1
if isPrime(p): yield p
Then the nth prime can be computed by the expression next(itertools.islice(genPrimes(), n, n+1)):
>>> next(itertools.islice(genPrimes(), 10000, 10001))
104743
Both methods return the 10001st prime instantly, as soon as you press the enter key.
If your interested in programming with prime numbers (or you just want to solve the prime number problems in Project Euler), you might be interested in this essay or in these entries at my blog.

Categories

Resources