简体   繁体   中英

What's wrong with my python solution to Project Euler #12?

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?

I see two bugs:

  • Your div function is broken: div(24) == 5 , while it should be 8
  • Your 1st triangular number would be 3 , 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.

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.

Solved

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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM