Just for fun, I implemented two solutions: one actually does and
counts the iterations. The other fakes it for the no-option and option
-s cases (but produces EXACTLY the same output) and only does the
iterations for the -v (for which it is slower).
Both coded with lots of hard-coded magic numbers, like 6-sided dice,
looking for 5s, etc, but easily modified.
---------------------------------
Straight iterative:
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:v] = 1
end
opts.on("-s", "--sample", "Run in sample mode") do |s|
options[:s] = 1
end
end.parse!
total_dice, at_least_5 = Integer(ARGV[0]), Integer(ARGV[1])
des=0
tot=6**total_dice
roll = Array.new(total_dice){1}
roll[0] = 0
(0..tot-1).each { |serial|
marker = ""
roll[0]+=1
(0..total_dice-1).each do |idx|
if roll[idx]==7
roll[idx]=1
roll[idx+1]+=1
else
break
end
end
if roll.select{|die| die==5}.length >= at_least_5
marker = " <=="
des += 1
end
if options[:v] || (options[:s] && (serial % 50000)==0 )
puts "#{serial+1} #{roll.inspect} #{marker}"
end
}
puts "\nNumber of desirable outcomes is #{des}"
puts "Number of possible outcomes is #{tot}"
puts "\nProbability is %16.16f" %(des.to_f/tot)
-----------------
Fake it when possible using closed-form calculation:
# Math.nCr(n,r) borrowed from Brian Candler's
# post in ruby_talk
def Math.nCr(n,r)
a, b = r, n-r
a, b = b, a if a < b # a is the larger
numer = (a+1..n).inject(1) { |t,v| t*v } # n!/r!
denom = (2..b).inject(1) { |t,v| t*v } # (n-r)!
numer/denom
end
class DiceAtLeastFive
def self.int_to_roll( serial, numdice )
# good for reality, too slow for a quiz - #pow is expensive.
# raise if serial >= 6**numdice
serial.to_s(6).rjust(numdice,"0").split(//).map!{|ele|
ele.to_i + 1 }.reverse
end
def self.total_rolls(numdice)
6**numdice
end
def self.desirable(numdice, min_fives)
(min_fives..numdice).inject(0) { |tot, val| tot +
(Math.nCr(numdice,val) * 5**(numdice-val)) }
end
end
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:iter_step] = 1
end
opts.on("-s", "--sample", "Run in sample mode") do |s|
options[:iter_step] = 50000
end
end.parse!
total_dice, at_least_5 = Integer(ARGV[0]), Integer(ARGV[1])
des=DiceAtLeastFive.desirable(total_dice,at_least_5)
tot=DiceAtLeastFive.total_rolls(total_dice)
if options[:iter_step]
(0..tot-1).step(options[:iter_step]) { |serial|
marker = ""
roll = DiceAtLeastFive.int_to_roll(serial, total_dice)
if roll.select{|die| die==5}.length >= at_least_5
marker = " <=="
end
puts "#{serial+1} #{roll.inspect} #{marker}"
}
end
puts "\nNumber of desirable outcomes is #{des}"
puts "Number of possible outcomes is #{tot}"
puts "\nProbability is %16.16f" %(des.to_f/tot)