```python
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from sympy import *
from math import log, ceil
from random import randint
import functools
```

---


```python
class GeneticAlgo:

    def __init__(self, a,b, functionOptions, 
                 populationOpts, operatorOpts, tournamentOpts,
                 freezeLimit = 6, precision = 10**(-6)):
        
        self.a, self.b = a, b
        self.func, self.funcVariables, self.funcFitness = functionOptions
        self.popNumber, self.popSize = populationOpts        
        self.mutationChance, self.crossChance = operatorOpts
        self.tournamentMembersNumber = tournamentOpts
        self.freezeLimit = freezeLimit
        self.precision = precision
        
        self.length = self.__initLength()
        self.population = self.__initPopulation()
        self.bits = self.__initMaxBits()
        self.realPrecision = self.__initRealPrecision()
        
        
        self.fittest = []
        
        self.infoData = {
            "OffspringNUmber": 0,
            "CurrentFittest": []
        }
        

        
# Initializaton functions
#----------------------------------------------------------------------

    def __initLength(self): return self.b - self.a
    
    def __initPopulation(self): return np.random.randint(self.a, self.b + 1, self.popSize)
    
    def __initMaxBits(self): return ceil(log(self.length/self.precision, 2))   
    
    def __initRealPrecision(self):
        
        maxBinaryinBaseTen = 2**self.bits / self.length
        
        return 1 / maxBinaryinBaseTen
    
    
# Main functions
#----------------------------------------------------------------------

    def solve(self, displayData = False):
        
        offspringNumber = 0
        freezeNumber = 0
        
        while (freezeNumber < self.freezeLimit):
                        
            self.population = self.__offspring()
            
            if (not self.__newFitElemExist()):
                freezeNumber+=1
            
            offspringNumber+=1
            
            if displayData:
                self.__displayData(freezeNumber, offspringNumber)
            
        
            
        self.infoData["OffspringNUmber"] = offspringNumber
            
        return self.fittest
            
        
    def __offspring(self):
            
        children = []

        while len(children) < self.popSize:

            selected = self.__selectMultiple(2)     
            mutated = self.__execute(self.__mutate, self.mutationChance, selected)
            crossed = self.__execute(self.__cross, self.crossChance, mutated)

            children.extend(self.__listToNumber(crossed))

        return np.array(children)       
        

    def __select(self):
    
        indexes = np.random.choice(self.popSize, self.tournamentMembersNumber)
        
        competitors = self.population[indexes]
        
        winner = self.__tournament(competitors)
        
        return self.__toBinary(winner)


    def __mutate(self, elems):

        indexes = np.random.randint(1, self.bits, 2)
        
        for idx in range(len(elems)):
            index = indexes[idx]
            
            elems[idx][index] = self.__bitFlip(elems[idx][index])

        return elems


    def __cross(self, elems):

        index = randint(1, self.bits)
        
        return self.__merge(elems, index)  
    
    
    
                
    def __newFitElemExist(self): 
        
        sortedPopulation = sorted(self.population, key = lambda elem: self.func.subs({x:elem}))
        
        self.infoData["CurrentFittest"].append(sortedPopulation[0])
        
        
        if not self.fittest: 
            self.fittest.append(sortedPopulation[0])            
            return True
        
        for elem in sortedPopulation:
            for currFittest in self.fittest:
                
                if self.__isFittest(elem, currFittest):                        
                    self.__resetFittestList()
                    self.fittest.append(elem)
                    return True
                
        return False
    
        
    def __isEqualFit(self, a, b): 
        print(f'{a}, {self.func.subs({x:a})}')
        print(f'{b}, {self.func.subs({x:b})}')     
        
        return (
            abs(self.func.subs({x:a})) - abs(self.func.subs({x:b})) <= self.realPrecision * 2 and \
            abs(b - a) > self.precision * 10 
        )
    
    def __isFittest(self, a, b): return True if self.funcFitness(self.func.subs({x:a}), self.func.subs({x:b})) else False    
    
    def __getFittest(self, a, b): return a if self.__isFittest(a, b) else b   
    
    
#----------------------------------------------------------------------    


    def __tournament(self, competitors): 
        
        winner = competitors[0]
        
        for idx in range(1, self.tournamentMembersNumber):
            winner = self.__getFittest(winner, competitors[idx])
            
        return winner
    
    
    def __merge(self, elems, index):   
        elem1, elem2 = elems    
    
        if index == 0: index+=1

        for idx in range(index, self.bits):
            elem1[idx], elem2[idx] = elem2[idx], \
                                     elem1[idx]
        return elem1, elem2
    
        
    def __evaluate(self): return [ (elem, self.func.subs({x:elem})) for elem in self.population ]
                      
    def __resetFittestList(self): self.fittest = []    
                      
    def __displayData(self, freezeNumber, offspringNumber):
        print("\n -------- ")
        #print(f'population - {self.population}')
        print(f'fittest - {self.fittest}')
        print(f'freeze number - {freezeNumber}')
        print(f'offspring number - {offspringNumber}')
        #print(self.__evaluate())
                      
    
# Helper functions
#----------------------------------------------------------------------
    
    def __selectMultiple(self, n): 
        return [self.__select() for i in range(n)]
    
    def __mutateMultiple(self, elems):
        return [self.__mutate(elem) for elem in elems]
    
    def __execute(self, func, probability, value):
        return func(value) if (randint(1, 100) / 100) < probability else value


# Utility functions
#----------------------------------------------------------------------
        
    
    def __toBinary(self, number): 
        
        baseTen = (number - self.a) * 2**self.bits / self.length
        
        return list("{0:b}".format(int(baseTen)).zfill(self.bits))
        
        
    def __toNumber(self, bitList): 
                
        baseTwo = sum([ int(bitList[i]) * (2**(self.bits - 1 - i)) for i in range(self.bits)])
        
        return self.a + baseTwo * (self.length / 2**self.bits)


    def __listToNumber(self, list): 
        return [ self.__toNumber(elem) for elem in list ]
    
    def __bitFlip(self, bit): 
        return '1' if bit == '0' else '0'
```

