A fun question! A little mathematical analysis can go a long way in reducing the amount of computation:
First of all, you don't actually need to iterate anything. To get the sum of all the numbers from 1 to n
, just get the midpoint between the first and last numbers, which is simply the arithmetic average (1+n)/2
. Then multiply this average by n
. So the sum of all the numbers from 1
to n
is:
((1+n)/2) * n
Similarly, the sum of all the multiples of a
from a
to z
(assuming z
is a multiple of a
) is the midpoint between a
and z
times the number of multiples of a
in the sequence a .. z
.
So, since there are 333 numbers in the sequence 3,6,9,12 ... 999
, the sum of all the multiples of 3 that are less than 1000 is:
((3+999)/2) * 333
or, in other words 333 x 501
.
And the sum of all the multiples of 5 that are less than 1000 would be:
((5+995)/2) * 199
which is 199 x 500
.
If we added these two sums together, we would be almost done, except for one little detail: each number that is a multiple of 15 was added twice. So we have to subtract those.
The sum of all the multiples of 15 that are less than 1000 is ((15+990)/2) * 66
, or 66 x 502.5
.
So our answer would be:
((3+999)/2) * 333 + ((5+995)/2) * 199 - ((15+990)/2) * 66
which is
166,833 + 99,500 - 33,165
or 233,168
.
A "technically correct, but totally useless" answer to the code challenge would be
def get_the_sum
233168
end
or without the syntactic shortcuts
def get_the_sum
sum = 233168
return sum
end
We can, however, write a more useful general purpose method
sum_all_multiples_ab(a,b,max)
that finds the sum of all multiples of a
or b
that are less than max
. Then the answer to your example problem would be given by:
sum_all_multiples_ab(3, 5, 1000)
We could define such a method like this:
def sum_all_multiples_ab(a, b, max)
sum = sum_all_multiples(a, max)
sum += sum_all_multiples(b, max)
sum -= sum_all_multiples(a*b, max)
sum
end
where sum_all_multiples()
is defined by
def sum_all_multiples(n,max)
count = (max-1)/n
last = count * n
sum = ((n + last) / 2.0) * count
sum
end
We need to specify 2.0
in the last computation because we want a real number result, whereas for count
(the number of values) and last
(the last multiple less than 1000 or max
) we want the truncated integer result.
(We will only get an odd number for the average when we have an even number of values, so the ultimate result will always be an integer.)
(1...1000).select { |x| x % 3 == 0 || x % 5 == 0 }.reduce(:+)
. Food for thought: en.wikipedia.org/wiki/Side_effect_(computer_science) and en.wikipedia.org/wiki/Functional_programming \$\endgroup\$