commit 47bc5d08c726253078ee45d20479ddaa26353549
parent e700eba136d4dabc9a9bbe120247e78bb2f913b5
Author: dracuxan <[email protected]>
Date: Wed, 8 Apr 2026 17:07:05 +0530
new: custom mutation methods
Diffstat:
4 files changed, 94 insertions(+), 19 deletions(-)
diff --git a/genetic/lib/genetic.ex b/genetic/lib/genetic.ex
@@ -66,11 +66,14 @@ defmodule Genetic do
)
end
- def mutation(population, _opts \\ []) do
+ def mutation(population, opts \\ []) do
+ mutation_fn = Keyword.get(opts, :mutation_type, &Toolbox.Mutation.gaussian/2)
+ mutation_rate = Keyword.get(opts, :mutation_rate, 0.05)
+
population
|> Enum.map(fn chromosome ->
- if :rand.uniform() < 0.05 do
- %Chromosome{chromosome | genes: Enum.shuffle(chromosome.genes)}
+ if :rand.uniform() < mutation_rate do
+ apply(mutation_fn, [chromosome, opts])
else
chromosome
end
diff --git a/genetic/lib/toolbox/mutation.ex b/genetic/lib/toolbox/mutation.ex
@@ -0,0 +1,68 @@
+defmodule Toolbox.Mutation do
+ alias Types.Chromosome
+
+ def flip(chromosome, _opts \\ []) do
+ genes =
+ chromosome.genes
+ |> Enum.map(&Bitwise.bxor(&1, 1))
+
+ %Chromosome{genes: genes, size: chromosome.size}
+ end
+
+ def flip_some(chromosome, opts \\ []) do
+ p = Keyword.get(opts, :p, 0.5)
+
+ genes =
+ chromosome.genes
+ |> Enum.map(fn g ->
+ if :rand.uniform() < p do
+ Bitwise.bxor(g, 1)
+ else
+ g
+ end
+ end)
+
+ %Chromosome{genes: genes, size: chromosome.size}
+ end
+
+ def scramble(chromosome, _opts \\ []) do
+ genes =
+ chromosome.genes
+ |> Enum.shuffle()
+
+ %Chromosome{genes: genes, size: chromosome.size}
+ end
+
+ def scramble_n(chromosome, _opts \\ []) do
+ n = 10
+ start = :rand.uniform(n - 1)
+
+ {lo, hi} =
+ if start + n >= chromosome.size do
+ {start - n, start}
+ else
+ {start, start + n}
+ end
+
+ head = Enum.slice(chromosome.genes, 0, lo)
+ mid = Enum.slice(chromosome.genes, lo, hi) |> Enum.shuffle()
+ tail = Enum.slice(chromosome.genes, hi, chromosome.size)
+ %Chromosome{genes: head ++ mid ++ tail, size: chromosome.size}
+ end
+
+ def gaussian(chromosome, _opts \\ []) do
+ mu = Enum.sum(chromosome.genes) / length(chromosome.genes)
+
+ sigma =
+ chromosome.genes
+ |> Enum.map(fn x -> (mu - x) * (mu - x) end)
+ |> Enum.sum()
+ |> Kernel./(length(chromosome.genes))
+
+ genes =
+ chromosome.genes
+ |> Enum.map(fn _ -> :rand.normal(mu, sigma) end)
+
+ %Chromosome{genes: genes, size: chromosome.size}
+ end
+end
diff --git a/genetic/scripts/codebreaker.exs b/genetic/scripts/codebreaker.exs
@@ -10,8 +10,8 @@ defmodule Codebreaker do
end
def fitness_function(chromosome) do
- target = "ILoveGeneticAlgorithms"
- encrypted = "LIjs`B`k`qlfDibjwlqmhv"
+ target = "dracuxan"
+ encrypted = "awdfp}dk"
cypher = fn word, key ->
word
@@ -19,12 +19,7 @@ defmodule Codebreaker do
|> Enum.map(fn x -> rem(bxor(x, key), 32768) end)
end
- key =
- chromosome.genes
- |> Enum.map(&Integer.to_string(&1))
- |> Enum.join("")
- |> String.to_integer(2)
-
+ key = generate_integer_key(chromosome.genes)
guess = List.to_string(cypher.(encrypted, key))
String.jaro_distance(target, guess)
end
@@ -33,14 +28,22 @@ defmodule Codebreaker do
best = hd(population)
best.fitness == 1
end
+
+ def generate_integer_key(key) do
+ key
+ |> Enum.map(&Integer.to_string(&1))
+ |> Enum.join("")
+ |> String.to_integer(2)
+ end
end
-{soln, _} = Genetic.run(Codebreaker, crossover_type: &Toolbox.Crossover.single_point/3)
+{soln, generation} =
+ Genetic.run(
+ Codebreaker,
+ crossover_type: &Toolbox.Crossover.single_point/3,
+ mutation_type: &Toolbox.Mutation.flip_some/2
+ )
-{key, ""} =
- soln.genes
- |> Enum.map(&Integer.to_string(&1))
- |> Enum.join("")
- |> Integer.parse(2)
+key = Codebreaker.generate_integer_key(soln.genes)
-IO.puts("\nThe key is #{key}")
+IO.puts("\nkey: #{key}\ngenerations passed: #{generation}")
diff --git a/genetic/scripts/one_max.exs b/genetic/scripts/one_max.exs
@@ -38,7 +38,8 @@ end
Genetic.run(
OneMax,
selection_type: &Toolbox.Selection.elite/2,
- crossover_type: &Toolbox.Crossover.uniform/3
+ crossover_type: &Toolbox.Crossover.uniform/3,
+ mutation_type: &Toolbox.Mutation.gaussian/2
)
IO.puts("\nfinal answer: #{inspect(soln)}")