Solves the 0-1 knapsack problem using genetic algorithms and compares the results with other methods.
The goal of this repository is to explore and implement in Julia a Genetic Algorithm, a subclass of Evolutionary Algorithms, that will be used to find a good solution for the Discrete (0-1) Knapsack problem. Other approaches (Brute Force, Greedy, Dynamic Programming) habe also been implemented in order to set a benchmark for the results of the Genetic algorithm.
The “0-1 Knapsack” problem is an Integer Programming optimization problem, restricted to the domain {0, 1}. Formally, given n items, their corresponding “weights” w, “values” v and a “knapsack capacity” W, the goal is to find a binary vector x that solves:
maximize xTv
subject to xTw <= W
That is, to choose the items which the sum of their weights does not exceed the knapsack capacity W and simulteanously maximize the total value.
The main idea behind Genetic Algorithms is to simulate the process of Natural Selection. An initial population consisting of candidate solutions is created, and then through
the population gets updated so that every new generation inherits features from the fit candidates,
those with the highest fitness (score). In the 0-1 Knapsack problem, the fitness of a solution is 0 if the solution is invalid (exceeds knapsack capacity), else the sum of the values selected by it.
The very nature of the Knapsack problem allows us to frame a genome (candidate solution) as a binary vector, which comes in as very handy for the processes mentioned above.
There are many approaches to genetic algorithms, some of them discussed here
The key concept, which has also been implemented in this repo, is the idea of Elitism. Elitism suggets that a fraction of the fittest genomes should survive “as is” in the next generation. This allows for more valid candidate solutions to be propagated in the population, thus reducing the amount of the invalid ones. We will see later the differences in results.
Enumerate all the possible solutions. There are n items totally, where each can either be selected or not. Thus, there are 2 possibilities for every item, giving an exponential
complexity: O(2n).
Sort all items by the key: weight/value in a descending order. Then start picking the items
in an ascending order, until there is no more space in the knapsack or the items have finished.
The complexity of this algorithm is: O(nlon) (because of the sorting). Note that this algorithm
will give always the best solution for continuous (fractional) Knapsack problem. In the discrete, it will only give an approximation.
The 0-1 Knapsack problem has 2 properties that allow it to be solved using DP methods:
Independent sub-problems
Using an optimal solution for the problem with (n-1) items and all the weights up to W, we can quickly dedude the optimal solution for the problem with n items using the formula:
OPT(n, W) = max(OPT(n-1, W - weight[n]) + value[n], OPT(n-1, W))
The complexity of this algorithm is O(nW).
Firstly, let’s get a taste of how slow the Brute-Force search is:
Yeap, no need to explain.
Let’s now see the runtime of the Greedy, DP and Genetic-Algorithm methods, for W = 10000:
W = 1000, n_items = 1000, population = 10000
W = 10000, n_items = 100, population = 1000
W = 10000, n_items = 100, population = 10000
It is clear to see that in these examples the Genetic algorithm surpasses the greedy algorithm in few iterations, by achieving at least as good results as the greedy, and sometimes better. These results are expected.