유전 알고리즘에 대한 질문입니다.
조회수 227 답변수 0 반응수 0 등록일 2018.06.08 15:48:11

유전 알고리즘을 만들고 있는데 3가지 부분을 만드는게 어려워서 질문을 올리게 되었습니다.

돌연변이와 교차 알고리즘 그리고 새 유전자 명시하는 코드 입니다.



#include <iostream>

#include <string.h>

#include <fstream>

#include <time.h>

#include <algorithm>



using namespace std;



const int NOI=50; // 물건의 개수=유전자의 길이

const int MW=40000; // 배낭의 최대 저장 무게

const int NOG=64; // 유전자의 개수



int GbestValue=0; // 최대 적합도(최대 가치)

int GbestWeight=0;  // 최고 무게



struct itemType{  //화물정보를 위한 구조체

int weight; // 화물의 무게

int value; // 화물의 가치

};



struct geneType{ // 유전자 구조체

char dna[NOI+1]; // 유전자 문자열 -> "0010010100100011101010110101...."

int wSum; // 위 유전자 상태에서의 무게합

int valueSum; // 그 때의 총가치

};



itemType items[NOI]; // 화물정보를 저장한 배열



geneType gene[2][NOG]; // 유전자 배열- 0: 이전 세대, 1: 이후 세대



bool cmp(const geneType &a, const geneType &b) // sort 함수를 위한 비교함수

{

return (a.valueSum > b.valueSum); // > : 내림차순, < : 오름차순

}



void read_data(){ // 파일로 부터 화물 정보를 읽어 items 배열에 저장

ifstream in("knapsack50.txt");



if (in.is_open()) {

for(int i=0;i<NOI;i++){

in >> items[i].weight >> items[i].value;

}

}

else {

cout << "파일을 찾을 수 없습니다!" << endl;

exit(-1);

}



in.close();

}



void w_sum(int g, int num){ // 이전(g=0), 이후(g=1) 세대 유전자들의 num번째 유전자 상태에 따른

    // 무게 합과 가치 합을 구해 저장하는 함수

int wsum=0;

int vsum=0;

for(int i=0;i<NOI;i++){

if(gene[g][num].dna[i] == '1'){

// 화물이 들어가는 경우(유전자열 문자가 '1'인 경우)에만 합을 구함

wsum = wsum + items[i].weight;

vsum = vsum + items[i].value;

}

}

gene[g][num].wSum = wsum;

gene[g][num].valueSum = vsum;

}



void init_gene(){ // 초기 64개의 유전자를 랜덤으로 생성하는 함수

int i=0;

while(i<NOG){

for(int j=0; j<NOI; j++){

if(rand()%2 == 1){ // 홀수이면 1, 짝수이면 0으로 세팅

gene[0][i].dna[j]='1';

}

else{

gene[0][i].dna[j]='0';

}

}

gene[0][i].dna[NOI] = '\0'; // 문자열 마지막은 항상 NULL로 막아야 함



w_sum(0, i); // 랜덤으로 생성된 유전자열의 무게합을 확인하기 위해 합을 구함

if(gene[0][i].wSum <= MW){

// 무게가 초과되지 않아야만 유효한 유전자. 그렇지 않으면 다시 생성

i++;

}

}



sort(gene[0], gene[0]+NOG, cmp); // 내림차순 정렬



GbestValue=gene[0][0].valueSum;

// 총가치를 기준으로 내림차순 정렬이므로 맨 앞의 유전자가 베스트 유전자

}



void copyGene(int des, int src){ // src 번째 이전 유전자를 dec 번째 새 유전자로 복사

// 이전 세대의 유전자들 중 가장 우수한 것을 그대로 넘기기 위함.(elitism)

strcpy(gene[1][des].dna, gene[0][src].dna);

gene[1][des].valueSum = gene[0][src].valueSum;

gene[1][des].wSum = gene[0][src].wSum;

}



// g 세대의 k, m번째 유전자 2개를 돌연변이시킴

void mutate(int g, int k, int m){



int place; // 교차위치 임시변수



//////////////////////////////////////////////////////////////////////

// 이 곳에 돌연변이 함수를 직접 작성해 봄

// k번째, m번째 유전자의 1곳, 또는 2곳 정도만 일정 확률로 돌연변이시킴

// 무작위 위치의 문자가 0이면 1, 1이면 0으로 치환

// 확률은 10% 이하로 낮게 잡는 것이 좋음. 돌연변이가 너무 많으면

// 널뛰기처럼 들쭉 날쭉해져서 무작위 탐색처럼 되고 최적해에 빨리 수렴하지 못함

//////////////////////////////////////////////////////////////////////





}



