인공지능 입문 - TSP 문제를 해결하는 유전 알고리즘 실험

1. 실험 목적:

유전자 알고리즘의 원리, 프로세스 및 코딩 전략을 숙지하고 마스터하며 유전자 알고리즘을 사용하여 조합 최적화 문제를 해결하고 TSP 문제를 해결하는 프로세스를 이해하며 결과에 대한 주요 매개 변수의 영향을 테스트합니다.

2. 실험 원리:

여행하는 세일즈맨 문제, TSP 문제(Traveling Salesman Problem)는 수학 분야에서 유명한 문제 중 하나입니다. 여행하는 사업가가 n개의 도시를 방문하려고 한다고 가정하면, 그는 가고자 하는 경로를 선택해야 하며, 경로의 한계는 각 도시는 한 번만 방문할 수 있고 마지막으로 출발한 원래 도시로 돌아와야 합니다. 경로 선택의 목표는 필요한 경로 거리가 모든 경로 중에서 최소값이 되는 것입니다. TSP 문제는 조합 최적화 문제입니다. 문제는 NPC 계산 복잡성이 있는 것으로 표시될 수 있습니다. 따라서 이 문제를 간단하게 해결할 수 있는 방법은 무엇이든 높이 평가되고 주목될 것입니다.

유전알고리즘의 기본 원리는 염색체에 있는 유전자에 작용하여 좋은 염색체를 찾아 문제를 해결하는 것으로, 알고리즘에 의해 생성된 각각의 염색체를 평가하고 적합도 값을 기준으로 염색체를 선택하여 적응력이 좋은 염색체가 번식기회가 더 많음 유전알고리즘에서 해결해야 할 문제의 숫자 즉 염색체를 무작위로 생성하여 초기 개체군을 형성하고 각 개체는 적합도를 통해 수치적 평가를 받음 적합도가 높은 개체를 선별하여 유전적 조작에 참여시키고, 유전적 조작 후 개체를 모아 다음 세대의 새로운 개체군을 형성하여 다음 단계의 진화를 진행한다. 이 새로운 인구에. 이 실험은 TSP 문제의 최단 경로를 풀기 위해 유전자 알고리즘을 사용해야 합니다.

3. 실험적 내용:

1. 실험시스템에서 주어진 유전알고리즘의 핵심코드 참조 유전알고리즘을 이용하여 동일한 인구규모, 최대수로 서로 다른 규모(예: 10개 도시, 30개 도시, 100개 도시)를 해결해야 함 반복 단계 및 독립 작업 수 TSP 문제에 대한 결과를 표 1에 입력합니다.

표 1 다양한 척도의 TSP 문제 해결을 위한 유전자 알고리즘의 결과

도시 크기

인구 규모

최대 반복 횟수

독립 실행 수

최고의 피트니스

최악의 체력

평균 체력

평균 실행 시간

10

100

100

10

25.1652

25.8521

25.3501

47p

30

100

100

10

10605.7

11868

11281.2

122.8초

100

100

100

10

44334.9

47149.1

46117.6

484.2초

  1. 모집단 크기를 100으로, 교차 확률을 0.8로, 돌연변이 확률을 0.8로 설정한 다음 돌연변이 전략(예: 인접한 두 지점 교환 돌연변이, 역돌연변이 또는 삽입 돌연변이 등)과 개별 선택 확률 할당을 추가합니다. 전략(예: 선형 정렬 또는 비선형 정렬에 따른 개별 선택 확률 할당)을 사용하여 30개 도시의 TSP 문제를 해결하고(30개 도시의 좌표는 다음과 같음) 결과를 표 2에 채웁니다.

30개의 도시 좌표:

x[0]=41, x[1]=37,x[2]=54,x[3]=25,x[4]=7,x[5]=2,x[6]=68,x [7]=71,x[8]=54,x[9]=83;

y[0]=94,y[1]=84,y[2]=67,y[3]=62,y[4]=64,y[5]=99,y[6]=58,y [7]=44,y[8]=62,y[9]=69;

x[10]=64,x[11]=18,x[12]=22,x[13]=83,x[14]=91,x[15]=25,x[16]=24,x [17]=58,x[18]=71,x[19]=74;

y[10]=60,y[11]=54,y[12]=60,y[13]=46,y[14]=38,y[15]=38,y[16]=42,y [17]=69,y[18]=71,y[19]=78;

