An Efficient Approach to Non-dominated Sorting for Evolutionary Multi-objective Optimization

Reference

Zhang X, Tian Y, Cheng R, et al. An efficient approach to nondominated sorting for evolutionary multiobjective optimization[J]. IEEE Transactions on Evolutionary Computation, 2014, 19(2): 201-213.

Python code implementation

import numpy as np
import functools
import time

# O(n) 当n等于一千万(10000000)时大概 1s

Pop_Size = 1000  # 种群大小
N = 10  # 决策空间大小
M = 10  # 目标数


class Individual(object):

    def __init__(self, gene, func):
        self.gene = gene
        self.func = func
        pass

    def __str__(self):
        return '[gene=%s\n func=%s]\n' % (self.gene, self.func)

    __repr__ = __str__

    pass


def cmp(pop1, pop2):
    i = 0
    while i < M:
        if pop1.func[i] < pop2.func[i]:
            return -1
        elif pop1.func[i] > pop2.func[i]:
            return 1
        else:
            i += 1
            # return 0
            pass
        pass
    return 0


def init_population():
    population = []
    for i in range(Pop_Size):
        gene = [np.random.randint(0, 2) for j in range(N)]
        func = [np.random.randint(0, Pop_Size/2) for j in range(M)]
        pop = Individual(gene, func)
        population.append(pop)
        pass
    return population


def jude_dominating(p, q):
    # 最小化问题
    i = 0
    flag = True
    cnt = 0
    while i < M:
        if p.func[i] > q.func[i]:
            flag = False
            break
            pass
        elif p.func[i] == q.func[i]:
            cnt += 1
            pass
        i += 1
        pass
    if flag:
        if cnt < M:  # 不能所有目标相等
            return True
        else:
            return False
        pass
    else:  # 存在一个目标不小于则不支配
        return False

    pass


def fast_sort(population):
    s = {}
    n = {}
    f = []
    pt = []  # 记录一层非支配层
    for p in population:
        st = []  # p支配的个体
        nt = 0  # 支配p的个体数
        for q in population:
            if jude_dominating(p, q):
                st.append(q)
                pass
            elif jude_dominating(q, p):
                nt += 1
                pass
            pass
        # s[p.idx] = st  # 第i个个体所支配的个体集合
        s[id(p)] = st
        # n[p.idx] = nt  # 支配的第i个个体的个体数
        n[id(p)] = nt
        if nt == 0:  # 没有个体支配p
            pt.append(p)
            pass
        pass
    f.append(pt)
    i = 0
    while len(f[i]) > 0:  # 由于判断条件,会多一个空层
        pt = []
        for p in f[i]:
            # for q in s[p.idx]:  # q下一层支配,则-1
            for q in s[id(p)]:  # q下一层支配,则-1
                n[id(q)] -= 1
                # n[q.idx] -= 1
                # if n[q.idx] == 0:  # 不存在支配q的个体,则加入当前层
                if n[id(q)] == 0:  # 不存在支配q的个体,则加入当前层
                    pt.append(q)
                    pass
                pass
            pass
        i += 1
        f.append(pt)
        pass
    f.pop()
    return f


def sequential_search_strategy(p, f):
    x = len(f)  # 已经被分配的支配层数
    k = 0  # 当前检索的层
    while True:
        flag = False
        for q in reversed(f[k]):
            if jude_dominating(q, p):
                flag = True
                break
                pass
            pass
        if flag:  # p 被f[k]中的个体支配
            k += 1
            if k >= x:
                t = []
                f.append(t)
                # return x + 1
                x += 1
                f[k].append(p)
                break
                pass
            pass

        else:  # p 不被f[k]中的个体支配
            # return k
            f[k].append(p)
            break
            pass

        pass
    pass


# 二分思想
def binary_search_strategy(p, f):
    x = len(f)  # 已经找到的支配层数
    k_min = 0  # 检索的最底层
    k_max = x  # 检索的最高层
    # k = int(np.floor((k_max+k_min)/2 + 1/2))  # 当前检索的层
    k = int((k_max+k_min)/2)
    while True:
        flag = False
        for q in reversed(f[k]):
            if jude_dominating(q, p):
                flag = True
                break
                pass
            pass

        if flag:  # k小了,要往上层走,直到k==k_max-1
            # k_min = k  # k层能支配p 需要判断高层是否有支配p的。
            if (k == k_max-1) and (k_max < x):  # k是支配p的直接上一层
                # return k_max
                f[k_max].append(p)
                break
                pass
            elif k == x-1:  # 已存在的最后一层(k)支配p,则需要新加入一层
                # return x+1
                # x += 1
                t = []
                f.append(t)
                f[x].append(p)
                x += 1
                break
                pass
            else:  # k层能支配p 但不是直接支配p的一层 需要判断高层是否有支配p的。
                # k = np.floor((k_max + k_min)/2 + 1/2)
                k_min = k
                k = int((k_max + k_min)/2)
                pass
            pass

        else:  # k大了,要往下层走,直到k==k_min+1
            if k == k_min:  # 当k==k_min 说明k是不能支配p的最小一层(就是说比k小的层通通支配p)
                # return k
                f[k].append(p)
                break
                pass
            else:  # k层不能支配p 但不是最小的层 需要往下层走。
                k_max = k
                k = int((k_max+k_min)/2)
                pass
            pass

        pass
    pass


