2012 ds 05

32
자료구조#5 최단거리 찾기 조 : C2 조장 : 11 박가혜 조원 : 11 김슬기 11 정민정 11 정예린 11 최보은

Upload: jungyerin

Post on 18-Nov-2014

648 views

Category:

Documents


4 download

DESCRIPTION

 

TRANSCRIPT

Page 1: 2012 Ds 05

자료구조#5

최단거리 찾기

조 : C2 조장 : 11 박가혜

조원 : 11 김슬기

11 정민정

11 정예린

11 최보은

Page 2: 2012 Ds 05

< 차례 >

1. 서론

-일정

-업무

-회의록

-자료조사

2. 본론

-초안

-최종안

3.결론

-복잡도 계산

Page 3: 2012 Ds 05

일정 1 2

1주차5/24(목)

프로젝트 시작전

그래프에 대한 설명들음

2주차

5/29(화)

프로젝트 시작

업무 설정

기초 회의

5/31(목)

문제 이해

플로이드 알고리즘에 대한

조사, 회의

3주차

6/5(화)

문제점 분석

알고리즘 회의

최종보고서 작성

조원 주요 업무

박가혜 조장 업무 및 소스 코딩

김슬기 자료조사 및 보고서 작성

정민정 자료조사 및 보고서 작성

정예린 알고리즘 구상 및 소스 코딩

최보은 회의록 작성 및 보고서 작성

1. 서론

(1)일정

(2)업무분담

Page 4: 2012 Ds 05

(3)회의록

<1차>

회의일시 2012년 05월 29일(화) 조 C2 작성자 11최보은참석자 11김슬기, 11박가혜, 11정민정, 11정예린

회의안건1. 프로젝트 기초 토의2. 업무분담3. 다음 일정 계획

회의내용

내 용 비 고

1. 프로젝트에 대한 기본 회의 -APSP의 개념의 이해 -이번 과제에 필요한 플로이드알고리즘에 대해 조사 -과제에 대한 기초 토의

2. 업무분담 -조장 : 박가혜 자료조사 : 김슬기, 정민정 회의록 : 최보은 소스 코딩 : 박가혜, 정예린

3. 다음 일정 계획 -자료조사는 토요일 저녁까지 마무리 지어 정리하도록 함 -저번 이산치 과제였던 MST와의 차이점은 무엇인지 생각해보기. -플로이드 알고리즘에 대해 각자 조사.

Page 5: 2012 Ds 05

<2차>

회의일시 2012년 05월 31일(목) 조 C2 작성자 11최보은참석자 11김슬기, 11박가혜, 11정민정, 11정예린

회의안건1. 문제이해2. 알고리즘 회의3. 다음 일정 계획

회의내용

내 용 비 고

1. 문제 이해 - 모든 노드를 방문해야 한다. - 그중 가장 작은 가중치 세 개를 출력한다. - 플로이드 알고리즘이 어떻게 프로젝트에 활용되는지 생각 해보기

2. 알고리즘 회의 - 모든 노드를 방문하게 만드는 방법이 무엇이 있을지 토의. - 경로의 출력에 대해 토의.

3. 다음 일정 계획 - 초기 보고서 틀 작성 - 토요일까지 자료조사 완료시키기 - 소스 코딩 완료시키기

Page 6: 2012 Ds 05

<3차>

회의일시 2012년 06월 05일(화) 조 C2 작성자 11최보은참석자 11김슬기, 11박가혜, 11정민정, 11정예린

회의안건1. 문제점 파악2. 알고리즘 회의3. 최종보고서 작성4. 다음 일정 계획

회의내용

내 용 비 고

1. 문제점 파악 - 초기 설정한 알고리즘과 이번 프로젝트와는 다르다는 것을 파악. - 수요일 이내로 토의를 해 새로 구현해야 함. - 초기 설정 : 모든 노드를 다 돌아야 한다.(초안)

2. 알고리즘 회의(최종소스) - 플로이드 알고리즘을 활용하여 구상 - 경우의 수를 두어 아무 두 점의 경로를 모두 살핌 - 재 구현해보기로 함.

3. 최종 보고서 작성 - 토의를 통해 보고서 작성의 틀 완성.

4. 다음 일정 계획 - 수요일 이내로 모든 마무리를 함.

Page 7: 2012 Ds 05

(4) 자료조사

[플로이드 알고리즘]

플로이드(Floyd) 알고리즘은 최단거리를 구하는 알고리즘이다. 다른 점은 다익스트라 알고리즘이 한

점에서 출발해서 각 정점에 최단거리를 구하지만, 플로이드 알고리즘은 모든 점점에서 출발해서 출발

