添加遍历-遗传算法求解

This commit is contained in:
weixin_46229132 2025-03-22 17:16:58 +08:00
parent a9ee5ceec7
commit c9db9244b3
8 changed files with 365 additions and 41 deletions

View File

@ -165,7 +165,7 @@ class GA(object):
continue
else:
flight_time += self.rectangles[point - 1]['flight_time'] # 注意,这里要减一!!!
bs_time += self.rectangles[point - 1]['comp_bs_time']
bs_time += self.rectangles[point - 1]['bs_time']
system_time = max(flight_time + car_info['car_time'], bs_time)
T_k_list.append(system_time)
T_max = max(T_k_list)

101
GA/main.py Normal file
View File

@ -0,0 +1,101 @@
import random
import math
import yaml
import numpy as np
from utils import if_valid_partition, GA_solver
from itertools import product
import json
from tqdm import tqdm
np.random.seed(42)
random.seed(42)
best_T = float('inf')
best_solution = None
best_row_boundaries = None
best_col_boundaries = None
params_file = 'params2.yml'
with open(params_file, 'r', encoding='utf-8') as file:
params = yaml.safe_load(file)
H = params['H']
W = params['W']
k = params['num_cars']
flight_time_factor = params['flight_time_factor']
comp_time_factor = params['comp_time_factor']
trans_time_factor = params['trans_time_factor']
car_time_factor = params['car_time_factor']
bs_time_factor = params['bs_time_factor']
flight_energy_factor = params['flight_energy_factor']
comp_energy_factor = params['comp_energy_factor']
trans_energy_factor = params['trans_energy_factor']
battery_energy_capacity = params['battery_energy_capacity']
# 定义数字列表
numbers = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
# 生成所有的排列情况取三次每次都可以从10个数中选
row_product = list(product(numbers, repeat=3))
# 对每种情况从小到大排序,并剔除重复的情况
row_cuts_set = set(
tuple(sorted(set(item for item in prod if item > 0))) for prod in row_product)
row_cuts_set = sorted(row_cuts_set)
col_product = list(product(numbers, repeat=3))
col_cuts_set = set(
tuple(sorted(set(item for item in prod if item > 0))) for prod in col_product)
col_cuts_set = sorted(col_cuts_set)
total_iterations = len(row_cuts_set) * len(col_cuts_set)
with tqdm(total=total_iterations, desc="Processing") as pbar:
for row_cuts in row_cuts_set:
for col_cuts in col_cuts_set:
row_boundaries = [0.0] + list(row_cuts) + [1.0]
col_boundaries = [0.0] + list(col_cuts) + [1.0]
# 这里面的距离不再是比例,而是真实距离!
rectrangles = if_valid_partition(row_boundaries, col_boundaries, params)
if not rectrangles:
pbar.update(1)
continue
else:
# 使用遗传算法求出每一种网格划分的可行解,然后选择其中的最优解
current_solution, current_time, to_process_idx = GA_solver(rectrangles, k)
if current_time < best_T:
best_T = current_time
best_solution = current_solution
best_row_boundaries = row_boundaries
best_col_boundaries = col_boundaries
# 将best_solution分解成每个车队的路径
found_start_points_indices = []
for i in range(len(best_solution)):
if best_solution[i] in to_process_idx:
found_start_points_indices.append(i)
car_paths = []
for j in range(len(found_start_points_indices) - 1):
from_index = found_start_points_indices[j]
end_index = found_start_points_indices[j + 1]
car_path = []
for k in range(from_index, end_index + 1):
rectrangle_idx = best_solution[k]
car_path.append(rectrangles[rectrangle_idx]['center'])
car_paths.append(car_path)
pbar.update(1)
# 输出最佳方案
print("Best solution:", best_solution)
print("Time:", best_T)
print("Row boundaries:", best_row_boundaries)
print("Col boundaries:", best_col_boundaries)
output_data = {
'row_boundaries': row_boundaries,
'col_boundaries': col_boundaries,
'car_paths': car_paths
}
with open(f'./solutions/traverse_ga_{params_file}.json', 'w', encoding='utf-8') as file:
json.dump(output_data, file, ensure_ascii=False, indent=4)

