RosettaCodeData/Task/Evolutionary-algorithm/Elixir/evolutionary-algorithm-3.el...

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