x[20]=87,x[21]=18,x[22]=13,x[23]=82,x[24]=62,x[25]=58,x[26]=45,x [27]=41,x[28]=44, x[29]=4;

y[20]=76,y[21]=40,y[22]=40,y[23]=7,y[24]=32,y[25]=35,y[26]=21,y [27]=26, 및[28]=35; y[29]=50;

표 2 서로 다른 돌연변이 전략 및 개별 선택 확률 할당 전략의 솔루션 결과

돌연변이 전략

개별 선택 확률 할당

최대 반복 횟수

독립 실행 수

최고의 피트니스

최악의 체력

평균 체력

평균 실행 시간

두 포인트 스왑

체력에 따른 비례분배

100

10

889.434

982.154

938.503

59년대

두 포인트 스왑

선형 정렬

100

10

488.833

567.304

513.735

51대

인접한 두 점을 교환

선형 정렬

100

10

1022.77

1104.54

1064.78

49.1초

인접한 두 점을 교환

체력에 따른 비례분배

100

10

878.549

969.802

939.414

58년대

3. 중국 34개 성급의 데이터를 감안할 때 TSP 문제를 해결하기 위해 이 데이터를 기반으로 개선된 유전자 알고리즘을 설계해야 합니다. 1) 개선된 알고리즘 전략 및 핵심 코드, 2) 개선된 알고리즘의 주요 매개변수 설정(모집단 크기, 교차 확률, 돌연변이 확률, 최대 반복 단계 수 등), 3) 최단 거리 제공이 필요합니다. 개선된 알고리즘을 통해 최종적으로 획득한 34개 지방도 사이 Path 값, 최적의 개별 및 알고리즘 실행시간, 4) 동일한 매개변수 설정(인구 크기, 교차 확률, 돌연변이 확률, 최대 반복 단계 수 등)이 주어지면, 기본 유전자 알고리즘(개선된 전략을 사용하지 않음)을 통해 34개 지방 수도의 최단 경로 값, 최적 개인 및 알고리즘 실행 시간을 얻습니다.

그림 1 중국 34개 성의 위치(1295.72)

표 3 34개 성읍 및 픽셀좌표표

도시

티베트

윈난성

사천

칭하이

닝샤

간쑤

몽골 내륙 지방

헤이룽장성

길림성

랴오닝

베이징

천진

도시 번호

1

2

4

5

6

7

8

9

10

11

12

X 좌표

100

187

201

187

221

202

258

352

346

336

290

297

Y 좌표

211

265

214

158

142

165

121

66

85

106

127

135

도시

허베이

산동

허난

산시성

산시성

안후이성

장쑤성

상하이

저장성

장시성

후베이

후난

도시 번호

13

14

15

16

17

18

19

20

21

22

23

24

X 좌표

278

296

274

265

239

302

316

334

325

293

280

271

Y 좌표

147

158

177

148

182

203

199

206

215

233

216

238

도시

구이저우

광시

광동

푸젠

하이난

마카오

홍콩

대만

충칭

신장

도시 번호

25

26

27

28

29

30

31

32

33

34

X 좌표

221

233

275

322

250

277

286

342

220

104

Y 좌표

253

287

285

254

315

293

290

263

226

77

x[0]=100, x[1]=187,x[2]=201,x[3]=187,x[4]=221,x[5]=202,x[6]=258,x [7]=352,x[8]=346,x[9]=336;

y[0]=211,y[1]=265,y[2]=214,y[3]=158,y[4]=142,y[5]=165,y[6]=121,y [7]=66,y[8]=85,y[9]=106;

x[10]=290,x[11]=297,x[12]=278,x[13]=296,x[14]=274,x[15]=265,x[16]=239,x [17]=302,x[18]=316,x[19]=334;

y[10]=127,y[11]=135,y[12]=147,y[13]=158,y[14]=177,y[15]=148,y[16]=182,y [17]=203,y[18]=199,y[19]=206;

x[20]=325,x[21]=293,x[22]=280,x[23]=271,x[24]=221,x[25]=233,x[26]=275,x [27]=322,x[28]=250, x[29]=277;

y[20]=215,y[21]=233,y[22]=216,y[23]=238,y[24]=253,y[25]=287,y[26]=285,y [27]=254, 및[28]=315; y[29]=293;

x[30]=286, x[31]=342,x[32]=220,x[33]=104;

y[30]=290,y[31]=263,y[32]=226,y[33]=77;

3.1 결과:

3.1.1 여기 두 가지 실험 결과가 있습니다. 차이점은 초기 시작점만 다릅니다.