130
GA/main_parallel.py Normal file
View File

@ -0,0 +1,130 @@
import random
import math
import yaml
import numpy as np
from utils import if_valid_partition, GA_solver
from itertools import product
import json
from tqdm import tqdm
from concurrent.futures import ProcessPoolExecutor, as_completed
np.random.seed(42)
random.seed(42)
params_file = 'params2.yml'
with open(params_file, 'r', encoding='utf-8') as file:
params = yaml.safe_load(file)
H = params['H']
W = params['W']
k = params['num_cars']
flight_time_factor = params['flight_time_factor']
comp_time_factor = params['comp_time_factor']
trans_time_factor = params['trans_time_factor']
car_time_factor = params['car_time_factor']
bs_time_factor = params['bs_time_factor']
flight_energy_factor = params['flight_energy_factor']
comp_energy_factor = params['comp_energy_factor']
trans_energy_factor = params['trans_energy_factor']
battery_energy_capacity = params['battery_energy_capacity']
# 定义数字列表
numbers = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
# 生成所有的排列情况取三次每次都可以从10个数中选
row_product = list(product(numbers, repeat=3))
# 对每种情况从小到大排序,并剔除重复的情况
row_cuts_set = set(
tuple(sorted(set(item for item in prod if item > 0))) for prod in row_product)
row_cuts_set = sorted(row_cuts_set)
col_product = list(product(numbers, repeat=3))
col_cuts_set = set(
tuple(sorted(set(item for item in prod if item > 0))) for prod in col_product)
col_cuts_set = sorted(col_cuts_set)
def process_partition(row_cuts, col_cuts):
row_boundaries = [0.0] + list(row_cuts) + [1.0]
col_boundaries = [0.0] + list(col_cuts) + [1.0]
rectrangles = if_valid_partition(row_boundaries, col_boundaries, params)
if not rectrangles:
return None # 过滤无效划分
current_solution, current_time, to_process_idx = GA_solver(rectrangles, k)
return (current_solution, current_time, row_boundaries, col_boundaries, to_process_idx, rectrangles)
if __name__ == "__main__": # 重要:在 Windows 上必须加这一行
best_T = float('inf')
best_solution = None
best_row_boundaries = None
best_col_boundaries = None
batch_size = 60 # 控制一次最多并行多少个任务
all_tasks = [(row_cuts, col_cuts) for row_cuts in row_cuts_set for col_cuts in col_cuts_set]
total_iterations = len(all_tasks)
with ProcessPoolExecutor(max_workers=batch_size) as executor:
futures = set()
results = []
with tqdm(total=total_iterations) as pbar:
for task in all_tasks:
if len(futures) >= batch_size: # 如果并行任务数达到 batch_size等待已有任务完成
for future in as_completed(futures):
results.append(future.result())
pbar.update(1) # 更新进度条
futures.clear() # 清空已完成的任务
futures.add(executor.submit(process_partition, *task)) # 提交新任务
# 处理剩余未完成的任务
for future in as_completed(futures):
results.append(future.result())
pbar.update(1)
# 处理计算结果,找到最优解
for result in results:
if result:
current_solution, current_time, row_boundaries, col_boundaries, to_process_idx, rectrangles = result
if current_time < best_T:
best_T = current_time
best_solution = current_solution
best_row_boundaries = row_boundaries
best_col_boundaries = col_boundaries
# 解析最佳路径
found_start_points_indices = []
for i in range(len(best_solution)):
if best_solution[i] in to_process_idx:
found_start_points_indices.append(i)
car_paths = []
for j in range(len(found_start_points_indices) - 1):
from_index = found_start_points_indices[j]
end_index = found_start_points_indices[j + 1]
car_path = []
for k in range(from_index, end_index + 1):
rectrangle_idx = best_solution[k]
if rectrangle_idx not in to_process_idx:
car_path.append(rectrangles[rectrangle_idx]['center'])
car_paths.append(car_path)
# 输出最佳方案
print("Best solution:", best_solution)
print("Time:", best_T)
print("Row boundaries:", best_row_boundaries)
print("Col boundaries:", best_col_boundaries)
output_data = {
'row_boundaries': row_boundaries,
'col_boundaries': col_boundaries,
'car_paths': car_paths
}
with open(f'./solutions/travse_ga_{params_file}.json', 'w', encoding='utf-8') as file:
json.dump(output_data, file, ensure_ascii=False, indent=4)