void crossover1p(int i, int j, int k, int m){

// 이전 i번째 j번째 유전자를 교차하여 k, m번째 새 유전자로 집어넣음

// 랜덤 위치의 단순 교차

char tempa[NOI+1];

char tempb[NOI+1];

while(1){



////////////////////////////////////////

/// 여기에 교차 알고리즘 작성해 보기 ///

////////////////////////////////////////





strcpy(gene[1][k].dna, tempa); // 생성된 첫번째 교차 유전자열을 복사

strcpy(gene[1][m].dna, tempb); // 생성된 두번째 교차 유전자열을 복사



mutate(1, k, m); // 돌연변이 시행



w_sum(1, k); // 무게 초과를 확인하기 위해 무게합 구함

w_sum(1, m);



if(gene[1][k].wSum <= MW && gene[1][m].wSum <= MW){

break; // 무게 조건이 둘 다 맞아야만 유효한 생성으로 함수 종료

}

}

}



void copyAllBack(){ // 이후 세대의 유전자들을 모두 이전 세대 변수로 복사

// 그래야만 동일한 방법으로 이전 세대 유전자로 다시 이후 세대 유전자를 반복하면서 생성

for(int i=0;i<NOG;i++){

strcpy(gene[0][i].dna, gene[1][i].dna);

gene[0][i].valueSum=gene[1][i].valueSum;

gene[0][i].wSum=gene[1][i].wSum;

}

}



void select_and_crossover(){

// 선택기법에 따라 선택한 이전 세대 유전자로 이후 세대 유전자 생성

// 엘리티즘을 반드시 사용하도록 함.

// 엘리티즘을 사용하지 않으면 널뛰기처럼 값이 들쭉 날쭉해져서 최적해로의 수렴이 어려움



copyGene(0, 0); // 엘리티즘용

copyGene(63, 0); // 엘리티즘용.



crossover1p(0,1,1,2);

crossover1p(0,3,3,4);

crossover1p(0,5,5,6);

crossover1p(0,7,7,8);

crossover1p(0,9,9,10);



////////////////////////////////////////////////////////

// 이 곳에 선택법에 따른 이전 유전자 2개 선택과

// 그 2개로 인해 생성되는 새 유전자를 명시하는 코드 작성

// 위의 예처럼 단순하게 다 나열해도 되고

// 반복문을 잘 활용하면 더 깔끔하게 작성할 수 있음

////////////////////////////////////////////////////////



sort(gene[1], gene[1]+NOG, cmp); // 새로 생성된 유전자들을 가치순으로 정렬

}



int main(){



double avg; // 모든 유전자들의 가치 평균값 저장

int sum; // 평균을 위한 합계



    ofstream out("result.txt");



srand (int(time(NULL))); // 랜던 함수 시드 설정



read_data(); // 화물 데이터 읽어들임



init_gene(); // 랜덤으로 초기 유전자 세트  생성



sum=0;

for(int i=0;i<NOG;i++){ // 초기 랜덤 유전자들의 평균 가치를 출력하기 위한 합계 산출

sum = sum + gene[0][i].valueSum;

}



// 초기 랜던 유전자들의 최고 가치, 그때의 무게합, 평균 가치를 출력

cout << gene[0][0].valueSum << ", " << gene[0][0].wSum << ", " << sum/NOG << endl;  

out << gene[0][0].valueSum << ", " << gene[0][0].wSum << ", " << sum/NOG << endl;  

for(int i=0;i<2000;i++){ // 진화를 2,000세대만 진행시켜서 동료들과 결과 비교



select_and_crossover(); // 선택법에 따른 선택과 교차를 시행하여 새로운 유전자 세트를 생성



copyAllBack(); // 반복을 위해 새 유전자 세트를 이전 유전자 세트로 복사



sum=0;

for(int i=0;i<NOG;i++){

sum = sum + gene[0][i].valueSum;

}// 새로 생성된 유전자들의 평균 가치를 출력하기 위한 합계 산출

// 세대별 변화를 보고 싶을 경우 아래 문의 주석 헤제

// cout << gene1[0].valueSum << ", " << gene1[0].wSum << ", " << sum/NOG << endl;  

// out << gene1[0].valueSum << ", " << gene1[0].wSum << ", " << sum/NOG << endl;



}



//최종 결과를 출력하는 부분

cout << gene[0][0].valueSum << ", " << gene[0][0].wSum << ", " << sum/NOG << endl;  

out << gene[0][0].valueSum << ", " << gene[0][0].wSum << ", " << sum/NOG << endl;  



out.close();



return 0;

}



답변 작성

질문에 적합한 답변을 상세히 작성해 주시기 바랍니다.

답변이 찬성되면 태그평판 +2점이 적립, 반대되면 태그평판 -1점 차감됩니다.

답변이 채택되면 태그평판 +10점이 적립됩니다.