① 그림 2와 같이 경로를 그립니다(python 기준, 시작점 15).

그림 2 최적해를 보여주는 도표

최단 경로 길이 1532.3994414550848

경로 표현: [23, 32, 24, 1, 2, 0, 33, 3, 5, 4, 12, 14, 16, 22, 21, 27, 31, 30, 26, 29, 28, 25, 20, 19, 18, 17, 13, 11, 9, 8, 7, 10, 6]

반복 횟수에 따라 경로 길이가 감소합니다.

그림 3 반복 횟수와 최적 솔루션 간의 관계

② 그림과 같이 경로를 그립니다(python 기준, 시작점 32).

그림 4는 최적의 솔루션을 보여주는 플롯입니다.

최단 경로 길이: 1551.0699954694958

경로 표현: [1, 28, 25, 24, 18, 20, 19, 31, 27, 29, 30, 26, 23, 21, 22, 16, 14, 17, 13, 9, 8, 7, 11, 10, 12, 15, 6, 4, 5, 0, 33, 3, 2]

반복 횟수에 따라 경로 길이가 감소합니다.

그림 5 최적해와 반복 횟수의 관계

3.1.2

개선되지 않은 경우 원본 프로그램을 사용하고 파라미터는 다음과 같이 설정합니다(C++ 기준).

염색체 길이: 34

최대 반복 단계: 500

인구 규모: 100

교차 확률: 0.5

돌연변이 확률 0.15

선발 작업 : 적합도 비율에 따라 개인의 선발 확률을 분배하고 룰렛으로 개인을 선발

크로스오버 작업: PMX 크로스오버

돌연변이 연산: 인접한 두 점 교환

결과는 그림 6에 나와 있습니다.

그림 6 결과

최적의 경로 길이는 6246.62입니다.

경로: 32-20-18-21-28-29-26-24-13-30-12-10-31-33-2-3-1-7-9-4-15-11-0-5- 8-6-17-16-23-25-19-22-27-14

실행 시간: 214.6초

개선된 알고리즘이 상당히 효과적임을 알 수 있습니다.

3.2 개선된 알고리즘 전략 및 핵심 코드 :

①파라미터 설정 :
#인구수

카운트=300

#시간 개선

개선_카운트=10000

#진화의 시간

itter_time=3000

# 강자의 정의 확률 설정, 즉 인구의 상위 30%가 강자

유지율=0.3

#약자의 생존 확률 설정

random_select_rate=0.5

#돌연변이율

mutation_rate=0.1

②경로 코딩:

도시 0, 1, 2, 3...33, 염색체 {x1, x2, x3...x33}에 번호를 매깁니다. 시작점은 임의로 설정할 수 있으며 이 프로그램에서는 15로 설정되어 있습니다.

③ 모집단 초기화:

프로그램의 실행 속도를 높이려면 초기 모집단의 선택에서 일부 더 나은 개인을 선택해야 합니다. 먼저 고전적인 근사 알고리즘(향상된 원 알고리즘)을 사용하여 더 나은 초기 모집단을 얻습니다. 알고리즘의 아이디어는 {1, 2, ... 33}과 같이 임의로 염색체를 생성하고, 두 도시의 순차적인 위치를 임의로 교환하고, 전체 거리가 줄어들면 염색체를 업데이트하여 변경하고, 더 이상 수정할 수 없을 때까지 반복합니다.

암호:

#인구 초기화

인구 = []

범위(개수)의 i에 대해:

    # 무작위로 개인 생성

    x = 인덱스.복사()

    랜덤.셔플(x)

    개선(x)

    인구.추가(x)

#개량

def 개선(x):

    i=0

    거리=get_total_distance(x)

    i<개선 횟수:

        # 랜딘트 [a,b]

        u=random.randint(0,len(x)-1)

        v = random.randint(0, len(x)-1)

        u!=v인 경우:

            new_x=x.복사()

            t=new_x[u]

            new_x[u]=new_x[v]

            new_x[v]=t

            new_distance=get_total_distance(new_x)

            if new_distance<거리:

                거리=new_distance

                x=new_x.copy()

        또 다른:

            계속하다

        i+=1

④전략 선택:

룰렛 휠 방식이 가장 많이 사용되는 선택 전략이지만 이 전략은 표본오차가 클 수 있으므로 자연선택을 이용하여 각 도시 간의 거리를 계산하여 행렬에 저장한다. 그 후 적응력이 강한 염색체를 선택하고, 적응력은 낮지만 살아남은 염색체를 선택(무작위 선택)합니다.

