引言
在复杂优化问题中,传统梯度方法容易陷入局部最优。受自然界鱼群行为启发,李晓磊等人于2002年提出人工鱼群算法(Artificial Fish Swarm Algorithm, AFSA)。该算法模拟鱼类觅食、聚群和追尾三种本能行为,通过群体协作在解空间中全局搜索最优解。与遗传算法、蚁群算法相比,AFSA对初始值不敏感、鲁棒性强,尤其适合高维非线性优化问题。本文将用Java完整实现AFSA,深入解析三种核心行为模式与算法收敛机制。
算法原理
人工鱼群算法将每条”人工鱼”视为解空间中的一个候选解。每条鱼通过感知周围环境,执行以下三种行为之一:
1. 觅食行为(Prey)
鱼在其视野范围内随机选择一个位置,若该位置食物浓度(目标函数值)更高,则向该方向移动一步;否则继续随机尝试,直到达到最大尝试次数。
2. 聚群行为(Swarm)
鱼向其视野内伙伴的中心位置移动,但仅当中心位置的食物浓度高于当前位置且伙伴密度不过大时执行。这模拟了鱼群趋向群体中心但又避免过度拥挤的自然倾向。
3. 追尾行为(Follow)
鱼向其视野内食物浓度最高的伙伴方向移动,但仅当该伙伴周围的食物浓度高于当前位置且密度不过大时执行。这模拟了鱼群跟随”领头鱼”的行为。
每条鱼在每一步评估三种行为的”收益”,选择最优行为执行。若所有行为均不优于当前位置,则执行随机移动。
Java实现
import java.util.*;
public class ArtificialFishSwarm {
/**
* 人工鱼个体
*/
static class Fish {
double[] position; // 位置(解向量)
double fitness; // 适应度(目标函数值)
Fish(int dim) {
this.position = new double[dim];
}
Fish cloneFish() {
Fish f = new Fish(position.length);
System.arraycopy(position, 0, f.position, 0, position.length);
f.fitness = fitness;
return f;
}
}
// 算法参数
private final int fishNum; // 鱼群数量
private final int dim; // 解空间维度
private final double visual; // 视野范围
private final double step; // 最大移动步长
private final double delta; // 拥挤度因子
private final int tryNumber; // 觅食最大尝试次数
private final int maxIter; // 最大迭代次数
private final double[] lowerBound; // 各维度下界
private final double[] upperBound; // 各维度上界
private Fish[] fishes; // 鱼群
private Fish bestFish; // 全局最优鱼
private final Random rand = new Random();
// 目标函数接口
@FunctionalInterface
interface ObjectiveFunction {
double evaluate(double[] x);
}
private final ObjectiveFunction objFunc;
public ArtificialFishSwarm(int fishNum, int dim, double visual, double step,
double delta, int tryNumber, int maxIter,
double[] lowerBound, double[] upperBound,
ObjectiveFunction objFunc) {
this.fishNum = fishNum;
this.dim = dim;
this.visual = visual;
this.step = step;
this.delta = delta;
this.tryNumber = tryNumber;
this.maxIter = maxIter;
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.objFunc = objFunc;
}
/**
* 初始化鱼群
*/
private void initialize() {
fishes = new Fish[fishNum];
bestFish = new Fish(dim);
bestFish.fitness = Double.POSITIVE_INFINITY;
for (int i = 0; i < fishNum; i++) {
fishes[i] = new Fish(dim);
for (int j = 0; j < dim; j++) {
fishes[i].position[j] = lowerBound[j] +
rand.nextDouble() * (upperBound[j] - lowerBound[j]);
}
fishes[i].fitness = objFunc.evaluate(fishes[i].position);
if (fishes[i].fitness < bestFish.fitness) {
bestFish = fishes[i].cloneFish();
}
}
}
/**
* 计算两个位置之间的欧氏距离
*/
private double distance(double[] a, double[] b) {
double sum = 0;
for (int i = 0; i < dim; i++) {
sum += Math.pow(a[i] - b[i], 2);
}
return Math.sqrt(sum);
}
/**
* 向目标位置移动一步
*/
private void moveTowards(Fish fish, double[] target) {
double[] newPos = new double[dim];
double dist = distance(fish.position, target);
if (dist < 1e-10) {
// 若距离极近,随机移动
for (int i = 0; i < dim; i++) {
newPos[i] = fish.position[i] +
step * rand.nextDouble() * (upperBound[i] - lowerBound[i]) * 0.1;
}
} else {
// 向目标方向移动 step * random 距离
double ratio = step * rand.nextDouble() / dist;
for (int i = 0; i < dim; i++) {
newPos[i] = fish.position[i] + (target[i] - fish.position[i]) * ratio;
}
}
// 边界处理:越界则投影到边界
for (int i = 0; i < dim; i++) {
newPos[i] = Math.max(lowerBound[i], Math.min(upperBound[i], newPos[i]));
}
fish.position = newPos;
fish.fitness = objFunc.evaluate(fish.position);
}
/**
* 觅食行为
*/
private void prey(Fish fish) {
for (int t = 0; t < tryNumber; t++) {
// 在视野内随机选择一个位置
double[] target = new double[dim];
for (int i = 0; i < dim; i++) {
double range = visual * (upperBound[i] - lowerBound[i]);
target[i] = fish.position[i] +
(rand.nextDouble() * 2 - 1) * range;
target[i] = Math.max(lowerBound[i], Math.min(upperBound[i], target[i]));
}
double targetFitness = objFunc.evaluate(target);
if (targetFitness < fish.fitness) {
moveTowards(fish, target);
return;
}
}
// 若尝试多次仍未找到更好位置,随机移动
double[] randomTarget = new double[dim];
for (int i = 0; i < dim; i++) {
randomTarget[i] = lowerBound[i] +
rand.nextDouble() * (upperBound[i] - lowerBound[i]);
}
moveTowards(fish, randomTarget);
}
/**
* 聚群行为
*/
private void swarm(Fish fish) {
double[] center = new double[dim];
int neighborCount = 0;
// 找出视野内的伙伴
for (Fish other : fishes) {
if (other != fish && distance(fish.position, other.position) < visual) {
for (int i = 0; i < dim; i++) {
center[i] += other.position[i];
}
neighborCount++;
}
}
if (neighborCount > 0) {
for (int i = 0; i < dim; i++) {
center[i] /= neighborCount;
}
double centerFitness = objFunc.evaluate(center);
// 若中心位置更优且不拥挤(密度因子判断)
if (centerFitness < fish.fitness &&
centerFitness * neighborCount < delta * fish.fitness) {
moveTowards(fish, center);
return;
}
}
// 若聚群条件不满足,执行觅食
prey(fish);
}
/**
* 追尾行为
*/
private void follow(Fish fish) {
Fish bestNeighbor = null;
double bestNeighborFitness = Double.POSITIVE_INFINITY;
// 找出视野内最优的伙伴
for (Fish other : fishes) {
if (other != fish && distance(fish.position, other.position) < visual) {
if (other.fitness < bestNeighborFitness) {
bestNeighborFitness = other.fitness;
bestNeighbor = other;
}
}
}
if (bestNeighbor != null) {
// 统计该伙伴视野内的伙伴数量
int neighborCount = 0;
for (Fish other : fishes) {
if (distance(bestNeighbor.position, other.position) < visual) {
neighborCount++;
}
}
// 若追尾目标更优且不拥挤
if (bestNeighbor.fitness < fish.fitness &&
bestNeighbor.fitness * neighborCount < delta * fish.fitness) {
moveTowards(fish, bestNeighbor.position);
return;
}
}
// 若追尾条件不满足,执行觅食
prey(fish);
}
/**
* 选择最优行为执行
*/
private void chooseBehavior(Fish fish) {
Fish preyFish = fish.cloneFish();
Fish swarmFish = fish.cloneFish();
Fish followFish = fish.cloneFish();
prey(preyFish);
swarm(swarmFish);
follow(followFish);
// 选择三种行为中效果最好的
double preyFit = preyFish.fitness;
double swarmFit = swarmFish.fitness;
double followFit = followFish.fitness;
if (preyFit <= swarmFit && preyFit <= followFit) {
fish.position = preyFish.position;
fish.fitness = preyFish.fitness;
} else if (swarmFit <= preyFit && swarmFit <= followFit) {
fish.position = swarmFish.position;
fish.fitness = swarmFish.fitness;
} else {
fish.position = followFish.position;
fish.fitness = followFish.fitness;
}
}
/**
* 执行优化
*/
public double[] optimize() {
initialize();
System.out.println("=== 人工鱼群算法优化 ===");
System.out.println("鱼群数量: " + fishNum);
System.out.println("维度: " + dim);
System.out.println("视野: " + visual);
System.out.println("步长: " + step);
System.out.println();
for (int iter = 0; iter < maxIter; iter++) {
for (Fish fish : fishes) {
chooseBehavior(fish);
if (fish.fitness < bestFish.fitness) {
bestFish = fish.cloneFish();
}
}
if (iter % 50 == 0 || iter == maxIter - 1) {
System.out.printf("迭代 %d: 最优适应度 = %.10f%n", iter, bestFish.fitness);
}
// 动态衰减视野和步长(增强收敛)
if (iter > 0 && iter % 20 == 0) {
// 此处可通过减小visual和step来增强局部搜索能力
}
}
System.out.println("\n最优解:");
for (int i = 0; i < dim; i++) {
System.out.printf(" x[%d] = %.10f%n", i, bestFish.position[i]);
}
System.out.printf("最优适应度: %.10f%n", bestFish.fitness);
return bestFish.position;
}
public static void main(String[] args) {
// 测试1:Sphere函数(全局最小值在0点,f(0)=0)
System.out.println("【测试1:Sphere函数 f(x) = sum(xi^2)】");
int dim = 10;
double[] lb = new double[dim];
double[] ub = new double[dim];
Arrays.fill(lb, -100);
Arrays.fill(ub, 100);
ArtificialFishSwarm afs1 = new ArtificialFishSwarm(
50, dim, 2.0, 0.5, 10.0, 5, 200,
lb, ub,
x -> {
double sum = 0;
for (double v : x) sum += v * v;
return sum;
}
);
afs1.optimize();
// 测试2:Rastrigin函数(多峰值,全局最小值在0点,f(0)=0)
System.out.println("\n【测试2:Rastrigin函数(多峰值)】");
ArtificialFishSwarm afs2 = new ArtificialFishSwarm(
80, 5, 3.0, 0.8, 15.0, 8, 300,
new double[]{-5.12, -5.12, -5.12, -5.12, -5.12},
new double[]{5.12, 5.12, 5.12, 5.12, 5.12},
x -> {
double sum = 0;
for (double v : x) {
sum += v * v - 10 * Math.cos(2 * Math.PI * v) + 10;
}
return sum;
}
);
afs2.optimize();
}
}
复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 单步时间 | (O(N^2 \cdot D)) | (N)条鱼,每条约计算与其他 (N) 条鱼的距离,维度 (D) |
| 总时间 | (O(T \cdot N^2 \cdot D)) | (T) 为迭代次数 |
| 空间复杂度 | (O(N \cdot D)) | 存储鱼群位置 |
| 并行潜力 | 高 | 每条鱼的行为可独立计算 |
实际应用中可通过空间索引(如k-d树)将邻居搜索优化到 (O(N \log N))。
应用场景
- 函数优化:高维非线性、多峰值、不可导函数的参数寻优。
- 神经网络训练:作为替代梯度下降的元启发式方法,避免陷入局部最优。
- 组合优化:旅行商问题、调度问题、背包问题的近似求解。
- 特征选择:在高维数据中选择最优特征子集。
- 聚类分析:将聚类中心优化问题转化为连续优化问题。
总结
人工鱼群算法以简洁的仿生规则实现了强大的全局搜索能力。三种行为的组合——觅食的探索性、聚群的平衡性、追尾的收敛性——构成了算法自适应搜索的核心。与遗传算法的”选择-交叉-变异”和蚁群算法的”信息素”相比,AFSA的随机视野和步长机制使其在解空间中的搜索更加灵活。掌握AFSA,就是掌握了一种从自然界借鉴智慧的优化范式。