【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は謹んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.18985

最短パス
投稿者---naru(2005/01/03 20:52:03)


ダイクストラのアルゴリズムを使って、最短パスを探すプログラムを作りましたが、うまく動くことができません。
どのようにすれば、うまく動くことができるのでしょうか。どうか、よろしくお願いします。
#include <stdio.h>
#include <stdlib.h>


/* 最大ノード数 */
#define MAX_NODES 64
#define MAX_EDGES 2016



/* ノードのデータ構造 */
struct node
{
  char label; /* ラベル */
  struct node_list *nd_list; /* 接続するリスト
                              */
};

/* 枝のデータ構造 */
struct edge
{
  int nd1_idx, nd2_idx;
  int weight; /* 枝重み */
};
/* リスト表現によるグラフの枝構造 */
struct node_list
{
  int nd_idx; /* ノード番号 */
  int eg_idx; /* 枝番号 */
  struct node_list *next; /* 次のポインタ */
};

/* ノード数 */
static int node_n;
/* 枝数 */
static int edge_n;
/* ノードの配列 */
static struct node node[MAX_NODES];
/* 枝の配列 */
static struct edge edge[MAX_EDGES];
/*最短パスの重み*/
int wgoukei;
/* プロトタイプ宣言 */
int input_graph(char * );
int add_node_list(struct node * , int , int );
int dai_search(char ,char);
/*
 *  メインプログラム
 */
int
main(
     int argc,    /* 引数の数 */
     char **argv  /* 引数 */
     )
{
  /*
    実行例:
    % a.out file a f
  */
  if(argc < 4){
    printf("正しく情報が入力されていません\n");
    exit(1);
  }

  /* グラフ情報をファイルから入力 */
  input_graph(argv[1]);
  /*ダイクストラで最短パスを検索*/
  dai_search(argv[2][0], argv[3][0]);
/* 終了 */
  return 0;
}



/*グラフの入力*/
int input_graph(char *fname)
{

  FILE *fp;
  int i,j,k;
  char n1,n2;
  int w;

  /*入力ファイルオープン*/
  if((fp=fopen(fname,"r"))==NULL)
    {
      printf("ファイルを開く際にエラーが発生しました\
n");
      exit(1);
    }


  /*ノード情報の読み込み*/
  fscanf(fp,"N=%d\n",&node_n);
 for(i=0;i<node_n;i++)
    {
      fscanf(fp,"%c\n",&n1);

      /*ラベルのセット*/
      node[i].label =n1;
      /*リストの初期化*/
      node[i].nd_list = NULL;
    }
/*枝情報の読み込み*/
  fscanf(fp,"E=%d\n",&edge_n);
  for(i=0;i<edge_n;i++)
    {
      /*ノード1ラベル、ノード2ラベル 枝の重み*/
      fscanf(fp,"%c %c %d",&n1,&n2,&w);

      for(j=0;j<node_n;j++)
        if(n1==node[j].label) break;
      for(k=0;k<node_n;k++)
        if(n2==node[k].label) break;

      edge[i].weight=w;
      /*枝の両端のノード番号をセット*/
      edge[i].nd1_idx=j;
      edge[i].nd2_idx=k;

      /*j番目のノードのリストにk番目のノードを追加*/
      /*枝番号も渡す*/
      add_node_list(&node[j],k,i);

      /*k番目のノードのリストにj番目のノードを追加*/
      /*枝番号も渡す*/
      add_node_list(&node[j],k,i);
    }
      fclose(fp);
      return 0;
    }