93
GA/utils.py Normal file
View File

@ -0,0 +1,93 @@
import numpy as np
from ga import GA
def if_valid_partition(row_boundaries, col_boundaries, params):
H = params['H']
W = params['W']
k = params['num_cars']
flight_time_factor = params['flight_time_factor']
comp_time_factor = params['comp_time_factor']
trans_time_factor = params['trans_time_factor']
car_time_factor = params['car_time_factor']
bs_time_factor = params['bs_time_factor']
flight_energy_factor = params['flight_energy_factor']
comp_energy_factor = params['comp_energy_factor']
trans_energy_factor = params['trans_energy_factor']
battery_energy_capacity = params['battery_energy_capacity']
# 根据分割边界生成所有矩形任务
rectangles = []
for i in range(len(row_boundaries) - 1):
for j in range(len(col_boundaries) - 1):
r1 = row_boundaries[i]
r2 = row_boundaries[i + 1]
c1 = col_boundaries[j]
c2 = col_boundaries[j + 1]
d = (r2 - r1) * H * (c2 - c1) * W # 任务的照片数量(矩形面积)
# 求解rho
rho_time_limit = (flight_time_factor - trans_time_factor) / \
(comp_time_factor - trans_time_factor)
rho_energy_limit = (battery_energy_capacity - flight_energy_factor * d - trans_energy_factor * d) / \
(comp_energy_factor * d - trans_energy_factor * d)
if rho_energy_limit < 0:
return []
rho = min(rho_time_limit, rho_energy_limit)
flight_time = flight_time_factor * d
comp_time = comp_time_factor * rho * d
trans_time = trans_time_factor * (1 - rho) * d
bs_time = bs_time_factor * (1 - rho) * d
# 计算任务矩形中心,用于后续车辆移动时间计算
center_r = (r1 + r2) / 2.0 * H
center_c = (c1 + c2) / 2.0 * W
rectangles.append({
# 'r1': r1, 'r2': r2, 'c1': c1, 'c2': c2,
'd': d,
'rho': rho,
'flight_time': flight_time,
'comp_time': comp_time,
'trans_time': trans_time,
'bs_time': bs_time,
'center': (center_r, center_c)
})
return rectangles
def GA_solver(rectangles, k):
num_city = len(rectangles) + 1 # 划分好的区域中心点+整个区域的中心
# 初始化坐标 (第一个点是整个区域的中心)
center_data = [[1 / 2.0, 1 / 2.0]]
for rec in rectangles:
center_data.append(rec['center'])
center_data = np.array(center_data)
# 关键有k架无人机则再增加N-1个`点` (坐标是起始点)这些点之间的距离是inf
for d in range(k - 1):
center_data = np.vstack([center_data, center_data[0]])
num_city += 1 # 增加欺骗城市
to_process_idx = [0]
# print("start point:", location[0])
for d in range(1, k): # 1, ... drone-1
# print("added base point:", location[num_city - d])
to_process_idx.append(num_city - d)
model = GA(num_drones=k, num_city=center_data.shape[0], num_total=20, data=center_data.copy(
), to_process_idx=to_process_idx, rectangles=rectangles)
Best_path, Best = model.run()
# 根据最佳路径计算各系统任务分配
if Best_path[0] not in to_process_idx:
Best_path.insert(0, 0)
if Best_path[-1] not in to_process_idx:
Best_path.append(0)
return Best_path, Best, to_process_idx

9
env.py
View File

