58 lines
1.8 KiB
Plaintext
58 lines
1.8 KiB
Plaintext
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
|