Project Euler #12 (Highly divisible triangular numbers) in Python 3.x











up vote
0
down vote

favorite
2












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))









share|improve this question




















  • 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















up vote
0
down vote

favorite
2












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))









share|improve this question




















  • 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













up vote
0
down vote

favorite
2









up vote
0
down vote

favorite
2






2





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))









share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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














  • 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










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():
...





share|improve this answer























  • 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


















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





share|improve this answer




























    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.





    share








    New contributor




    Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.


















      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
      });


      }
      });














      draft saved

      draft discarded


















      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():
      ...





      share|improve this answer























      • 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















      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():
      ...





      share|improve this answer























      • 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













      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():
      ...





      share|improve this answer














      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():
      ...






      share|improve this answer














      share|improve this answer



      share|improve this answer








      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


















      • 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












      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





      share|improve this answer

























        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





        share|improve this answer























          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





          share|improve this answer












          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






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Apr 3 at 9:41









          Peter Taylor

          15.5k2658




          15.5k2658






















              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.





              share








              New contributor




              Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
              Check out our Code of Conduct.






















                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.





                share








                New contributor




                Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.




















                  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.





                  share








                  New contributor




                  Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.









                  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.






                  share








                  New contributor




                  Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.








                  share


                  share






                  New contributor




                  Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.









                  answered 6 mins ago









                  Matt Cao

                  11




                  11




                  New contributor




                  Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.





                  New contributor





                  Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.






                  Matt Cao is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.






























                      draft saved

                      draft discarded




















































                      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.




                      draft saved


                      draft discarded














                      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





















































                      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







                      Popular posts from this blog

                      Ellipse (mathématiques)

                      Quarter-circle Tiles

                      Mont Emei