Project Euler Problem 1 – Sum of Multiples of 3 and 5
This is my first in a series of posts where I’ll be working through Project Euler Problems in Ruby. My goal is to write the code in an “object oriented” way along with unit tests.
There are a few reasons why I am doing this.
- To practice object oriented design and apply lessons from Practical Object Oriented Design in Ruby
- To practice unit testing and experiment with minitest
- To practice problem solving with algorithms
And last, but not least I’m doing it because I think it’s fun.
I will post the code on GitHub.
Here is the first Project Euler problem. I remember getting a problem very similar to this one at a job interview at one of my first coding jobs.
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
Here was my solution before refactoring:
require 'minitest/autorun' class Problem1 def self.sum_of_multiples_of_3_and_5 num sum = 0 (1..num-1).each do |i| if i%5 == 0 || i%3 == 0 sum += i end end sum end end class Problem1Test < MiniTest::Unit::TestCase def test_sum_of_multiples_of_3_and_5 result = Problem1.sum_of_multiples_of_3_and_5 10 assert_equal 23, result end end
I built a simple test case based on the example in the question. Then I wrote the class along with the class method to run the calculation. We iterate through each number from 1 up to one below the number passed in to the method. We add it to the sum if it is a multiple of 5 or 3. We can test that with the modulus operator. Then we return the sum.
At this point, we're able to get the right answer using this class. While the code works, it is limited to only multiples of 3 and 5. Let's refactor the test so we can pass in any two numbers into an Problem Solver instance.
Here is my solution after refactoring:
require 'minitest/autorun' class Problem1 attr_accessor :first, :second def initialize first, second @first = first @second = second end def solve num sum = 0 (1..num-1).each do |i| if i % @first == 0 || i % @second == 0 sum += i end end sum end end class Problem1Test < MiniTest::Unit::TestCase def test_sum_of_multiples_of_3_and_5 solver = Problem1.new 3, 5 answer = solver.solve 10 assert_equal 23, answer end def test_sum_of_multiples_of_2_and_4 solver = Problem1.new 2, 4 answer = solver.solve 10 assert_equal 20, answer end end
Now we can instantiate a Problem1 object with two parameters. There is a way to pass in any number of parameters using the ruby splat * or simply passing in an array, but I'll leave that up as an exercise for the reader.
Thanks for reading.