当前位置: 首页 > ai >正文

运筹学之引力搜索

目录

  • 一、历史
  • 二、精髓思想
  • 三、案例与代码实现

一、历史

  • 问:谁在什么时候提出引力搜索算法?
  • 答:引力搜索算法(Gravitational Search Algorithm,GSA)是由伊朗学者 Esmaeil Rashedi、Hossein Nezamabadi-pour 和 Seyed S. Saryazdi 于 2009年提出的。。
  • 论文:Rashedi, E., Nezamabadi-pour, H., & Saryazdi, S. (2009).
    “GSA: A Gravitational Search Algorithm”
    Information Sciences, 179(13), 2232–2248.
    DOI: 10.1016/j.ins.2009.03.004
  • 动机:群体智能优化。
    在这里插入图片描述

二、精髓思想

  • 精髓:万有引力!
  • 解释:“利用解之间的引力相互作用,引导搜索群体向最优解聚集。”核心思想和传统的基于位置和速度的群体智能(如粒子群)不同,GSA 将每个个体看作有“质量”的物体,质量代表其优劣,质量越大越有吸引力,越能“拉动”其他解靠近自己。这种吸引力推动群体动态更新位置,从而寻找最优解。
  • 通俗:就这么说吧,万有引力连解空间都适用!
    伟哉,这个让牛吃草停顿了一下的男人。
    在这里插入图片描述
    总结下来其实就5个step:
  1. 初始化一个宇宙,里面有一堆小星球(解空间→解)
  2. 计算星球(解)的质量Mass
  3. 计算两个星球(解)之间的距离R
  4. 套用万有引力公式
  5. 动起来,更新位置

三、案例与代码实现

  • 题干:
    6艘船靠3个港口,已知达到时间arrival和卸货时长handling,求最优停靠方案,即等待时长最短。

  • 数据:

ships = {'S1': {'arrival': 0.1, 'handling': 4},'S2': {'arrival': 2.1, 'handling': 3},'S3': {'arrival': 4.2, 'handling': 2},'S4': {'arrival': 6.3, 'handling': 3},'S5': {'arrival': 5.5, 'handling': 2},'S6': {'arrival': 3.9, 'handling': 4},'S7': {'arrival': 3.7, 'handling': 4},'S8': {'arrival': 3.4, 'handling': 4},
}
  • 实现:
  • step1:初始化一个宇宙,里面有一堆小星球(解空间→解)
# 初始化星球(每个个体是一个泊位分配方案)
ship_ids = list(ships.keys())   # 船名
num_ships = len(ship_ids)   # 船数
num_berths = 3  # 泊位数
population_size = 20    # 粒子数def initialize_population():return [np.random.randint(0, num_berths, size=num_ships) for _ in range(population_size)]
  • step2:计算星球(解)的质量Mass

质量的大小对应到就是自适应函数的值,自适应度函数如下:

# 计算适应度(总等待时间)
def calculate_fitness(assignment):berths = {i: [] for i in range(num_berths)}for i, berth in enumerate(assignment):berths[berth].append((ship_ids[i], ships[ship_ids[i]]['arrival'], ships[ship_ids[i]]['handling']))total_wait_time = 0for b in berths.values():b.sort(key=lambda x: x[1])  # 到达时间排序current_time = 0for ship_id, arrival, handling in b:start_time = max(arrival, current_time)wait_time = start_time - arrivaltotal_wait_time += wait_timecurrent_time = start_time + handlingreturn total_wait_time

那么质量如何定义呢?

# 计算每个解的得分
fitness = np.array([calculate_fitness(ind) for ind in population])
# 取最好和最差,上下限
best_idx = np.argmin(fitness)
best_solution = population[best_idx]
best_fitness = fitness[best_idx]worst = np.max(fitness)
best = np.min(fitness)# 离最差的值越远,mass就越大,同时值域放缩到0-1。1e-9意图是分母不为0。
masses = (worst - fitness) / (worst - best + 1e-9)# 使得加起来等于1,例如:
# masses = [2.1, 0.5, 1.4, 0.8]
# np.sum(masses) = 2.1 + 0.5 + 1.4 + 0.8 = 4.8
# masses = [2.1/4.8, 0.5/4.8, 1.4/4.8, 0.8/4.8] ≈ [0.4375, 0.1042, 0.2917, 0.1667]
masses = masses / np.sum(masses)
  • step3:计算两个星球(解)之间的距离R

