From: "Thomas A. Reilly" Date: 2002-03-22T12:26:01+09:00 Subject: Re: ruby simulations --=====================_468518974==_ Content-Type: text/plain; charset="us-ascii"; format=flowed When I had learned of ruby I had thought that it would be useful to create a quick and dirty simulation. Living in Louisiana we have small towns almost everyone who has the same last name and I have also wondered about the growth of communities such as the Mayflower community and the lost colony in Virginia. It was a great way to learn ruby. Tom Reilly --=====================_468518974==_ Content-Type: text/plain; charset="us-ascii" Content-Disposition: attachment; filename="FAMNAM.RB" #---------------------------------------------------------------- # # famnam.rb - family name simulation # #---------------------------------------------------------------- $mar_rate = .11 $birth_rate = .21 $divorce_rate = .013 $pstart = 200 # of people at the start of simulation $simyrs = 1000 # years of simulation def lookup(arg,argt,argf) i = 0 if argt.size == argf.size argt.each_index do |i| if argt[i] >= arg break end end if argt[i + 1] == argt[i] argf[i] else delta = (argf[i + 1] - argf[i])/(argt[i + 1] - argt[i]) dd = (arg - argt[i]) / (argt[i + 1] - argt[i]) argf[i] + (dd * delta) end else print "ARRAY SIZE ERROR ", argt.size, " ", argf.size, "\n" exit end end #---------------------------------------------------------------------- class Birth def initialize(father, mother) @sex = rand(2) if @sex == 1 @dom = $yr + lookup(rand() * 100,$ixm,$ixa) else @dom = $yr + lookup(rand() * 100,$ixf,$ixa) end @dob = $yr @pid = getgene @name = "" @married = false @gene = Array.new if father == nil @gene[0] = getgene @gene[1] = getgene @gene[2] = getgene mkname else @name = father.name @gene[0] = father.pid @gene[1] = father.gene[0] @gene[2] = father.gene[1] end if mother == nil @gene[3] = getgene @gene[4] = getgene @gene[5] = getgene else @gene[3] = mother.pid @gene[4] = mother.gene[0] @gene[5] = mother.gene[1] mkname if father == nil end end def view print "name ", @name, " pid ", @pid, " dob ", @dob, " mort ", @dom, " sex ", @sex, "\n" print " parents " print gene.each {|i| print i, " "} print "---\n" end def name @name end def pid @pid end def gene @gene end def dom @dom end def rip $yr > @dom end def married @married end def marry @married = true end def unmarry @married = false end def age $yr - @dob end def sex @sex end def dob @dob end private def mkname @name = "" (1..5).each do |i| ch = rand(25) ch += "A".slice(0) @name = @name + ch.chr end end def getgene $pix += 1 end end #-------------------------------------------------------------------------- public class Population def initialize @plist = Array.new @marriages = Array.new @male_eligible = Array.new @female_eligible = Array.new @birth_count = 0 @marriage_count = 0 @died = 0 @name_count = 0 $pstart.times do @plist.push(Birth.new(nil,nil)) end end def birth(dad,mom) @plist.push(Birth.new(dad,mom)) end def size @plist.size end def kill @died = 0 #| @plist.each_index do |person| #| print @plist[person].dom, " ", @plist[person].dom.type, "\n" #| end @plist.each {|per| @died += 1 if per.rip} @plist.delete_if {|per| per.rip} @plist.compact @died end def each for i in @plist yield(i) end end # - Count the number of names in the population def names names = Hash.new(0) @plist.each {|p| names[p.name] += 1} @name_count = names.size end def stats print "YEAR ", $yr, ", POPULATION ", @plist.size, ", MARRIAGES ", @marriage_count, ", BIRTHS ", @birth_count, ", DEATHS ", @died, ", NAMES ", @name_count, "\n" exit if @plist.size == 0 end def marriage_end @marriages.each_index do |m| msw = false if $yr > @marriages[m].him.dom || $yr > @marriages[m].her.dom @marriages[m].unmarry @marriages[m].smend else if rand() < $divorce_rate @marriages[m].unmarry @marriages[m].smend end end end @marriages.delete_if {|mp| mp.mend == true} @marriages.compact end def year marry make_babies names marriage_end kill stats wstat end def make_babies @birth_count = 0 @marriages.each do |m| if $yr - m.her.dob > 15 && $yr - m.her.dob < 45 if rand() < $birth_rate @birth_count += 1 @plist.push(Birth.new(m.him,m.her)) end end end end def marry @male_eligible.clear @female_eligible.clear @marriage_count = 0 @plist.each do |p| if p.sex == 1 && p.age > 16 && p.married == false then @male_eligible.push(p) end if p.sex == 0 && p.age > 16 && p.married == false then @female_eligible.push(p) end end melig = @male_eligible.size felig = @female_eligible.size if melig < felig marmin = melig else marmin = felig marmin = felig end (1..marmin).each do |i| marsw = false malep = rand(melig) femp = rand(felig) man_elig = @male_eligible[malep] lady_elig = @female_eligible[femp] # insest prevention lady_elig.gene.each do |fi| man_elig.gene.each do |mi| if fi == mi marsw = true end end end marsw = true if rand() > $mar_rate if marsw == false @marriages.push(Marriage.new(@male_eligible[ malep ], @female_eligible[ femp])) @marriage_count += 1 # @male_eligible[malep].delete # @female_eligible[femp].delete melig -= 1 felig -= 1 end end end def pop_dist pop_a = Array.new(15,0) pop_tot = 0 @plist.each {|p| pop_a[(p.age / 10).to_i] += 1} pop_a.each {|i| pop_tot += i} pop_a.each_index do |i| pf = 1000.0 * pop_a[i].to_f / pop_tot.to_f printf("%d %0.2d\n",i,pf) end end def wstat $stfile.write("#{$yr}\t#{size}\t#{@marriages.size}\t#{@male_eligible.size}\t#{@female_eligible.size}\t#{@birth_count}\t#{@marriage_count}\t#{@died}\t#{@name_count}\n") end end #---------------------------------------------------------------------- class Marriage def initialize(him,her) @mend_flag = false @him = him @her = her @him.marry @her.marry end def unmarry @him.unmarry @her.unmarry end def her @her end def him @him end def mend @mend end def smend @mend = true end end #---------------------------------------------------------------------- $yr = 1 $pix = 0 $ixa = [1,2,3,4,5,10,20,30,40,50,60,70,80,90,100,110,120,120] $ixm = [0,1.33,2,2,2.67,3.33,4,4.67,5.33,14,24,47.33,77.33,96,99.33,100,100,100] $ixf = [0,1.33,2,2,2,2,2.67,2.67,4,8,5.33,22,50,88.67,98,99.33,100,100] $stfile = File.open("stfile","w") $stfile.write("year\tsize\tmarriages\tmale_eligible\tfemale_eligible\tbirth_count\tmarriage_count\tdied\tname_count\n") population = Population.new (1..$simyrs).each do population.year $yr += 1 end population.pop_dist names = Hash.new(0) population.each {|p| names[p.name] += 1} names.each {|n,c| $stfile.write "#{n}\t#{c}\n"} $stfile.close  --=====================_468518974==_--