diff --git a/greedy_solver.py b/greedy_solver.py index 682ff34..8a12b63 100644 --- a/greedy_solver.py +++ b/greedy_solver.py @@ -3,6 +3,8 @@ import yaml import matplotlib.pyplot as plt import matplotlib.patches as patches import numpy as np +import json + def calculate_max_photos_per_flight(params): """计算每次飞行能拍摄的最大照片数量 @@ -21,217 +23,84 @@ def calculate_max_photos_per_flight(params): # 基于时间约束求解rho:飞行时间 = 计算时间 + 传输时间 # flight_time_factor * d = comp_time_factor * rho * d + trans_time_factor * (1-rho) * d - rho_time = (flight_time_factor - trans_time_factor) / (comp_time_factor - trans_time_factor) + rho_time = (flight_time_factor - trans_time_factor) / \ + (comp_time_factor - trans_time_factor) # 基于能量约束求解最大照片数d # battery_energy_capacity = flight_energy_factor * d + comp_energy_factor * rho * d + trans_energy_factor * (1-rho) * d - energy_per_photo = (flight_energy_factor + - comp_energy_factor * rho_time + - trans_energy_factor * (1 - rho_time)) - - max_photos = math.floor(battery_energy_capacity / energy_per_photo) - - return max_photos, rho_time + energy_per_photo = (flight_energy_factor + + comp_energy_factor * rho_time + + trans_energy_factor * (1 - rho_time)) + + max_photos = battery_energy_capacity / energy_per_photo + + return max_photos + def solve_greedy(params): """使用贪心算法求解任务分配问题""" H = params['H'] W = params['W'] - k = params['num_cars'] # 系统数量 - car_time_factor = params['car_time_factor'] - bs_time_factor = params['bs_time_factor'] - flight_time_factor = params['flight_time_factor'] - - # 计算每次飞行能拍摄的最大照片数 - photos_per_flight, rho = calculate_max_photos_per_flight(params) - print(f"贪心无人机计算的情况下,每次飞行能拍摄的最大照片数: {photos_per_flight}") - print(f"卸载率 rho: {rho:.3f}") - - # 用较小的边长来划分网格 - min_side = min(H, W) - next_side = photos_per_flight // min_side + k = params['num_cars'] # 车辆数量 - # 初始化任务分配列表 - tasks = [[] for _ in range(k)] - - if min_side == H: - grid_h = min_side - grid_w = next_side - num_rows = 1 - num_cols = round(W / grid_w) + # 1. 首先将区域均匀切分成k行 + row_ratio = 1 / k + row_boundaries = [i * row_ratio for i in range(k + 1)] - current_col = 0 - for i in range(math.ceil(num_cols / k)): - for j in range(k): - tasks[j].append((0, current_col)) - current_col += 1 - if current_col == num_cols: - break - else: - grid_w = min_side - grid_h = next_side - num_cols = 1 - num_rows = round(H / grid_h) - - current_row = 0 - for i in range(math.ceil(num_rows / k)): - for j in range(k): - tasks[j].append((current_row, 0)) - current_row += 1 - if current_row == num_rows: - break + # 2. 计算每次飞行能拍摄的最大照片数量 + photos_per_flight = calculate_max_photos_per_flight(params) + print(f"每次飞行能拍摄的最大照片数: {photos_per_flight}") - print(f"网格大小: {grid_w}x{grid_h}") - print(f"网格数量: {num_rows}x{num_cols}") - print(f"任务分配情况: {tasks}") - - # 计算区域中心点 - center_x = W / 2 - center_y = H / 2 - - # 为每个系统计算完成时间 - system_times = [] + # 3. 针对每一行计算网格划分 + row_start = row_boundaries[0] + row_end = row_boundaries[1] + row_height = (row_end - row_start) * H + + # 计算每个网格的宽度 + # 网格面积 = row_height * grid_width = photos_per_flight + grid_width = photos_per_flight / row_height + col_ratio = grid_width / W + + col_boundaries = [0] + ratio = 0 + while (ratio + col_ratio) < 1: + ratio += col_ratio + col_boundaries.append(ratio) + col_boundaries.append(1) + + car_paths = [] for i in range(k): - if not tasks[i]: # 如果该系统没有分配任务 - system_times.append(0) - continue - - # 生成该系统负责的网格中心坐标 - grids = [] - for row, col in tasks[i]: - if min_side == H: - # 如果H是较小边,那么row=0,col递增 - # TODO 最后一个网格的中心点不能这么算 - grid_center_x = (col + 0.5) * grid_w - grid_center_y = (row + 0.5) * grid_h - else: - # 如果W是较小边,那么col=0,row递增 - grid_center_x = (col + 0.5) * grid_w - grid_center_y = (row + 0.5) * grid_h - grids.append((grid_center_x, grid_center_y)) - - # 计算车辆路径长度(从中心点出发) - car_distance = math.hypot(center_x - grids[0][0], center_y - grids[0][1]) # 从中心到第一个网格 - for j in range(len(grids)-1): - car_distance += math.hypot(grids[j+1][0] - grids[j][0], - grids[j+1][1] - grids[j][1]) # 网格间距离 - car_distance += math.hypot(grids[-1][0] - center_x, - grids[-1][1] - center_y) # 从最后一个网格回到中心 - - # 计算时间 - num_photos = len(grids) * photos_per_flight # 该系统需要拍摄的总照片数 - flight_time = flight_time_factor * num_photos # 飞行时间 - car_time = car_time_factor * car_distance # 车辆移动时间 - bs_time = bs_time_factor * (1 - rho) * num_photos # 基站计算时间 - - total_time = max(flight_time + car_time, bs_time) - system_times.append(total_time) - - print(f"\n系统 {i} 详细信息:") - print(f"负责的网格数: {len(grids)}") - print(f"总照片数: {num_photos}") - print(f"车辆移动距离: {car_distance:.2f}") - print(f"飞行时间: {flight_time:.2f}") - print(f"车辆时间: {car_time:.2f}") - print(f"基站时间: {bs_time:.2f}") - print(f"总完成时间: {total_time:.2f}") - - # 找出最大完成时间 - max_time = max(system_times) - print(f"\n最大完成时间: {max_time:.2f}") - - # 准备返回结果 - result = { - 'max_time': max_time, - 'system_times': system_times, - 'photos_per_flight': photos_per_flight, - 'grid_w': grid_w, - 'grid_h': grid_h, - 'num_rows': num_rows, - 'num_cols': num_cols, - 'tasks': tasks, - 'rho': rho - } - - return result + car_path = list(range(i * (len(col_boundaries) - 1), + (i+1) * (len(col_boundaries) - 1))) + car_paths.append(car_path) + + return row_boundaries, col_boundaries, car_paths -def plot_results(params, result): - """可视化结果""" - H = params['H'] - W = params['W'] - k = params['num_cars'] - - plt.rcParams['font.family'] = ['sans-serif'] - plt.rcParams['font.sans-serif'] = ['SimHei'] - - # 创建图形 - fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5)) - - # 1. 绘制系统完成时间对比 - ax1.bar(range(k), result['system_times']) - ax1.set_title('各系统完成时间对比') - ax1.set_xlabel('系统编号') - ax1.set_ylabel('完成时间(秒)') - - # 2. 绘制网格划分示意图 - ax2.set_xlim(0, W) - ax2.set_ylim(0, H) - - # 为不同系统的网格使用不同颜色 - colors = plt.cm.rainbow(np.linspace(0, 1, k)) - - # 绘制网格和系统分配 - grid_w = result['grid_w'] - grid_h = result['grid_h'] - tasks = result['tasks'] - - # 绘制每个系统的网格 - for system_idx, system_tasks in enumerate(tasks): - for row, col in system_tasks: - rect = patches.Rectangle( - (col * grid_w, row * grid_h), - grid_w, grid_h, - linewidth=1, - edgecolor='black', - facecolor=colors[system_idx], - alpha=0.3 - ) - ax2.add_patch(rect) - - # 在网格中心添加系统编号 - center_x = (col + 0.5) * grid_w - center_y = (row + 0.5) * grid_h - ax2.text(center_x, center_y, str(system_idx), - ha='center', va='center') - - # 添加中心点标记 - ax2.plot(W/2, H/2, 'r*', markersize=15, label='区域中心') - ax2.legend() - - ax2.set_title('网格划分和系统分配示意图') - ax2.set_xlabel('宽度') - ax2.set_ylabel('高度') - - plt.tight_layout() - plt.show() def main(): + # --------------------------- + # 需要修改的超参数 + # --------------------------- + params_file = 'params_50_50_3' + # 读取参数 - with open('params.yml', 'r', encoding='utf-8') as file: + with open(params_file + '.yml', 'r', encoding='utf-8') as file: params = yaml.safe_load(file) - + # 求解 - result = solve_greedy(params) - - # 输出结果 - print("\n求解结果:") - print(f"最大完成时间: {result['max_time']:.2f} 秒") - print("\n各系统完成时间:") - for i, time in enumerate(result['system_times']): - print(f"系统 {i}: {time:.2f} 秒") - - # 可视化 - plot_results(params, result) + row_boundaries, col_boundaries, car_paths = solve_greedy(params) + + # --------------------------- + # 输出最佳方案 + # --------------------------- + output_data = { + 'row_boundaries': row_boundaries, + 'col_boundaries': col_boundaries, + 'car_paths': car_paths + } + with open(f'./solutions/greedy_{params_file}.json', 'w', encoding='utf-8') as f: + json.dump(output_data, f, ensure_ascii=False, indent=4) + if __name__ == "__main__": main() diff --git a/mtkl_sovler2.py b/mtkl_sovler2.py index 90abafb..ab39f08 100644 --- a/mtkl_sovler2.py +++ b/mtkl_sovler2.py @@ -3,6 +3,7 @@ import math import yaml import json import numpy as np +from tqdm import tqdm # 固定随机种子,便于复现 random.seed(42) @@ -11,7 +12,7 @@ random.seed(42) # --------------------------- # 需要修改的超参数 # --------------------------- -num_iterations = 10000000000 +num_iterations = 100000000 # 随机生成分区的行分段数与列分段数 R = random.randint(0, 3) # 行分段数 C = random.randint(0, 3) # 列分段数 @@ -44,7 +45,7 @@ battery_energy_capacity = params['battery_energy_capacity'] best_T = float('inf') best_solution = None -for iteration in range(num_iterations): +for iteration in tqdm(range(num_iterations), desc="蒙特卡洛模拟进度"): # 直接切值 horiz = [random.random() for _ in range(R)] horiz = sorted(set(horiz)) diff --git a/solutions/greedy_params_50_50_3.json b/solutions/greedy_params_50_50_3.json new file mode 100644 index 0000000..2f8b9d2 --- /dev/null +++ b/solutions/greedy_params_50_50_3.json @@ -0,0 +1,35 @@ +{ + "row_boundaries": [ + 0.0, + 0.3333333333333333, + 0.6666666666666666, + 1.0 + ], + "col_boundaries": [ + 0, + 0.3008, + 0.6016, + 0.9024000000000001, + 1 + ], + "car_paths": [ + [ + 0, + 1, + 2, + 3 + ], + [ + 4, + 5, + 6, + 7 + ], + [ + 8, + 9, + 10, + 11 + ] + ] +} \ No newline at end of file diff --git a/solutions/mtkl_params_50_50_3.json b/solutions/mtkl_params_50_50_3.json new file mode 100644 index 0000000..18d8215 --- /dev/null +++ b/solutions/mtkl_params_50_50_3.json @@ -0,0 +1,30 @@ +{ + "row_boundaries": [ + 0.0, + 0.2145965901664986, + 0.4480907391063933, + 0.7086202525448272, + 1.0 + ], + "col_boundaries": [ + 0.0, + 0.5136187465626157, + 1.0 + ], + "car_paths": [ + [ + 4, + 2, + 0 + ], + [ + 6, + 7 + ], + [ + 5, + 3, + 1 + ] + ] +} \ No newline at end of file diff --git a/visualization.py b/visualization.py index 5d83013..3165558 100644 --- a/visualization.py +++ b/visualization.py @@ -1,59 +1,206 @@ import matplotlib.pyplot as plt import matplotlib.patches as patches import json +import math -def visualize_solution(row_boundaries, col_boundaries, car_paths_coords, W, H): - plt.rcParams['font.family'] = ['sans-serif'] - plt.rcParams['font.sans-serif'] = ['SimHei'] - fig, ax = plt.subplots() +def visualize_solution(row_boundaries, col_boundaries, car_paths_coords, W, H, rho_list): + region_center = (H / 2.0, W / 2.0) + + # 创建正方形图像 + fig, ax = plt.subplots(figsize=(8, 8)) # 设置固定的正方形大小 ax.set_xlim(0, W) ax.set_ylim(H, 0) # 调整y轴方向,原点在左上角 - ax.set_title("区域划分与车-机-巢系统覆盖") - ax.set_xlabel("区域宽度") - ax.set_ylabel("区域高度") + + # 设置英文标题和标签 + # ax.set_title("Monte Carlo", fontsize=12) + ax.set_title("Greedy", fontsize=12) + # ax.set_title("Enumeration-Genetic Algorithm", fontsize=12) + # ax.set_title("DQN fine-tuning", fontsize=12) - # 定义若干颜色以区分不同系统(系统编号从0开始) - colors = ['red', 'blue', 'green', 'orange', 'purple', 'cyan', 'magenta'] + ax.set_xlabel("Region Width", fontsize=10) + ax.set_ylabel("Region Height", fontsize=10) - # 绘制区域中心 - region_center = (H / 2.0, W / 2.0) - ax.plot(region_center[1], region_center[0], - 'ko', markersize=8, label="区域中心") + # 定义配色方案(使用更专业的配色) + colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', + '#9467bd', '#8c564b', '#e377c2', '#7f7f7f'] # 绘制行分割边界 - for row in row_boundaries: - ax.axhline(y=row * H, color='black', linestyle='--') + for row in row_boundaries[1:-1]: + ax.axhline(y=row * H, color='gray', linestyle='--', alpha=0.5) # 绘制列分割边界 - for col in col_boundaries: - ax.axvline(x=col * W, color='black', linestyle='--') + for col in col_boundaries[1:-1]: + ax.axvline(x=col * W, color='gray', linestyle='--', alpha=0.5) # 绘制每辆车的轨迹并标注区域序号 for system_id, path in enumerate(car_paths_coords): 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}") + + # 使用箭头绘制路径 + for i in range(len(path)-1): + # 绘制带箭头的线段 + ax.annotate('', + xy=(x[i+1], y[i+1]), + xytext=(x[i], y[i]), + arrowprops=dict(arrowstyle='->', + color=colors[int(system_id) % len(colors)], + lw=2, + mutation_scale=15), + zorder=1) + + # 绘制路径点 + ax.plot(x, y, 'o', markersize=6, + color=colors[int(system_id) % len(colors)], + label=f"System {system_id}", + zorder=2) - # 标注每个区域的序号 - for idx, (px, py) in enumerate(zip(x[1:-1], y[1:-1])): # 跳过起点和终点 - ax.text(px, py, str(idx), color='black', fontsize=8, ha='center', va='center') + # 标注每个区域的序号(将序号向上偏移一点) + for idx, (px, py) in enumerate(zip(x[1:-1], y[1:-1])): + offset = H * 0.02 # 根据区域高度设置偏移量 + ax.text(px, py - offset, str(idx), + color='black', + fontsize=9, + ha='center', + va='bottom', + bbox=dict( + facecolor='none', + edgecolor='none', + alpha=0.7, + pad=0.5)) + + # 绘制区域中心(设置最高的zorder确保在最上层) + ax.plot(region_center[1], region_center[0], + 'k*', markersize=12, label="Region Center", + zorder=3) # 添加图例 - ax.legend() + ax.legend(loc='upper right', fontsize=9) + + # 保持坐标轴比例相等 + ax.set_aspect('equal', adjustable='box') + + # 调整布局,确保所有元素都显示完整 + plt.tight_layout() + + # 显示网格 + ax.grid(True, linestyle=':', alpha=0.3) + + # 在每个矩形区域左上角标注rho值 + rho_idx = 0 + for i in range(len(row_boundaries) - 1): + for j in range(len(col_boundaries) - 1): + # 获取矩形左上角坐标 + x = col_boundaries[j] * W + y = row_boundaries[i] * H + + # 添加一个小的偏移量,避免完全贴在边界上 + offset_x = W * 0.02 + offset_y = H * 0.02 + + # 标注rho值 + ax.text(x + offset_x, y + offset_y, + f'ρ={rho_list[rho_idx]:.2f}', + color='black', + fontsize=8, + ha='left', + va='top', + bbox=dict(facecolor='white', + edgecolor='none', + alpha=0.7, + pad=0.5), + zorder=2) + rho_idx += 1 + plt.show() +def restore_from_solution(row_boundaries, col_boundaries, car_paths, 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) + 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({ + 'rho': rho, + 'flight_time': flight_time, + 'bs_time': bs_time, + 'center': (center_r, center_c) + }) + + system_times = [] + # 根据car_paths计算时间 + for car_idx in range(k): + car_path = car_paths[car_idx] + flight_time = sum(rectangles[point]['flight_time'] + for point in car_path) + bs_time = sum(rectangles[point]['bs_time'] for point in car_path) + + # 计算车的移动时间,首先在轨迹的首尾添加上大区域中心 + car_time = 0 + for i in range(len(car_path) - 1): + first_point = car_path[i] + second_point = car_path[i + 1] + car_time += math.dist(rectangles[first_point]['center'], rectangles[second_point]['center']) * \ + car_time_factor + car_time += math.dist(rectangles[car_path[0]] + ['center'], [H / 2, W / 2]) * car_time_factor + car_time += math.dist(rectangles[car_path[-1]] + ['center'], [H / 2, W / 2]) * car_time_factor + system_time = max(flight_time + car_time, bs_time) + system_times.append(system_time) + print(f"系统{car_idx}的总时间: {system_time}") + print(f"最终时间: {max(system_times)}") + + rho_list = [rectangle['rho'] for rectangle in rectangles] + return rectangles, rho_list + + if __name__ == "__main__": import yaml # --------------------------- # 需要修改的超参数 # --------------------------- - params_file = 'params3' - solution_file = r'solutions\trav_ga_params3_parallel.json' + params_file = 'params_50_50_3' + solution_file = r'solutions\greedy_params_50_50_3.json' with open(params_file + '.yml', 'r', encoding='utf-8') as file: params = yaml.safe_load(file) @@ -70,27 +217,17 @@ if __name__ == "__main__": col_boundaries = best_solution['col_boundaries'] car_paths = best_solution['car_paths'] + rectangles, rho_list = restore_from_solution( + row_boundaries, col_boundaries, car_paths, params) + # 计算分块区域的中心点坐标 - rectangle_centers = [] - 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 # 任务的照片数量(矩形面积) + rectangles_centers = [rectangle['center'] for rectangle in rectangles] - # 计算任务矩形中心,用于后续车辆移动时间计算 - center_r = (r1 + r2) / 2.0 * H - center_c = (c1 + c2) / 2.0 * W - - rectangle_centers.append((center_r, center_c)) - # 将car_paths里的index换成坐标 car_paths_coords = [[] for _ in range(k)] for car_idx in range(k): car_path = car_paths[car_idx] for point in car_path: - car_paths_coords[car_idx].append(rectangle_centers[point]) + car_paths_coords[car_idx].append(rectangles_centers[point]) - visualize_solution(row_boundaries, col_boundaries, car_paths_coords, W, H) + visualize_solution(row_boundaries, col_boundaries, car_paths_coords, W, H, rho_list)