Project Euler Series: Problem 1

July 28, 2016

Dylan Richards

Solving the problems on Projet Euler is a great way to dive into programming. In this post, I’d like to solve Problem 1, while covering some Ruby language fundamentals. We’ll look at three different approaches to solving the same problem.

Let’s get started.

  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.

Iteration 1

The first thing we’ll need is a collection of numbers from 1 to 999.

  
    numbers = (1..999).to_a # => [1, 2, 3, 4, ..., ..., 999]
  

Next, we’ll need a starting value.

  
    answer = 0
  

Here’s the fun part. Let’s use an each loop to iterate over each of those numbers.

  
    numbers.each do |number|
    end
  

Every time we encounter a number that’s a multiple of either 3 or 5, we need to increment the answer value.

  
    if number % 3 == 0 || number % 5 == 0
      answer += number # => Add the number to the eventual answer if it's evenly divisible by 3 or 5
    end
  

You should now have this:

  
    numbers = (1..999).to_a
    answer = 0

    numbers.each do |number|
      if number % 3 == 0 || number % 5 == 0
        answer += number
      end
    end
  

Now we just need to return the answer.

  
    p answer # => 233168
  

Done.

We can do better, though. Whenever you find yourself creating an empty array, putting items into it, then finally returning it, there may be an opportunity to use Array#map. #map creates a new array containing the values returned by the block. Check it out:

Iteration 2

  
    numbers = (1..999).to_a.map { |num| num if num % 3 == 0 || num % 5 == 0 }
  

The code above takes the array of numbers 1-999 and creates a new array of elements that match the condition in the block. In this case, it will be numbers that are evenly divisible by 3 or 5.

Let’s sum them up.

  
    numbers.reduce(&:+)
  

Wait – What’s this?

  
    euler-1.rb:5:in each: undefined method '+' for nil:NilClass (NoMethodError)
    from euler-1.rb:5:in reduce
    from euler-1.rb:5:in main
  

This error is telling us that the addition method isn’t available on an element in our array, which happens to be nil. Why are there nil elements in the array? Simple. Our map returned nil for each element that didn’t match the condition.

Before we can sum the array, we must first remove the nil elements with Array#compact.

  
    numbers.compact.reduce(&:+) #=> 233168
  

And there you have it.

Iteration 3

We can still do a little bit better, though. Array#select allows us to, well, select elements in an array that match a condition. It won’t return nil for elements that don’t match, which will save us from having to remove nil elements from the array before adding them up.

  
    p (1..999).select { |num| num % 3 == 0 || num % 5 == 0 }.reduce(:+) #=> 233168
  

And that’s it.

Comments

comments powered by Disqus