핵심 코드:

# 자연 선택

데프 선택(인구):

    # 전체 거리를 작은 것부터 큰 것까지 정렬

    graded = [[인구의 x에 대한 get_total_distance(x), x]]

    graded = [정렬된(graded)에서 x에 대한 x[1]]

    # 가장 적응력이 좋은 염색체를 선택

    retain_length = int(len(graded) * retain_rate)

    부모 = graded[:retain_length]

    # 덜 적응하지만 살아남은 염색체를 선택합니다.

    graded[retain_length:]의 염색체:

        random.random() < random_select_rate인 경우:

            부모.추가(염색체)

    부모를 돌려보내다

⑤ 변형 전략:

주어진 돌연변이율에 따라 선택된 돌연변이 개체에 대해 1<u<v<w<33을 만족하는 세 개의 정수를 무작위로 선택하고 나중에 v와 u 사이(u와 v 포함)의 유전자 세그먼트를 w에 삽입합니다. 원래 두 개의 인접한 포인트 스왑 돌연변이를 교체하고 두 포인트 스왑의 돌연변이 전략을 교체하십시오. 

핵심 코드:

#돌연변이

def 돌연변이(자식):

    범위(len(어린이))의 i에 대해:

        random.random() < mutation_rate인 경우:

            자식=자녀[i]

            u=random.randint(1,len(자식)-4)

            v = random.randint(u+1, len(자식)-3)

            w= random.randint(v+1, len(자식)-2)

            자식=자녀[i]

            자식=자식[0:u]+자식[v:w]+자식[u:v]+자식[w:]

4. 실험보고서와 소스 프로그램을 제출한다.

3. 실험 결과 :

실험 콘텐츠 1 결과:

표 1 다양한 척도의 TSP 문제 해결을 위한 유전자 알고리즘의 결과

도시 크기

인구 규모

최대 반복 횟수

독립 실행 수

최고의 피트니스

최악의 체력

평균 체력

평균 실행 시간

10

100

100

10

25.1652

25.8521

25.3501

47p

30

100

100

10

10605.7

11868

11281.2

122.8초

100

100

100

10

44334.9

47149.1

46117.6

484.2초

실험 콘텐츠 2 결과:

표 2 서로 다른 돌연변이 전략 및 개별 선택 확률 할당 전략의 솔루션 결과

돌연변이 전략

개별 선택 확률 할당

최대 반복 횟수

독립 실행 수

최고의 피트니스

최악의 체력

평균 체력

평균 실행 시간

두 포인트 스왑

체력에 따른 비례분배

100

10

889.434

982.154

938.503

59년대

두 포인트 스왑

선형 정렬

100

10

488.833

567.304

513.735

51대

인접한 두 점을 교환

선형 정렬

100

10

1022.77

1104.54

1064.78

49.1초

인접한 두 점을 교환

체력에 따른 비례분배

100

10

878.549

969.802

939.414

58년대

실험 콘텐츠 3에 있는 실험 콘텐츠 3의 결과:

경로는 그림 2에 나와 있습니다.

최단 경로 길이 1532.3994414550848

경로 표현: [23, 32, 24, 1, 2, 0, 33, 3, 5, 4, 12, 14, 16, 22, 21, 27, 31, 30, 26, 29, 28, 25, 20, 19, 18, 17, 13, 11, 9, 8, 7, 10, 6]

4. 실험적 사고와 경험 :

  1. 서로 다른 규모의 TSP 문제를 해결하기 위한 유전자 알고리즘의 알고리즘 성능을 분석합니다.

도시의 크기가 클수록 반복 횟수가 많고 최적의 솔루션을 찾는 시간이 길어지고 결과를 기다리는 시간이 길어집니다.

2. 1개의 돌연변이 전략과 1개의 개별 선택 확률 분포 전략을 추가하고, 30개 도시에서 TSP 문제를 해결할 때 알고리즘 결과에 대한 서로 다른 돌연변이 전략과 개별 선택 확률 분포 전략의 영향을 비교합니다.

개인을 선택하기 위해 인접 2점 돌연변이 및 선형 정렬을 추가했습니다.

돌연변이 전략:

void change1(vector<int>& K, int N){//mutation 전략: 인접한 두 점 사이에서 mutation 교환

int i = next_int() %N;

swap(K[i], K[(i + 1) % N]);

}

