Skip to content

Instantly share code, notes, and snippets.

@anandgeorge
Created June 19, 2021 12:48
Show Gist options
  • Save anandgeorge/2fb584eb2d429248318f5276b8510fdd to your computer and use it in GitHub Desktop.
Save anandgeorge/2fb584eb2d429248318f5276b8510fdd to your computer and use it in GitHub Desktop.
Genetic algorithms in Elixir basic algorithm explained in detail as comments
population = for _ <- 1..100, do: for _ <- 1..1000, do: Enum.random(0..1)
# evaluate, selection, crossover and algorithm are anonymous functions.
evaluate =
fn population ->
# population is a list of lists [[1,1,0,0,1...1,1,0],[1,1,1,0,0...0,1,1]] 100 lists, 1000 bits each
Enum.sort_by(population, &Enum.sum/1, &>=/2)
# sort the list of lists by sum of bits so lists with higher number of one's are at the top. Mapper sums each row. Sorter is descending &>=/2
# note the use of anonymous functions defined by &.
end
selection =
fn population ->
# population is a list of lists [[1,1,0,0,1...1,1,0],[1,1,1,0,0...0,1,1]] 100 lists, 1000 bits each
Enum.chunk_every(population, 2)
# chunk into sets of 2 [[[1,1,0,0,1...1,1,1],[1,1,1,1,0...1,1,1]],[[1,1,0,0,0...0,1,1],[1,1,1,1,1...1,1,0]]]
|> Enum.map(&List.to_tuple(&1)) # convert to tuples because it is needed for the next step
# convert to a list of tuples of lists [{[1,1,0,0,1...1,1,1],[1,1,1,1,0...1.1.1]},{[1,1,0,0,0...0,1,1],[1,1,1,1,1...1,1,0]}]
end
crossover =
fn population ->
#population is a list of tuples of lists [{[1,1,0,0,1...1,1,1],[1,1,1,1,0...1.1.1]},{[1,1,0,0,0...0,1,1],[1,1,1,1,1...1,1,0]}]
Enum.reduce(population, [],
fn {p1, p2}, acc -> # {p1, p2} corresponds to {[1,1,0,0,1],[1,1,1,1,0]} so p1 = [1,1,0,0,1] and p2 = [1,1,1,1,0]
cx_point = :rand.uniform(1000)
{{h1, t1}, {h2, t2}} = {Enum.split(p1, cx_point), Enum.split(p2, cx_point)}
# p1 = [1,1,0,0,1] gets split into two lists one 0 to cx_point, other cx_point to end {[1,1,1,0],[1,0,1,1]}
# p2 = [1,1,0,0,1] gets split into two lists one 0 to cx_point, other cx_point to end {[1,0,1,1],[1,0,0,1]}
# and the result combined to a tuple so you end up with
# {{[1,1,1,0],[1,0,1,1]}, {[1,0,1,1],[1,0,0,1]}} so h1 = [1,1,1,0], t1 = [1,0,1,1], h2 = [1,0,1,1], t2 = [1,0,0,1]
[h1 ++ t2, h2 ++ t1 | acc]
# list h1 is appended to t2 and list h2 is appended to t1 giving two list. These lists are added to the accumulator. So accumulator is eventually and list of lists, similar to the original population
end
)
end
algorithm =
fn population, algorithm ->
best = Enum.max_by(population, &Enum.sum/1)
IO.write("\rCurrent Best: " <> Integer.to_string(Enum.sum(best)))
if Enum.sum(best) == 1000 do
best
else
population
|> evaluate.()
|> selection.()
|> crossover.()
|> algorithm.(algorithm)
end
end
solution = algorithm.(population, algorithm)
IO.write("\n Answer is \n")
IO.inspect solution
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment