# import
import random
import pandas as pd
import openpyxl
import numpy as np
import matplotlib.pyplot as plt
import copy
# calling the data
Parameter=pd.read_csv(r"C:\Users\user\Desktop\Parameter.csv")
Test_50_Data=pd.read_csv(r"C:\Users\user\Desktop\Test_50_Data.csv")
Test_50_Data = Test_50_Data.iloc[1:,1:] # 여기서 행이 16개만 뽑힘
Test_50_Data.head()
# number of row and column
people_row=len(Test_50_Data) #16 타임 시간 6:00~ 7:30
people_column=len(Test_50_Data.columns)-30 # 20 라인
#coefficients
zero_payload_desel=Parameter.iloc[0,1]
zero_payload_electric=Parameter.iloc[1,1]
variable_electric=Parameter.iloc[0,2]
variable_desel=Parameter.iloc[1,2]
ratio_desel=Parameter.iloc[0,3]
ratio_electric=Parameter.iloc[1,3]
# lists
fitness_value=[]
num_generation=[]
last_optimal=[]
repaired_population=[]
operating_interval = []
parameter_answer=[]
# Ebs constraint
Ebs_constraint=80
Ebs_constraint_list= pd.DataFrame([Ebs_constraint]*16).T
#interval of dispatching and distance
dispatching_interval = [5, 10, 20, 20, 10, 5, 5, 10, 20, 20, 10, 5, 10, 5, 5, 20, 10, 20, 5, 5, 10, 20, 20,10, 20, 20, 10, 5]
distance = [59, 30, 39, 70, 86, 60, 70, 35, 62, 57, 82, 44, 53, 74, 84, 48, 46, 87, 81, 50, 61, 34, 42, 49 ]
dispatching_interval = dispatching_interval[:20]
distance = distance[:20]
# operatng_interval
for _ in range(len(distance)) :
if distance[_] <=30 :
operating_interval .append(1)
elif distance[_] >31 and distance[_] <=60:
operating_interval .append(2)
else :
operating_interval.append(3)
# repair_rule
def repair_rule(repair):
repair = pd.DataFrame(repair)
while repair.values.sum() >= Ebs_constraint and repair.values.sum() >= Ebs_constraint :
row = random.randint(0,people_row-1)
col = random.randint(0,people_column-1)
if repair.iloc[row, col] == 1 :
repair.iloc[row, col] = 0
repair = repair.values.tolist()
return repair
# initialize population
def initialize_population(population_size):
pre_population = [[[random.randint(0, 1) for _ in range(people_column)] for _ in range(people_row)] for _ in range(population_size)]
# for i in range(population_size) :
# repaired_population.append(repair_rule(pre_population[i]))
# return repaired_population
return pre_population
#interpreter
def interpreter(subject):
interpreted = subject
for col in range (len(operating_interval)) : # 20번
if operating_interval[col] == 2:
for row in range(1, interpreted .shape[0]) :
if row % 2 != 0 : #
interpreted .iloc[row , col] =2
elif operating_interval[col] == 3:
for row in range(1, interpreted .shape[0]) :
if row % 4 != 0 :
interpreted .iloc[row , col] =2
return interpreted
def changing_capacity(interpreted): # 거리 고려 완료
def_list = copy.deepcopy(Ebs_constraint_list)
for col in range (len(operating_interval)) : # 20번
if operating_interval[col] == 1:
for row in range(interpreted.shape[0]-1):
try:
if interpreted.iloc[row, col] == 1:
def_list.iloc[:,row+1:row+13] -= 1
# Ebs_constraint_list.iloc[row+13, col] += 1
except IndexError:
continue
elif operating_interval[col] == 2:
for row in range(interpreted.shape[0]-1):
try:
if interpreted.iloc[row, col] == 1:
def_list.iloc[:,row+1:row+25] -= 1
except IndexError:
continue
else :
for row in range(interpreted.shape[0]-1):
try:
if interpreted.iloc[row, col] == 1:
def_list.iloc[:,row+1:row+37] -= 1
except IndexError:
continue
return def_list
# fitness function
def fitness(individual): #a = best_individual = 16x20
global people
total_value = 0
individual = pd.DataFrame(individual)
interpreted = interpreter(individual)
num_row_Ebs = interpreted.apply(lambda x: x.value_counts().get(1,0 ), axis=1)
num_row_Ebs = pd.DataFrame(num_row_Ebs)
Ebs_capacity= changing_capacity(interpreted)
# print(num_row_Ebs)
# print(Ebs_capacity)
for i in range(len(num_row_Ebs)):
if num_row_Ebs.iloc[i] >Ebs_capacity.iloc[i] :
return 1/99999999
for row in range(people_row): # 16
for column in range(people_column): # 20
if interpreted.iloc[row, column] == 1:
total_value += (distance[column] * zero_payload_electric + variable_electric * distance[column] * people.iloc[row, column]) * 0.6
elif interpreted.iloc[row, column] ==0 :
total_value += (distance[column] * zero_payload_desel + variable_desel * distance[column] * people.iloc[row,column]) * 3.06
return 1/total_value
# 교차 연산 (두 부모에서 두 자식 생성)
def crossover(parent1, parent2): # 3x50 을 2개 받아왔음
crossover_point = random.randint(1, len(parent1) - 2)
child1 = np.concatenate((parent1[:crossover_point], parent2[crossover_point:])) # 16x20의 형태를 갖춰야한다 , 수정 바람
child2 = np.concatenate((parent2[:crossover_point] , parent1[crossover_point:]))
child1 = child1.tolist()
child2 = child2.tolist()
return child1, child2
# 돌연변이 연산 (돌연변이가 발생할 확률이 있음)
def mutate(individual, mutation_rate):
mutated_individual = [[] for _ in range(people_row)]
for i in range(len(individual)):
for gene in individual[i]:
if random.random() < mutation_rate:
mutated_individual[i].append(1 - gene)
else:
mutated_individual[i].append(gene)
return mutated_individual
# 유전 알고리즘 메인 함수
def genetic_algorithm(population_size, generations, crossover_rate, mutation_rate):
population = initialize_population(population_size)
global count
global last_optimal
global parameter_answer
global fitness_value
for generation in range(generations):
population.sort(key=fitness, reverse=True) ###########################################################
# 적합도가 가장 높은 개체 출력
best_individual = population[0]
a=best_individual
print(f"Generation {generation + 1}: Best Individual - {best_individual}, Fitness - {1/(fitness(a))}")
fitness_value.append(1/fitness(a))
num_generation.append(generation+1)
# 새로운 세대 생성
new_population = []
# 엘리트 개체(적합도가 가장 높은 개체)는 그대로 다음 세대로 전이 , 10개
for i in range(int(population_size/10)):
new_population.append(population[i])
# 교차 연산과 돌연변이 연산을 통해 새로운 개체 생성 ,# 여기에 룰렛 룰 적용하면 될듯 random choice 를 바꿔 #여기서 population 을 잘못 만들고 있음
while len(new_population) < population_size:
parent1 = random.choice(population) # parent 1 = 16x20 matrix
parent2 = random.choice(population)
child1, child2 = crossover(parent1, parent2)
child1 = mutate(child1, mutation_rate)
child2 = mutate(child2, mutation_rate)
# child1 = repair_rule(child1) # repaired
# child2 = repair_rule(child2)
new_population.extend([child1,child2])
# 현재 세대 갱신
population = new_population
last_optimal=a #population[0]
parameter_answer.append(last_optimal)
return a #population[0] =best individaul
if __name__ == "__main__":
people=Test_50_Data
genetic_algorithm(population_size=20, generations=1, crossover_rate=0.8, mutation_rate=0.05)
print("last optimal " )
print(last_optimal)
print("people")
# print("---------------------")
# print(people_row)
# print(people_column)
# print(Test_50_Data)
last_optiaml = pd.DataFrame(last_optimal)
sum = last_optiaml.values.sum()
row_sums = last_optiaml.sum(axis=1)
print("total ebs = " , sum)
print("dispatching_interval = ", dispatching_interval)
print("operatining_interval : ", operating_interval)
print( "Row Sums:")
print(row_sums)
# figure
plt.plot(num_generation, fitness_value, marker='o')
plt.title('Fitness over Generations')
plt.xlabel('Generation')
plt.ylabel('Fitness')
plt.grid(True)
plt.show()
추가
거리에 따라 돌아오는 데 까지 걸리는 시간을 1, 2, 3 으로 나눴음
거리가 1일 경우 12타임 사용불가,
거리가 2일 경우 24타임 사용 불가,
거리가 3일경우 36타임 사용 불가
fitness 를 계산하기전에 interpreted 해야함
이때 interpreted 된다는 것은 배차간격 때문에 여러개의 1 , 0 이 하나의 1 등으로 바뀐다는 것
예를들어 배차간격이 20분인 라인은 4개의 숫자가 하나의 숫자로 해석됨
이렇게 하는 과정을
1 0 0 1 >> 1 2 2 2
1 1 1 1 >>> 1 2 2 2
1 0 0 0 >>> 1 2 2 2
0100 >>> 0222
이런식으로 바꿈
fitness 계산할 때에는
1과 0만 계산에 포함하고 2는 계산에 포함하지 않음
이를 통해서 interpreted 의 fitness 를 계산할 수 있음.