한 정점을 제외한 모든 정점을 도착점으로 하는 최단거리를 구하는 알고리즘이다. 플로이드 알고리즘

의 대략적인 코딩은 다음과 같다.

for k:=1 to n do

for i:=1 to n do

for j:=1 to n do

if e[i,j] > e[i,k] + e[k,j] then

e[i,j] := e[i,k] + e[k,j];

실제로 모든 쌍의 최단거리를 찾는 부분은 위의 5줄에 불과하다. 나머지 부분은 최단거리의

'경로'를 찾는 과정이다.

알고리즘에 대해서 약간 설명을 하자면 i, j간에 기존에 알려진 최단거리 e[i, j]와 노드 k를

거치는 최단거리 e[i, k] + e[k, j]를 먼저 비교한다. 노드 k를 경유하는 것이 원래의 최단

거리보다 짧다면 최단거리를 갱신한다. for문의 순서가 틀리면 제대로 동작하지 않을 수 있

다.

보통의 경우 가중그래프는 노드 u와 v간에 에지가 없음을 표현할 때 e[u,v]에 0을 기록해

주는 것에 반해 이 Floyd알고리즘은 약간 다른 표기법을 필요로 한다.

u, v간에 에지가 없으면 : u = v일 때 0

: u <> v일 때 무한대(∞)

u, v간에 에지가 있으면 : 에지의 가중치

문제는 컴퓨터로는 무한대를 표현할 수 있는 방법이 없다는 것이다. 이럴 때에는 적당히 큰

값을 써주면 됩니다. 하지만 너무 큰 값을 넣을 경우 e[i, k] + e[k, j]를 계산하는 과정에

서 overflow 에러가 발생할 수 있다. 또한 너무 작으면 최단거리를 구하는 과정이 제대로

동작하지 않게 된다.

  옆의 그림이 가중치 방향성 그래프인데, 예를 들

면, v3에서 v1으로 가는 가중치(거리/비용)가 -1 이

고, v1에서 v3로 직접 가는 방법은 없다는 의미이

다. v1에서 v3 로 가기 위해서는 v1 -> v2 -> v3 의 

방법①과 v1 -> v2 -> v4 -> v3 의 방법②가 존재하는

데 이런 여러 경로 중에서 가중치의 합이 가장 적은 경

로를 구하는 것이다. 방법① 경로는 -1 + 2 + 4 = 5가

되고, 방법② 경로는 -1 + 2 + 3 + 7 = 11이 된다. 그

러므로 최종적으로 최단 경로는 v1 ->

Page 8: 2012 Ds 05

D

-1A B C

A 0 4 11

B 6 0 2

C 3 ∞ 0

1 단계 - 어떠한 경로도 거치지 않을 때

항상 자신에서 자신으로 가는 값은 0이 된다.

A에서 C로 갈 때 11이라는 것과 C에서 B로

갈 때 무한대라는 것을 주목하라.

D

-2A B C

A 0 4 7

B 6 0 2

C 3 ∞ 0

2 단계 - 경로 하나를 거칠 수 있을 때 (예제에

서는 정점이 3개밖에 안되므로 2단계가 사실상

마지막이 된다.)

For 문을 돌면서 하나씩 검사해 나간다. A에서

A로 가는 방법은 물론 0이며 A에서 B로 가는

방법또한 변함없이 한가지 뿐이다. A에서 C로

갈 때 두 가지 방법이 생기는데 첫째, A >C 의

방법으로 값은 11이다. 둘째, A에서 B를 거쳐

C로 가는 방법으로 값은 3 + 4 = 7이다. 따라

서 왼쪽 표에서 11을 7로 바꾼다.

D

-2A B C

A 0 4 7

B 5 0 2

C 3 ∞ 0

For 문을 돌면서 계속 검사해 나간다. B에서 A

로 갈 때 두 가지 방법이 생기는데 첫째, B >A

의 방법으로 값은 6이다. 둘째, B에서 C를 거쳐

A로 가는 방법으로 값은 2 + 3 = 5이다. 따라

서 왼쪽 표에서 6을 5로 바꾼다. B에서 B로는

0이며 B에서 C로 가는데는 2가지 방법이 생겼

지만 또다른 방법은 6 + 11 = 17이나 되므로

현재의 것이 최적이기에 바꾸지 않는다.

v2 -> v3(방법①)가 된다.

 플로이드 알고리즘은 우선 옆의 그래프를 W 라고 하는 행렬(배열)에 저장한 후에, 플로이

드 최단경로 알고리즘(함수)을 실행하면서 최단경로를 D라는 행렬(배열)에다가 점점 최단경

로의 값으로 갱신해 나간다.

예를 들기 위한 그래프를 가져왔다.