개별 선택 전략:

if(make_p==1)//선형 정렬

    {

        for(int i=0;i<popsize;i++)

ps[i]=(200-2*i)/팝크기*(팝크기+1);

}

실험 결과에서 선형 정렬이 더 좋고 무작위 2점 교환 전략이 인접한 2점 교환 전략보다 분명히 더 좋습니다.

3. 유전자 알고리즘의 개선된 전략이 34개 도시의 TSP 문제 해결 결과에 미치는 영향을 비교 분석한다.

주된 이유는 적합도 값의 선택에서 상대적으로 큰 개선이 이루어졌기 때문입니다.좋은 개인을 먼저 선택하고 일부 가난한 개인을 무작위로 선택합니다.일부 매개 변수의 코드를 개선하여 선택 및 돌연변이를 만들 수 있습니다. 얻고자 하는 것의 최적해가 더 좋다 개선을 사용하지 않을 경우 최종 연산에서 얻을 수 있는 최적해는 그림 7에서와 같이 약 7.8천이며 정확도는 충분하지 않다.

 

그림 7 개선되지 않은 실행 결과

4. 실험 경험을 요약합니다.

다음과 같은 다른 많은 개선 전략이 있습니다.

① 그리디 알고리즘을 사용하여 인구를 초기화하는 각 개별 세대의 기본 아이디어는 첫 번째 유전자로 n 도시에서 ccurrent 도시를 무작위로 선택한 다음 나머지 n-1 도시에서 도시 cnext(cnext)를 찾는 것입니다. cnext는 ccurrent에 가장 가까운 도시)를 두 번째 유전자로 지정한 다음 나머지 n-2개의 도시(cnext+1은 cnext에 가장 가까운 도시)에서 cnext+1 도시를 세 번째 유전자로 찾는 식으로 Traverse n까지 계속 진행합니다. 지금까지 도시. 탐욕 알고리즘에 의해 생성된 일부 모집단의 비율은 크지 않으며, 초기 모집단의 전반적인 품질을 향상시키면서 초기 모집단의 다양성에 영향을 미치지 않아 최적화 속도를 높이는 데 도움이 됩니다. 코드에 익숙하지 않기 때문에 모집단을 초기화하는 그리디 알고리즘으로 변경하지 못했습니다.

② 적절하게 적합도를 확대하여 분수 부분을 고려하는 것도 가능하지만, 실험 실행 결과로 볼 때 그다지 의미가 없으며, 최적해는 상대적으로 평균이며, 여전히 거리가 상당히 멀다.

③개선된 교차 연산자: 기본 유전자 알고리즘에서 일반적으로 사용되는 2점 교차 연산자. 부모의 좋은 유전자는 효과적으로 물려받을 수 없습니다. 2포인트 3세그먼트 랜덤 크로스오버를 사용할 수 있습니다.

개선되지 않은 소스 코드:

#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<time.h>
using namespace std;
typedef vector<int> VI;
typedef vector<VI> VVI;
#define PB push_back
#define MP make_pair