---

### Initialization


```python
variables = x = Symbol("x")
```


```python
def fitnessFunc(a,b): return abs(a) < abs(b)
```


```python
a, b = -5, 5
func = x**2 + 5*x + 6 #x*sin(10*pi*x) + 1 

functionOpts = func, variables, fitnessFunc
populationOpts = populationNumber, populationSize = 5, 100
operatorOpts = mutation, crossover = 0.2, 0.8
tournamentMembersOpts = 4
freezeLimit = 6
precision = 10**(-6)
```

### Calculation


```python
ga = GeneticAlgo(a,b, functionOpts, populationOpts, operatorOpts, tournamentMembersOpts, freezeLimit, precision)


ga.solve(True)
```

    
     -------- 
    fittest - [-2.1875]
    freeze number - 0
    offspring number - 1
    
     -------- 
    fittest - [-2.8749924898147583]
    freeze number - 0
    offspring number - 2
    
     -------- 
    fittest - [-2.0234376192092896]
    freeze number - 0
    offspring number - 3
    
     -------- 
    fittest - [-2.9902344942092896]
    freeze number - 0
    offspring number - 4
    
     -------- 
    fittest - [-2.9993897676467896]
    freeze number - 0
    offspring number - 5
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 0
    offspring number - 6
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 1
    offspring number - 7
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 2
    offspring number - 8
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 3
    offspring number - 9
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 4
    offspring number - 10
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 5
    offspring number - 11
    
     -------- 
    fittest - [-3.0000001192092896]
    freeze number - 6
    offspring number - 12





    [-3.0000001192092896]


fittest = []
population = []

ga = GeneticAlgo(a,b, func,variables, fitnessFunc, (5, 100), (0.2, 0.8), 2, freezeLimit, precision)
fitVal = ga.solve()
    
rangee = np.arange(0, ga.infoData["OffspringNUmber"])
    
fittest.extend(ga.infoData["CurrentFittest"])
solutionInOffspring = rangee

print(fitVal)
plt.plot(solutionInOffspring, fittest)
plt.xlabel('Offspring')
plt.ylabel('Current fittest')solutionInOffspring = []
population = []

for i in range(10, 1010, 100):
    ga = GeneticAlgo(a,b, func,variables, fitnessFunc, (5, i), (0.2, 0.8), 2, freezeLimit, precision)
    fitVal = ga.solve()
    
    population.append(i)
    solutionInOffspring.append(ga.infoData["OffspringNUmber"])

print(fitVal)
plt.plot(population, solutionInOffspring)
plt.xlabel('Population Size')
plt.ylabel('Offspring')solutionInOffspring = []
mutation = []

for i in np.arange(0.00, 1, 0.05):
    ga = GeneticAlgo(a,b, func,variables, fitnessFunc, (5, 100), (i, 0.8), 2, freezeLimit, precision)
    fitVal = ga.solve()
    
    mutation.append(i)
    solutionInOffspring.append(ga.infoData["OffspringNUmber"])

print(fitVal)
plt.plot(mutation, solutionInOffspring)
plt.xlabel('Mutation')
plt.ylabel('Offspring')solutionInOffspring = []
tournamentMembers = []

for i in np.arange(2, 20, 1):
    ga = GeneticAlgo(a,b, func,variables, fitnessFunc, (5, 500), (0.2, 0.8), i, freezeLimit, precision)
    fitVal = ga.solve()
    
    tournamentMembers.append(i)
    solutionInOffspring.append(ga.infoData["OffspringNUmber"])

print(fitVal)
plt.plot(tournamentMembers, solutionInOffspring)
plt.xlabel('Tournament members')
plt.ylabel('Offspring')

```python

```

4682e6b6022e5cdcc5805fccbc52812323d596a0
ipynb
Jupyter Notebook
GeneticAlgo_OneVariable.ipynb
Paraml3sS/Genetic-Algorithm
5c0beb3460d7cf323712f7ef735c0d38efabb20d
MIT
GeneticAlgo_OneVariable.ipynb
Paraml3sS/Genetic-Algorithm
5c0beb3460d7cf323712f7ef735c0d38efabb20d
MIT
GeneticAlgo_OneVariable.ipynb
Paraml3sS/Genetic-Algorithm
5c0beb3460d7cf323712f7ef735c0d38efabb20d
MIT
Qwen/Qwen-72B
1. YES
2. YES