@ -244,25 +244,25 @@ class PartitionMazeEnv(gym.Env):
new_row = current_row - 1
else: # 错误的移动给一些惩罚?
new_row = current_row
# reward -= 10
# reward -= 1
elif move_dir == 'down':
if current_row < len(self.row_cuts) - 2:
new_row = current_row + 1
else:
new_row = current_row
# reward -= 10
# reward -= 1
elif move_dir == 'left':
if current_col > 0:
new_col = current_col - 1
else:
new_col = current_col
# reward -= 10
# reward -= 1
elif move_dir == 'right':
if current_col < len(self.col_cuts) - 2:
new_col = current_col + 1
else:
new_col = current_col
# reward -= 10
# reward -= 1
# 如果移动不合法或者动作为stay则保持原位置
# 检查是否移动
@ -320,7 +320,6 @@ class PartitionMazeEnv(gym.Env):
# # TODO 让奖励在baseline附近变化更剧烈
# # reward = math.exp(-T / self.BASE_LINE) * 1000
reward += self.BASE_LINE / real_T * 5
print(real_T, "="*20)
# if reward > self.BASE_LINE:
# reward -= 200

View File

@ -12,7 +12,8 @@ num_iterations = 10000
# ---------------------------
# 参数设置
# ---------------------------
with open('params.yml', 'r', encoding='utf-8') as file:
params_file = 'params2.yml'
with open(params_file, 'r', encoding='utf-8') as file:
params = yaml.safe_load(file)
H = params['H']
@ -38,8 +39,8 @@ best_solution = None
for iteration in range(num_iterations):
# 随机生成分区的行分段数与列分段数
R = random.randint(0, 5) # 行分段数
C = random.randint(0, 5) # 列分段数
R = random.randint(0, 3) # 行分段数
C = random.randint(0, 3) # 列分段数
# 生成随机的行、列分割边界
horiz = [np.clip(np.floor(random.random() * 10) /10, 0.0, 0.9) for _ in range(R)]
@ -174,7 +175,7 @@ if best_solution is not None:
print("行分割边界:", best_solution['row_boundaries'])
print("列分割边界:", best_solution['col_boundaries'])
print("每辆车的运行轨迹情况:")
car_paths = {}
car_paths = []
for i in range(k):
num_tasks = len(best_solution['system_tasks'][i])
print(
@ -193,7 +194,7 @@ if best_solution is not None:
print(
f" -> 任务{j}({current_pos[0]:.1f}, {current_pos[1]:.1f})", end="")
print(" -> 区域中心")
car_paths[i] = car_path
car_paths.append(car_path)
# 保存分区边界和车辆轨迹到JSON文件
output_data = {
@ -201,7 +202,7 @@ if best_solution is not None:
'col_boundaries': [boundary / W for boundary in best_solution['col_boundaries']],
'car_paths': car_paths
}
with open('./solutions/best_solution_mtkl.json', 'w', encoding='utf-8') as f:
with open(f'./solutions/mtkl_{params_file}.json', 'w', encoding='utf-8') as f:
json.dump(output_data, f, ensure_ascii=False, indent=4)
else:
print("在给定的模拟次数内未找到满足所有约束的方案。")

View File

@ -1,9 +1,9 @@
{
"row_boundaries": [
0.0,
0.2,
0.4,
0.7,
0.3,
0.6,
0.8,
1.0
],
"col_boundaries": [
@ -11,44 +11,44 @@
0.5,
1.0
],
"car_paths": {
"0": [
"car_paths": [
[
[
15.0,
12.5
22.5,
37.5
],
[
5.0,
12.5
]
],
"1": [
[
42.5,
12.5
],
[
42.5,
7.5,
37.5
]
],
"2": [
[
[
27.5,
7.5,
12.5
],
[
27.5,
45.0,
37.5
]
],
[
[
22.5,
12.5
],
[
35.0,
12.5
],
[
35.0,
37.5
],
[
15.0,
37.5
],
[
5.0,
37.5
45.0,
12.5
]
]
}
]
}

View File

@ -29,7 +29,7 @@ def visualize_solution(row_boundaries, col_boundaries, car_paths, W, H):
ax.axvline(x=col * W, color='black', linestyle='--')
# 绘制每辆车的轨迹
for system_id, path in car_paths.items():
for system_id, path in enumerate(car_paths):
path = [(region_center[0], region_center[1])] + path + [(region_center[0], region_center[1])]
y, x = zip(*path)
ax.plot(x, y, marker='o', color=colors[int(system_id) % len(colors)], label=f"系统 {system_id}")