모든 정점을 가상적으로 줄을 세워서 나열한다.

Page 9: 2012 Ds 05

D

-2A B C

A 0 4 7

B 5 0 2

C 3 7 0

C에서 A로 가는 방법은 변함없이 한가지 뿐

이며, 드디어 C에서 B로 갈 수 있게 되었다.

C >A >B 의 방법을 통해 3 + 4 = 7의 값으

로 B에 도달할 수 있다. 따라서 왼쪽 표의 무

한대를 7로 수정한다.C에서 C로 가는 방법은

물론 0이다.

D

-2A B C

A 0 4 7

B 5 0 2

C 3 7 0

완성된 표이다.

아래는 플로이드 알고리즘을 구현하는 소스이다.

플로이드 알고리즘 소스에서 쓰일 텍스트 파일(floyd_array.txt)

4

0 2 99 99

5 0 4 3

-1 99 0 4

99 1 7 0

#include <stdio.h>

#include <malloc.h>

int** make_array(int n); // 배열 동적 메모리 할당

void read_array(int *D[], int n); // 파일에서 배열 값 읽음

void print_array(int* D[], int n); // 배열값 출력

int** floyd(int* W[],int* P[], int n); // 플로이드 최단 경로 알고리즘

void print_path(int *P[], int q, int r); // 최단경로 출력

FILE* file; // 배열을 읽기 위한 파일 식별자

int main()

{

int q, r, n;

int **D, **W, **P; // 프로그램에서 쓰일 배열

file = fopen("floyd_array.txt", "r"); // 배열 값을 담고 있는 파일

if(file == NULL) {

perror("File open error");

return 0;

}

fscanf(file, "%d", &n); // 배열의 행과 열의 수 N x N

W = make_array(n); // N x N 행렬을 메모리에 할당

read_array(W, n); // 파일로 부터 W 행렬에 값 입력

Page 10: 2012 Ds 05

printf("Ployd Shortest Path Algorithm\nW weight array :\n");

print_array(W, n); // W 행렬 출력

printf("\n");

P = make_array(n+1); // 경로 출력을 원활히 하기 위해 0번째 행과 열을 쓰지 않음

D = floyd(W, P, n); // 플로이드 최단 경로 알고리늠 실행해서 결과 행렬을 D 행렬에 입력

printf("D shortest array :\n");

print_array(D, n); // 결과 D 행렬 출력(K값에 따른 배열 출력시 생략)

printf("\nStart vertex(1 ~ %d) : ", n); // 출력할 경로의 시작, 끝점 입력

scanf("%d", &q);

printf("End vertex(1 ~ %d) : ", n);

scanf("%d", &r);

if ( q <= 0 || r <= 0 || q > n || r > n) { // 시작,끝을 벗어나면 에러

printf("Error : Plz, Input number 1 ~ %d\n", n);

return 0;

}

printf("\n"); // 입력받은 시작점과 끝점 간의 최단 경로 출력

printf("Shortest Path from v%d to v%d\n : v%d -->", q, r, q);

print_path(P, q, r);

printf(" v%d\n", r);

printf("Distance : %d\n", D[q-1][r-1]); // 거리 출력

free(D); // 사용한 메모리 해제

free(W);

free(P);

fclose(file); // 열었던 파일을 닫아줌

return 0;

}

int** make_array(int n) // n x n 행렬의 메모리를 할당후 배열의 시작 주소를 반환

{

int i;

int** array;

array = (int**)malloc(sizeof(int*)*n);

for(i = 0; i < n; i++)

array[i] = (int*)malloc(sizeof(int)*n);

return array;

}

void read_array(int *D[], int n) // 파일로 부터 행렬 입력 받음

{

int i, j;

for(i = 0; i < n; i++) {

for( j = 0; j < n; j++) {

fscanf(file, "%d", &D[i][j]);

}

}

}

void print_array(int* D[], int n) // 지정된 배열의 값을 출력

{

Page 11: 2012 Ds 05

int i, j;

for (i = 0; i < n; i++) {

for(j = 0; j < n; j++) {

if( D[i][j] == 99 ) // 99 이면 무한대의 의미로 oo를 표시

printf(" oo");

else // 무한대가 아니면 원래 값을 출력

printf("%3d", D[i][j]);

}

printf("\n");

}

}

int** floyd(int* W[], int* P[], int n)

{

int i, j, k;

int** D;

D = make_array(n); // 반환할 D 행렬을 만듬

for (i = 0; i < n; i++)

for(j = 0; j < n; j++)

D[i][j] = W[i][j]; // D 행렬 = W 행렬

for (k = 0; k < n; k++) {

for (i = 0; i < n; i++)

for(j = 0; j < n; j++)

if (D[i][j] > D[i][k] + D[k][j]) {

P[i+1][j+1] = k+1; // 경로를 구하기 위한 배열

D[i][j] = D[i][k] + D[k][j];

}

//printf("\nK = %d :\n", k+1); // K의 값 출력

//print_array(D, n); // K의 변화에 따른 배열 값 출력

}

return D;

}

