3
\$\begingroup\$

I have done #1 of Project Euler's in Ruby. Using two different approaches. One uses set, another uses reduce.

require 'set'
start = Time.now
s1 = Set.new (1..1000/3.floor).map { |x| 3 * x }
s2 = Set.new (1..1000/5.floor).map { |x| 5 * x }
sum = s1.merge(s2).to_a.inject(:+)
duration = Time.now - start 
puts "Sum: #{sum}, time elapsed #{duration} s"

Second method

start = Time.now
sum = (1..1000).select{ |x| x % 3 == 0 || x % 5 == 0 }.reduce(:+)
duration = Time.now - start

The second method consistently performs better than first method:

method 1: time elapsed 0.000364 s

method 2: time elapsed 0.000198 s

I wonder if it is a correct way to time how long the process takes. And whether it is considered to be performant. Also curious how other languages performs compares to this two algo. Any advice are welcomed~

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Mar 30, 2015 at 8:38
\$\endgroup\$
1
  • \$\begingroup\$ Just an observation (and a personal preference), I favor .zero?, so x % 3 == 0 becomes (x % 3).zero?, and same with the second check. \$\endgroup\$ Commented Mar 31, 2015 at 18:36

2 Answers 2

2
\$\begingroup\$

Performing benchmarks with such small values is pretty much meaningless, you will usually be concerned with larger values. And this is how you tipically compare the time performance of different snippets:

require 'benchmark'
require 'set'
n = 1_000_000
Benchmark.bm do |b|
 b.report("sets") do
 s1 = Set.new (1..n/3.floor).map { |x| 3 * x }
 s2 = Set.new (1..n/5.floor).map { |x| 5 * x }
 sum = s1.merge(s2).inject(0, :+)
 end
 b.report("select") do
 sum = (1..n).select { |x| x % 3 == 0 || x % 5 == 0 }.reduce(0, :+)
 end
end

Output:

 user system total real
sets 0.550000 0.020000 0.570000 ( 0.580492)
select 0.200000 0.000000 0.200000 ( 0.207238)
answered Mar 30, 2015 at 11:50
\$\endgroup\$
0
1
\$\begingroup\$

Code duplication:

s1 = Set.new (1..n/3.floor).map { |x| 3 * x }
s2 = Set.new (1..n/5.floor).map { |x| 5 * x }

The 2 lines above are pretty similar aren't them? What if you want to add 100 more divisors? Will you write 100 more lines? I suggest using some functions:

def divisible_by_up_to(divisor, limit)
 Set.new (1..limit/divisor.floor).map { |x| divisor * x}
end
def divisible_by_all_up_to(divisors_list, limit)
 # This is pseudo-code
 set.merge_all([divisible_by_up_to(limit, divisor)
 for divisors in divisors_list])
end

Your code than becomes more easily exapandable, modular and readable

sum = divisible_by_all_up_to([3,5], 1000).to_a.inject(:+)
answered Mar 30, 2015 at 12:06
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.