def ens_ss(population):

    f = [[]]
    # population_sort = sorted(population, key=lambda individual: individual.f1)
    # print(population)
    population_sort = sorted(population, key=functools.cmp_to_key(cmp))
    # print(population_sort)
    for pop in population_sort:
        sequential_search_strategy(pop, f)
        # binary_search_strategy(pop, f)
        pass
    return f


def ens_bs(population):

    f = [[]]
    # population_sort = sorted(population, key=lambda individual: individual.f1)
    # print(population)
    population_sort = sorted(population, key=functools.cmp_to_key(cmp))
    # print(population_sort)
    for pop in population_sort:
        # sequential_search_strategy(pop, f)
        binary_search_strategy(pop, f)
        pass
    return f


pp = init_population()

print('********************fast_sort********************')
t1 = time.time()
ff = fast_sort(pp)
print(time.time()-t1)

# for tt in ff:
#     print('front size=', len(tt))
#     print(tt)
#     pass

print('********************binary_search********************')
t1 = time.time()
ff = ens_bs(pp)
print(time.time()-t1)

# for tt in ff:
#     print('front size=', len(tt))
#     print(tt)
#     pass

print('********************sequential_search********************')
t1 = time.time()
ff = ens_ss(pp)
print(time.time()-t1)

# for tt in ff:
#     print('front size=', len(tt))
#     print(tt)
#     pass

Run result

 Result analysis

当种群大小population_size=1000,目标数M=10时,binary_search 和 sequential_search 都是该论文提出的非支配排序方法,从运行结果能看出,该论文提出的排序方法优于传统的快速非支配排序算法(fast_sort[1])。

[1] Deb K, Pratap A, Agarwal S, et al. A fast and elitist multiobjective genetic algorithm: NSGA-II[J]. IEEE transactions on evolutionary computation, 2002, 6(2): 182-197.

MOEAs的相关研究及应用

参考

[4] Yang X, Zou J, Yang S, et al. A Fuzzy Decision Variables Framework for Large-scale Multiobjective Optimization[J]. IEEE Transactions on Evolutionary Computation, 2021.

[5] Zou J, Yang X, Liu Z, et al. Multiobjective Bilevel Optimization Algorithm Based on Preference Selection to Solve Energy Hub System Planning Problems[J]. Energy, 2021: 120995.

[6] Liu J, Yang X, Liu Z, et al. Investigation and evaluation of building energy flexibility with energy storage system in hot summer and cold winter zones[J]. Journal of Energy Storage, 2022, 46: 103877.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NSGA-II (Non-dominated Sorting Genetic Algorithm II) is a popular multi-objective optimization algorithm that is widely used in various fields such as engineering, finance, and biology. It is an extension of the standard genetic algorithm and uses a non-dominated sorting technique to rank the solutions based on their dominance relationship. To implement NSGA-II in Python, we can use the DEAP (Distributed Evolutionary Algorithms in Python) library. DEAP provides a comprehensive set of tools for implementing various evolutionary algorithms, including NSGA-II. Here is a simple example of how to use DEAP to implement NSGA-II in Python: ```python import random from deap import base, creator, tools, algorithms # Define the fitness function (minimize two objectives) creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0)) # Define the individual class (a list of two floats) creator.create("Individual", list, fitness=creator.FitnessMin) # Initialize the toolbox toolbox = base.Toolbox() # Define the range of the two objectives BOUND_LOW, BOUND_UP = 0.0, 1.0 # Define the evaluation function (two objectives) def evaluate(individual): return individual[0], individual[1] # Register the evaluation function and the individual class toolbox.register("evaluate", evaluate) toolbox.register("individual", tools.initCycle, creator.Individual, (random.uniform(BOUND_LOW, BOUND_UP) for _ in range(2)), n=1) toolbox.register("population", tools.initRepeat, list, toolbox.individual) # Define the genetic operators toolbox.register("mate", tools.cxSimulatedBinaryBounded, low=BOUND_LOW, up=BOUND_UP, eta=20.0) toolbox.register("mutate", tools.mutPolynomialBounded, low=BOUND_LOW, up=BOUND_UP, eta=20.0, indpb=1.0/2) toolbox.register("select", tools.selNSGA2) # Define the main function def main(seed=0): random.seed(seed) # Initialize the population pop = toolbox.population(n=100) # Evaluate the initial population fitnesses = [toolbox.evaluate(ind) for ind in pop] for ind, fit in zip(pop, fitnesses): ind.fitness.values = fit # Run the algorithm pop = algorithms.eaMuPlusLambda(pop, toolbox, mu=100, lambda_=100, cxpb=0.9, mutpb=0.1, ngen=100, verbose=False) # Print the final population print("Final population:") for ind in pop: print(ind, ind.fitness.values) if __name__ == "__main__": main() ``` This code defines a simple two-objective optimization problem and uses NSGA-II to find the Pareto front. The `creator` module is used to define the fitness and individual classes. The `toolbox` is used to register the genetic operators and the evaluation function. Finally, the `algorithms` module is used to run the algorithm and obtain the Pareto front.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值