void print_path(int *P[], int q, int r) // P행렬을 이용해서 q부터 r까지의 이동 경로를 출력

{

if(P[q][r] != 0) {

print_path(P, q, P[q][r]);

printf(" v%d -->", P[q][r]);

print_path(P, P[q][r], r);

}

}

Page 12: 2012 Ds 05

실행결과

>>

다르게 구현된 소스를 모아봤다.

void _back(int i, int j){

if(!b[i][j]){

printf("%d ", i);

return;

}

_back(a, b[i][j]);

_back(b[i][j], b);

}

void back(int i, int j){

_back(i,j);

printf("%d\n", j);

}

void floyd(){

for(int i = 1; i <= N; i++){

for(int j = 1; j <= N; j++){

d[i][j] = w[i][j]; //k=0

}

}

for(int k = 1; k <= N; k++){

for(int i = 1; i <= N; i++){

for(int j = 1; j <= N; j++){

Page 13: 2012 Ds 05

if(d[i][j] > d[i][k] + d[k][j]){

d[i][j] = d[i][k] + d[k][j];

b[i][j] = k;

}

}

}

}

}

----------------------------------------

#include <iostream.h>

#define N 8 /* 정점의 수 */

#define M 9999

int a[N+1][N+1]={{0,0,0,0,0,0,0,0,0}, /* 인접행렬 */

{0,0,1,7,2,M,M,M,M},

{0,1,0,M,M,2,4,M,M},

{0,7,M,0,M,M,2,3,M},

{0,2,M,M,0,M,M,5,M},

{0,M,2,M,M,0,1,M,M},

{0,M,4,2,M,1,0,M,6},

{0,M,M,3,5,M,M,0,2},

{0,M,M,M,M,M,6,2,0}};

void path(int q,int r) // 경로 찾기

{

if(p[q][r] != 0)

{

path(q,p[q][r]);

cout << "v" << p[q][r] << "-->";

path(p[q][r],r);

}

}

void main()

{

int i,j,k;

//플로이드 알고리즘

for(k=1;k<=N;k++)

Page 14: 2012 Ds 05

for(i=1;i<=N;i++)

for(j=1;j<=N;j++)

if(a[i][j] > a[i][k] + a[k][j])

a[i][j] = a[i][k] + a[k][j];

path(1,8); //노드1에서 노드8까지의 최단경로

/* 출력

for(i=1;i<=N;i++)

{

for(j=1;j<=N;j++)

cout << a[i][j] << " ";

cout << endl;

}

*/

}

여기까지 폴로이드 알고리즘에 대해 알아보았고 아래에는 최단 경로를 구할 수 있는 다른

방법인 다익스트라 알고리즘에 대해 알아볼 것이다.

< 차이점 >

두개의 정점 사이의 최단 경로를 찾는 Dijkstra의 알고리즘은 시간 복잡도 O(n2) 이므로,

모든 정점쌍의 최단 경로를 구하려면 Dijkstra의 알고리즘을 n번 반복해야 하므로 전체 복

잡도는 O(n3) 이 된다. 한번에 모든 정점 간의 최단 경로를 구하는 Floyd의 알고리즘은 3

중 반복문이 실행되므로 시간 복잡도는 O(n3) 이다.

Dijkstra 알고리즘이 Greedy한 방법이었다면 Floyd 알고리즘은 동적계 획법이 들어간

보다 고차원적인 알고리즘이다.

Greedy 방법은 최적해를 구하는 문제를 풀기위해서 행하는 각 단계에서의 선택시, 그 단계

에서의 상황만 고려해서 최선의 선택을 한다. Dijkstra 알고리즘의 시간복잡도가 O(n^2) 인

데 반해 플로이드 알고리즘은 O(n^3)이다. 이것만 봐서는 Floyd 알고리즘이 더 느릴 것이

라고 생각 하기 쉽다.Dijkstra 알고리즘이 한번의 루프를 돌 때마다 하는 일이 많다보니(복

잡하다보니) 실제로는 Floyd가 빠른 경우가 상당히 많다.

Page 15: 2012 Ds 05

[다익스트라 알고리즘]

- 다익스트라(Dijkstra, Edsger  W.  Dijkstra) 알고리즘은 A에서 B까지의 최단거리와 경

