プロが教えるわが家の防犯対策術!

下記のプログラムを改良して2点交叉にしたいのですが、遺伝子アルゴリズムもC++も初心者なので、よくわかりません。どなたかお知恵を貸してください。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "come.h"

int kousa()
{
unsigned int max=100;
unsigned int max2=17;

int a,b,c,d;
int i,v,w,y,n,o,k,p,f;

char kari[1][17];
w=0;//カウンタ
n=0;
srand((unsigned)time(NULL));

for(v=0;v<100;v++)
{
for(i=0;i<17;i++)
{
get[v][i]=next[v][i]; //next[175][17]からget[100][17]へコピーする(親)
}
}

for(o=0;o<100;o++)
{
for(k=0;k<17;k++)
{
take[o][k]=next[v][k]; //next[175][17]からtake[100][17]へコピーする(子)
}
}
for(d=0;d<50;d++)
{
a=(rand()%max); //1つ目の交叉する列を見つける
b=(rand()%max); //2つ目の交叉する列を見つける
c=(rand()%max2); //交叉ポイント見つける

for(;c<17;c++) //1点交叉
{
kari[n][c]=get[a][c];
get[a][c]=get[b][c];
get[b][c]=get[n][c];
}
for(y=0;y<17;y++)
{
take[w][y]=get[a][y]; //get[100][17]からtake[50][17]へコピー
take[w+1][y]=get[b][y];
}
w=w+2;
}
for(p=0;p<100;p++)
{
for(f=0;f<17;f++)
{
next[nextpop][f]=take[p][f];
}
nextpop++;
}
return 0;
}

A 回答 (2件)

恐れ入ります。

No.1で回答したものです。

>回答のプログラムを直接入力すると、kari[i] = get[father][i];の所で「左のオペランドが、左辺値になっていません」、というエラーが起きるので、変数iの部分がどう変えればいいか教えてもらえませんか?

参考にしていただいて、自身で作り直していただくのが一番なのですが、
追加で回答しておきます。
できるだけ元々のソースを残したまま、追加・修正しました。
(追加した部分は、ソース内にコメントを入れました。元ソースと比較して、何をしているか考えてみて下さい)

なお、再度繰り返しますが掲載されたソースが「部分抜粋」で、「ソース全て」ではありません。全て載せていただけなければ、これ以上は判断が付きかねます。
(どんなプログラムの一部なのか推測するしかないので、弄りようがないです)
動かなかった場合は、全てのソースをおみせ願えないでしょうか。


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "come.h"

int kousa()
{
unsigned int max=100;
unsigned int max2=17;

int a,b,c,d;
int father, mother, point_1, point_2, temporary;/* 2点交叉で使う変数 */
int i,v,w,y,n,o,k,p,f;

char kari[1][17];
w=0;//カウンタ
n=0;
father = mother = point_1 = point_2 = temporary = 0; /* 初期化 */
srand((unsigned)time(NULL));

for(v=0;v<100;v++)
{
for(i=0;i<17;i++)
{
get[v][i]=next[v][i]; //next[175][17]からget[100][17]へコピーする(親)
}
}

for(o=0;o<100;o++)
{
for(k=0;k<17;k++)
{
take[o][k]=next[v][k]; //next[175][17]からtake[100][17]へコピーする(子)
}
}
for(d=0;d<50;d++)
{
father = (rand() % max); /* 1つめの交叉する列を見つける */
mother = (rand() % max); /* 2つめの交叉する列を見つける */
point_1 = (rand() % max2); /* 交叉ポイントを見つける */
point_2 = (rand() % max2); /* 交叉ポイントを見つける */
/*↓から2点交叉*/
for(i = 0; i < 17; i++) {
if (i < point_1) {
temporary = get[father][i];
get[father][i] = get[mother][i];
get[mother][i] = temporary;
}
if (i < point_2) {
temporary = get[father][i];
get[father][i] = get[mother][i];
get[mother][i] = temporary;
}
}
/* ↑まで2点交叉 */

for(y=0;y<17;y++)
{
take[w][y]=get[a][y]; //get[100][17]からtake[50][17]へコピー
take[w+1][y]=get[b][y];
}
w=w+2;
}
for(p=0;p<100;p++)
{
for(f=0;f<17;f++)
{
next[nextpop][f]=take[p][f];
}
nextpop++;
}
return 0;
}
    • good
    • 0

恐れ入ります。



ざっと読みましたが、質問に掲載されたプログラムが部分抜粋のようですので、不明点が多いです。推測して回答します。

遺伝的アルゴリズムの基礎はご存じですか?

