You could try a sort of uniform crossover.
In general Uniform Crossover uses a fixed mixing ratio between two parents and the operator evaluates each gene in the parent chromosomes for exchange with a probability of 0.5
.
Using Python syntax:
import random
p = [['a','b','c','d'], ['e','f','a','b']] # parents
for i in range(len(p[0])):
offspring.append(p[random.randint(0, 1)][i])

Given the uniqueness constraint, the basic scheme has to be modified:
import random
p = [['a','b','c','d'], ['e','f','a','b']] # parents
for i in range(len(p[0])):
if p[1][i] in p[0]: # cannot risk crossover, keep basic gene
offspring.append(p[0][i])
else: # standard uniform crossover
offspring.append(p[random.randint(0, 1)][i])
The constraint is "automatically" satisfied and you have a smaller search space.
Note that the crossover is somewhat bound to the first parent (p[0]
) and we get a limited number of variations:
CHROMOSOME FREQUENCY
abcd *************************
efcd ************************
ebcd ************************
afcd ************************
In this regards a small improvement is:
if p[1][i] in offspring or p[1][i] in p[0][i:]:
offspring.append(p[0][i])
else:
offspring.append(p[random.randint(0, 1)][i])
and
CHROMOSOME FREQUENCY
efcd ******
afcd ************
efad ******
ebcd ************
efcb ******
efab ******
ebad ************
abcd *************************
afcb ************
But the "trick" works only for some parents. E.g. switching the parents:
p = [['e','f','a','b'], ['a','b','c','d']]
you have again:
CHROMOSOME FREQUENCY
efcd *************************
efcb ************************
efad *************************
efab ************************
The edge recombination operator is another possibility:
ERO creates a path that is similar to a set of existing paths (parents) by looking at the edges rather than the vertices. The main application of this is for crossover in genetic algorithms when a genotype with non-repeating gene sequences is needed such as for the travelling salesman problem.
(not sure it's your case)