require 'silicium' require 'plotter' require 'chunky_png' include Silicium::Plotter module Combinatorics def factorial(n) (1..n).inject(:*) || 1 end ## #Function C(n,k) def combination(n, k) f = fact(n, k) if n < k or k <= 0 -1 else f[0] / (f[2] * f[1]) end end ## #Function A(n,k) def arrangement(n, k) f = fact(n, k) if n < k or k <= 0 -1 else f[0] / f[1] end end private def fact(n, k) res = [1,1,1] fact_n_greater_1(n, k, res) if n > 1 res end def fact_n_greater_1(n,k, res) c = 1 for i in 2..n c *= i determining_i([i, n, k, c], res) end res[0] = c end def determining_i(arr, res) res[1] = arr[3] if arr[0] == arr[1] - arr[2] res[2] = arr[3] if arr[0] == arr[2] end end module Dice ## # Class represents a polyhedron # csides - number or sides # sides - array of sides(unusual for custom polyhedrons) class Polyhedron def csides @csides end def sides @sides end def to_s sides end ## # initializing polyhedron's variables # there are two ways how to create it # 1: by number (6) - creates polyhedron with 6 sides [1,2,3,4,5,6] # 2: by array ([1,3,5]) - creates polyhedron with 3 sides [1,3,5] def initialize(sides) @csides = 1 @sides = [1] if sides.class == Integer and sides > 1 @csides = sides (2..sides).each {|i| @sides << i} elsif sides.class == Array and sides.size > 0 @csides = sides.size @sides = sides.sort end end ## # ability to throw a polyhedron def throw @sides[rand(0..@csides-1)] end end ## # Class represents a PolyhedronsSet class PolyhedronSet def initialize(arr) @pons = parse_pons(arr).sort_by{|item| -item.csides} @percentage = count_chance_sum end # hash with chances of getting definite score def percentage @percentage end ## # returns array of polyhedrons def to_s @pons.map {|item| item.to_s} end ## # ability to throw a polyhedron set using hash of chances def throw sum = 0 r = rand @percentage.each do |item| sum += item[1] if sum >= r item[0] break end end end ## # creating a graph representing chances of getting points def make_graph_by_plotter(x = percentage.size * 10, y = percentage.size * 10) filename = 'tmp/percentage.png' File.delete(filename) if File.exist?(filename) plotter = Image.new(x, y) plotter.bar_chart(percentage, 1, color('red @ 1.0')) plotter.export(filename) end private def parse_pons(arr) res = [] arr.each do |item| if item.is_a?(Integer) or item.is_a?(Array) res << Polyhedron.new(item) elsif item.is_a?(Polyhedron) res << item end end res end def count_chance_sum_chances_step(arr1, arr2, arr3, h) n = 0 m = 0 sum = 0 q = Queue.new h1 = {} while m < arr2.size sum = m_0([sum, n, m], q, h, arr1) sum -= q.pop if q.size > arr2.size || m > 0 h1[arr1[n] + arr2[m]] = sum arr3 << (arr1[n] + arr2[m]) nmarr = n_less_arr1_size(n, arr1, m) n, m = nmarr[0], nmarr[1] end h1 end def m_0(arr, q, h, arr1) if arr[2] == 0 a = h[arr1[arr[1]]] q << a arr[0] += a end arr[0] end def n_less_arr1_size(n, arr1, m) if n < arr1.size - 1 n += 1 else m += 1 end [n,m] end def count_chance_sum h = {} @pons[0].sides.each { |item| h[item] = 1 } arr3 = @pons[0].sides for i in 1..@pons.size - 1 arr1 = arr3 arr3 = [] arr2 = @pons[i].sides h1 = count_chance_sum_chances_step(arr1, arr2, arr3, h) h = h1 end res = {} fchance = @pons.inject(1) { |mult, item| mult * item.csides } arr3.each {|item| res[item] = Float(h[item]) / fchance} res end end end module BernoulliTrials include Combinatorics include Math def bernoulli_formula_and_laplace_theorem(n, k, all, suc = -1) if suc != -1 p = suc.fdiv all else p = all end q = 1 - p if n * p * q < 9 # Laplace theorem give satisfactory approximation for n*p*q > 9, else Bernoulli formula combination(n, k) * (p ** k) * (q ** (n - k)) # Bernoulli formula C(n,k) * (p ^ k) * (q ^ (n-k)) else gaussian_function((k - n * p).fdiv sqrt(n * p * q)).fdiv(sqrt(n * p * q)) # Laplace theorem φ((k - n*p)/sqrt(n*p*q)) / sqrt(n*p*q) end end def gaussian_function(x) if x < 0 # φ(-x) = φ(x) x * -1 end a = exp(-(x ** 2) / 2).fdiv sqrt(2 * PI) # φ(x) = exp(-(x^2/2)) / sqrt(2*π) end end