質問に掲載されたプログラムは
・100の個体で、遺伝子数は17。(遺伝子の中身は不明)
・100の個体を、ランダムに50ペア選び交叉させる。
・交叉後、親の個体は保持されない。子が親に取って代わる。
・親集団は交叉中に変化する。親集団は全て破棄され、子が生まれる度に次集団にコピーされる。
# 1点交叉の get[b][c]=get[n][c];は、get[b][c]=kari[n][c];の間違いかと思いますがどうでしょうか?

通常遺伝的アルゴリズムと呼ばれているモノからすると、かなり変則的なアルゴリズムになっています。一度参考書などで確認された方がよろしいかと思います。

上記のプログラムのまま、2点交叉にしたいだけでしたら簡単です。
1点交叉は
親A 00000
親B 11111
の時に、
子C 11000
子D 00111
のようになります。

2点交叉は
親A 00000
親B 11111
の時に、
子C 11001
子D 00110
のように、交換場所が2カ所になるだけです。
つまり

father = (rand() % max);
mother = (rand() % max);
point_1 = (rand() % max2);
point_2 = (rand() % max2);

for(i = 0; i < 17; i++) {
if (i < point_1) {
kari[i] = get[father][i];
get[father][i] = get[mother][i];
get[mother][i] = kari[i];
}
if (i < point_2) {
kari[i] = get[father][i];
get[father][i] = get[mother][i];
get[mother][i] = kari[i];
}
}

これで、point_1かつpoint_2の場合には2回交換して元に戻ることで、2点交叉が実現できます。(もっとエレガントな方法もありますが、理解しづらくなってしまいますので。ご自身でお考え下さい)

変数名の名前の付け方や、冗長部分のまとめ方などは、参考書を参考にしながらご自身でなされた方がよいかと思います。

この回答への補足

回答のプログラムを直接入力すると、kari[i] = get[father][i];の所で「左のオペランドが、左辺値になっていません」、というエラーが起きるので、変数iの部分がどう変えればいいか教えてもらえませんか?

補足日時:2008/01/14 20:37
    • good
    • 0
この回答へのお礼

お礼が遅くなって申し訳ありません。とても参考になりました。

お礼日時:2008/01/14 20:37

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q遺伝的アルゴリズムのプログラミングについてですが・・・

遺伝的アルゴリズムのプログラムの基本的な流れが↓のページ
http://www.sist.ac.jp/~suganuma/kougi/other_lecture/SE/opt/GA/GA.htm に
【1.初期化 2.生物集団の評価 3.交叉 4.突然変異 5.各個体の評価 6.淘汰】と書かれてあるのですが、
f(x) = sin(3x) + 0.5sin(9x) + sin(15x + 50) [0.1]区間の最大値を求める↓のプログラム
http://www.sist.ac.jp/~suganuma/cpp/3-bu/18-sho/genetic/C++/gene_f.txt 
に当てはめるとどの部分がどこに当たるのでしょうか…(また、このプログラムはどこからどのように読んでいけばいいのでしょうか…)。一応コメントが書かれていますがよく分かりません><;
わかる方がいらっしゃいましたらよろしくお願いしますm( _ _ )m

また、遺伝的アルゴリズムのプログラミングをする際の注意点があれば教えてください。

Aベストアンサー

> 1.初期化

Init_std()関数

> 2.生物集団の評価

Function::Adap()関数

> 3.交叉

C_copy(); // 親のコピー
C_point(kosa, k_point); // 多点交叉
C_uniform(kosa); // 一様交叉
C_mean(kosa); // 平均化交叉
のいずれか、指定された方法で。

> 4.突然変異

M_alle(mute); // 対立遺伝子への置換
M_move(mute); // 移動
M_inv(mute, wd); // 逆位
M_scram(mute, wd); // スクランブル
M_chg(mute, wd); // 転座
M_dup(mute, wd); // 重複
M_per(mute, wd, m_mean, m_std); // 摂動
のいずれか、指定された方法で。

> 5.各個体の評価

Function::Adap()関数

> 6.淘汰

S_roul(elite)関数


makefileを見る限りは、肝心な所は外部の species.h species.cpp とかでやってるのではないかと。

> 1.初期化

Init_std()関数

> 2.生物集団の評価

Function::Adap()関数

> 3.交叉

C_copy(); // 親のコピー
C_point(kosa, k_point); // 多点交叉
C_uniform(kosa); // 一様交叉
C_mean(kosa); // 平均化交叉
のいずれか、指定された方法で。

> 4.突然変異