int next_int()
{
	return rand()*(RAND_MAX+1)+rand();
}
double next_double()
{
	return (double(rand()*(RAND_MAX+1)+rand()))/((RAND_MAX+1)*RAND_MAX+RAND_MAX);
}
void pmx(VI& a,VI& b,int pointcnt)//PMX交叉
{
	int sa=next_int()%pointcnt,sb=next_int()%pointcnt;//随机选择两交叉位
	int temp;
	if (sa>sb)
	{
		temp=sa;
		sa=sb;
		sb=temp;
	}//保证交叉位sa<=sb
	VI aa(pointcnt),bb(pointcnt);
	int i;	
	for(i=0;i<pointcnt;i++)
	{
		aa[i]=a[i],bb[i]=b[i];	
	}
	VI m1(pointcnt,-1);
	VI m2(pointcnt,-1);	
	VI v1tov2(pointcnt,-1);
	for(i=sa;i<=sb;i++)
	{   
		m1[aa[i]]=-2;	//m1存放aa非交叉段可代换的基因
		m2[bb[i]]=-2;	//m2存放aa非交叉段可代换的基因
	}	
	for(i=0;i<pointcnt;i++)	{   
		
		if(m2[i]==m1[i])//去掉m1和m2中重复代换的基因
		{
			m2[i]=-1;
			m1[i]=-1;
		}
	}
	int aaa=0;
	for(i=sa;i<=sb;i++)
	{
		if ((m1[aa[i]]==-2)&&(m2[bb[i]]==-2))
		{
			v1tov2[aa[i]]=bb[i];//v1tov2存放首先可以确定的互换基因
			m1[aa[i]]=-1;
			m2[bb[i]]=-1;
			aaa++;			
		}
	}
	if(aaa!=(sb-sa+1))
	{
		for(i=0;i<pointcnt;i++)	
			if (m1[i]==-2) 
			{
				int aflag=0;
				for(int j=0;j<pointcnt;j++)	
					if( (m2[j]==-2) && (aflag==0))//寻找并确定可以互换的基因
					{
						v1tov2[i]=j; 
						aflag=1;
						aaa++;
						m2[j]=-1;
						m1[i]=-1;					
					}
			}

	}
	for(i=sa;i<=sb;i++)	{	
	
		swap(aa[i],bb[i]);	//交换sa到sb之间的基因串
	}

	for(i=0;i<pointcnt;i++)//查找染色体aa中sa之前和sb之后的基因是否有重复
	{   
		if ((i<sa)||(i>sb))
			for (int j=sa;j<=sb;j++)
			{
				if(aa[i]==aa[j])  //有重复基因
				{  
					for(int k=0;k<pointcnt;k++)
						if(aa[i]==v1tov2[k])  
							aa[i]=k;	//进行互换			
				
				}
			}
		
	}
		
	for(i=0;i<pointcnt;i++)//查找染色体bb中sa之前和sb之后的基因是否有重复
	{  
		if ((i<sa)||(i>sb))
			for (int j=sa;j<=sb;j++)
			{	
				if(bb[i]==bb[j]) //有重复基因			
					bb[i]=v1tov2[bb[i]];	//进行互换				
			}
	
	}
	a=aa;
	b=bb;
	
}

vector<double> x,y;
double fitness(const VI& v,int pointcnt)//计算适应度
{
	double r=0;
	for(int i=0;i<pointcnt;i++)
	{
		double dx=x[v[i]]-x[v[(i+1)%pointcnt]];
		double dy=y[v[i]]-y[v[(i+1)%pointcnt]];
		r+=sqrt(dx*dx+dy*dy);//个体的适应度为相邻两城市之间的距离平方的平方根和
	}
	return 1.0/r;
}
void change0(vector<int>& K,int N)//变异策略:两点互换
{
	int i=next_int()%N;
	int d=next_int()%(N-1);
	int j=(i+1+d)%N;
	swap(K[i],K[j]);
}


void change1(vector<int>& K, int N){//变异策略:相邻两点互换变异 
	int i = next_int() % N;
	swap(K[i], K[(i + 1) % N]);	
}



void mutate(VI& route,int mutate_type,int pointcnt)
{
	
	if(mutate_type==0)//两点互换
		change0(route,pointcnt);	
	if(mutate_type==1)//相邻两点互换变异 
	    change1(route,pointcnt);
}



bool pair_dec(const pair<double,VI*>& a,const pair<double,VI*>& b)
{
	return a>b;
}

