简体   繁体   中英

Genetic algorithm - ordered crossover in python

I have implemented a genetic algorithm in python 3, and have posted a question on code review with no answers yet, basically because my algorithm is running very slowly. By selectively commenting out different parts of my code, I have narrowed down the bottleneck to this section of code, the crossover algorithm:

def crossover(self, mum, dad):
    """Implements ordered crossover"""

    size = len(mum.vertices)

    # Choose random start/end position for crossover
    alice, bob = [-1] * size, [-1] * size
    start, end = sorted([random.randrange(size) for _ in range(2)])

    # Replicate mum's sequence for alice, dad's sequence for bob
    for i in range(start, end + 1):
        alice[i] = mum.vertices[i]
        bob[i] = dad.vertices[i]

    # # Fill the remaining position with the other parents' entries
    # current_dad_position, current_mum_position = 0, 0
    #
    # for i in chain(range(start), range(end + 1, size)):
    #
    #     while dad.vertices[current_dad_position] in alice:
    #         current_dad_position += 1
    #
    #     while mum.vertices[current_mum_position] in bob:
    #         current_mum_position += 1
    #
    #     alice[i] = dad.vertices[current_dad_position]
    #     bob[i] = mum.vertices[current_mum_position]
    #
    # # Return twins
    # return graph.Tour(self.g, alice), graph.Tour(self.g, bob)
    return mum, dad

The part which is commented out makes my program runtime go from ~7 seconds to 5-6 minutes (I am running 5000 iterations of the GA). Is there any way this ordered crossover can be carried out more efficiently?


What the crossover function does

For those unfamiliar, I am implementing an order-based crossover (OX2). Given two arrays of consecutive integers (the parents), two random start/end positions are selected.

  mum   =   4   9   2   8   3   1   5   7   6
  dad   =   6   4   1   3   7   2   8   5   9
                    ^           ^
                  start        end

The two children then share the resulting slices:

  child 1   =   _   _   2   8   3   1   _   _   _
  child 2   =   _   _   1   3   7   2   _   _   _
                        ^           ^

Now the remaining slots are filled in with the entries of the other parents in the order in which they appear, as long as repetitions are avoided. So since child 1 has their slice taken from mum, the rest of the entries are taken from dad. First we take 6, then 4, then next we take 7 (not taking 1 and 3 since they already appear in child 1 from mum), then 5, then 9. So

  child 1   =   6   4   2   8   3   1   7   5   9

and similarly,

  child 2   =   4   9   1   3   7   2   8   5   6

This is what I am implementing in the function.

I can only guess that your problem lies with the fact that your while-loop and the increment within is not limited to the actual size of the vertices vector, put a hard limit and test again:

     while current_dad_position < size and dad.vertices[current_dad_position] in alice:
         current_dad_position += 1

     while current_mom_position < size and mum.vertices[current_mum_position] in bob:
         current_mum_position += 1

I feel compelled to say that this might not necessarily result a unique solution, as I do not know how the how the algorithm, should behave if there are not enough unique unique singular vertices available to chose from because they violate your 'not from the other parent' restriction.

For anybody to test this out, I would recommend to complete your code with an simple example input and not commenting out the code in question, but rather to mark its BEGIN and END with comments.

Okay with the knowledge that the problem is uniquely solvable becuae of construction, here is how it should look like:

import random
import numpy as np

def crossover(mum, dad):
    """Implements ordered crossover"""

    size = len(mum.vertices)

    # Choose random start/end position for crossover
    alice, bob = [-1] * size, [-1] * size
    start, end = sorted([random.randrange(size) for _ in range(2)])

    # Replicate mum's sequence for alice, dad's sequence for bob
    alice_inherited = []
    bob_inherited = []
    for i in range(start, end + 1):
        alice[i] = mum.vertices[i]
        bob[i] = dad.vertices[i]
        alice_inherited.append(mum.vertices[i])
        bob_inherited.append(dad.vertices[i])

    print(alice, bob)
    #Fill the remaining position with the other parents' entries
    current_dad_position, current_mum_position = 0, 0

    fixed_pos = list(range(start, end + 1))       
    i = 0
    while i < size:
        if i in fixed_pos:
            i += 1
            continue

        test_alice = alice[i]
        if test_alice==-1: #to be filled
            dad_trait = dad.vertices[current_dad_position]
            while dad_trait in alice_inherited:
                current_dad_position += 1
                dad_trait = dad.vertices[current_dad_position]
            alice[i] = dad_trait
            alice_inherited.append(dad_trait)

        #repeat block for bob and mom
        i +=1

    return alice, bob

with

class Mum():
    def __init__(self):
        self.vertices =[  4,   9,   2,   8,   3,   1,   5,   7,   6 ]

class Dad():
    def __init__(self):
        self.vertices =  [ 6 ,  4  , 1  , 3 ,  7 ,  2  , 8  , 5 ,  9 ]

mum = Mum()  
dad = Dad()
a, b =  crossover(mum, dad) 
# a = [6, 4, 2, 8, 3, 1, 5, 7, 9]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM