65 lines
1.8 KiB
Plaintext
65 lines
1.8 KiB
Plaintext
defmodule Log do
|
|
def show(offspring,i) do
|
|
IO.puts "Generation: #{i}, Offspring: #{offspring}"
|
|
end
|
|
|
|
def found({target,i}) do
|
|
IO.puts "#{target} found in #{i} iterations"
|
|
end
|
|
end
|
|
|
|
defmodule Evolution do
|
|
# char list from A to Z; 32 is the ord value for space.
|
|
@chars [32 | Enum.to_list(?A..?Z)]
|
|
|
|
def select(target) do
|
|
(1..String.length(target)) # Creates parent for generation 0.
|
|
|> Enum.map(fn _-> Enum.random(@chars) end)
|
|
|> mutate(to_charlist(target),0)
|
|
|> Log.found
|
|
end
|
|
|
|
# w is used to denote fitness in population genetics.
|
|
|
|
defp mutate(parent,target,i) when target == parent, do: {parent,i}
|
|
defp mutate(parent,target,i) do
|
|
w = fitness(parent,target)
|
|
prev = reproduce(target,parent,mu_rate(w))
|
|
|
|
# Check if the most fit member of the new gen has a greater fitness than the parent.
|
|
if w < fitness(prev,target) do
|
|
Log.show(prev,i)
|
|
mutate(prev,target,i+1)
|
|
else
|
|
mutate(parent,target,i+1)
|
|
end
|
|
end
|
|
|
|
# Generate 100 offspring and select the one with the greatest fitness.
|
|
|
|
defp reproduce(target,parent,rate) do
|
|
[parent | (for _ <- 1..100, do: mutation(parent,rate))]
|
|
|> Enum.max_by(fn n -> fitness(n,target) end)
|
|
end
|
|
|
|
# Calculate fitness by checking difference between parent and offspring chars.
|
|
|
|
defp fitness(t,r) do
|
|
Enum.zip(t,r)
|
|
|> Enum.reduce(0, fn {tn,rn},sum -> abs(tn - rn) + sum end)
|
|
|> calc
|
|
end
|
|
|
|
# Generate offspring based on parent.
|
|
|
|
defp mutation(p,r) do
|
|
# Copy the parent chars, then check each val against the random mutation rate
|
|
Enum.map(p, fn n -> if :rand.uniform <= r, do: Enum.random(@chars), else: n end)
|
|
end
|
|
|
|
defp calc(sum), do: 100 * :math.exp(sum/-10)
|
|
defp mu_rate(n), do: 1 - :math.exp(-(100-n)/400)
|
|
end
|
|
|
|
Evolution.select("METHINKS IT IS LIKE A WEASEL")
|