lib/games_dice/probabilities.rb in games_dice-0.3.7 vs lib/games_dice/probabilities.rb in games_dice-0.3.8
- old
+ new
@@ -181,22 +181,12 @@
raise TypeError, "parameter to add_distributions is not a GamesDice::Probabilities"
end
combined_min = pd_a.min + pd_b.min
combined_max = pd_a.max + pd_b.max
- new_probs = Array.new( 1 + combined_max - combined_min, 0.0 )
- probs_a, offset_a = pd_a.to_ao
- probs_b, offset_b = pd_b.to_ao
- probs_a.each_with_index do |pa,i|
- probs_b.each_with_index do |pb,j|
- k = i + j
- pc = pa * pb
- new_probs[ k ] += pc
- end
- end
- GamesDice::Probabilities.new( new_probs, combined_min )
+ add_distributions_internal combined_min, combined_max, 1, pd_a, 1, pd_b
end
# Combines two distributions with multipliers to create a third, that represents the distribution
# created when adding weighted results together.
# @param [Integer] m_a Weighting for first distribution
@@ -215,22 +205,11 @@
combined_min, combined_max = [
m_a * pd_a.min + m_b * pd_b.min, m_a * pd_a.max + m_b * pd_b.min,
m_a * pd_a.min + m_b * pd_b.max, m_a * pd_a.max + m_b * pd_b.max,
].minmax
- new_probs = Array.new( 1 + combined_max - combined_min, 0.0 )
- probs_a, offset_a = pd_a.to_ao
- probs_b, offset_b = pd_b.to_ao
-
- probs_a.each_with_index do |pa,i|
- probs_b.each_with_index do |pb,j|
- k = m_a * (i + offset_a) + m_b * (j + offset_b) - combined_min
- pc = pa * pb
- new_probs[ k ] += pc
- end
- end
- GamesDice::Probabilities.new( new_probs, combined_min )
+ add_distributions_internal combined_min, combined_max, m_a, pd_a, m_b, pd_b
end
# Returns a symbol for the language name that this class is implemented in. The C version of the
# code is noticeably faster when dealing with larger numbers of possible results.
# @return [Symbol] Either :c or :ruby
@@ -244,24 +223,25 @@
# @return [GamesDice::Probabilities] new distribution
def repeat_sum n
n = Integer( n )
raise "Cannot combine probabilities less than once" if n < 1
raise "Probability distribution too large" if ( n * @probs.count ) > 1000000
- revbin = n.to_s(2).reverse.each_char.to_a.map { |c| c == '1' }
pd_power = self
pd_result = nil
- max_power = revbin.count - 1
- revbin.each_with_index do |use_power, i|
- if use_power
+ use_power = 1
+ loop do
+ if ( use_power & n ) > 0
if pd_result
pd_result = GamesDice::Probabilities.add_distributions( pd_result, pd_power )
else
pd_result = pd_power
end
end
- pd_power = GamesDice::Probabilities.add_distributions( pd_power, pd_power ) unless i == max_power
+ use_power = use_power << 1
+ break if use_power > n
+ pd_power = GamesDice::Probabilities.add_distributions( pd_power, pd_power )
end
pd_result
end
# Calculates distribution generated by summing best k results of n iterations
@@ -273,10 +253,12 @@
n = Integer( n )
k = Integer( k )
raise "Cannot combine probabilities less than once" if n < 1
# Technically this is a limitation of C code, but Ruby version is most likely slow and inaccurate beyond 170
raise "Too many dice to calculate numbers of arrangements" if n > 170
+ check_keep_mode( kmode )
+
if k >= n
return repeat_sum( n )
end
new_probs = Array.new( @probs.count * k, 0.0 )
new_offset = @offset * k
@@ -307,10 +289,25 @@
GamesDice::Probabilities.new( new_probs, new_offset )
end
private
+ def self.add_distributions_internal combined_min, combined_max, m_a, pd_a, m_b, pd_b
+ new_probs = Array.new( 1 + combined_max - combined_min, 0.0 )
+ probs_a, offset_a = pd_a.to_ao
+ probs_b, offset_b = pd_b.to_ao
+
+ probs_a.each_with_index do |pa,i|
+ probs_b.each_with_index do |pb,j|
+ k = m_a * (i + offset_a) + m_b * (j + offset_b) - combined_min
+ pc = pa * pb
+ new_probs[ k ] += pc
+ end
+ end
+ GamesDice::Probabilities.new( new_probs, combined_min )
+ end
+
def check_probs_array probs_array
raise TypeError unless probs_array.is_a?( Array )
probs_array.map!{ |n| Float(n) }
total = probs_array.inject(0.0) do |t,x|
if x < 0.0 || x > 1.0
@@ -323,45 +320,45 @@
end
probs_array
end
def calc_keep_distributions k, q, kmode
- if kmode == :keep_best
- keep_distributions = [ GamesDice::Probabilities.new( [1.0], q * k ) ]
- if p_gt(q) > 0.0 && k > 1
- kd_probabilities = given_ge( q + 1 )
- (1...k).each do |n|
- extra_o = GamesDice::Probabilities.new( [1.0], q * ( k - n ) )
- n_probs = kd_probabilities.repeat_sum( n )
- keep_distributions[n] = GamesDice::Probabilities.add_distributions( extra_o, n_probs )
- end
+ kd_probabilities = calc_keep_definite_distributions q, kmode
+
+ keep_distributions = [ GamesDice::Probabilities.new( [1.0], q * k ) ]
+ if kd_probabilities && k > 1
+ (1...k).each do |n|
+ extra_o = GamesDice::Probabilities.new( [1.0], q * ( k - n ) )
+ n_probs = kd_probabilities.repeat_sum( n )
+ keep_distributions[n] = GamesDice::Probabilities.add_distributions( extra_o, n_probs )
end
- elsif kmode == :keep_worst
- keep_distributions = [ GamesDice::Probabilities.new( [1.0], q * k ) ]
- if p_lt(q) > 0.0 && k > 1
- kd_probabilities = given_le( q - 1 )
- (1...k).each do |n|
- extra_o = GamesDice::Probabilities.new( [1.0], q * ( k - n ) )
- n_probs = kd_probabilities.repeat_sum( n )
- keep_distributions[n] = GamesDice::Probabilities.add_distributions( extra_o, n_probs )
- end
- end
- else
- raise "Keep mode #{kmode.inspect} not recognised"
end
+
keep_distributions
end
+ def calc_keep_definite_distributions q, kmode
+ kd_probabilities = nil
+ case kmode
+ when :keep_best
+ p_definites = p_gt(q)
+ kd_probabilities = given_ge( q + 1 ) if p_definites > 0.0
+ when :keep_worst
+ p_definites = p_lt(q)
+ kd_probabilities = given_le( q - 1 ) if p_definites > 0.0
+ end
+ kd_probabilities
+ end
+
def calc_p_table q, p_maybe, kmode
- if kmode == :keep_best
+ case kmode
+ when :keep_best
p_kept = p_gt(q)
p_rejected = p_lt(q)
- elsif kmode == :keep_worst
+ when :keep_worst
p_kept = p_lt(q)
p_rejected = p_gt(q)
- else
- raise "Keep mode #{kmode.inspect} not recognised"
end
[ p_rejected, p_maybe, p_kept ]
end
# Convert hash to array,offset notation
@@ -384,9 +381,13 @@
def calc_expected
total = 0.0
@probs.each_with_index { |v,i| total += (i+@offset)*v }
total
+ end
+
+ def check_keep_mode kmode
+ raise "Keep mode #{kmode.inspect} not recognised" unless [:keep_best,:keep_worst].member?( kmode )
end
end # class GamesDice::Probabilities
# @!visibility private