patternpythonMinor
Project Euler Problem 12 - Highly Divisible Triangular Number - Python Solution Optimization
Viewed 0 times
problemnumberhighlydivisibleoptimizationprojecteulerpythonsolutiontriangular
Problem
Project Euler Problem 12 asks (paraphrased):
Considering triangular numbers Tn = 1 + 2 + 3 + … + n, what is the first Tn with over 500 divisors? (For example, T7 = 28 has six divisors: 1, 2, 4, 7, 14, 28.)
I have written the following solution. The code is calculates the correct answer, but the run time is appalling (6m9s on a 2.4GHz i7!!!)...
I know Python is definitely not the fastest of languages, but I'm clearly missing a trick here.
Could anyone highlight any optimizations that could be made to bring the execution time down to something sensible?
Addendum: I have since made to revisions of the code The first is the
Considering triangular numbers Tn = 1 + 2 + 3 + … + n, what is the first Tn with over 500 divisors? (For example, T7 = 28 has six divisors: 1, 2, 4, 7, 14, 28.)
I have written the following solution. The code is calculates the correct answer, but the run time is appalling (6m9s on a 2.4GHz i7!!!)...
I know Python is definitely not the fastest of languages, but I'm clearly missing a trick here.
Could anyone highlight any optimizations that could be made to bring the execution time down to something sensible?
from math import sqrt, floor
class findDivisors:
def isPrime(self, num):
for i in range(2, int(floor(sqrt(num)) + 1)):
if not num % i:
return False
return True
def numDivisors(self, num):
factors = {}
numFactors = 2
currNum = num
currFactor = 2
while not self.isPrime(currNum):
if not currNum % currFactor:
if currFactor not in factors:
factors[currFactor] = 1
else:
factors[currFactor] += 1
currNum = currNum / currFactor
else:
currFactor += 1
if currNum not in factors:
factors[currNum] = 1
else:
factors[currNum] += 1
total = 1
for key in factors:
total *= factors[key] + 1
return total
def firstWithNDivisors(self, noDivisors):
triGen = genTriangular()
candidate = triGen.next()
while self.numDivisors(candidate) < noDivisors:
candidate = triGen.next()
return candidate
def genTriangular():
next = 1
current = 0
while True:
current = next + current
next += 1
yield current
if __name__ == '__main__':
fd = findDivisors()
print fd.firstWithNDivisors(5)Addendum: I have since made to revisions of the code The first is the
Solution
Here is one possible improvement:
Don't check whether
Result and timing for
and without checking for prime:
In fact with this improvement alone the
There are very likely much more efficient algorithms to do this. One can probably make use of the fact that the
Don't check whether
currNum is prime, instead simply search factors until currNum becomes 1. Checking for prime takes O(sqrt(n)) time, but the rest of the loop checking for factors takes approximately O(1) per loop iteration, so you can save those O(sqrt(n)) here:while currNum > 1:
if not currNum % currFactor:
if currFactor not in factors:
factors[currFactor] = 1
else:
factors[currFactor] += 1
currNum = currNum / currFactor
else:
currFactor += 1
total = 1
for key in factors:
total *= factors[key] + 1Result and timing for
firstWithNDivisors(300) with the original code:2162160 2.21085190773and without checking for prime:
2162160 0.0829100608826In fact with this improvement alone the
firstWithNDivisors(501) only takes 2.3 sec on my machine.There are very likely much more efficient algorithms to do this. One can probably make use of the fact that the
n-th triangle number is n*(n+1)/2.Code Snippets
while currNum > 1:
if not currNum % currFactor:
if currFactor not in factors:
factors[currFactor] = 1
else:
factors[currFactor] += 1
currNum = currNum / currFactor
else:
currFactor += 1
total = 1
for key in factors:
total *= factors[key] + 12162160 2.210851907732162160 0.0829100608826Context
StackExchange Code Review Q#39348, answer score: 2
Revisions (0)
No revisions yet.