Genéticos: Problema da Mochila em Python

De Aulas
Revisão de 19h02min de 17 de março de 2022 por Admin (discussão | contribs)

Afluentes: Inteligência Artificial, Modelos Evolutivos e Tratamento de Incertezas

Definição do Problema

Para mostrar uma implementação de um Algoritmo Genético, iremos trabalhar com o Problema da Mochila.

  • Problema de otimização combinatória (NP-Completo)
  • Estudado por mais de um século (desde ~1897)
  • Resolvido por vários algoritmos
Ag mochila.png

Podemos representar a aptidão de uma solução como a soma dos valores dos objetos e com uma penalidade para soluções que passam do limite de volume.

  • Queremos o maior valor possível sem ultrapassar o limite da mochila.
  • Vários itens que gostaria de levar em uma mochila
  • Cada item tem um peso e um benefício, ou valor
  • Há uma capacidade limite de peso
  • Deve-se carregar itens com o máximo valor total sem superar o limite de peso

Arquivo de Configuração

 1{
 2  "capacity": "22",
 3  "itens": [
 4    { "name": "a", "price": "5", "weight": "7" },
 5    { "name": "b", "price": "8", "weight": "8" },
 6    { "name": "c", "price": "3", "weight": "4" },
 7    { "name": "d", "price": "2", "weight": "10" },
 8    { "name": "e", "price": "7", "weight": "4" },
 9    { "name": "f", "price": "9", "weight": "6" },
10    { "name": "g", "price": "4", "weight": "4" }
11  ]
12}

Programa em Python

  1import json
  2from random import *
  3
  4
  5# Lê o arquivo de configuração com a informação dos
  6# itens disponíveis, preço e pêso
  7def load():
  8    config = open('config.json')
  9    data = json.load(config)
 10    config.close()
 11    return data
 12
 13
 14def create_population():
 15    global data, population
 16    population = []
 17    for p in range(population_size):
 18        element = []
 19        for i in range(len(data["itens"])):
 20            # 1 se o elemento está na mochila e 0 se ele está ausente
 21            element = element + [randint(0, 1)]
 22        element = element + [compute_fitness(element)]  # aptidão
 23        element = element + [0, 0]  # aptidão acumulada e se foi selecionado
 24        population = population + [element]
 25    # return population
 26
 27
 28def unselect_all():
 29    global data, population
 30    for i in range(len(population)):
 31        population[i][len(data["itens"]) + 2] = 0
 32
 33def clone_element(element):
 34    clone = []
 35    for i in range(len(element)):
 36        clone += [element[i]]
 37    return clone
 38
 39
 40def compute_fitness(element):
 41    global data
 42    price = 0
 43    weight = 0
 44    for i in range(len(data["itens"])):
 45        if element[i] == 1:
 46            price = price + int(data["itens"][i]["price"])
 47            weight = weight + int(data["itens"][i]["weight"])
 48    fitness = price
 49    if weight > int(data["capacity"]):
 50        fitness = fitness - 100
 51    return fitness
 52
 53
 54def evaluate():
 55    global population
 56    for i in range(len(population)):
 57        compute_fitness(population[i])
 58
 59
 60def selection(best):
 61    print("selection", best)
 62    unselect_all()
 63    global data, population, population_size, selection_size
 64    # procura o menor fitness negativo
 65    lower = 0
 66    fitness_pos = len(data["itens"])
 67    for i in range(len(population)):
 68        if population[i][fitness_pos] < lower:
 69            lower = population[i][fitness_pos]
 70
 71    # Se houve aptidão negativa, subtrai o menor valor de todos os
 72    # elementos para não termos aptidão negativa
 73    if lower < 0:
 74        for i in range(len(population)):
 75            population[i][fitness_pos] = population[i][fitness_pos] - lower
 76
 77    # ordena a população com base no fitness
 78    population = sorted(population, key=lambda x: x[fitness_pos])
 79
 80    # calcula a aptidão acumulada e aptidão total
 81    fitness_total = 0
 82    for i in range(len(population)):
 83        fitness_total = fitness_total + population[i][fitness_pos]
 84        population[i][fitness_pos + 1] = fitness_total
 85
 86    # seleciona os n melhores. Se for pra escolher quem vai sobreviver
 87    # escolhe o tamanho da população
 88    aux = selection_size
 89    if not best:
 90        aux = population_size
 91
 92    for j in range(aux):
 93        last = 0
 94        num = randint(0, fitness_total)
 95        # encontra a posição da roleta do selecionado
 96        found = False
 97        i = 0
 98        while not found:
 99            current = population[i][fitness_pos + 1]
100            if last < num and num <= current:
101                is_selected = population[i][fitness_pos + 2]
102                # se já está selecionado, pega o próximo não selecionado da roleta
103                while is_selected:
104                    i = i + 1
105                    # se chegou ao final, volta no primeiro
106                    if i >= len(population):
107                        i = 0
108                    is_selected = population[i][fitness_pos + 2]
109                # seleciona
110                population[i][fitness_pos + 2] = 1
111                found = True
112            last = current
113            i += 1
114            if i >= len(population):
115                i = 0
116
117
118def crossover():
119    print("crossover")
120    global data, population, crossover_size, selection_size
121    selected_pos = len(data["itens"]) + 2
122    unselected = []
123    selected = []
124    for i in range(len(population)):
125        if population[i][selected_pos] == 0:
126            unselected = unselected + [population[i]]
127        else:
128            selected = selected + [population[i]]
129    # randomiza para fazer pares aleatórios
130    shuffle(selected)
131    i = 0
132    while i < len(selected):
133        # deseleciona os pais
134        selected[i][selected_pos] = 0
135        selected[i+1][selected_pos] = 0
136        # clona os pais criando os filhos
137        child_a = clone_element(selected[i])
138        child_b = clone_element(selected[i+1])
139        # faz as trocas genéticas
140        for j in range(crossover_size):
141            cross_gen = randint(0, len(data["itens"]) - 1)
142            child_a[cross_gen] = selected[i+1][cross_gen]
143            child_b[cross_gen] = selected[i][cross_gen]
144        compute_fitness(child_a)
145        compute_fitness(child_b)
146        # coloca os filhos criados na lista unselected, que depois
147        # irá compor a população
148        unselected = unselected + [child_a, child_b]
149        i = i + 2
150    population = unselected + selected
151
152
153def mutation():
154    print("mutation")
155    global mutation_elements_size, mutation_size, population, data
156    size = randint(0, mutation_elements_size)
157    for i in range(size):
158        element_pos = randint(1, len(population)) - 1
159        for j in range(mutation_size):
160            mutation_gen = randint(0, len(data["itens"]) - 1)
161            if population[element_pos][mutation_gen] == 1:
162                population[element_pos][mutation_gen] = 0
163            else:
164                population[element_pos][mutation_gen] = 1
165
166
167def survival():
168    print("survival")
169    global population, data
170    selected_pos = len(data["itens"]) + 2
171    aux = population[:]
172    population = []
173    for i in range(len(aux)):
174        if aux[i][selected_pos] == 1:
175            population += [aux[i]]
176
177
178# PROGRAMA PRINCIPAL ------------------------------------------
179data = load()
180population_size = 50
181selection_size = 20
182crossover_size = 2
183mutation_elements_size = 10
184mutation_size = 2
185epochs = 100
186
187create_population()
188for epoch in range(epochs):
189    print("epoch", epoch)
190    selection(True)
191    crossover()
192    mutation()
193    # evaluate()
194    selection(False)
195    survival()
196    # show population
197    for i in range(len(population)):
198        print(population[i])