1. 程式人生 > >基於Openmp的遺傳演算法

基於Openmp的遺傳演算法

概述

並行運算:

平行計算或稱平行計算是相對於序列計算來說的。它是一種一次可執行多個指令的演算法,目的是提高計算速度,及通過擴大問題求解規模,解決大型而複雜的計算問題。所謂平行計算可分為時間上的並行和空間上的並行。 時間上的並行就是指流水線技術,而空間上的並行則是指用多個處理器併發的執行計算。
平行計算(Parallel Computing)是指同時使用多種計算資源解決計算問題的過程,是提高計算機系統計算速度和處理能力的一種有效手段。它的基本思想是用多個處理器來協同求解同一問題,即將被求解的問題分解成若干個部分,各部分均由一個獨立的處理機來平行計算。平行計算系統既可以是專門設計的、含有多個處理器的超級計算機,也可以是以某種方式互連的若干臺的獨立計算機構成的叢集。通過平行計算叢集完成資料的處理,再將處理的結果返回給使用者。
為利用平行計算,通常計算問題表現為以下特徵:
(1)將工作分離成離散部分,有助於同時解決;
(2)隨時並及時地執行多個程式指令;
(3)多計算資源下解決問題的耗時要少於單個計算資源下的耗時。

OpenMp:

OpenMP是由OpenMP Architecture Review Board牽頭提出的,並已被廣泛接受,用於共享記憶體並行系統的多處理器程式設計的一套指導性編譯處理方案(Compiler Directive)。OpenMP支援的程式語言包括C、C++和Fortran;而支援OpenMp的編譯器包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMp提供了對並行演算法的高層的抽象描述,程式設計師通過在原始碼中加入專用的pragma來指明自己的意圖,由此編譯器可以自動將程式進行並行化,並在必要之處加入同步互斥以及通訊。當選擇忽略這些pragma,或者編譯器不支援OpenMp時,程式又可退化為通常的程式(一般為序列),程式碼仍然可以正常運作,只是不能利用多執行緒來加速程式執行。

進化演算法:

進化演算法,或稱“演化演算法” (evolutionary algorithms, EAS) 是一個“演算法簇”,儘管它有很多的變化,有不同的遺傳基因表達方式,不同的交叉和變異運算元,特殊運算元的引用,以及不同的再生和選擇方法,但它們產生的靈感都來自於大自然的生物進化。與傳統的基於微積分的方法和窮舉法等優化演算法相比,進化計算是一種成熟的具有高魯棒性和廣泛適用性的全域性優化方法,具有自組織、自適應、自學習的特性,能夠不受問題性質的限制,有效地處理傳統優化演算法難以解決的複雜問題。
本報告主要使用OpenMp解決基於多智慧體進化演算法的並行問題。在本實驗中,目標函式的維度設定為8000,在序列條件下,在本地計算機中的執行時間為21.454s秒,在加入適當的並行方法之後執行時間為7.192s,運算速度為原來的2.84倍。

演算法描述

本實驗模擬的是基於多智慧體的進化演算法,其演算法流程包括以下步驟:引數設定,種群初始化,鄰居競爭,鄰居交叉,變異,自學習,五個步驟。其中我們將變數 x 的個數設定為8000,智慧體網格大小 Lsize 設定為5,交叉概率 Pc 設定為0.2,變異概率 Pm 為0.1,自學習網格大小 sLsize 設定為3,迭代次數 Gmax 設定為200,演算法流程為:
這裡寫圖片描述
本實驗的優化目標函式為:
f(x)=0.1{sin2(3πx1)+i=1n1(xi1)2[1+sin2(3πxi+1)]+(xn1)2[1+sin2(2πxn)]}+i=1nu(xi,5,100,4)
其中變數取值範圍 S 為:
S=[50,50]n
其中 n 表示變數的個數。

並行設計

1、進化演算法中國適應度函式的計算次數較多,且可以進行並行化設計,所以在適應度函式的計算中加入求和的並行運算:

void Fitness(LL &P)   //計算適應度以及能量
{
    P.fitness = 0.0;
    double u[x_num];
    double a = 5.0, k = 100.0, m = 4.0, b1 = 0.0;
    int i;
#pragma omp parallel for reduction(+:b1)     
    for (i = 0; i < x_num; i++)
    {
        if (P.x[i] >= -a && P.x[i] <= a)
        {
            u[i] = 0;
        }
        else if (P.x[i] > a)
        {
            u[i] = k * pow((P.x[i] - a), m);
        }
        else if (P.x[i] < -a)
        {
            u[i] = k * pow(-P.x[i] - a, m);
        }
        b1 += u[i];
    }
    double a1 = 0;

#pragma omp parallel for reduction(+:a1)
    for (i = 0; i < x_num - 1; i++)
    {
        a1 += (P.x[i] - 1) * (1 + 10 * pow(sin(PI * P.x[i + 1] * 3), 2)) * (P.x[i] - 1);
    }
    P.fitness = 0.1 * (pow(sin(3 * PI * P.x[0]), 2) + a1 + (pow(sin(P.x[x_num - 1] * 2 * PI), 2)) + 1) * pow(P.x[x_num - 1], 2) + b1;
    P.energy = -P.fitness;
}

