In the sequential encoding, some illegal descendants might be generated where the corresponding approaches such as refusal, punishment orrepair are required.
For example: After the crossover operator, the children’s encoding is out of expectation:
P1: 21|345|67 P2: 43|125|76
C1: 21|125|67 P2: 43|345|67
There are various kinds of techniques to fix the illegal situation:
1) PartiallyMapped Crossover
Step 1: Select the proper splitting point X, Y
Step 2: Switch the middle part, and fix them
Step 3: Determine the map relationship
Step 4: Map the rest to the corresponding sequence based on the mapped relationship
import copy
import random
def partial_map(parent1, parent2, insert=None):
chromosome_len = len(parent1)
child1 = copy.deepcopy(parent1)
child2 = copy.deepcopy(parent2)
if insert is None:
insert = random.sample(range(1, chromosome_len), 2)
map_relation = map(lambda x, y: (x, y), child1[min(insert):max(insert)], child2[min(insert):max(insert)])
for (key, item) in map_relation:
child1[parent1.index(key)] = item
child1[parent1.index(item)] = key
child2[parent2.index(item)] = key
child2[parent2.index(key)] = item
return child1, child2
2) Order Crossover
Step 1: Select the splitting point X, Y
Step 2: Switch the middle part and fix the pairs
Step 3: List the first genes behind the splittingpoint Y, and remove the fixed genes
Step 4: Generate the sequence from the first position behind the splitting point Y
This crossover can keep the adjacent relationship and precedence order, and meets the requirement of TSP, but neglects the bit feature.
import random
def order_crossover(parent1, parent2, insert=None):
chromosome_len = len(parent1)
if insert is None:
insert = random.sample(range(1, chromosome_len), 2)
# list all the genes in an original order
gene_list1 = parent1[max(insert):] + parent1[:max(insert)]
gene_list2 = parent2[max(insert):] + parent2[:max(insert)]
fixed_part1 = parent1[min(insert):max(insert)]
fixed_part2 = parent2[min(insert):max(insert)]
filter_list1 = filter(lambda x: x not in fixed_part2, gene_list1)
filter_list2 = filter(lambda x: x not in fixed_part1, gene_list2)
child1 = filter_list1[-min(insert):] + fixed_part2 + filter_list1[:-min(insert)]
child2 = filter_list2[-min(insert):] + fixed_part1 + filter_list2[:-min(insert)]
return child1, child2
3) Cycle Crossover
The basic idea is that the bit of the position should be the same as the parents.
Step 1: select the first element of P1 as the first bit of C1,
Select the first element of P2 as the first bit of C2
Step 2: Find the first element of P2 in P1 and assign the element to the relative position in C1. Repeat the process until the first element in P1 is found in P2, which is called a cycle.
Step 3: Continue to take turns to operate on the most fore genes in P1, P2. (In the first step the first bit will be assigned to C1 while the second step assigned to C2, and in the third step, the first bit will be assigned to C1 again)
Step 4: Repeat the above process until all the bits are completed
import numpy as np
def cycle_crossover(parent1, parent2):
chromosome_len = len(parent1)
child1 = np.zeros(chromosome_len, dtype=int)
child2 = np.zeros(chromosome_len, dtype=int)
rest_index = range(0, chromosome_len)
index1 = []
index2 = []
indicator = 0
while indicator < chromosome_len:
if indicator % 2 == 0:
start = rest_index[0]
while start < chromosome_len:
value = parent2[start]
index1.append(start)
if value == parent1[rest_index[0]]:
break
start = parent1.index(value)
rest_index = filter(lambda x: x not in index1, rest_index)
if indicator % 2 == 1:
start = rest_index[0]
while start < chromosome_len:
value = parent2[start]
index2.append(start)
if value == parent1[rest_index[0]]:
break
start = parent1.index(value)
rest_index = filter(lambda x: x not in index2, rest_index)
if len(rest_index) == 0:
break
indicator += 1
child1[index1] = np.array(parent1)[index1]
child1[index2] = np.array(parent2)[index2]
child2[index1] = np.array(parent2)[index1]
child2[index2] = np.array(parent1)[index2]
return child1.tolist(), child2.tolist()