diff --git a/algorithms/genetic_bucket_algorithm.py b/algorithms/genetic_bucket_algorithm.py
index 121df27096cf57d464dbac6c286357d9ff98cb3a..98c7df7d07f900b1b407c6a07b1ec88c7d75ed75 100644
--- a/algorithms/genetic_bucket_algorithm.py
+++ b/algorithms/genetic_bucket_algorithm.py
@@ -1,7 +1,7 @@
-import cmath
-import random
-from copy import deepcopy
-from math import cos, degrees, inf, log10, pi, radians, sin
+from random import randrange, random, choice
+from cmath import exp, phase
+from math import log10, pi
+from typing import List
 
 from utils.pattern import compute_pattern
 
@@ -9,13 +9,17 @@ from .base_algorithm import BaseAlgorithm
 
 
 class Chromosome:
-    def __init__(self, N, bit_count):
-        self.gene = [Chromosome.new_gene(bit_count) for i in range(N)]
+    def __init__(self, n, bit_count):
+        self.gene = [Chromosome.new_gene(bit_count) for _ in range(n)]
         self.fitness = float("nan")
+        self.needs_update = True
+
+    def get_score(self):
+        return -20 * log10(abs(self.fitness))
 
     @staticmethod
     def new_gene(bit_count):
-        return random.randrange(0, 2 ** bit_count)
+        return randrange(0, 2 ** bit_count)
 
 
 class GeneticBucketAlgorithm(BaseAlgorithm):
@@ -23,8 +27,10 @@ class GeneticBucketAlgorithm(BaseAlgorithm):
     discrete values.
     """
 
+    chromosomes: List[Chromosome]
+
     def __init__(self, options):
-        BaseAlgorithm.__init__(self, options)
+        super().__init__(self, options)
         self.main_ang = options.main_ang
         self.sample_size = options.sample_size
         self.null_degrees = options.null_degrees
@@ -32,6 +38,7 @@ class GeneticBucketAlgorithm(BaseAlgorithm):
         self.bit_count = options.bit_count
         self.bit_resolution = options.bit_resolution
         self.mutation_factor = options.mutation_factor
+        self.chromosomes = []
         self.buckets = [[]] * 8
 
         self.check_parameters()
@@ -41,43 +48,58 @@ class GeneticBucketAlgorithm(BaseAlgorithm):
         assert len(self.null_degrees) == 1
 
     def solve(self):
-        self.intialize_sample()
-        self.update_fitness()
-        self.sort_fitness()
-        for generation in range(self.gen_to_repeat):
-            for ii in range(self.sample_size // 2, self.sample_size - 1, 2):
-                bucket_idx = random.randrange(4)
-                p1 = random.choice(self.buckets[bucket_idx])
-                p2 = random.choice(self.buckets[bucket_idx + 4])
+        self.initialize_sample()
+        self.organize_sample()
 
-                self.crossover(p1, p2, ii, ii + 1)
-            # self.mutate_sample()
-            self.update_fitness()
-            self.sort_fitness()
-
-        return self.make_weights(self.chromosomes[0])
-
-    def mutate_sample(self):
-        for chromosome in self.chromosomes[1:]:  # for all except the best chromosome
-            for idx in range(self.N):
-                if random.random() <= self.mutation_factor:
-                    chromosome.gene[idx] = Chromosome.new_gene(self.bit_count)
+        for generation in range(self.gen_to_repeat):
+            self.create_children()
+            self.mutate_sample()
+            self.organize_sample()
+
+        return (self.make_weights(self.chromosomes[0]), self.chromosomes[0].get_score())
+
+    def create_children(self):
+        for ii in range(self.sample_size // 2, self.sample_size - 1, 2):
+            bucket_idx = randrange(8)
+            p1 = choice(self.buckets[bucket_idx])
+            p2 = min(
+                self.buckets[7 - bucket_idx], key=lambda x: abs(x.fitness + p1.fitness)
+            )
+            self.crossover(p1, p2, ii, ii + 1)
 
-    def update_fitness(self, use_exact_angle=True):
+    def organize_sample(self):
+        # Update fitness
+        for chromosome in self.chromosomes:
+            if chromosome.needs_update:
+                chromosome.fitness = compute_pattern(
+                    N=self.N,
+                    k=self.k,
+                    weights=self.make_weights(chromosome),
+                    degrees=self.null_degrees,
+                    use_absolute_value=False,
+                )[0]
+                chromosome.needs_update = False
+
+        # Sort sample by fitness
+        self.chromosomes.sort(key=lambda x: x.get_score(), reverse=True)
+
+        # Allocate chromosomes to their respective buckets
         self.buckets = [[]] * 8
         for chromosome in self.chromosomes:
-            chromosome.fitness = compute_pattern(
-                N=self.N,
-                k=self.k,
-                weights=self.make_weights(chromosome),
-                degrees=self.null_degrees,
-                use_absolute_value=False,
-            )[0]
-            bucket_idx = int(((cmath.phase(chromosome.fitness) + pi) / (2 * pi)) * 8) % 8
+            bucket_idx = int(((phase(chromosome.fitness) + pi) / (2 * pi)) * 8) % 8
             self.buckets[bucket_idx].append(chromosome)
 
-    def sort_fitness(self):
-        self.chromosomes.sort(key=lambda x: -20 * log10(abs(x.fitness)), reverse=True)
+    def mutate_sample(self):
+        # For all except the best chromosome
+        for original in range(1, self.sample_size):
+            mutated = original + self.sample_size - 1
+            self.chromosomes[mutated].needs_update = True
+
+            for ii in range(self.N):
+                if random() <= self.mutation_factor:
+                    self.chromosomes[mutated].gene[ii] = Chromosome.new_gene(self.bit_count)
+                else:
+                    self.chromosomes[mutated].gene[ii] = self.chromosomes[original].gene[ii]
 
     def make_weights(self, chromosome):
         weights = []
@@ -88,7 +110,7 @@ class GeneticBucketAlgorithm(BaseAlgorithm):
                 * pi
                 / (2 ** self.bit_resolution)
             )
-            weights.append(cmath.exp(1j * angle))
+            weights.append(exp(1j * angle))
         return weights
 
     def crossover(self, p1, p2, c1, c2):
@@ -98,7 +120,7 @@ class GeneticBucketAlgorithm(BaseAlgorithm):
             self.chromosomes[c1].gene[ii] = (g1 + g2) // 2
             self.chromosomes[c2].gene[ii] = (g1 + g2 + 1) // 2
 
-    def intialize_sample(self):
+    def initialize_sample(self):
         self.chromosomes = [
-            Chromosome(self.N, self.bit_count) for i in range(self.sample_size)
+            Chromosome(self.N, self.bit_count) for _ in range(self.sample_size * 2 - 1)
         ]