如何计算两个星球的距离R(即两个泊位分配方案之间的差异程度)

# 如何计算两个粒子的距离R(即两个泊位分配方案之间的差异程度)
# 举例说明:
# 假设有4艘船:
# population[0] = [0, 1, 1, 2]  表示 S1->泊位0,S2->泊位1,S3->泊位1,S4->泊位2
# population[1] = [1, 1, 0, 2]  表示 S1->泊位1,S2->泊位1,S3->泊位0,S4->泊位2
# 差异向量为 population[0] - population[1] = [-1, 0, 1, 0]
# 欧几里得距离 R = √((-1)^2 + 0^2 + 1^2 + 0^2) = √2 ≈ 1.414
# R 表示两个方案在搜索空间中的距离,距离越小表示越相似,引力越强
R = np.linalg.norm(population[i] - population[j]) # 两个粒子的距离
  • step4:套用万有引力公式

有了质量,有了距离,再来个G常数,就可以直接套用万有引力公式了~

G0 = 100 # 初始引力常数
alpha = 20  # 控制G衰减的速度
max_generations = 50    # 迭代次数G = G0 * np.exp(-alpha * gen / max_generations) # 引力慢慢变小,gen 是指某次迭代gravity = compute_gravity(masses, i, j, R, G) # 引力公式# 引力函数
def compute_gravity(masses, i, j, R, G):return G * (masses[i] * masses[j]) / (R + 1e-9)

有了力还需要有方向,再加点随机因素,得到最后的合力!如下:

direction = population[j] - population[i] # 向量方向
force += random.random() * gravity * direction  # 随机扰动后的合力
  • step5:动起来,更新位置

有了力和方向,那就动起来吧,怎么动?那不就是物理题嘛~

acc = force / (masses[i] + 1e-9)    # 加速度a,F=m*a
velocities[i] = random.random() * velocities[i] + acc   # 速度更新 vt = v0 + at
new_pos = population[i] + velocities[i] # 更新位置

附上完整代码:

