Solving the One-Max Problem Again
Genetic Algorithms in Elixir — by Sean Moriarity (25 / 101)
👈 Understanding Hyperparameters | TOC | What You Learned 👉
With your framework built, it’s time to apply it to the One-Max problem. Open a terminal and create a new file in the scripts directory named one_max.exs, like this:
$ touch scripts/one_max.exsOpen the one_max.exs file. Now, think about what your framework already accomplishes for you and what parts of the problem you need to define. What are the problem-specific parameters you need to pass into run?
Once you determine what these parameters are, you’ll need to start defining them. Logically, the first one is how you encode chromosomes. Remember, for the One-Max problem, you’re looking for the maximum sum of a bitstring of length N. To keep things consistent, your length should be 1000. This means your solutions should be bitstrings of length N.
To define your genotype, at the top of the one_max.exs file, add the following:
genotype = fn -> for _ <- 1..1000, do: Enum.random(0..1) endThis should look similar to what you did in Chapter 1, Writing Your First Genetic Algorithm. All that’s missing is the outside for-loop. However, if you recall, the outside for-loop is taken care of for you in the initialization step, so it’s unnecessary now.
The next step is to define your fitness function and termination criteria. How did you evaluate solutions in the previous chapter? Remember, you want a maximum sum, so fitness is just the sum. What’s the maximum possible sum you can achieve with a bitstring of length 1000? 1000. Therefore, your termination criteria or max_fitness is 1000.
Below genotype, define your fitness function and termination criteria like this:
fitness_function = fn chromosome -> Enum.sum(chromosome) end
max_fitness = 1000You might notice that you can just pass &Enum.sum/1 into the run function. That’s perfectly fine; however, it won’t always work out like that. It may make more sense to be more verbose so you really understand what’s going on.
All you need to do now is call run/4 with your predetermined parameters and extract your solution. Remember that run/4 returns the best solution once it is found. You’ll want to assign this to a variable so you can inspect its contents later on.
Below fitness_function, add the following:
soln = Genetic.run(fitness_function, genotype, max_fitness)
IO.write("\n")
IO.inspect(soln)Because this file gets run inside of a Mix project, you can call module functions defined in the Mix project. You assign soln to the result of Genetic.run/4 and output its contents to the console. This simply runs the genetic algorithm then inspects the resulting solution.
You’ll notice that, for now, nothing was passed in place of opts. That’s okay, because you’ve already defined default configuration options.
The next step is to open a terminal and navigate to your Mix project. Once there, you can run your genetic algorithm like this:
$ mix run scripts/one_max.exs
Current Best: 1000
[1,1,1...,1]You should note that you must use mix run and not elixir like in the previous chapter. Using mix ensures your Mix project is compiled and loaded before the script is executed. From this point forward, you’ll be using mix to run your algorithms. For every algorithm, you’ll be told exactly when you can compile and run your code from a terminal.
👈 Understanding Hyperparameters | TOC | What You Learned 👉
Genetic Algorithms in Elixir by Sean Moriarity can be purchased in other book formats directly from the Pragmatic Programmers. If you notice a code error or formatting mistake, please let us know here so that we can fix it.

