pythonbit-manipulationgenetic-algorithmmutationcrossover

Cross-Over with string Encoded Chromosomes


I am implementing Genetic Algorithm (GA). There are 43 numbers [Ambulance Locations] to choose from (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39) , I choose 3 places since I have 3 ambulances.

I can only put my ambulance in 3 locations among 1-39 locations (Restriction).

A chromosome sample: [000010000000000000100000000000100000000]. This represents that I want to put my Ambulance on the 5th, 19th, and 31 positions. The bits against positions 5th, 19th, and 31 are 1 and rest positions are 0. In other words, I am turning on 5-bit, 19-bit, and 31-bit.

Let's say

Candidate solution 1 (111000000000000000000000000000000000000) 

and

Candidate Solution 2 (000000000000000000000000000000000000111)

My candidate solutions are encoded in 39-bits,I want to do a cross-over these 39-bits. Above two candidates solutions are a bit weird. What can be done here?

What can be a good approach to perform cross-over while puting ambulances in 3 locations among 1-39 locations?


Solution

  • OPs question

    There are many ways to perform crossover - in this example as we must end up with the same number of bits in the input and the output I have used a crossover between the bits, allowing for 0-3 bits to get swapped between the two inputs.

    The solution provided is dynamic and will work for any length chromosome with any number of flipped bits (1s).

    import random
    from typing import Union
    def crossover(cs1: str, cs2: str) -> Union[str, str]:
        if len(cs1) != len(cs2):
            raise Exception("Candidate solutions input are of different length.")
        # get the flipped bits in each string
        cs1_bits = [index for index, gene in enumerate(cs1) if gene == "1"]
        cs2_bits = [index for index, gene in enumerate(cs2) if gene == "1"]
        if len(cs1_bits) != len(cs2_bits):
            raise Exception("Candidate solutions input have different number of flipped bits.")
        index: int = random.randint(0, len(cs1_bits))
        sol1_bits, sol2_bits = cs1_bits[:index] + cs2_bits[index:], cs2_bits[:index] + cs1_bits[index:]
        output_1 = ""
        output_2 = ""
        for i in range(len(cs1)):
            if i in sol1_bits:
                output_1 += "1"
            else:
                output_1 += "0"
            if i in sol2_bits:
                output_2 += "1"
            else:
                output_2 += "0"
        return output_1, output_2
    

    Example input/output for OPs solution.

    # Input
    "111000000000000000000000000000000000000", "000000000000000000000000000000000000111"
    # Output
    '110000000000000000000000000000000000001', '001000000000000000000000000000000000110'
    

    How to perform standard crossover

    You perform crossover in a genetic algorithm by selecting a random index along the length of the number of values, in your instance, you will choose a value between 0-38 (39 options).

    You will then split both of the inputs and join the front of cs1 onto the back of cs2 and vice versa.

    from typing import Union
    import random
    def crossover(cs1: str, cs2: str) -> Union[str, str]:
        index: int = random.randint(0, len(cs1))
        return cs1[:index] + cs2[index:], cs2[:index] + cs1[index:]
    

    If you have the values:

    cs1 = "111000000000000000000000000000000000000"
    cs2 = "000000000000000000000000000000000000111"
    

    Crossing over with an index of 1 will return:

    output_1 = "100000000000000000000000000000000000111"
    output_2 = "011000000000000000000000000000000000000"
    

    Crossing over with an index of 5 will return:

    output_1 = "111000000000000000000000000000000000111"
    output_2 = "000000000000000000000000000000000000000"