/*ノードリストへ接続するノード情報の追加*/
int add_node_list(
                  struct node *nd,/*ノード*/
                  int nd_num,/*追加番号*/
                  int eg_num/*追加番号*/
                  )
{
  struct node_list *nl;

  nl =(struct node_list *)malloc(sizeof(struct node_l
ist));

  nl->nd_idx = nd_num;
  nl->eg_idx = eg_num;
  nl->next = nd->nd_list;
  nd->nd_list = nl;

  return 0;
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> 多重レスすみません 18986 naru 2005/01/03 20:55:03


No.18986

多重レスすみません
投稿者---naru(2005/01/03 20:55:03)


多重レスですみません。プログラムが長くなってしまいまして、このような形になってしまいました。以下はダイクストラのソースの部分です。

int dai_search(char s,char g)
{
  int wmin;
  int nd1,nd2;
  int ni;
  int i;
  int s_idx; /* 始点ノード番号*/
  int g_idx; /*終点のノード番号*/
  int emin_idx;/*最も軽い枝番号*/
  struct node_list *nl;

  /* 枝のフラグ (最大枝探索用) */
  /*dskfj 木の枝の候補となっているか */
  /*
    0:  まだ候補になっていない
    1:  候補となっている
    -1: 最大木の枝として確定
    -2: ループを生じるために候補から除外
  */

  int is_candidate[MAX_EDGES];
/*ノードの配列(ループチェック用)*/
  /*
    0:木に加えられていない
    1:木にすでに加えられている
  */
  int is_added[MAX_NODES];

  /*フラグの初期化*/
  for(i = 0; i < edge_n; i ++) is_candidate[i]=0;
  for(i = 0; i < node_n; i ++) is_added[i] = 0;

  /*始点を探索*/
  for(i=0;i<node_n;i++)
    {
      if(s==node[i].label)
        {
          s_idx=i;
          break;
        }
    }
  /*終点を探索*/
  for(i=0;i<node_n;i++)
    {
      if(g==node[i].label)
        {
          g_idx=i;
          break;
        }
}
  /*探索地点を始点へセット*/
  ni = s_idx;

  /* 始点に繋がる枝を候補とする */
  for(nl = node[ni].nd_list; nl != NULL; nl = nl->nex
t){
    is_candidate[nl->eg_idx] = 1;
  }

  /*g_idxになるまで*/
  while(ni==g_idx)
    {

      /*重み最小の枝を探索*/
      wmin =-1;
      /* 始点に繋がる枝を候補とする */
      for(nl = node[ni].nd_list; nl != NULL;
          nl = nl->next)
        {
          is_candidate[nl->eg_idx] = 1;
      }

      for(i=0;i<edge_n;i++)
        {
/*
            - 候補となっていない枝(0)
            - 確定している枝(-1),
         - 除外した枝(-2),
         は飛ばす
          */
          if(is_candidate[i]!=1) continue;

          /*最も軽い重みの枝を次々に更新する*/
          if(edge[i].weight < wmin)
    {
      emin_idx=i;
      wmin =edge[i].weight;
    }
        }
/* 最小枝eminの両端のノード */
      nd1 = edge[emin_idx].nd1_idx;
      nd2 = edge[emin_idx].nd2_idx;

      /* emaxがループをつくる枝の場合は飛ばす */
      if(is_added[nd1] == 1 && is_added[nd2] == 1)
{
        /* 枝候補から除外する */
        is_candidate[emin_idx] = -2;
        continue;
      }

      /* 最短パスの枝として確定させる */
      is_candidate[emin_idx] = -1;

      wgoukei=wmin;
      /* 最短パスを出力 */
      printf("%c",node[ni].label);

      /* 加えた枝emaxの両端のノードを訪問済フラグを立
てる*/
      is_added[nd1] = 1;
      is_added[nd2] = 1;
      ni= nd2;
/* 最大枝emaxの第1ノードにつながる枝を木候\
         補にする */
      for(nl = node[nd1].nd_list; nl != NULL; nl= nl-
>next){
        /*
          - 確定している枝(-1),
          - 除外した枝(-2),
          は飛ばす
        */
        if(is_candidate[nl->eg_idx]<0) continue;

        /* まだ候補となっていない枝であれば */
        if(is_candidate[nl->eg_idx] == 0){
          is_candidate[nl->eg_idx] = 1;
        }
      }

      /* 最大枝emaxの第2ノードにつながる枝を木候\
         補にする */
      for(nl = node[nd2].nd_list; nl != NULL; nl\
            = nl->next){
  /*
    - 確定している枝(-1),
    - 除外した枝(-2),
    は飛ばす
  */
 if(is_candidate[nl->eg_idx]<0) continue;

        /* まだ候補となっていない枝であれば */
  if(is_candidate[nl->eg_idx] == 0){
    is_candidate[nl->eg_idx] = 1;
  }
      }
      ni=nd2;
    }
  return 0;
}



よろしくお願いします。


この投稿にコメントする

削除パスワード

No.18987

Re:多重レスすみません
投稿者---あかま(2005/01/03 21:33:05)


プログラムを読む側の欲しい情報。
1.入力ファイルの内容
2.出力形式
3.どこがどううまくいかないのか

答える人はバグ取り屋ではないので、"バグを探すところから初めてくれ"みたいなのはNGです。
あとプログラムが変なところで改行されてたり、インデントが変だったりするのですが直すのは無理ですか?


この投稿にコメントする

削除パスワード

No.18988

Re:多重レスすみません
投稿者---naru(2005/01/03 21:42:58)


>プログラムを読む側の欲しい情報。
>1.入力ファイルの内容
>2.出力形式
>3.どこがどううまくいかないのか
>
>答える人はバグ取り屋ではないので、"バグを探すところから初めてくれ"みたいなのはNGです。
>あとプログラムが変なところで改行されてたり、インデントが変だったりするのですが直すのは無理ですか?


すみませんでした。質問の仕方が間違っていました。入力ファイルは以下のものです。
/*--------------------*/
N=6
a
b
c
d
e
f
E=10
a b 1
a c 5
a d 3
b c 1
b e 2
c d 5
c f 1
d f 2
e d 5
e f 2
/*------------------------*/
このファイルから、始点と終点を入力して、その最短パスを求めるプログラムなのですが、うまく出力することができません。(printfをしているのに)プログラムの改行、インデントは直すことはできます。すみませんでした。



この投稿にコメントする

削除パスワード

No.18989

Re:多重レスすみません
投稿者---あかま(2005/01/03 21:53:11)


>/*--------------------*/
>N=6
>a
>b
>c
>d
>e
>f
>E=10
>a b 1
>a c 5
>a d 3
>b c 1
>b e 2
>c d 5
>c f 1
>d f 2
>e d 5
>e f 2
>/*------------------------*/

これはつまり、a〜fの6つのノードがあって、
a→bにいくのに1
a→cにいくのに5
のコストがかかるというのでいいのでしょうか?
双方向でb→aも1です?

で、ダイクストラ法って2次元配列で解いてるのはみたことあるけど、
木構造では初めて見ます。
読んではみますが、理解できなかったらごめんなさい。


この投稿にコメントする

削除パスワード

No.18990

Re:多重レスすみません
投稿者---naru(2005/01/03 22:05:47)


>>/*--------------------*/
>>N=6
>>a
>>b
>>c
>>d
>>e
>>f
>>E=10
>>a b 1
>>a c 5
>>a d 3
>>b c 1
>>b e 2
>>c d 5
>>c f 1
>>d f 2
>>e d 5
>>e f 2
>>/*------------------------*/
>
>これはつまり、a〜fの6つのノードがあって、
>a→bにいくのに1
>a→cにいくのに5
>のコストがかかるというのでいいのでしょうか?
>双方向でb→aも1です?
>
このグラフは、無方向グラフです。ですので、双方向はないです。あとは、その通りです。こちらこそよろしくお願いします。見ていただけるのであれば、幸いです。


この投稿にコメントする

削除パスワード

No.18991

Re:多重レスすみません
投稿者---あかま(2005/01/03 22:36:37)


input_graph()までしか読んでないけど。
とりあえず怪しいのを1つ


/*j番目のノードのリストにk番目のノードを追加*/
/*枝番号も渡す*/
add_node_list(&node[j],k,i);

/*k番目のノードのリストにj番目のノードを追加*/
/*枝番号も渡す*/
add_node_list(&node[j],k,i);//ここ。インデックスが間違ってる?そもそも片方向のグラフなのに登録の必要ある?




この投稿にコメントする

削除パスワード

No.18993

Re:多重レスすみません
投稿者---naru(2005/01/03 23:05:27)


>
input_graph()までしか読んでないけど。
とりあえず怪しいのを1つ


/*j番目のノードのリストにk番目のノードを追加*/
/*枝番号も渡す*/
add_node_list(&node[j],k,i);

/*k番目のノードのリストにj番目のノードを追加*/
/*枝番号も渡す*/
add_node_list(&node[j],k,i);//ここ。インデックスが間違ってる?そもそも片方向のグラフなのに登録の必要ある?


すみません。無方向ですので、a→b、b→aもありえます。遅れてすみません。


この投稿にコメントする

削除パスワード

No.18997

Re:多重レスすみません
投稿者---あかま(2005/01/04 01:19:11)


実行してみたら読み込みがうまくいってない。
int input_graph()の次の行を修正。

fscanf(fp,"%c %c %d",&n1,&n2,&w);
↓
fscanf(fp," %c %c %d",&n1,&n2,&w);//""の先頭に空白を一つ入れた

さっき指摘したのも修正忘れずに。
add_node_list(&node[j],k,i);//二つあるうちの一つ
↓
add_node_list(&node[k],j,i);


肝心のdai_search()を読んでみたけれども、残念ながら理解できず。
構造体と配列が多すぎて混乱してしまう。
とりあえずスタートからゴールまでの最短コストを出すdai_search()を書いてみました。
辿った経路が出ませんが参考になれば。改造すれば経路もでるはず。

#define MAX_COST 9999
//↑スタートからゴールまでのコストがMAX_COSTを超えてはいけない
struct cost_check{
    int cost;//スタートノードから別ノードへの最低コスト量
    int check;//0=最低コストが暫定状態,1=最低コストが確定
};

int dai_search(char s,char g)
{
    int i;
    int s_idx; /* 始点ノード番号*/
    int g_idx; /*終点のノード番号*/
    struct node_list *nl;
    struct cost_check node_cost[MAX_NODES];
    int min_cost_idx;//最小値のコストを保存しておくindex

    /*始点を探索*/
    for(i=0;i<node_n;i++){
        if(s==node[i].label){
            s_idx=i;
            break;
        }
    }
    /*終点を探索*/
    for(i=0;i<node_n;i++){
        if(g==node[i].label){
            g_idx=i;
            break;
        }
    }

    /*フラグの初期化*/
    for(i = 0; i < node_n; i ++){
        node_cost[i].cost = MAX_COST;
        node_cost[i].check = 0;
    }
    node_cost[s_idx].cost = 0;//スタートからスタートへのコストは0
    node_cost[s_idx].check = 1;//スタートからスタートへのコストは確定
    //コストの確定作業に入る
    while(node_cost[g_idx].check == 0){
        for(i=0,min_cost_idx=g_idx;i < node_n;i++){//g_idxのコストが確定するまでループ
            if(node_cost[i].check == 0) continue;
            for(nl=node[i].nd_list ; nl != NULL; nl = nl->next){
                if(node_cost[i].cost + edge[nl->eg_idx].weight < node_cost[nl->nd_idx].cost) node_cost[nl->nd_idx].cost = node_cost[i].cost + edge[nl->eg_idx].weight;
                if(node_cost[nl->nd_idx].check == 0 && node_cost[nl->nd_idx].cost < node_cost[min_cost_idx].cost) min_cost_idx = nl->nd_idx;//最低値のコストが出たらそのインデックスを保存

            }
        }
        //スタートからどこか一つのノードへのコストが確定
        node_cost[min_cost_idx].check = 1;
        printf("確定%c->%c:%d\n",node[s_idx].label,node[min_cost_idx].label,node_cost[min_cost_idx].cost);
    }
    return 1;
}



あと改善案。

struct node_listのeg_idxをweightに変更すればstruct edgeはいらないかと
struct node_list{
    int nd_idx;
    int weight;
    struct node_list *next;
};

これでstatic edge[MAX_EDGES]がなくなって、
上のedge[nl->eg_idx].weightがnl->weightになってすっきり。



この投稿にコメントする

削除パスワード

No.18998

Re:多重レスすみません
投稿者---naru(2005/01/04 01:52:43)


ありがとうございます。
適当に、思いつくままに作ってしまったので、色々とごちゃごちゃしてしまったようですみませんでした。もう少しすっきりとかけるように頑張ってみます。色々とお世話になりました。また、結果が出ましたらカキコします。とりあえず、教えて頂いたプログラムを理解して作成しなおして見たいと思います。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