111 lines
4.2 KiB
Python
111 lines
4.2 KiB
Python
import random
|
||
import numpy as np
|
||
|
||
class NSGA2:
|
||
def __init__(self, pop_size, obj_num):
|
||
self.pop_size = pop_size
|
||
self.obj_num = obj_num
|
||
|
||
def fast_non_dominated_sort(self, pop_obj):
|
||
"""快速非支配排序"""
|
||
pop_size = len(pop_obj)
|
||
dominated = [[] for _ in range(pop_size)] # 被支配个体列表
|
||
rank = [0] * pop_size # 个体的非支配等级
|
||
n = [0] * pop_size # 支配该个体的个体数量
|
||
|
||
# 计算每个个体的支配关系
|
||
for p in range(pop_size):
|
||
for q in range(pop_size):
|
||
if p != q:
|
||
# p支配q
|
||
if all(pop_obj[p][i] <= pop_obj[q][i] for i in range(self.obj_num)) and \
|
||
any(pop_obj[p][i] < pop_obj[q][i] for i in range(self.obj_num)):
|
||
dominated[p].append(q)
|
||
# q支配p
|
||
elif all(pop_obj[q][i] <= pop_obj[p][i] for i in range(self.obj_num)) and \
|
||
any(pop_obj[q][i] < pop_obj[p][i] for i in range(self.obj_num)):
|
||
n[p] += 1
|
||
|
||
# 找到等级为0的个体
|
||
if n[p] == 0:
|
||
rank[p] = 0
|
||
|
||
# 计算其他等级
|
||
current_rank = 0
|
||
while True:
|
||
next_rank = []
|
||
for p in range(pop_size):
|
||
if rank[p] == current_rank:
|
||
for q in dominated[p]:
|
||
n[q] -= 1
|
||
if n[q] == 0 and rank[q] == 0:
|
||
rank[q] = current_rank + 1
|
||
next_rank.append(q)
|
||
if not next_rank:
|
||
break
|
||
current_rank += 1
|
||
|
||
return rank
|
||
|
||
def crowding_distance(self, pop_obj, rank):
|
||
"""计算拥挤度距离"""
|
||
pop_size = len(pop_obj)
|
||
distance = [0.0] * pop_size
|
||
max_rank = max(rank) if rank else 0
|
||
|
||
# 对每个等级的个体计算拥挤度
|
||
for r in range(max_rank + 1):
|
||
# 获取当前等级的个体索引
|
||
current_indices = [i for i in range(pop_size) if rank[i] == r]
|
||
if len(current_indices) <= 1:
|
||
continue
|
||
|
||
# 对每个目标函数进行排序
|
||
for m in range(self.obj_num):
|
||
# 按目标m的值排序
|
||
sorted_indices = sorted(current_indices, key=lambda x: pop_obj[x][m])
|
||
min_val = pop_obj[sorted_indices[0]][m]
|
||
max_val = pop_obj[sorted_indices[-1]][m]
|
||
|
||
# 边界个体的拥挤度设为无穷大
|
||
distance[sorted_indices[0]] = float('inf')
|
||
distance[sorted_indices[-1]] = float('inf')
|
||
|
||
# 计算中间个体的拥挤度
|
||
for i in range(1, len(sorted_indices) - 1):
|
||
if max_val - min_val == 0:
|
||
continue
|
||
distance[sorted_indices[i]] += (pop_obj[sorted_indices[i + 1]][m] - pop_obj[sorted_indices[i - 1]][m]) / (max_val - min_val)
|
||
|
||
return distance
|
||
|
||
def selection(self, pop, pop_obj):
|
||
"""选择操作:基于非支配排序和拥挤度的锦标赛选择,惩罚拥挤度为0的个体"""
|
||
pop_size = len(pop)
|
||
rank = self.fast_non_dominated_sort(pop_obj)
|
||
distance = self.crowding_distance(pop_obj, rank)
|
||
selected = []
|
||
|
||
for _ in range(pop_size):
|
||
# 随机选择两个个体进行锦标赛
|
||
i = random.randint(0, pop_size - 1)
|
||
j = random.randint(0, pop_size - 1)
|
||
|
||
# 优先选择等级低(更优)的个体
|
||
if rank[i] < rank[j]:
|
||
selected.append(pop[i])
|
||
elif rank[i] > rank[j]:
|
||
selected.append(pop[j])
|
||
# 等级相同则选择拥挤度大的个体(惩罚拥挤度为0的重复解)
|
||
else:
|
||
# 对拥挤度为0的个体进行惩罚
|
||
if distance[i] == 0 and distance[j] > 0:
|
||
selected.append(pop[j])
|
||
elif distance[j] == 0 and distance[i] > 0:
|
||
selected.append(pop[i])
|
||
elif distance[i] >= distance[j]:
|
||
selected.append(pop[i])
|
||
else:
|
||
selected.append(pop[j])
|
||
|
||
return selected |