class other_population
{
public:
	int popsize,pointcnt;//种群规模,染色体长度
	double pc,pm;//交叉概率,变异概率
	vector<pair<double,VI*> >pop;//种群
	pair<double,VI*> bestofpop;//最好个体
	int cross_type;//交叉类型
	int mutate_type;//变异类型
	int make_p;//个体概率分配策略类型
	int select_type;//个体选择类型
	int toursize;//竞赛规模
	double bestp;//最好个体选择概率
	other_population(int a,int b,int c,int f,int g,double d,double e,int h,double j,int m)
	{
		popsize=a,pointcnt=b,cross_type=c,mutate_type=f,make_p=g,pc=d,pm=e,toursize=h,bestp=j,select_type=m;
		for(int i=0;i<popsize;i++)//初始化种群
		{
			VI* v=new VI(pointcnt);
			for(int j=0;j<pointcnt;j++)
				(*v)[j]=j;
			random_shuffle(v->begin(),v->end());
			pop.PB(MP(fitness(*v,pointcnt),v));
		}
		sort(pop.begin(),pop.end(),pair_dec);
		bestofpop.first=pop[0].first;//初始时最好个体的适应度
		bestofpop.second=new VI(*pop[0].second);//初始时最好个体的染色体
	}
	~other_population()
	{
		for(int i=0;unsigned(i)<pop.size();i++)
			delete pop[i].second;
		delete bestofpop.second;
	}
	void next()//产生下一代种群
	{
		vector<double> ps(popsize);
		if(make_p==0) //按适应度比例分配个体的选择概率
		{
			double sum=0;
			for(int i=0;i<popsize;i++)
				sum+=pop[i].first;//计算种群的适应度和
			for(int i=0;i<popsize;i++)
				ps[i]=pop[i].first/sum;
		}	
		
		
		if(make_p==1)//线性排序 
	    {
	        for(int i=0;i<popsize;i++)
			ps[i]=(200-2*i)/popsize*(popsize+1);	
		}
	
		if(select_type==0)//轮盘赌选择个体
		{
			vector<pair<double,VI*> > select_res;
			vector<double> addsum(popsize);
			for(int i=0;i<popsize-1;i++)//计算个体的累计概率
			{
				if(i==0)
					addsum[i]=ps[0];
				else
					addsum[i]=addsum[i-1]+ps[i];
			}
			addsum[popsize-1]=1;//1.5;
			for(int i=0;i<popsize;i++)
			{
				double rd=next_double();
				int r=lower_bound(addsum.begin(),addsum.end(),rd)-addsum.begin();
				VI* v=new VI(*pop[r].second);
				select_res.PB(MP(fitness(*v,pointcnt),v));
			}
			for(int i=0;i<popsize;i++)
				delete pop[i].second;
			pop=select_res;
		}		
		for(int cc=0;cc<popsize/2;cc++)//随机选择两个个体,然后进行交叉
		{
			int a=next_int()%popsize;
			int b=(a+1+(next_int()%(popsize-1)))%popsize;
			if(next_double()<pc)//随机数小于交叉概率,进行交叉
			{
				if(cross_type==0)//pmx交叉
					pmx(*pop[a].second,*pop[b].second,pointcnt);
			
				pop[a].first=fitness(*pop[a].second,pointcnt);//计算交叉后个体a的适应度
				if(bestofpop.first<pop[a].first)//更新最好个体
				{
					bestofpop.first=pop[a].first;
					delete bestofpop.second;
					bestofpop.second=new VI(*pop[a].second);
				}
				pop[b].first=fitness(*pop[b].second,pointcnt);//计算交叉后个体b的适应度
				if(bestofpop.first<pop[b].first)//更新最好个体
				{
					bestofpop.first=pop[b].first;
					delete bestofpop.second;
					bestofpop.second=new VI(*pop[b].second);
				}
			}
		}
		for(int i=pop.size()-1;i>=0;i--)//进行变异
			if(next_double()<pm)//随机数小于变异概率,进行变异
			{
				mutate(*pop[i].second,mutate_type,pointcnt);//变异
				pop[i].first=fitness(*pop[i].second,pointcnt);//计算变异后个体的适应度
			}
		sort(pop.begin(),pop.end(),pair_dec);//从大到小排序
		if(bestofpop.first<pop[0].first)//更新最好个体
		{
			delete bestofpop.second;
			bestofpop.first=pop[0].first;
			bestofpop.second=new VI(*pop[0].second);
		}
	}
};

