Skip to content

Instantly share code, notes, and snippets.

@stephancom
Created January 4, 2017 06:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stephancom/174aa5df0f8e689a30669328a111ee1d to your computer and use it in GitHub Desktop.
Save stephancom/174aa5df0f8e689a30669328a111ee1d to your computer and use it in GitHub Desktop.
FizzBuzz - generalized, improved, tested, benchmarked
# fizzbuzz benchmark - comparing different versions of the classic
# stephan.com
require 'rspec'
require 'benchmark'
require 'formatador'
# suppress output
def no_stdout
ostd = $stdout
$stdout = StringIO.new
yield
$stdout = ostd
end
# generalized version with classic method
def genfizz(n,msgs)
1.upto(n) do |i|
str = msgs.inject('') do |str, (m, text)|
(i%m==0) ? str + text : str
end
puts str.empty? ? i : str
end
end
def fizzbuzz(n)
genfizz(n, 3 => 'fizz', 5 => 'buzz')
end
# generalized version with seive method
def seivefizz(n, msgs)
texts = msgs.inject([]) do |texts, (m, text)|
(m..n).step(m) do |i|
texts[i] = (texts[i] || '') + text
end
texts
end
1.upto(n) do |i|
puts texts[i] || i
end
end
# tests to make sure these things actually work
shared_examples 'fizzbuzz' do
let(:count) { fizzout.length }
it 'should call puts N times' do
expect(STDOUT).to receive(:puts).exactly(count).times
end
it 'should print the expected output' do
fizzout.each_with_index do |str, i|
expect(STDOUT).to receive(:puts).once.with(str).ordered
end
end
end
describe 'classic fizzbuzz' do
let(:fizzout) { [1, 2, 'fizz', 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz', 11, 'fizz', 13, 14, 'fizzbuzz'] }
it_should_behave_like 'fizzbuzz'
after do
fizzbuzz(count)
end
end
describe 'seive fizzbuzz' do
let(:msgs) { { 3 => 'fizz', 5 => 'buzz', 7 => 'yahoo', 11 => 'yada' } }
let(:fizzout) { [1, 2, 'fizz', 4, 'buzz', 'fizz', 'yahoo', 8, 'fizz', 'buzz',
'yada', 'fizz', 13, 'yahoo', 'fizzbuzz', 16, 17, 'fizz', 19, 'buzz',
'fizzyahoo', 'yada', 23, 'fizz', 'buzz', 26, 'fizz', 'yahoo', 29, 'fizzbuzz',
31, 32, 'fizzyada', 34, 'buzzyahoo', 'fizz', 37, 38, 'fizz', 'buzz'] }
it_should_behave_like 'fizzbuzz'
after do
seivefizz(count, msgs)
end
end
# generate seive of aristothenes for use in bechmarking
primecount = 4000
primes = (0..primecount).to_a
2.upto(Math.sqrt(primecount)) do |p|
next unless primes[p]
((p*2)..primecount).step(p) do |i|
primes[i] = nil
end
end
primes = primes[2..-1].compact
# puts "#{primes.count} primes:", primes.inspect
# benchmarking
# versions = {
# 2 => { 3 => 'fizz', 5 => 'buzz' },
# 4 => { 3 => 'fizz', 5 => 'buzz', 7 => 'yahoo', 11 => 'yada' }
# }
# create different fizzbuzz message sets for benchmarking
powers = [2, 4, 8, 16, 32, 64, 128, 256, 512]
versions = {}
powers.each do |power|
versions[power] = {}
primes[0..power].each do |p|
versions[power][p] = "fizz_#{p}_"
end
end
n_values = [10, 100, 1000, 10000, 100000, 1000000, 10000000]
marks = {}
progress = Formatador::ProgressBar.new(powers.count * n_values.count)
versions.each do |power, msgs|
marks[power] = {}
n_values.each do |n|
progress.increment
# puts "N=#{n}, #{power} msgs"
classic = Benchmark.measure { no_stdout { genfizz(n, msgs) }}
seive = Benchmark.measure { no_stdout { seivefizz(n, msgs) }}
marks[power][n] = if classic.utime == seive.utime
1
elsif seive.utime == 0
'inf'
else
(classic.utime / seive.utime).round(2) # store ratio of classic to seive time
end
# Benchmark.bm do |b|
# b.report("classic") { no_stdout { genfizz(n, msgs) } }
# b.report("seive") { no_stdout { seivefizz(n, msgs) } }
# end
end
marks[power]['msgs'] = power
end
Formatador.display_table marks.values
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment