Reputation: 13371
Possible Spoiler Warning
I've spent ages trying to improve this algorithm and work out what's going wrong, but I can't seem to work out why the outputted answer is incorrect.
I'm trying to solve Project Euler #12, to get the lowest triangular number to have over 500 divisors, but it says my answer is incorrect.
Here is my Python code:
import time
# function to get the number of divisors
def div(n):
d=2
for i in range(2,int(n**.5)+2):
if (n % i) == 0:
d += 1
return d
start = time.time()
w = True
n=m=1
while w:
n += 1
s = (n*(n+1))/2 # nth triangle number
r = div(s)
if r > m:
m = r
print s,"has",r,"divisors"
if r > 500:
w = False
print "Solved in",((time.time()-start)*1000),"milliseconds"
And the output for that code is this (in 66 seconds):
3 has 2 divisors
6 has 4 divisors
36 has 6 divisors
120 has 9 divisors
...
76576500 has 289 divisors
103672800 has 325 divisors
236215980 has 385 divisors
842161320 has 513 divisors
Solved in 65505.5799484 milliseconds
However, if I input 842161320 into the Project Euler problem, it says it's incorrect.
What am I doing wrong?
Upvotes: 1
Views: 1745
Reputation: 13371
With the help of Niklas, and changing my div
method, I've got the answer. Also, it's now only takes 8% of the time my previous algorithm took.
The algorithm now looks like this:
import time
import itertools
def div(n):
d=0
for i in range(1,int(n**.5)+1):
if (n % i) == 0:
d += 1
return 2*d
start = time.time()
s = m = 0
for i in itertools.count(1):
s += i
r = div(s)
if r > m:
m = r
print s,"has",r,"factors"
if div(s) > 500:
break
print "Solved in",((time.time()-start)*1000),"milliseconds"
Upvotes: 0
Reputation: 95358
I see two bugs:
div
function is broken: div(24) == 5
, while it should be 83
, although it should be 1
You could implement a working div
like this:
import math
def divisors(n):
return sum(1 for x in range(1, n+1) if n % x == 0)
Also, that code is inefficient as hell, some suggestions to improve it are:
Instead of calculating the n
th triangular number using your formula, use a rolling sum:
import itertools
s = 0
for i in itertools.count(1):
s += i
if div(s) > 500:
print s
break
Also, Use prime factors to calculate the number of divisors. You can create a prime factor cache for maximum performance.
Upvotes: 5
Reputation: 532333
You are undercounting the total number of divisors. 36, for example, has 9 divisors: 1,2,3,4,6,9,12,18,36. It's true the algorithm only needs to test numbers smaller than sqrt(n)
to find all divisors, but you still need to include the implied "large" divisors in your count.
Upvotes: 2