로를 구한다. 시간 복잡도는 O(N^2)이다.

  원리는 그리디적으로 돌게 되지만 항상(거리가 음수(-) 이거나 싸이클이 없는 그래프 일

때) 최적해가 나온다. 다익스트라 알고리즘으로는 최대거리는 구할 수 없다.

  일단 시작점에서 갈 수 있는 곳을 다 보고.- 모든 값은 N개의 테이블에 저장을 한다-  

그중 가장 작은 값을 선택하고 그 값에서 다시 모든 경우를 본다. 그리고 지금까지 갱신했

던 최적해보다 작다면 갱신한다.

다익스트라의 최단경로 알고리즘은 갈망법(greedy method)으로 접근한다. 갈망법은 눈 앞

에 보이는데로 계속 최선의 선택을 하는 것이다. 거시적 관점보다는 인간과 같이 눈 앞에

노인 상황에 탐욕적으로 접근한다고 하여 붙여진 이름이다.

다익스트라의 최단경로 알고리즘에서는 전제조건이 붙는다. 정점(vertex)과 정점사이의

간선(edge)을 거치는 비용을 가중치(weight)라고 하는데 이 가중치는 음의 값이어서는 안된

다. 만약 음의 값이 있었다간 그 구간을 무한히 돌게 된다.

최단경로 알고리즘의 기본 아이디어는 다음과 같다.

0) 시작점을 '최단경로를 찾은 정점 리스트'에 담아둔다.

1) '최단경로를 찾은 정점 리스트'에 들어있는 정점으로부터 인접한 정점들중 가장 가중치가

적은 정점을 하나를 탐색한다. 이는 곧 해당 정점까지의 최단경로이다.

2) 리스트내에서의 정점들을 기준으로 가장 가중치가 적은 정점을 다시 탐색한다. 이때 발

견되는 정점까지의 가중치는 직전 정점까지의 가중치 + 발견된 정정까지의 가중치로 계산

된다. (엄밀히 말해 1.과 2.는 같다)

3) 가능한 모든 정점을 연결할 때까지 반복한다.

>>요약

시작 정점부터 가장 가까운 정점을 선택하고 이를 리스트에 담아 기억하고 있다가 이들 정

점들로부터 시작점으로부터의 가중치가 적은 간선 하나만 선택한다. 계속 반복해서 모든 경

로를 잇게 되면 시작점부터 모든 점까지의 최단경로는 모두 구해지는 것.

Page 16: 2012 Ds 05

그림으로 보면

Page 17: 2012 Ds 05

그림을 보시는 바와 같이 최단경로 알고리즘은 시작점으로 부터 모든 정점까지의 최단거리

Page 18: 2012 Ds 05

를 계산할 수 있다. 시작점으로 부터 가장 가까운 거리의 정점을 우선 찾고 시작점부터 발

견된 정점까지의 거리(가중치)를 별도로 기억한다(distance). 그리고 발견된 모든 정점들로

부터 가장 가중치가 적은 정점을 계속해서 찾아나감으로서 최적의 경로를 모두 탐색하는 것

이다. 핵심은 가중치가 누적되고, 가장 적은 가중치의 정점을 계속해서 찾아가는 이른바 갈

망법인 것이다.

다익스트라 알고리즘을 c로 구현한 소스이다.

graph.dat 파일

01.702.0 1 503.0 3 1004.1 2 505.1 4 2006.2 4 1007.3 4 2008.3 5 3009.6 3 20

소스코드 파일

001.#include<stdio.h>002.#include<stdlib.h>003.#include<limits.h>004. 005. 006.#define BIG_INT 8192007.#define MAX_SIZE 10008.#define FOUND 1009.#define NOT_FOUND -1010. 011. 0 1 2 . v o i d l o a d _ g r a p h _ f r o m _ f i l e ( c h a r * filename, int graph[][MAX_SIZE], int* n);013.int choose(int* distance, int* found, int n);014.void shortest_path(int v, int graph[][MAX_SIZE], int* distance, int* found,int* pre_vertex, int n);015.void print_path(int* pre_vertex, int n);016. 017. 018.int main(int argc, char** argv) {019.int graph[MAX_SIZE][MAX_SIZE];020.int distance[MAX_SIZE];021.int found[MAX_SIZE];022.int pre_vertex[MAX_SIZE];023. 024. 025.int v;026.int n; // num of vertex027. 028. 029.load_graph_from_file("graph.dat", graph, &n);030.shortest_path(0, graph, distance, found, pre_vertex, n);

Page 19: 2012 Ds 05