M_alle(mute); // 対立遺伝子への置換
M_move(mute); // 移動
M_inv(mute, wd); // 逆位
M_scram(mute, wd); // スクランブル
M_chg(mute, wd); // 転座
M_dup(mute, wd); // 重複
M_per(mute, wd, m_mean...続きを読む

Q交叉について

c言語を使い始めて、TSPについて勉強中で、遺伝的アルゴリズムの交叉(順序交叉)について以下のプログラムを作ってみたのですが、凄く遅いです。答えはきちんと出るのですが、循環交叉を取り入れたプログラムは5000ループでも1秒弱で出るのに対して20秒近くかかってしまいます。ループに無駄が多そうなのですが思いつきません。ヒントでも宜しいのでご教授下さい。
順序交叉とは、例えば
x[i] = 3,4,5,2,1とy[i] = 4,3,5,1,2(i = 0~)という数字が与えられたときtargetを2とすると
x[i] = 3,4, y[i] = 4,3まではそのまま受け継ぎ、3以降は相手方の数字を左から順番に使われていないものを取り入れていくものです。この場合だと、x[i] = 3,4,5,1,2 y[i] = 4,3,5,2,1となります。

#define a 5
int main(void)
{
   int x[a] = {3, 4, 5, 2, 1}, y[a] = {4, 3, 5, 1, 2};
   int target, i, j, k, l;
   int x2[a], y2[a];
   srand((unsigned)time(NULL));

   target = rand () % a;
   for (i = 0; i < target; i++) {
      x2[i] = x[i];
      y2[i] = y[i];
   }
   for (i = target; i < a; i++) {
      x2[i] = 0;
      y2[i] = 0;
   }
   j = 0;
   k = 0;
   while (k != a - target) {
      for (i = 0; i < target + k; i++) {
         for (l = 0; l < target + k; l++) {
            if (x2[l] == y[j])
               j++;
         }
      }

      if (x2[target + k] != y[j]) {
         x2[target + k] = y[j];
         k++;
         j++;
      }
      else
         j++;
   }
   yについても同様の作業です

}

c言語を使い始めて、TSPについて勉強中で、遺伝的アルゴリズムの交叉(順序交叉)について以下のプログラムを作ってみたのですが、凄く遅いです。答えはきちんと出るのですが、循環交叉を取り入れたプログラムは5000ループでも1秒弱で出るのに対して20秒近くかかってしまいます。ループに無駄が多そうなのですが思いつきません。ヒントでも宜しいのでご教授下さい。
順序交叉とは、例えば
x[i] = 3,4,5,2,1とy[i] = 4,3,5,1,2(i = 0~)という数字が与えられたときtargetを2とすると
x[i] = 3,4, y[i] = 4...続きを読む

Aベストアンサー

パッと思いついたところだけ。
出現済みチェック(かな?)の二重ループ。jがインクリメントされなければ、次のiのループでもインクリメントされないので、ループしなくても良いかと。こんな感じ:

while (k != a - target) {
for (i = 0; i < target + k; i++) {
int prev_j = j; // jを記憶。
for (l = 0; l < target + k; l++) {
if (x2[l] == y[j])
j++;
}
printf( "j: %d->%d?n", prev_j, j ); //デバッグ用。
if ( prev_j == j ){break;} // jに変化無ければブレーク。
}

遺伝子が重複無く存在するような表現式なのだとしたら、以下のやり方でもいけるかも。

for (i = 0; i < target; i++) { // 交叉直前まで遺伝。
x2[i] = x[i];
}
for (i = 0; i < a; i++) { // 片親を作業領域にコピー。
y2[i] = y[i];
}
for ( j = 0; j < target; j++ ){
for ( i = 0; i < a; i++ ){
if ( x2[j] == y2[i] ){ y2[i] = 0; } // 既に遺伝済みのものは引き継がないよう片親側に0でマーク。
}
}
i = 0;
for( k = target; k < a; k++ ){ // 片親から残りを遺伝。
while ( y2[i] == 0 ){ i++; } // マークされている部分をスキップ。
x2[k] = y2[i++];
}
}

パッと思いついたところだけ。
出現済みチェック(かな?)の二重ループ。jがインクリメントされなければ、次のiのループでもインクリメントされないので、ループしなくても良いかと。こんな感じ:

while (k != a - target) {
for (i = 0; i < target + k; i++) {
int prev_j = j; // jを記憶。
for (l = 0; l < target + k; l++) {
if (x2[l] == y[j])
j++;
}
printf( "j: %d->%d?n", prev_j, j ); //デバッグ用。
...続きを読む


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング