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

An Efficient Approach to Non-dominated Sorting for Evolutionary Multi-objective Optimization_第1张图片

 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.

你可能感兴趣的:(多目标进化算法,多目标进化优化)