defmodule Evolution do def select(target) do # Generate char list from A to Z; 32 is the ord value for space. chars = (?A..?Z) |> Enum.to_list() |> List.insert_at(0, 32) (1..String.length(target)) # Creates parent for generation 0. |> Enum.map(fn _-> Rand.char(chars) end) |> mutate(String.to_char_list(target),0,[],chars) |> Log.found() end # w is used to denote fitness in population genetics. def mutate(parent,target,i,_,_) when target == parent, do: [parent|i] def mutate(parent,target,i,_,chars) when target != parent do w = fitness(parent,target) prev = reproduce(target,parent,w,0,mu_rate(w),chars) # Check if the most fit member of the new gen has a greater fitness than the parent. if w < fitness(prev,target) do parent = prev Log.show(parent,i) end mutate(parent,target,i+1,prev,chars) end # Generate 100 offspring and select the one with the greatest fitness. def reproduce(target,parent,_,_,rate,chars) do (1..100) |> Enum.to_list() |> Stream.map(fn _-> mutation(parent,rate,chars) end) |> List.insert_at(0, parent) |> Enum.max_by(fn n -> fitness(n,target) end) end # Calculate fitness by checking difference between parent and offspring chars. def fitness(t,r) do (0..length(t)-1) |> Stream.map(fn n -> abs(Enum.at(t,n) - Enum.at(r,n)) end) |> Enum.reduce(fn a,b -> a + b end) |> calc() end # Generate offspring based on parent. def mutation(p,r,chars) do # Copy the parent chars, then check each val against the random mutation rate (0..length(p)-1) |> Stream.map(fn n -> Enum.at(p,n) end) |> Enum.map(fn n -> if Rand.num <= r, do: Rand.char(chars), else: n end) end def calc(sum), do: 100 * :math.exp(sum/-10) def mu_rate(n), do: 1 - :math.exp(-(100-n)/400) end