vferraz
vferraz

Reputation: 469

Roulette Wheel Selection for non-ordered fitness values

I need to have a fitness proportionate selection approach to a GA, however my population cant loose the structure (order), in this case while generating the probabilities, I believe the individuals get the wrong weights, the program is:

population=[[[0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1], [6], [0]], 
[[0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1], [4], [1]], 
[[0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0], [6], [2]],
[[1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0], [4], [3]]]

popultion_d={'0,0,1,0,1,1,0,1,1,1,1,0,0,0,0,1': 6, 
'0,0,1,1,1,0,0,1,1,0,1,1,0,0,0,1': 4, 
'0,1,1,0,1,1,0,0,1,1,1,0,0,1,0,0': 6, 
'1,0,0,1,1,1,0,0,1,1,0,1,1,0,0,0': 4}

def ProbabilityList(population_d):
    fitness = population_d.values()
    total_fit = (sum(fitness))
    relative_fitness = [f/total_fit for f in fitness]
    probabilities = [sum(relative_fitness[:i+1]) for i in range(len(relative_fitness))]
    return (probabilities)

def FitnessProportionateSelection(population, probabilities, number):
    chosen = []
    for n in range(number):
        r = random.random()
        for (i, individual) in enumerate(population):
            if r <= probabilities[i]:
                chosen.append(list(individual))
                break
    return chosen

number=2

The population element is: [[individual],[fitness],[counter]]

The probabilities function output is: [0.42857142857142855, 0.5714285714285714, 0.8571428571428571, 1.0]

What I notice here is that the previous weight is summed up to the next one, not necessarily being in crescent order, so a think a higher weight is given to the cromosome with a lowest fitness.

I dont want to order it because I need to index the lists by position later, so I think I will have wrong matches.

Anyone knows a possible solution, package or different approach to perform a weighted the selection in this case?

p.s: I know the dictionary may be redundant here, but I had several other problems using the list itself.

Edit: I tried to use random.choices() as you can see below (using relative fitness):

def FitnessChoices(population, probabilities, number):
    return random.choices(population, probabilities, number)

But I get this error: TypeError: choices() takes from 2 to 3 positional arguments but 4 were given

Thank you!

Upvotes: 0

Views: 1445

Answers (1)

tsabsch
tsabsch

Reputation: 2221

Using random.choices is certainly a good idea. You just need to understand the function call. You have to specify, whether your probabilities are marginal or cumulated. So you could use either

import random

def ProbabilityList(population_d):
    fitness = population_d.values()
    total_fit = sum(fitness)
    relative_fitness = [f/total_fit for f in fitness]
    return relative_fitness

def FitnessChoices(population, relative_fitness, number):
    return random.choices(population, weights = relative_fitness, k = number)

or

import random

def ProbabilityList(population_d):
    fitness = population_d.values()
    total_fit = sum(fitness)
    relative_fitness = [f/total_fit for f in fitness]
    cum_probs = [sum(relative_fitness[:i+1]) for i in range(len(relative_fitness))]
    return cum_probs

def FitnessChoices(population, cum_probs, number):
    return random.choices(population, cum_weights = cum_probs, k = number)

I'd recommend you to have a look at the differences between keyword and positional arguments in python.

Upvotes: 1

Related Questions