dodany raport
This commit is contained in:
parent
9b693a0d1c
commit
2f47d59eba
144
genetic_algorithm.md
Normal file
144
genetic_algorithm.md
Normal file
@ -0,0 +1,144 @@
|
||||
## Raport z podprojektu genetic_algorithm
|
||||
#### Autorka - Magdalena Biadała
|
||||
Program wykorzystuje algorytm genetyczny w celu znalezienia najlepszego dopasowania paczek do regałów, przy czym:
|
||||
- każda paczka musi zostać umieszczona na jakimś regale
|
||||
- waga wszystkich paczek umieszczoanych na regale nie może przekroczyć pojemności regału
|
||||
- paczki umieszczane są na regałach których parametry, takie jak temperatura czy wilgotność powietrza pozwalają na bezpieczne jej przechowywanie, bez zniszczenia jej zawartości.
|
||||
|
||||
Algorytm jako struktury danych przyjmuje 2 listy:
|
||||
|
||||
Listę *packs* w której indeksy oznaczają numery paczek, a wartości oznaczają wagę poszczególnych paczek. Na przykład:
|
||||
lista [2,6,2,5] to struktura, gdzie:
|
||||
- paczka o nr 0 ma wagę 2
|
||||
- paczka o nr 1 ma wagę 6
|
||||
- paczka o nr 2 ma wagę 2
|
||||
- paczka o nr 3 ma wagę 5
|
||||
|
||||
Listę *racks* w której indeksy oznaczają numery regałów, a wartości oznaczają pojemność poszczególnych regałów. Na przykład:
|
||||
lista [15,13,20,16] to struktura, gdzie:
|
||||
- regał o nr 0 ma pojemność 15
|
||||
- regał o nr 1 ma pojemność 13
|
||||
- regał o nr 2 ma pojemność 20
|
||||
- regał o nr 3 ma pojemność 16
|
||||
|
||||
|
||||
#### Program zawiera kilka ważnych funkcji:
|
||||
|
||||
```python
|
||||
def first_gen():
|
||||
first_generation = []
|
||||
for individual in range(generation_size):
|
||||
individual = []
|
||||
for pack in range(number_of_packages):
|
||||
r = random.randint(0,number_of_racks-1)
|
||||
individual.append(r)
|
||||
first_generation.append(individual)
|
||||
return first_generation
|
||||
```
|
||||
>funkcja *first_gen()* tworzy pierwsze pokolenie osobników;
|
||||
|
||||
```python
|
||||
def evaluation(individual):
|
||||
rest_of_capacity = racks.copy()
|
||||
for i in range(number_of_packages):
|
||||
rest_of_capacity[individual[i]] -= packages[i]
|
||||
fitness = 0
|
||||
for i in range(number_of_racks):
|
||||
if rest_of_capacity[i] < 0:
|
||||
fitness += rest_of_capacity[i]
|
||||
elif rest_of_capacity[i] == 0:
|
||||
fitness += amount_of_promotion
|
||||
return fitness
|
||||
```
|
||||
> funkcja *evaluation()* ocenia wybranego osobnika pod względem tego, czy:
|
||||
- regały nie są przepełnione
|
||||
- coś.
|
||||
|
||||
>Dodatkowo delikatnie promuje ona osobniki, króre wykorzystują pojemność regałów w całości, a na koniec zwraca parametr *fitness*.
|
||||
|
||||
|
||||
```python
|
||||
def roulette(generation):
|
||||
evaluations = []
|
||||
for i in range(generation_size):
|
||||
individual_fitness = evaluation(generation[i])
|
||||
evaluations.append(individual_fitness)
|
||||
maximum = min(evaluations)
|
||||
normalized = [x+(-1*maximum)+1 for x in evaluations]
|
||||
sum_of_normalized = sum(normalized)
|
||||
roulette_tab = [x/sum_of_normalized for x in normalized]
|
||||
for i in range(1,generation_size-1):
|
||||
roulette_tab[i] += roulette_tab[i-1]
|
||||
roulette_tab[generation_size-1] = 1
|
||||
survivors = []
|
||||
for individual in range(generation_size):
|
||||
random_number = random.random()
|
||||
interval_number = 0
|
||||
while random_number > roulette_tab[interval_number]:
|
||||
interval_number += 1
|
||||
survivors.append(generation[interval_number])
|
||||
return survivors
|
||||
```
|
||||
> funkcja *roulette()*:
|
||||
- liczy parametr *fitness* (oznaczający dopasowanie) dla każdego osobnika w pokoleniu
|
||||
- tworzy tablicę z przedziałami, przy czym im większy *fitness* osobnika tym większy jego przedział, a tym samym szansa że zostanie wylosowany
|
||||
- losuje liczby z zakresu od 0 do 1 (liczb jest tyle ile osobników w pokoleniu)
|
||||
-zwraca tych osobników, dla krótych wylosoana liczba wpadła w odpowiadający im przedział.
|
||||
|
||||
```python
|
||||
def crossover(individual1, individual2):
|
||||
cut = random.randint(1,number_of_packages-1)
|
||||
new1 = individual1[:cut]
|
||||
new2 = individual2[:cut]
|
||||
new1 = new1 + individual2[cut:]
|
||||
new2 = new2 + individual1[cut:]
|
||||
return new1, new2
|
||||
```
|
||||
>funkcja *crossover()* zajmuje się krzyżowaniem osobników.
|
||||
|
||||
```python
|
||||
def mutation(individual):
|
||||
locus = random.randint(0,number_of_packages-1)
|
||||
individual[locus] = random.randint(0,number_of_racks-1)
|
||||
return individual
|
||||
```
|
||||
>funkcja *mutation()* dokonuje mutacji osobnika.
|
||||
#### Inicjalizacja pierwszego pokolenia
|
||||
Pierwsze pokolenie jest tworzone za pomocą funkcji *first_gen()*. Osobnikiem jest lista której indeksy oznaczają numery paczek, a wartości oznaczają numery regałów. Na przykład:
|
||||
osobnik [3,0,5,5] to przyporządkowanie, gdzie:
|
||||
- paczka nr 0 leży na regale nr 3
|
||||
- paczka nr 1 leży na regale nr 0
|
||||
- paczka nr 2 leży na regale nr 5
|
||||
- paczka nr 3 leży na regale nr 5.
|
||||
|
||||
Dla każdego osobnika w pierwszym pokoleniu liczony jest parametr *fitness*.
|
||||
|
||||
#### Główna pętla programu
|
||||
Dla każdego pokolenia wykonywane są kolejno funkcje:
|
||||
- roulette()
|
||||
>pozostawia ona tylko część osobników w pokoleniu, część z nich zostaje zmultiplikowana
|
||||
|
||||
- crossover()
|
||||
>dla pokolenia które pozostało po ruletce
|
||||
|
||||
- mutation()
|
||||
> ta funkcja wykonywana jest tylko z pewnym prawdopodobieństem określonym w zmiennej *mutation_prob*.
|
||||
- evaluation()
|
||||
>dla pokolenia które pozostawiły po sobie poprzednie funkcje znalezione zostaje maksimum (największa wartość *fitness*).
|
||||
|
||||
#### Wynik działania algorytmu
|
||||
Algorytm zwraca osobnika (przyporządkowanie) dla którego wartość fitness była największa.
|
||||
|
||||
#### Przeprowadzone testy
|
||||
W celu dobrania wstępnych wartości parametrów:
|
||||
- *mutation_prob* (prawdopodobieństwo mutacji),
|
||||
- *generation_size* (wielkość pojedynczego pokolenia),
|
||||
- *number_of_generations* (liczba pokoleń),
|
||||
|
||||
został przeprowadzony test. Wykonywał on 200 razy algorytm genetyczny dla wybranych parametrów, a następnie liczył średnią i medianę maksymalnej wartości *fitness* znalezionej w każdej z prób. W poniższej tabeli oprócz średniej i mediany można także odczytać czas działania algorytmu.
|
||||
|
||||
![alt text](https://git.wmi.amu.edu.pl/s444360/SI_2020/src/master/wyniki_testu.PNG "tabela wyników")
|
||||
|
||||
|
||||
|
||||
- *amount_of_promotion* (wartość jaka zostaje dodana do *fitness* osobnika, jeśli wykorzysta jakiś regał w pełni)
|
Loading…
Reference in New Issue
Block a user