int main()
{
	srand((unsigned)time(NULL));
	int CASNUM,POINTCNT,POPSIZE,GENERATIONS;
	//scanf("%d",&CASNUM);//输入实验次数
	CASNUM=10;//输入实验次数
	cout << "实验次数:" << CASNUM << "次" << endl;
	//scanf("%d%d%d",&POINTCNT,&POPSIZE,&GENERATIONS);//输入染色体长度(城市数),种群规模,最大迭代步数
	POINTCNT=30;//输入染色体长度(城市数)
	POPSIZE=100,GENERATIONS=100;//输入种群规模,最大迭代步数
	x.resize(POINTCNT);
	y.resize(POINTCNT);
//	x[0]=0, x[1]=1.1,x[2]=3.5,x[3]=3,x[4]=7,x[5]=8,x[6]=4,x[7]=4.5,x[8]=9,x[9]=2;
	//x[10]=10, x[11]=11.1,x[12]=13.5,x[13]=13,x[14]=17,x[15]=18,x[16]=14,x[17]=14.5,x[18]=19,x[19]=12;
//	y[0]=1.1,y[1]=3,y[2]=2,y[3]=4,y[4]=5.1,y[5]=8,y[6]=4,y[7]=4.5,y[8]=9,y[9]=2;
	//y[10]=11.1,y[11]=13,y[12]=12,y[13]=14,y[14]=15.1,y[15]=18,y[16]=14,y[17]=14.5,y[18]=19,y[19]=12;
	
x[0]=41, x[1]=37,x[2]=54,x[3]=25,x[4]=7,x[5]=2,x[6]=68,x[7]=71,x[8]=54,x[9]=83;
y[0]=94,y[1]=84,y[2]=67,y[3]=62,y[4]=64,y[5]=99,y[6]=58,y[7]=44,y[8]=62,y[9]=69;
x[10]=64,x[11]=18,x[12]=22,x[13]=83,x[14]=91,x[15]=25,x[16]=24,x[17]=58,x[18]=71,x[19]=74;
y[10]=60,y[11]=54,y[12]=60,y[13]=46,y[14]=38,y[15]=38,y[16]=42,y[17]=69,y[18]=71,y[19]=78;
x[20]=87,x[21]=18,x[22]=13,x[23]=82,x[24]=62,x[25]=58,x[26]=45,x[27]=41,x[28]=44, x[29]=4;
y[20]=76,y[21]=40,y[22]=40,y[23]=7,y[24]=32,y[25]=35,y[26]=21,y[27]=26,y[28]=35; y[29]=50;

	
	cout<<"城市数="<<POINTCNT<<endl;
	cout<<"各城市坐标:"<<endl;
	//srand((unsigned)time(NULL));
/*	for(int i=10;i<POINTCNT;i++)
	{
		x[i]=next_int()%1000;y[i]=next_int()%1000;
	}*/
	for(int i=0;i<POINTCNT;i++)
	{
		//scanf("%lf%lf",&x[i],&y[i]);//输入各个城市的坐标		
		cout<<"["<<x[i]<<", "<<y[i]<<"]"<<endl;//输出各个城市的坐标				
	}
	cout<<"染色体长度:"<<POINTCNT<<endl;
	cout<<"最大迭代步数:"<<GENERATIONS<<endl;
	cout<<"种群规模:"<<POPSIZE<<endl;
	int select_type,make_p_type,k,cross_type,mutate_type;
	double q,pc,pm;
	
	select_type=0,make_p_type=1,k=5;//输入个体选择方法类型,个体选择概率分配类型,竞赛规模
	q=0.5,pc=0.8,pm=0.8;//输入最好个体选择概率,交叉概率,变异概率
	cross_type=0,mutate_type=0;//输入交叉类型,变异类型
	
	
	cout<<"交叉概率:"<<pc<<endl;
	cout<<"变异概率"<<pm<<endl;
	cout<<"选择操作:按适应度比例分配个体的选择概率,轮盘赌选择个体"<<endl;
	cout<<"交叉操作:PMX交叉"<<endl;
	cout<<"变异操作:两点互换"<<endl;
	double best=1e9,worst=0,sum=0;
	VI res;
	clock_t start_time;
	start_time=clock();
	cout << endl;
	cout << "正在计算中......" << endl;
	for(int cas=0;cas<CASNUM;cas++)//
	{
		other_population gen(POPSIZE,POINTCNT,cross_type,mutate_type,make_p_type,pc,pm,k,q,select_type);
	
		for(int g=0;g<GENERATIONS;g++)//进行迭代进化
			gen.next();
		if(best>1.0/gen.bestofpop.first)//更新最好适应度
		{
			best=1.0/gen.bestofpop.first;
			res=*gen.bestofpop.second;//存放最好个体的染色体
		}
		if(worst<1.0/gen.bestofpop.first)//更新最差适应度
			worst=1.0/gen.bestofpop.first;
		sum+=1.0/gen.bestofpop.first;//计算各次最好个体的适应度之和
	}
	cout << endl; 
	cout << CASNUM << "次遗传算法求解的结果如下:" << endl;
	clock_t	end_time=clock();
	double durTime=double(end_time-start_time);	
	sum/=CASNUM;//计算平均适应度
	cout<<"最好适应度:"<<best<<"\n"<<"最差适应度:"<<worst<<"\n"<<"平均适应度:"<<sum<<"\n";
	cout<<"输出最好解:";
	for(int i=0;i<POINTCNT;i++)//输出解
	{
		cout<<res[i];//输出各城市
		if (i<POINTCNT-1)
			cout<<"-";
	}
	cout<<endl;
	cout<<"平均运行时间:"<<durTime/CASNUM<<"s"<<endl;
	cout<<endl;

	return 0;
}

추천

출처blog.csdn.net/cangzhexingxing/article/details/124015579