Project Euler Problem 1 – Sum of Multiples of 3 and 5

02.03.2015

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.

  1. To practice object oriented design and apply lessons from Practical Object Oriented Design in Ruby
  2. To practice unit testing and experiment with minitest
  3. 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.