031.print_path(pre_vertex, n);032.return 0;033.}034. 035. 0 3 6 . v o i d l o a d _ g r a p h _ f r o m _ f i l e ( c h a r * filename, int graph[][MAX_SIZE], int* n) {037.FILE* fp = fopen(filename, "r");038.int v, w; // start vertex, end vertex039.int weight;040. 041. 042.int i, j;043. 044. 045.if (fp == NULL) {046.printf("file not found exception.");047.exit(1);048.}049. 050.fscanf (fp, "%d", n);051.if (*n >MAX_SIZE) { printf("too many vertex"); exit(1);}052. 053.for (i=0; i<*n; ++i) {054.for (j=0; j<*n; ++j) {055.graph[i][j] = BIG_INT;056.}057.}058. 059. 060.while (!feof(fp)) {061.fscanf(fp,"%d %d %d",&v, &w,&weight);062.graph[v][w] = weight;063.}064.}065. 066. 067.int choose(int* distance, int* found, int n) {068.int i;069.int min = INT_MAX;070.int minpos = -1;071. 072. 073.for (i=0; i<n; ++i) {074.if (found[i] >NOT_FOUND) continue;075.if (distance[i] >= min) continue;076. 077.min = distance[i];078.minpos = i;079.}080.return minpos;081.}082. 083. 084.void shortest_path(int v, int graph[][MAX_SIZE], int* distance, int* found, int* pre_vertex, int n) {085.int i, u, w;

Page 20: 2012 Ds 05

086.//int pre_vertex;087. 088. 089.// init090.for (i=0; i<n; ++i) {091.distance[i] = graph[v][i];092.found[i] = NOT_FOUND;093.if ( distance[i] >0 &&distance[i] <BIG_INT) pre_vertex[i] = v;094.else pre_vertex[i] = -1;095.}096. 097. 098.distance[v] = 0;099.found[v] = FOUND;100. 101.for (i=0; i<n-1; ++i) {102.u = choose(distance, found, n); //가장 가까운 정점을 찾는다.103.found[u] = FOUND;104. 105. 106.for (w=0; w<n; ++w) { // 최단 경로상의 정점들로부터 인접정점들까지의 모든 distance를 계산한다.107.if (found[w] >NOT_FOUND) continue;108.if (distance[u] + graph[u][w] >= distance[w]) continue;109. 110. 111.distance[w] = distance[u] + graph[u][w];112.pre_vertex[w] = u;113.}114.}115.}116. 117. 118.void print_path(int* pre_vertex, int n) {119.int i;120.int cv; // current vertex121.for (i=0; i<n; ++i) {122.cv = i;123. 124. 125.printf("shortest path to %d :[%d]", cv, cv);126.while (pre_vertex[cv] >= 0) {127.printf("<-[%d]", pre_vertex[cv]);128.cv = pre_vertex[cv];129.}130.printf("\n");131.}132. 133. 134.}

Page 21: 2012 Ds 05

[참고 URL]

http://hisexperience.tistory.com/entry/Floyd-%EC%95%8C%EA%B3%A0%EB%A6%AC%E

C%A6%98

http://blog.naver.com/petasvia?Redirect=Log&logNo=40001825831

http://internet512.chonbuk.ac.kr/datastructure/graph/graph2_5_15.htm

http://thinkberry.co.kr/3376

2. 본론

[초안]

#include<stdio.h>

#include<windows.h>

#include<string.h>

#include<stdlib.h>

void powerset(int *array_ind, int scan_size,int size,int scan_ind)

{

int i,j=0;

array_ind[scan_ind]++;

for(i=scan_ind+1;i<scan_size;i++)

{

array_ind[i]=array_ind[i-1]+1;

}

if(array_ind[scan_ind]==size-(scan_size-1-scan_ind) && scan_ind!=0)

{

powerset(array_ind,scan_size,size,scan_ind-1);

}

}

int main()

{

FILE *point;

char a,arr[100];

int m=0;

int x=1,y=0;/*구조체에 넣을떄 위치 탐색하는 변수*/

Page 22: 2012 Ds 05

int i,j;

char save[100][100];

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

char *in;

int *ind;

int nodem;

int k=0;

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

point=fopen("C://file.txt"/*이부분에 읽어오고자하는 텍스트 파일이 저장된 곳 써주

면 되*/,"r");

if(point==NULL)

{

return 1;

}

for(i=0;i<100;i++)

{

arr[i]='\0';

}

while(1) //텍스트 파일에 저장되어 있는 문장들을 배열에 넣어서 저장.

{

a=fgetc(point);

arr[m]=a;

//printf("%d %d",n,m);

if(a==EOF)

{

break;

}

else if(a=='\n')

{

continue;

}

else

{

m++;

}

}

fclose(point);

system("PAUSE");

Page 23: 2012 Ds 05

//printf("n=%d m=%d",n,m);

for(i=0;i<m;i++) //배열에 저장된 문자들을 소문자로 변환하여 다시 배열에 저장.

{

printf("%c",arr[i]);

//printf("\n%d",m);

}

printf("\n%d\n",m);

save[0][0]=arr[0];

for(i=1;i<m;i++)

{

save[x][y]=arr[i];

if(i%3==0)

{

x++;

y=0;

}

else

{

y++;

}

}

for(i=0;i<x;i++)

{

for(j=0;j<3;j++)

{

printf("%c",save[i][j]);

}

printf("\n");

}

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

in=(char*)malloc(sizeof(char)*x);

printf("\n");

for(i=0;i<x-1;i++)

{

Page 24: 2012 Ds 05

in[i]=save[i+1][2];

printf("i=%d %c",i,in[i]);

}

printf("\n");

nodem=atoi(save[0]);

ind=(int*)malloc(sizeof(int)*(nodem-1));

for(j=0;j<nodem-1;j++)// 동적할당 된 배열 초기화

{

ind[j]=k;

printf("%d",ind[j]);

k++;

}

printf("\n");

while(1)

{

for(j=0;j<nodem-1;j++)

{

printf("[%c]",in[ind[j]]);

}

powerset(ind,nodem-1,x-1,nodem-2); //,c는 입력받은 숫자,i-1은 원소

의 갯수

printf("\n");

if(ind[0]==x-nodem+1)

{

break;

}

}

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

}

초안 설명 : 노드를 모두 방문하려면 최소노드의 개수 중에 노드의 -1만큼의 간선을

가야한다. 그래서 path와 가중치의 정보를 저장한 배열 중, 예를 들면 노드가 4개일

경우 3개의 간선을 선택하는 경우를 모두 뽑아 따로 저장 후 그 중 모든 노드를 가는

경우 중에서 최단거리 3개를 뽑아 출력한다.

Page 25: 2012 Ds 05

[최종안]

#include <stdio.h>

#include <malloc.h>

int** make_array(int n); // 배열 동적 메모리 할당

void read_array(int *D[], int n); // 파일에서 배열 값 읽음

void print_array(int* D[], int n); // 배열값 출력

int** floyd(int* W[],int* P[], int n); // 플로이드 최단 경로 알고리즘

void print_path(int *P[], int q, int r); // 최단경로 출력

int count_total_line( FILE* fp );

FILE* file; // 배열을 읽기 위한 파일 식별자

struct pl

{

int path[20];

int weight;

};

int main()

{

struct pl arr[100],temp;

int i=0,j=0,m=0,v;

int q, r, n;

int line=0;

int **D, **W, **P; // 프로그램에서 쓰일 배열

file = fopen("floyd_array.txt", "r"); // 배열 값을 담고 있는 파일

if(file == NULL) {

perror("File open error");

return 0;

}

for(m=0;m<100;m++)

{

for(v=0;v<20;v++)

{

arr[m].path[v]='\0';

}

arr[m].weight='\0';

}

int c=0;

int yyy;

int rr;

Page 26: 2012 Ds 05

int rrr;

fscanf(file, "%d", &n); // 배열의 행과 열의 수 N x N

for(yyy=n-1;yyy>0;yyy--)

{

c=c+yyy;

}

// printf("yyy=%d\n",c);

int **rudfh;

rudfh = (int**)malloc(sizeof(int*)*c);

for(yyy = 0; yyy < 20; yyy++)

rudfh[yyy] = (int*)malloc(sizeof(int)*yyy);

W = make_array(n); // N x N 행렬을 메모리에 할당

read_array(W, n); // 파일로 부터 W 행렬에 값 입력

// printf("Ployd Shortest Path Algorithm\nW weight array :\n");

// print_array(W, n); // W 행렬 출력

// printf("\n");

P = make_array(n+1); // 경로 출력을 원활히 하기 위해 0번째 행과 열을 쓰지

않음

D = floyd(W, P, n); // 플로이드 최단 경로 알고리늠 실행해서 결과 행렬을 D

행렬에 입력

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

/* for(i=0;i<n;i++)

{

for(j=0;j<n;j++)

{

arr[m].weight=D[i][j];

m++;

}

}

for(i=0;i<n*n;i++)

{

printf("%d ",arr[i].weight);

}

printf("\n");*/

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

// printf("D shortest array :\n");

// print_array(D, n); // 결과 D 행렬 출력(K값에 따른 배열 출력시 생략)

Page 27: 2012 Ds 05

// printf("\nStart vertex(1 ~ %d) : ", n); // 출력할 경로의 시작, 끝점 입력

// scanf("%d", &q);

// printf("End vertex(1 ~ %d) : ", n);

// scanf("%d", &r);

for(rr=1;rr<n;rr++)

{

j=0;

for(rrr=rr+1;rrr<=n;rrr++)

{

q=rr;

r=rrr;

if ( q <= 0 || r <= 0 || q > n || r > n) { // 시작,끝을 벗어나면 에러

//printf("Error : Plz, Input number 1 ~ %d\n", n);

}

//printf("\n"); // 입력받은 시작점과 끝점 간의 최단 경로 출력

arr[i].path[j]=q;

j++;

// printf("Shortest Path from v%d to v%d\n : v%d -->", q, r, q);

if(P[q][r] != 99)

{

arr[i].path[j]=P[q][r];

j++;

}

else

{

}

arr[i].path[j]=r;

j++;

// printf(" v%d\n", r);

arr[i].weight= D[q-1][r-1];

i++;

j=0;

// printf("Distance : %d\n", D[q-1][r-1]); // 거리 출력

Page 28: 2012 Ds 05

}

}

//노드의 경로와 가중치를 저장한 구조체를

for(m=0;m<i;m++)

{

for(v=0;v<i;v++)

{

if(arr[m].weight<arr[v].weight)

{

temp=arr[v];

arr[v]=temp;

temp=arr[m];

}

}

}

for(m=0;m<3;m++)

{

printf("노드의 경로 : ");

for(v=0;arr[m].path[v]!='\0';v++)

{

printf("v%d ",arr[m].path[v]);

}

//printf("%d",v);

printf("가중치 : %d\n",arr[m].weight);

}

free(D); // 사용한 메모리 해제

free(W);

free(P);

fclose(file); // 열었던 파일을 닫아줌

}

Page 29: 2012 Ds 05

int** make_array(int n) // n x n 행렬의 메모리를 할당후 배열의 시작 주소를 반환

{

int i;

int y;

int** array;

array = (int**)malloc(sizeof(int*)*n);

for(i = 0; i < n; i++)

array[i] = (int*)malloc(sizeof(int)*n);

for(i=0;i<n;i++)

{

for(y=0;y<n;y++)

{

array[i][y]=99;

}

}

return array;

}

void read_array(int *D[], int n) // 파일로 부터 행렬 입력 받음

{

int i, j;

char cc,dd;

int nn;

while(1)

{

fscanf(file,"%c",&cc);

if(cc=='\n')

fscanf(file,"%c",&cc);

fscanf(file,"%c",&dd);

fscanf(file,"%d",&nn);

if((cc>64 && cc<91) && (dd>64 && dd<91))

{

D[cc-65][dd-65]=nn;

// printf("%d\n",D[cc-65][dd-65]);

}

else

{

break;

}

Page 30: 2012 Ds 05

}

// for(i = 0; i < n; i++) {

// for( j = 0; j < n; j++) {

// fscanf(file, "%d", &D[i][j]);

// }

// }

}

void print_array(int* D[], int n) // 지정된 배열의 값을 출력

{

int i, j;

for (i = 0; i < n; i++) {

for(j = 0; j < n; j++) {

if( D[i][j] == 99 ) // 99 이면 무한대의 의미로 oo를 표시

printf(" oo");

else // 무한대가 아니면 원래 값을 출력

printf("%3d", D[i][j]);

}

printf("\n");

}

}

int** floyd(int* W[], int* P[], int n)

{

int i, j, k;

int** D;

D = make_array(n); // 반환할 D 행렬을 만듬

for (i = 0; i < n; i++)

for(j = 0; j < n; j++)

D[i][j] = W[i][j]; // D 행렬 = W 행렬

for (k = 0; k < n; k++) {

for (i = 0; i < n; i++)

for(j = 0; j < n; j++)

if (D[i][j] > D[i][k] + D[k][j]) {

P[i+1][j+1] = k+1; // 경로를 구하기 위한 배열

D[i][j] = D[i][k] + D[k][j];

}

//printf("\nK = %d :\n", k+1); // K의 값 출력

//print_array(D, n); // K의 변화에 따른 배열 값 출력

}

return D;

Page 31: 2012 Ds 05

}

/*void print_path(int *P[], int q, int r) // P행렬을 이용해서 q부터 r까지의 이동 경로를

출력

{

if(P[q][r] != 99) {

//print_path(P, q, P[q][r]);

printf("v%d->", P[q][r]);

//print_path(P, P[q][r], r);

}

}*/

[실행결과]

최종 발표 때 교수님의 요구조건을 이행하지 못하였습니다. 그러므로 그전에 완료된 결

과소스를 최종안으로 두었습니다. ㅜ..

Page 32: 2012 Ds 05

3. 결론

[시간복잡도]

->플로이드 알고리즘을 사용

했으므로 3중for문이 들어가기

때문에

결과 : O()이다.