2
\$\begingroup\$

I'm looking to optimize a simple sum calculator used in Project Euler+. The premise is simple: Find the sum of all the numbers divisible by 3 or 5 under X.

At first I did it like so:

def main(testCases):
    final_answer = []
    for x in range(testCases):
        answer = 0
        test = long(input())
        for num in range(test):
            if (num % 5 == 0 or num % 3 == 0):
                print(num)
                answer += num
        final_answer.append(str(answer))
    print('\n'.join(final_answer))
main(input())

Which worked alright. Then I realized it couldn't handle anything like 10,000,000,000 at all, which was bad, so I tried:

def main(testCases):
    for x in range(testCases):
        answer = 0
        num = input()

        Temp = (num - 1) // 3
        answer += (Temp * (3 + Temp * 3)) // 2
        Temp = (num - 1) // 5
        answer += (Temp * (5 + Temp * 5)) // 2
        Temp = (num - 1) // 15
        answer -= (Temp * (15 + Temp * 15)) // 2
        print answer
main(input())

Which was way faster / capable of giant numbers. However, it seems really ugly and unoptimized, but maybe it's in my head. Can anyone help me optimize my answer?

Sample

Input:

2
10
100

Output:

23
2318
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

One way would be to find the sum of all numbers divisible by three, five and fifteen. To do this define another function that calculates the sum of first n number using the formula \$\sum\limits_{k=1}^n k = \frac{n(n+1)}{2}\$. Now to find the sum of all number divisible by three all we need to do is pass the quotient of num // 3 to sum_of_n_numbers and multiple its result by 3, same thing of five and fifteen as well.

In the end we simply subtract the sum of all fifteen from sum of five and three because we counted those twice.

def sum_of_n_numbers(n):
    return (n*(n+1))/2.0

def ashwch(test_cases):
    for _ in xrange(test_cases):
        num = int(raw_input()) - 1
        sum_of_threes = 3 *  sum_of_n_numbers(num//3)
        sum_of_fives = 5 *  sum_of_n_numbers(num//5)
        sum_of_fifteenes = 15 *  sum_of_n_numbers(num//15)
        print sum_of_three + sum_of_fives - sum_of_fifteenes

Few other points:

  • Don't use variable names that start with capital letter for normal variables, such variable names are recommended for class names.

  • In case number of test-cases are huge you will end up creating a huge list in memory because range() returns a list in Python 2. This applies to your first code as well where you've used range() for the number itself, so your computer will crash with inputs like 10,000,000,000. So, better use xrange() in Python 2.

  • I have used the // operator instead of / because it will round down the number in both Python 2 and 3.

  • As this is from a programming challenge you may not need to worry about malicious inputs, but try not to use input() in Python 2 as it evals anything that user enters. So an input like __import__('os').system('ls -l') can be deadly if you replace ls -l with something else. So, use raw_input with int().

>>> input()
__import__('os').system('ls -l')
total 586952
-rw-rw-r-- 1 ashwini ashwini   3468213 Jan 27 02:54 1.txt
-rw-rw-r-- 1 ashwini ashwini   3467213 Jan 27 03:10 4.txt
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.