Code: Select all
print sum(x for x in xrange(1,1000) if x % 3 == 0 or x % 5 == 0)
Really though, it's not too bad for a beginner programmer. I remember I did something similar when I solved that the first time. Kudos on the comments as well, they're saying what the code doesn't. Information in comments should never overlap information in the code.
Some comments on the code itself:
Reusing variables is usually considered bad. You can make as many of them as you want, and give them any name you want, so there's no reason why you should have to reuse l in two loops. Problems with reusing variables is that you might've e.g. forgotten to reset it before the second loop. Also, when you know you have to loop a finite number of steps and/or using a loop-variable, a for loop is usually preferable (in python looping x amount of times is simply 'for i in xrange(x):', more about xrange later). While is for when you have no easy way to know how many loops it takes to finish, (e.g. 'while n != 1: if n % 2: n = n * 3 + 1; else: n = n / 2') but this distiction is rather fuzzy, so don't worry too much about it. Here, however, it's not hard to see there are 1000/3 numbers divisible by 3 below 1000, and 1000/5 numbers divisible by 5. For loops also initialize your variables for you, so you don't have to worry about resetting them. In most other languages, for also introduces scope, so the looping variable isn't visible after you've finished with the loop. For some unknown reason, this doesn't apply to python... Most consider it a bug.
A list has an append method, so instead of doing 'threes[-1:] += [threel]' you can just do 'threes.append(threel)'.
Two things to help you discover such things are 'dir' and '__doc__'. 'dir' is a method that returns all attributes of an object, and '__doc__' is an attribute all objects have that contains a documentation string, hopefully describing the object. This is useful in helping you not reinventing the wheel. For example, you might have a string containing a chapter of a book. You want to work on each sentance individually, so you'd like some way to automatically split the string into a list of strings containing sentances. If you do 'dir(string)' you'll see that it has a method by the promising name of 'split', so you try that. Unfortunately, what you got back was a list of words, not sentances. by trying 'print string.__doc__' you get the docstring, telling you the default is to split on space, but if you want you can supply you own split token, such as the period.
There's always the internet too.
Functions are your friend. you've got two pieces of code that look almost identical with just a few minor adjustments. In the vast majority of cases this means it should probably be a function:
Code: Select all
def findAllMultiples(multipleOf, maxNumber):
numberList = 
for number in xrange(1, maxNumber / multipleOf + 1): # We need +1 because xrange stops on the number before
You can then replace half of your code with 'threes = findAllMultiples(3, limit); fives = findAllMultiples(5, limit)', and you also don't have to worry about resetting l.
Now for a bit on the algorithm:
Using a set was clever, but it would've been even cleverer to realize you don't actually need the collection
of the numbers, just their sum. The problem here is the duplicates, and that's the remainder comes in. I'm not sure how familiar you are with it, so I'm going to assume you're not for the sake of completeness. I remember being taught about division and remainder in elementary school and used division all the time since then, it being an integral part of mathematics. Remainder, however, I had completely forgotten about until I started coding, where it became immediately useful and more prevailent than division. In python (and most languages) it's the operator %, which is actually the modulo operator, which is slightly different for negative numbers. Anyway, the following equation is always true except when b = 0: a / b = b * n + r. Here, n is the result of the division (remember, integer division always returns an integer, rounded towards zero) and r is the remainder of the division. This tells us that when r = 0 a must be a multiple of b, just because n has to be an integer.
Before we go on I'll explain range/xrange. Both do the same, essentially, but differ in how
they do it. When given a number, range produces a list with all the numbers from 0 to that number minus one (the length of the list will be equal to the number which is usually what we want. When looping, the elements of the list are usually unimportant, but the length is not because it decides how many loops we'll do). 'range(5)' produces '[0,1,2,3,4]'. When given two numbers, it produces a list from the first number to the second minus one, so 'range(3,6)' produces '[3,4,5]'. When given three numbers it produces a list from the first, to the second (but not the second itself), using increments of the third, so 'range(3,10,3)' produces '[3,6,9]'. You can also give it negative increments, so 'range(5,0,-1)' produces '[5,4,3,2,1]'
xrange works just the same as range, except instead of creating all the numbers and returning a list, instead it returns a generator
that produces the numbers one at a time when asked to. The for loop is smart enough to know what to do with a generator, making it possible to loop over all the numbers in a range without actually creating the list itself. The advantage of using a generator is that you don't have to waste memory and time creating and moving around large lists, while the disadvantage is that once a generator has returned something it forgets everything about it, making it impossible to re-use them. (You could create several generators generating the same thing, but that means calculating the same things several times as well when a list means only doing it once).
Combining remainder and xrange we can easily loop over all numbers below 1000 and collect the ones we want:
Code: Select all
totalSum = 0
for number in xrange(1,1000):
if number % 3 == 0: # is a multiple of 3
totalSum += number
if number % 5 == 0 and not number % 3 == 0: # is a multiple of 5 and NOT a multiple of 3 (if it is, we already added it)
totalSum += number
Once we've gotten this far, we see that we can remove the 'and not number % 3 == 0' bit by using 'elif' because that won't be run unless the first if is false. Seeing how both conditional statements do exactly the same, we can combine them into one by oring them together: 'if number % 3 == 0 or number % 5 == 0:'
If you're up on python syntax, you might know about list comprehension, which looks like '[statement(element) for element in sequence if condition(element)]', e.g. '[x*2 for x in xrange(1,5) if x != 4]' produces '[2,4,6,10]'. You might also know about generator comprehension, which is exactly the same except it looks like '(statement(element) for element in sequence if condition(element) )' and produces a generator instead of a list. If it's the only argument to a function, one pair of parenthesis is enough. This means we can easily feed the for loop a sequence of only the interesting numbers and removing the if, using 'for number in (x for x in xrange(1,1000) if x % 3 == 0 or x % 5 == 0):'.
You might also know about the 'sum' function, which takes a sequence of numbers and returns the sum of all of them, which is exactly what we do with our for loop, so we can replace that as well, yielding the final 'sum(x for x in xrange(1,1000) if x % 3 == 0 or x % 5 == 0)'.
There is an even more clever way to solve this problem, by only generating the numbers that are multiples of 3, 5 or both in such a way that there are no duplicates, but if you're able to do that you get the solution to problem 204 for free as well so I won't go into details.
I've also noticed I have a habit of turning the targeted intelligence level of my explanations down the more I explain until a kindergardener could make sense of it. Sorry if I did that here, I'm not being condescending!
It is practically impossible to teach good programming to students who are motivated by money: As potential programmers they are mentally mutilated beyond hope of regeneration.