import random
import numpy as np# 船舶数据
ships = {'S1': {'arrival': 0.1, 'handling': 4},'S2': {'arrival': 2.1, 'handling': 3},'S3': {'arrival': 4.2, 'handling': 2},'S4': {'arrival': 6.3, 'handling': 3},'S5': {'arrival': 5.5, 'handling': 2},'S6': {'arrival': 3.9, 'handling': 4},'S7': {'arrival': 3.7, 'handling': 4},'S8': {'arrival': 3.4, 'handling': 4},
}ship_ids = list(ships.keys())   # 船名
num_ships = len(ship_ids)   # 船数
num_berths = 3  # 泊位数
population_size = 20    # 粒子数
max_generations = 50    # 迭代次数
G0 = 100 # 初始引力常数
alpha = 20  # 控制G衰减的速度# 初始化种群(每个个体是一个泊位分配方案)
def initialize_population():return [np.random.randint(0, num_berths, size=num_ships) for _ in range(population_size)]# 计算适应度(总等待时间)
def calculate_fitness(assignment):berths = {i: [] for i in range(num_berths)}for i, berth in enumerate(assignment):berths[berth].append((ship_ids[i], ships[ship_ids[i]]['arrival'], ships[ship_ids[i]]['handling']))total_wait_time = 0for b in berths.values():b.sort(key=lambda x: x[1])  # 到达时间排序current_time = 0for ship_id, arrival, handling in b:start_time = max(arrival, current_time)wait_time = start_time - arrivaltotal_wait_time += wait_timecurrent_time = start_time + handlingreturn total_wait_time# 引力函数
def compute_gravity(masses, i, j, R, G):return G * (masses[i] * masses[j]) / (R + 1e-9)# 主函数:引力搜索算法
def gsa_schedule():population = initialize_population()    # 初始化20个个体velocities = [np.zeros(num_ships) for _ in range(population_size)]  # 每个个体在解空间中的“速度”,初始化为 0for gen in range(max_generations):# 计算每个解的得分fitness = np.array([calculate_fitness(ind) for ind in population])# 取最好和最差,上下限best_idx = np.argmin(fitness)best_solution = population[best_idx]best_fitness = fitness[best_idx]worst = np.max(fitness)best = np.min(fitness)# 离最差的值越远,mass就越大,同时值域放缩到0-1。1e-9意图是分母不为0。masses = (worst - fitness) / (worst - best + 1e-9)# 使得加起来等于1,例如:# masses = [2.1, 0.5, 1.4, 0.8]# np.sum(masses) = 2.1 + 0.5 + 1.4 + 0.8 = 4.8# masses = [2.1/4.8, 0.5/4.8, 1.4/4.8, 0.8/4.8] ≈ [0.4375, 0.1042, 0.2917, 0.1667]masses = masses / np.sum(masses)G = G0 * np.exp(-alpha * gen / max_generations) # 引力慢慢变小# 两两粒子计算for i in range(population_size):force = np.zeros(num_ships)for j in range(population_size):if i != j:# 如何计算两个粒子的距离R(即两个泊位分配方案之间的差异程度)# 举例说明:#   假设有4艘船:#     population[0] = [0, 1, 1, 2]  表示 S1->泊位0,S2->泊位1,S3->泊位1,S4->泊位2#     population[1] = [1, 1, 0, 2]  表示 S1->泊位1,S2->泊位1,S3->泊位0,S4->泊位2#   差异向量为 population[0] - population[1] = [-1, 0, 1, 0]#   欧几里得距离 R = √((-1)^2 + 0^2 + 1^2 + 0^2) = √2 ≈ 1.414#   R 表示两个方案在搜索空间中的距离,距离越小表示越相似,引力越强R = np.linalg.norm(population[i] - population[j]) # 两个粒子的距离gravity = compute_gravity(masses, i, j, R, G) # 引力公式direction = population[j] - population[i] # 向量方向force += random.random() * gravity * direction  # 随机扰动后的合力acc = force / (masses[i] + 1e-9)    # 加速度a,F=m*avelocities[i] = random.random() * velocities[i] + acc   # 速度更新 vt = v0 + atnew_pos = population[i] + velocities[i] # 更新位置# 保证位置合法new_pos = np.clip(np.round(new_pos), 0, num_berths - 1)population[i] = new_pos.astype(int)return best_solution, best_fitnessif __name__ == "__main__":# 执行算法best_assignment, best_total_wait = gsa_schedule()# 输出调度结果berth_assignments = {i: [] for i in range(num_berths)}for idx, berth in enumerate(best_assignment):ship = ship_ids[idx]berth_assignments[berth].append((ship, ships[ship]['arrival'], ships[ship]['handling']))# 展示调度排序(按到达时间排序)for berth, ship_list in berth_assignments.items():ship_list.sort(key=lambda x: x[1])print(f"\n泊位 {berth + 1}:")for ship_id, arrival, handling in ship_list:print(f"  {ship_id}: 到达 {arrival}, 作业 {handling}")print(f"\n🚢 最小总等待时间:{best_total_wait:.2f}")
http://www.xdnf.cn/news/678.html

相关文章:

  • Mac上Cursor无法安装插件解决方法
  • 【KWDB 创作者计划】_上位机知识篇---ESP32-S3Arduino
  • Aerich实战指南:零基础掌握异步ORM数据库迁移工具
  • Linux `init 6` 相关命令的完整使用指南
  • Android Gradle多渠道打包
  • 【自然语言处理与大模型】Linux环境下Ollama下载太慢了该怎么处理?
  • AIStarter新版本发布:模型、插件与工作流的高效管理工具
  • 【C++初阶】第15课—模版进阶
  • 多模态大语言模型arxiv论文略读(三十)
  • 设计心得——函数参数的封装
  • Redis缓存降级的4种策略
  • QT实现串口透传的功能
  • OSPF综合实验——企业边界路由器、LSA收敛
  • 车载测试用例开发-如何平衡用例覆盖度和测试效率的方法论
  • VSCode 扩展离线下载方法
  • 如何编写JavaScript插件
  • 5、openfeign
  • 初识javascript
  • C++11特性补充
  • 【Ollama:本地LLM工具】
  • JavaScript 位掩码常量教程
  • MySQL 视图
  • Python内存管理之隔代回收机制详解
  • Redis 缓存—处理高并发问题
  • 宝塔面板部署 Dify-latest 最新版本
  • 第38讲|AI + 农业病虫害预测建模
  • Nacos配置中心客户端加载配置文件源码解析
  • 密码学数据加密标准DES
  • 使用Python可视化洛伦兹变换
  • C语言笔记(鹏哥)上课板书+课件汇总(结构体)-----数据结构常用