Project Euler #12 (Highly divisible triangular numbers) in Python 3.x
up vote
0
down vote
favorite
I'm a beginner to programming and just started python, I've been trying to work through Project Euler challenges.
I wrote a program to solve Project Euler #12, which asks for the smallest number of the form 1+ 2 + 3 + … + n which has over 500 divisors. It works perfectly with smaller numbers like 100, but takes too long trying to find up to 500 factors. I gave up waiting after about 20 minutes. I'd like some help with optimizing my code to make it faster.
I basically made a function that finds the triangular number of a given number and another function that counts the number of factors/divisors of a number. I made a while
loop where a number keeps increasing till the triangular number of itself has more than 500 factors.
And here is my code:
#Starts the timer to measure time taken for code to run:
import time
start_time = time.time()
#Returns the triangular number of 'num':
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
#Declares x (starting value):
x = 0
#main:
while True:
#Checks if number of factors for the triangular of x is above 500
if facCount(triangular(x)) > 500:
#Sets the value of 'ans' to the triangular of 'x':
ans = triangular(x)
#Exits the loop since answer is found
break
x += 1
#Prints answer (the first triangular number that has more than 500 factors):
print(ans)
#Prints the time taken for code to execute:
print("--- %s seconds ---" % (time.time() - start_time))
python python-3.x programming-challenge time-limit-exceeded mathematics
add a comment |
up vote
0
down vote
favorite
I'm a beginner to programming and just started python, I've been trying to work through Project Euler challenges.
I wrote a program to solve Project Euler #12, which asks for the smallest number of the form 1+ 2 + 3 + … + n which has over 500 divisors. It works perfectly with smaller numbers like 100, but takes too long trying to find up to 500 factors. I gave up waiting after about 20 minutes. I'd like some help with optimizing my code to make it faster.
I basically made a function that finds the triangular number of a given number and another function that counts the number of factors/divisors of a number. I made a while
loop where a number keeps increasing till the triangular number of itself has more than 500 factors.
And here is my code:
#Starts the timer to measure time taken for code to run:
import time
start_time = time.time()
#Returns the triangular number of 'num':
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
#Declares x (starting value):
x = 0
#main:
while True:
#Checks if number of factors for the triangular of x is above 500
if facCount(triangular(x)) > 500:
#Sets the value of 'ans' to the triangular of 'x':
ans = triangular(x)
#Exits the loop since answer is found
break
x += 1
#Prints answer (the first triangular number that has more than 500 factors):
print(ans)
#Prints the time taken for code to execute:
print("--- %s seconds ---" % (time.time() - start_time))
python python-3.x programming-challenge time-limit-exceeded mathematics
1
(Welcome to CR!) With every self-respecting programming challenge, brute force is going to be too slow no matter how competently coded: think (and tag) algorithm.
– greybeard
Mar 30 at 10:38
1
BTW I believe that online code judge engines like Project Euler, Code Chef or others are just a waste of time and drive you into adopting bad programming behaviors. It's worth to better spend time on serious programming books and tutorials.
– πάντα ῥεῖ
Mar 30 at 10:58
1
@πάνταῥεῖ especially that most tasks heavily focus on math or other riddles that you try to solve rather than on the actual software design... and so are the reviews, mostly about math and not software
– t3chb0t
Mar 30 at 15:45
@t3chb0t It does depend on what you're interested in. If you enjoy math challenges and programming, you might try to combine the two to create a well-functioning and well-looking program. :)
– Daniel
Mar 30 at 16:05
@πάνταῥεῖ While I agree in general, Project Euler does not have an online judge. They just write in the general instructions that if your code takes longer than a few minutes (or even seconds), you are probably doing it wrong.
– Graipher
Apr 2 at 16:29
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I'm a beginner to programming and just started python, I've been trying to work through Project Euler challenges.
I wrote a program to solve Project Euler #12, which asks for the smallest number of the form 1+ 2 + 3 + … + n which has over 500 divisors. It works perfectly with smaller numbers like 100, but takes too long trying to find up to 500 factors. I gave up waiting after about 20 minutes. I'd like some help with optimizing my code to make it faster.
I basically made a function that finds the triangular number of a given number and another function that counts the number of factors/divisors of a number. I made a while
loop where a number keeps increasing till the triangular number of itself has more than 500 factors.
And here is my code:
#Starts the timer to measure time taken for code to run:
import time
start_time = time.time()
#Returns the triangular number of 'num':
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
#Declares x (starting value):
x = 0
#main:
while True:
#Checks if number of factors for the triangular of x is above 500
if facCount(triangular(x)) > 500:
#Sets the value of 'ans' to the triangular of 'x':
ans = triangular(x)
#Exits the loop since answer is found
break
x += 1
#Prints answer (the first triangular number that has more than 500 factors):
print(ans)
#Prints the time taken for code to execute:
print("--- %s seconds ---" % (time.time() - start_time))
python python-3.x programming-challenge time-limit-exceeded mathematics
I'm a beginner to programming and just started python, I've been trying to work through Project Euler challenges.
I wrote a program to solve Project Euler #12, which asks for the smallest number of the form 1+ 2 + 3 + … + n which has over 500 divisors. It works perfectly with smaller numbers like 100, but takes too long trying to find up to 500 factors. I gave up waiting after about 20 minutes. I'd like some help with optimizing my code to make it faster.
I basically made a function that finds the triangular number of a given number and another function that counts the number of factors/divisors of a number. I made a while
loop where a number keeps increasing till the triangular number of itself has more than 500 factors.
And here is my code:
#Starts the timer to measure time taken for code to run:
import time
start_time = time.time()
#Returns the triangular number of 'num':
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
#Declares x (starting value):
x = 0
#main:
while True:
#Checks if number of factors for the triangular of x is above 500
if facCount(triangular(x)) > 500:
#Sets the value of 'ans' to the triangular of 'x':
ans = triangular(x)
#Exits the loop since answer is found
break
x += 1
#Prints answer (the first triangular number that has more than 500 factors):
print(ans)
#Prints the time taken for code to execute:
print("--- %s seconds ---" % (time.time() - start_time))
python python-3.x programming-challenge time-limit-exceeded mathematics
python python-3.x programming-challenge time-limit-exceeded mathematics
edited Mar 30 at 15:18
Sᴀᴍ Onᴇᴌᴀ
8,13861752
8,13861752
asked Mar 30 at 10:32
Flaming_Dorito
1132
1132
1
(Welcome to CR!) With every self-respecting programming challenge, brute force is going to be too slow no matter how competently coded: think (and tag) algorithm.
– greybeard
Mar 30 at 10:38
1
BTW I believe that online code judge engines like Project Euler, Code Chef or others are just a waste of time and drive you into adopting bad programming behaviors. It's worth to better spend time on serious programming books and tutorials.
– πάντα ῥεῖ
Mar 30 at 10:58
1
@πάνταῥεῖ especially that most tasks heavily focus on math or other riddles that you try to solve rather than on the actual software design... and so are the reviews, mostly about math and not software
– t3chb0t
Mar 30 at 15:45
@t3chb0t It does depend on what you're interested in. If you enjoy math challenges and programming, you might try to combine the two to create a well-functioning and well-looking program. :)
– Daniel
Mar 30 at 16:05
@πάνταῥεῖ While I agree in general, Project Euler does not have an online judge. They just write in the general instructions that if your code takes longer than a few minutes (or even seconds), you are probably doing it wrong.
– Graipher
Apr 2 at 16:29
add a comment |
1
(Welcome to CR!) With every self-respecting programming challenge, brute force is going to be too slow no matter how competently coded: think (and tag) algorithm.
– greybeard
Mar 30 at 10:38
1
BTW I believe that online code judge engines like Project Euler, Code Chef or others are just a waste of time and drive you into adopting bad programming behaviors. It's worth to better spend time on serious programming books and tutorials.
– πάντα ῥεῖ
Mar 30 at 10:58
1
@πάνταῥεῖ especially that most tasks heavily focus on math or other riddles that you try to solve rather than on the actual software design... and so are the reviews, mostly about math and not software
– t3chb0t
Mar 30 at 15:45
@t3chb0t It does depend on what you're interested in. If you enjoy math challenges and programming, you might try to combine the two to create a well-functioning and well-looking program. :)
– Daniel
Mar 30 at 16:05
@πάνταῥεῖ While I agree in general, Project Euler does not have an online judge. They just write in the general instructions that if your code takes longer than a few minutes (or even seconds), you are probably doing it wrong.
– Graipher
Apr 2 at 16:29
1
1
(Welcome to CR!) With every self-respecting programming challenge, brute force is going to be too slow no matter how competently coded: think (and tag) algorithm.
– greybeard
Mar 30 at 10:38
(Welcome to CR!) With every self-respecting programming challenge, brute force is going to be too slow no matter how competently coded: think (and tag) algorithm.
– greybeard
Mar 30 at 10:38
1
1
BTW I believe that online code judge engines like Project Euler, Code Chef or others are just a waste of time and drive you into adopting bad programming behaviors. It's worth to better spend time on serious programming books and tutorials.
– πάντα ῥεῖ
Mar 30 at 10:58
BTW I believe that online code judge engines like Project Euler, Code Chef or others are just a waste of time and drive you into adopting bad programming behaviors. It's worth to better spend time on serious programming books and tutorials.
– πάντα ῥεῖ
Mar 30 at 10:58
1
1
@πάνταῥεῖ especially that most tasks heavily focus on math or other riddles that you try to solve rather than on the actual software design... and so are the reviews, mostly about math and not software
– t3chb0t
Mar 30 at 15:45
@πάνταῥεῖ especially that most tasks heavily focus on math or other riddles that you try to solve rather than on the actual software design... and so are the reviews, mostly about math and not software
– t3chb0t
Mar 30 at 15:45
@t3chb0t It does depend on what you're interested in. If you enjoy math challenges and programming, you might try to combine the two to create a well-functioning and well-looking program. :)
– Daniel
Mar 30 at 16:05
@t3chb0t It does depend on what you're interested in. If you enjoy math challenges and programming, you might try to combine the two to create a well-functioning and well-looking program. :)
– Daniel
Mar 30 at 16:05
@πάνταῥεῖ While I agree in general, Project Euler does not have an online judge. They just write in the general instructions that if your code takes longer than a few minutes (or even seconds), you are probably doing it wrong.
– Graipher
Apr 2 at 16:29
@πάνταῥεῖ While I agree in general, Project Euler does not have an online judge. They just write in the general instructions that if your code takes longer than a few minutes (or even seconds), you are probably doing it wrong.
– Graipher
Apr 2 at 16:29
add a comment |
3 Answers
3
active
oldest
votes
up vote
4
down vote
accepted
Your function facCount
, which should be called something like count_factors
, according to Python's official style-guide, PEP8, can be greatly improved by noticing that if num % i == 0
, then automatically num % num / i == 0
(in other words, factors always come in pairs of two, except for if the number is a square, in which case you double count one). This means you only need to check factors up to $sqrt{n}$:
from math import sqrt
def count_factors(num):
"""Return the number of factors of `num`"""
sum_ = 2 * sum(num % i == 0 for i in range(1, int(sqrt(num)) + 1))
if int(sqrt(num))**2 == num:
sum_ -= 1
return sum_
I also added a docstring describing what the function does in an accessible way.
The other improvement concerns the way you get the triangular numbers. While it is good to know Gauss formula, IMO it is here easier to manually calculate them. Your function needs to do one increment, one multiplication and one division, when all you really need is one addition per loop iteration:
from itertools import count
def triangular_nums():
"""Yield the triangular numbers"""
t = 0
for i in count():
t += i
yield t
If, for some reason, you dislike itertools
, you can also replace it with a while True
loop.
In Python 3+, you can use the new function itertools.accumulate
(3.2+) and the new keyword combination yield from
(3.3+), as mentioned in the comments by @MaartenFabré:
from itertools import accumulate, count
def triangular_nums():
yield from accumulate(count())
With this your main
function (which you should either make a real function or at least put under an if __name__ == "__main__":
guard) becomes:
def main():
for t in triangular_nums():
if count_factors(t) > 500:
return t
if __name__ == "__main__":
ans = main()
if ans is not None:
print(ans)
The timing you should factor out and make into a decorator:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
print("--- %s seconds ---" % (time.time() - start))
return ret
return wrapper
@timeit
def main():
...
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
|
show 3 more comments
up vote
1
down vote
The point of this particular Project Euler question is to teach you about the number-of-divisors function.
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
Observe that some of the factors of triangular(n)
are also factors of triangular(n+1)
. Why? How can you use that reason to avoid doing the same work more than once?
Leaving aside the algorithmic considerations,
def triangular(num):
return int((num*(num+1))/2)
doesn't need int
if you use integer division:
def triangular(num):
return num * (num + 1) // 2
add a comment |
up vote
0
down vote
You have to check against until num/2, num/2 is the largest factor for a even number, the sqrt is to get the largest prime factor. And attach itself to the list at last.
New contributor
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f190852%2fproject-euler-12-highly-divisible-triangular-numbers-in-python-3-x%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
Your function facCount
, which should be called something like count_factors
, according to Python's official style-guide, PEP8, can be greatly improved by noticing that if num % i == 0
, then automatically num % num / i == 0
(in other words, factors always come in pairs of two, except for if the number is a square, in which case you double count one). This means you only need to check factors up to $sqrt{n}$:
from math import sqrt
def count_factors(num):
"""Return the number of factors of `num`"""
sum_ = 2 * sum(num % i == 0 for i in range(1, int(sqrt(num)) + 1))
if int(sqrt(num))**2 == num:
sum_ -= 1
return sum_
I also added a docstring describing what the function does in an accessible way.
The other improvement concerns the way you get the triangular numbers. While it is good to know Gauss formula, IMO it is here easier to manually calculate them. Your function needs to do one increment, one multiplication and one division, when all you really need is one addition per loop iteration:
from itertools import count
def triangular_nums():
"""Yield the triangular numbers"""
t = 0
for i in count():
t += i
yield t
If, for some reason, you dislike itertools
, you can also replace it with a while True
loop.
In Python 3+, you can use the new function itertools.accumulate
(3.2+) and the new keyword combination yield from
(3.3+), as mentioned in the comments by @MaartenFabré:
from itertools import accumulate, count
def triangular_nums():
yield from accumulate(count())
With this your main
function (which you should either make a real function or at least put under an if __name__ == "__main__":
guard) becomes:
def main():
for t in triangular_nums():
if count_factors(t) > 500:
return t
if __name__ == "__main__":
ans = main()
if ans is not None:
print(ans)
The timing you should factor out and make into a decorator:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
print("--- %s seconds ---" % (time.time() - start))
return ret
return wrapper
@timeit
def main():
...
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
|
show 3 more comments
up vote
4
down vote
accepted
Your function facCount
, which should be called something like count_factors
, according to Python's official style-guide, PEP8, can be greatly improved by noticing that if num % i == 0
, then automatically num % num / i == 0
(in other words, factors always come in pairs of two, except for if the number is a square, in which case you double count one). This means you only need to check factors up to $sqrt{n}$:
from math import sqrt
def count_factors(num):
"""Return the number of factors of `num`"""
sum_ = 2 * sum(num % i == 0 for i in range(1, int(sqrt(num)) + 1))
if int(sqrt(num))**2 == num:
sum_ -= 1
return sum_
I also added a docstring describing what the function does in an accessible way.
The other improvement concerns the way you get the triangular numbers. While it is good to know Gauss formula, IMO it is here easier to manually calculate them. Your function needs to do one increment, one multiplication and one division, when all you really need is one addition per loop iteration:
from itertools import count
def triangular_nums():
"""Yield the triangular numbers"""
t = 0
for i in count():
t += i
yield t
If, for some reason, you dislike itertools
, you can also replace it with a while True
loop.
In Python 3+, you can use the new function itertools.accumulate
(3.2+) and the new keyword combination yield from
(3.3+), as mentioned in the comments by @MaartenFabré:
from itertools import accumulate, count
def triangular_nums():
yield from accumulate(count())
With this your main
function (which you should either make a real function or at least put under an if __name__ == "__main__":
guard) becomes:
def main():
for t in triangular_nums():
if count_factors(t) > 500:
return t
if __name__ == "__main__":
ans = main()
if ans is not None:
print(ans)
The timing you should factor out and make into a decorator:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
print("--- %s seconds ---" % (time.time() - start))
return ret
return wrapper
@timeit
def main():
...
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
|
show 3 more comments
up vote
4
down vote
accepted
up vote
4
down vote
accepted
Your function facCount
, which should be called something like count_factors
, according to Python's official style-guide, PEP8, can be greatly improved by noticing that if num % i == 0
, then automatically num % num / i == 0
(in other words, factors always come in pairs of two, except for if the number is a square, in which case you double count one). This means you only need to check factors up to $sqrt{n}$:
from math import sqrt
def count_factors(num):
"""Return the number of factors of `num`"""
sum_ = 2 * sum(num % i == 0 for i in range(1, int(sqrt(num)) + 1))
if int(sqrt(num))**2 == num:
sum_ -= 1
return sum_
I also added a docstring describing what the function does in an accessible way.
The other improvement concerns the way you get the triangular numbers. While it is good to know Gauss formula, IMO it is here easier to manually calculate them. Your function needs to do one increment, one multiplication and one division, when all you really need is one addition per loop iteration:
from itertools import count
def triangular_nums():
"""Yield the triangular numbers"""
t = 0
for i in count():
t += i
yield t
If, for some reason, you dislike itertools
, you can also replace it with a while True
loop.
In Python 3+, you can use the new function itertools.accumulate
(3.2+) and the new keyword combination yield from
(3.3+), as mentioned in the comments by @MaartenFabré:
from itertools import accumulate, count
def triangular_nums():
yield from accumulate(count())
With this your main
function (which you should either make a real function or at least put under an if __name__ == "__main__":
guard) becomes:
def main():
for t in triangular_nums():
if count_factors(t) > 500:
return t
if __name__ == "__main__":
ans = main()
if ans is not None:
print(ans)
The timing you should factor out and make into a decorator:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
print("--- %s seconds ---" % (time.time() - start))
return ret
return wrapper
@timeit
def main():
...
Your function facCount
, which should be called something like count_factors
, according to Python's official style-guide, PEP8, can be greatly improved by noticing that if num % i == 0
, then automatically num % num / i == 0
(in other words, factors always come in pairs of two, except for if the number is a square, in which case you double count one). This means you only need to check factors up to $sqrt{n}$:
from math import sqrt
def count_factors(num):
"""Return the number of factors of `num`"""
sum_ = 2 * sum(num % i == 0 for i in range(1, int(sqrt(num)) + 1))
if int(sqrt(num))**2 == num:
sum_ -= 1
return sum_
I also added a docstring describing what the function does in an accessible way.
The other improvement concerns the way you get the triangular numbers. While it is good to know Gauss formula, IMO it is here easier to manually calculate them. Your function needs to do one increment, one multiplication and one division, when all you really need is one addition per loop iteration:
from itertools import count
def triangular_nums():
"""Yield the triangular numbers"""
t = 0
for i in count():
t += i
yield t
If, for some reason, you dislike itertools
, you can also replace it with a while True
loop.
In Python 3+, you can use the new function itertools.accumulate
(3.2+) and the new keyword combination yield from
(3.3+), as mentioned in the comments by @MaartenFabré:
from itertools import accumulate, count
def triangular_nums():
yield from accumulate(count())
With this your main
function (which you should either make a real function or at least put under an if __name__ == "__main__":
guard) becomes:
def main():
for t in triangular_nums():
if count_factors(t) > 500:
return t
if __name__ == "__main__":
ans = main()
if ans is not None:
print(ans)
The timing you should factor out and make into a decorator:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
print("--- %s seconds ---" % (time.time() - start))
return ret
return wrapper
@timeit
def main():
...
edited Apr 3 at 8:32
answered Apr 2 at 19:28
Graipher
23.1k53384
23.1k53384
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
|
show 3 more comments
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
This was very helpful! Took a while to understand it exactly since I'm a newbie :P but thanks! :D
– Flaming_Dorito
Apr 6 at 18:55
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
Actually, sorry for the late reply, but could you expand the count_factors function by using a multi-line for loop instead of the single line ones you used, I'm kinda unfamiliar with single-line for loops. Thanks.
– Flaming_Dorito
Apr 18 at 12:59
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
@Flaming_Dorito In that case you should make yourself familiar with Python's list-comprehensions/generator-expressions. This particular one is roughly equivalent to: gist.github.com/graipher/cbdf5bbcaa7e30f48392e04f85fec9e6
– Graipher
Apr 18 at 13:03
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
Thanks, good idea, I probably should :) Also, I managed to make it a multiline function: pastebin.com/xV6yU7Ai But it was faster than the one you provided, I'm kinda confused since it does the exact same thing as your function. I figured it should take the same time. But it takes approximately a fifth of the time almost every time.
– Flaming_Dorito
Apr 18 at 13:20
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
You can see the time difference by running this: pastebin.com/fuRPfg4H
– Flaming_Dorito
Apr 18 at 13:24
|
show 3 more comments
up vote
1
down vote
The point of this particular Project Euler question is to teach you about the number-of-divisors function.
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
Observe that some of the factors of triangular(n)
are also factors of triangular(n+1)
. Why? How can you use that reason to avoid doing the same work more than once?
Leaving aside the algorithmic considerations,
def triangular(num):
return int((num*(num+1))/2)
doesn't need int
if you use integer division:
def triangular(num):
return num * (num + 1) // 2
add a comment |
up vote
1
down vote
The point of this particular Project Euler question is to teach you about the number-of-divisors function.
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
Observe that some of the factors of triangular(n)
are also factors of triangular(n+1)
. Why? How can you use that reason to avoid doing the same work more than once?
Leaving aside the algorithmic considerations,
def triangular(num):
return int((num*(num+1))/2)
doesn't need int
if you use integer division:
def triangular(num):
return num * (num + 1) // 2
add a comment |
up vote
1
down vote
up vote
1
down vote
The point of this particular Project Euler question is to teach you about the number-of-divisors function.
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
Observe that some of the factors of triangular(n)
are also factors of triangular(n+1)
. Why? How can you use that reason to avoid doing the same work more than once?
Leaving aside the algorithmic considerations,
def triangular(num):
return int((num*(num+1))/2)
doesn't need int
if you use integer division:
def triangular(num):
return num * (num + 1) // 2
The point of this particular Project Euler question is to teach you about the number-of-divisors function.
def triangular(num):
return int((num*(num+1))/2)
#Returns the number of factors(divisors) of 'num':
def facCount(num):
summ = 0
for i in range(1, num+1):
if num % i == 0:
summ += 1
return summ
Observe that some of the factors of triangular(n)
are also factors of triangular(n+1)
. Why? How can you use that reason to avoid doing the same work more than once?
Leaving aside the algorithmic considerations,
def triangular(num):
return int((num*(num+1))/2)
doesn't need int
if you use integer division:
def triangular(num):
return num * (num + 1) // 2
answered Apr 3 at 9:41
Peter Taylor
15.5k2658
15.5k2658
add a comment |
add a comment |
up vote
0
down vote
You have to check against until num/2, num/2 is the largest factor for a even number, the sqrt is to get the largest prime factor. And attach itself to the list at last.
New contributor
add a comment |
up vote
0
down vote
You have to check against until num/2, num/2 is the largest factor for a even number, the sqrt is to get the largest prime factor. And attach itself to the list at last.
New contributor
add a comment |
up vote
0
down vote
up vote
0
down vote
You have to check against until num/2, num/2 is the largest factor for a even number, the sqrt is to get the largest prime factor. And attach itself to the list at last.
New contributor
You have to check against until num/2, num/2 is the largest factor for a even number, the sqrt is to get the largest prime factor. And attach itself to the list at last.
New contributor
New contributor
answered 9 mins ago
Matt Cao
11
11
New contributor
New contributor
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f190852%2fproject-euler-12-highly-divisible-triangular-numbers-in-python-3-x%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
(Welcome to CR!) With every self-respecting programming challenge, brute force is going to be too slow no matter how competently coded: think (and tag) algorithm.
– greybeard
Mar 30 at 10:38
1
BTW I believe that online code judge engines like Project Euler, Code Chef or others are just a waste of time and drive you into adopting bad programming behaviors. It's worth to better spend time on serious programming books and tutorials.
– πάντα ῥεῖ
Mar 30 at 10:58
1
@πάνταῥεῖ especially that most tasks heavily focus on math or other riddles that you try to solve rather than on the actual software design... and so are the reviews, mostly about math and not software
– t3chb0t
Mar 30 at 15:45
@t3chb0t It does depend on what you're interested in. If you enjoy math challenges and programming, you might try to combine the two to create a well-functioning and well-looking program. :)
– Daniel
Mar 30 at 16:05
@πάνταῥεῖ While I agree in general, Project Euler does not have an online judge. They just write in the general instructions that if your code takes longer than a few minutes (or even seconds), you are probably doing it wrong.
– Graipher
Apr 2 at 16:29