2、始化過程中,每個智慧體之間不影響,因此加入並行運算:

void Init_L(LL P[Lsize][Lsize])    //初始化函式
{
    int i, j, k;
#pragma omp parallel private(i, j, k)
#pragma omp for schedule(dynamic)
    for (i = 0; i < Lsize; i++)
    {
        for (j = 0; j < Lsize; j++)
        {
            for (k = 0; k < x_num; k++)
            {
                P[i][j].x[k] = rand() / (RAND_MAX + 1.0) * (upper - lower) - (upper - lower) / 2.0;
            }
            Fitness(P[i][j]);
        }
    }
}

3、在執行過程中多次用到的選擇智慧體最優鄰居函式,且不互相影響,因此也將其並行設計:

LL Nmax(LL L[Lsize][Lsize], int i, int j)  //找到鄰居中的最大值 其中的i,j是需要P的座標
{
    LL max1[5];
    max1[1] = (i == 0) ? L[Lsize - 1][j] : L[i - 1][j];
    max1[2] = (j == 0) ? L[i][Lsize - 1] : L[i][j - 1];
    max1[3] = (i == Lsize - 1) ? L[0][j] : L[i + 1][j];
    max1[4] = (j == Lsize - 1) ? L[i][0] : L[i][j + 1];
    max1[0] = max1[1];
#pragma omp parallel for
    for (int k = 2; k < 5; k++)
    {
        if (max1[0].energy <= max1[k].energy)
            max1[0] = max1[k];
    }
    return max1[0];
}

4、在鄰居互相競爭函式中要對基因中的每個元素進行操作,因此也將其並行設計:

#pragma omp parallel for
        for (int i = 0; i < x_num; i++)
        {
            a = B.x[i] + (rand() / (RAND_MAX + 1.0) * 2 - 1) * (B.x[i] - P1.x[i]);
            if (a < lower)
            {
                P1.x[i] = lower;
                continue;
            }
            else if (a > upper)
            {
                P1.x[i] = upper;
                continue;
            }
            P1.x[i] = a;
        }
    }

5、在變異操作中也需要對基因中的每個元素進行操作,對其進行並行設計:

void Mutation(LL &P, int t)  //變異作用
{
#pragma omp parallel for
    for (int i = 0; i < x_num; i++)
    {
        if (rand() / (RAND_MAX + 1.0) < 1 / x_num)
        {
            continue;
        }
        else
        {
            P.x[i] += rand() / (RAND_MAX + 1.0) / t * 2 - 1.0 / (t + 0.0);
            P.x[i] = P.x[i] < lower ? lower : P.x[i];
            P.x[i] = P.x[i] > upper ? upper : P.x[i];
        }
    }
    Fitness(P);
}

6、在自學習函式中,需要對最優個體建立子網路,其中每個網格中的智慧體也不互相影響,因此對其進行並行處理:

int i, j, k;
#pragma omp parallel private(i,j,k) 
#pragma omp for schedule(dynamic)
    for (i = 0; i < sLsize; i++)  //首先生成這個自學習舉證
    {
        for (j = 0; j < sLsize; j++)
        {
            if (i == 0 || j == 0)
            {
                sL[i][j] = P;
            }
            else
            {
                for (k = 0; k < x_num; k++)
                {
                    double Temp;
                    Temp = rand() / (RAND_MAX + 1.0)  * sR * 2 - sR + 1;
                    if (P.x[k] * Temp < lower)
                    {
                        sL[i][j].x[k] = lower;
                        continue;
                    }
                    else if (P.x[k] * Temp > upper)
                    {
                        sL[i][j].x[k] = upper;
                        continue;
                    }
                    sL[i][j].x[k] = P.x[k] * Temp;
                }
            }
            Fitness(sL[i][j]);
            if (sL[i][j].energy > sBest.energy)
                sBest = sL[i][j];
        }
    }

實驗結果

分別在序列和並行兩種條件下進行十次實驗,其結果為:

這裡寫圖片描述
由結果可知並行程式平均執行時間為7.192s,序列程式平均執行時間為20.454s,是並行程式的2.843倍。