Skip to content

Instantly share code, notes, and snippets.

@sergeant-wizard
Last active August 29, 2015 14:05
Show Gist options
  • Save sergeant-wizard/5c509af3e525f068b611 to your computer and use it in GitHub Desktop.
Save sergeant-wizard/5c509af3e525f068b611 to your computer and use it in GitHub Desktop.
# file spec/libs/lottery_spec.rb
require 'spec_helper'
RSpec.describe 'lib/lottery' do
describe 'Lottery.pick' do
let(:characters) { create_list(:character, 2) }
let(:num_samples) { 10000 }
subject do
num_chosen = 0
num_samples.times do
num_chosen += 1 if Lottery.pick(characters) == characters.first
end
{ num_chosen: num_chosen, real_probability: Lottery.probabilities(characters).first }
end
it 'returns a model with probability within the tolerance interval' do
n = num_samples
p = subject[:real_probability]
q = 1.0 - p
# 3.89 = inverse of normal distribution function with alpha=0.9999
expect(subject[:num_chosen]).to(
be_within( 3.89 * Math.sqrt(n * p * q) ).of(n * p),
'this test will fail once in 10000 times. don\'t freak out if it does, you\'re lucky :)')
end
end
end
# file lib/lottery.rb
class Lottery
def self.pick(models, weight_key = :weight)
weight_total = models.to_a.sum(&weight_key)
random_number = rand(0...weight_total)
sum = 0
models.each do |model|
return model if random_number < sum += model[weight_key]
end
end
def self.probabilities(models, weight_key = :weight)
weight_total = models.to_a.sum(&weight_key)
models.map { |model| model[weight_key].to_f / weight_total.to_f }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment