최단경로를 구하는 알고리즘... Floyd Shortest Path

 아무래도 최근에 가장 많은 시간을 할애하고 있는 것이 이 알고리즘 수업의 과제이다 보니, 자꾸만 포스팅이 이런 내용만 쓰게 된다. 이번의 과제는 알고리즘 자체는 어려운 것이 아니였고, 교수님이 그려주신 그래프를 보고, 플로이드의 최단 경로를 구하는 알고리즘으로 구하는 것이였다.
  옆의 그림이 가중치 방향성 그래프인데, 예를 들면, v3에서 v1으로 가는 가중치(거리/비용)가 -1 이고, v1에서 v3로 직접 가는 방법은 없다는 의미이다. v1에서 v3 로 가기 위해서는 v1 -> v2 -> v3 의 방법①과 v1 -> v2 -> v4 -> v3 의 방법②가 존재하는데 이런 여러 경로 중에서 가중치의 합이 가장 적은 경로를 구하는 것이다. 방법① 경로는 -1 + 2 + 4 = 5가 되고, 방법② 경로는 -1 + 2 + 3 + 7 = 11이 된다. 그러므로 최종적으로 최단 경로는 v1 -> v2 -> v3(방법①)가 된다.

 플로이드 알고리즘은 우선 옆의 그래프를 W 라고 하는 행렬(배열)에 저장한 후에, 플로이드 최단경로 알고리즘(함수)을 실행하면서 최단경로를 D라는 행렬(배열)에다가 점점 최단경로의 값으로 갱신해 나간다(자세한 설명은 "플로이드 알고리즘" 이라고만 검색해도 많이 나오니 참고). 어째든 이번 과제는 이 "최단경로를 구하는 플로이드 알고리즘"이였다.
플로이드 알고리즘 소스에서 쓰일 텍스트 파일(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 행렬에 값 입력
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) // 지정된 배열의 값을 출력
{
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);
}
}

실행 결과는 대충 이런 분위기이다.
 사실 플로이드 알고리즘보다는 포인터를 이용한 NxN 배열을 다루는 것과 파일에서 배열값을 읽어오는데 좀 더 중점을 두어서 짜보았다. 계속 쓰다 보니 조금은 포인터를 다루는 방법을 알것도 같은 기분도 든다.
    참고자료
  • 알고리즘(Foundatations of Algorithms Using C++ Pseudocode) - Neapolitan, Naimipour 저 / 도경구 역

by sakuragi | 2007/04/12 22:28 | :: R space :: 과제 | 트랙백(2) | 덧글(16)

트랙백 주소 : http://sakuragis.egloos.com/tb/3300749
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from [B]logging!! at 2007/04/19 02:23

제목 : 플로이드 알고리즘
출처 : http://internet512.chonbuk.ac.kr/datastructure/graph/graph2_5_16.htm 동작원리 자체는 매우 간단하다. A에서 B로 갈 수 있는 경로가 있고, 또한 B에서 C로 갈 수 있는 경로가 있다고 한다면 결국 A에서 C로 갈 수 있는 경로가 있다고 할 수 있다. 만약 A에서 C로 '직접' 갈 수 있는 경로가 원래 존재했는데 직접 갈 때의 비용이 B를 거쳐 C로 가는 것보다 많이 든다고 하자. ......more

Tracked from His Experience at 2009/12/12 05:45

제목 : Floyd 알고리즘
원문 아무래도 최근에 가장 많은 시간을 할애하고 있는 것이 이 알고리즘 수업의 과제이다 보니, 자꾸만 포스팅이 이런 내용만 쓰게 된다. 이번의 과제는 알고리즘 자체는 어려운 것이 아니였고, 교수님이 그려주신 그래프를 보고,플로이드의 최단 경로를 구하는 알고리즘으로 구하는 것이였다. 옆의 그림이 가중치 방향성 그래프인데, 예를 들면, v3에서 v1으로 가는 가중치(거리/비용)가 -1 이고, v1에서 v3로 직접 가는 방법은 없다는 의미이다. v1......more

Commented by LinDol at 2007/04/13 11:16
난왜 학교 다닐때 이런걸 안한건지 ㅜ_ㅜ
Commented by 환상경 at 2007/04/13 19:02
후.... 이건 또 뭐래요 ~_~ 봐도 전혀 이해가 안되는군요 쩝;;;;

ps. 이글루스 수정된건가요?
Commented by sakuragi at 2007/04/13 22:15
LinDol, 저도 안하고 졸업할 뻔 했죠. 어쩌다 보니 듣게 되었네요. :)
환상경, 이번 건 좀 머리 아팠음. 안쓰던 포인터를 난무하다보니.. 에고공.. ㅜㅡ
이글루스 글쓰기가 수정되었네요. 리눅스에서는 윈도우와는 다른 방식으로 이미지 & 파일 업로드를 처리하는 모양이예요 :)
Commented by nidev at 2007/04/15 09:05
알고리즘은 재밌는 것이죠. 요즘 저도 알고리즘에 푹 빠진.
Commented by sakuragi at 2007/04/15 18:57
nidev, 재미는 있는 것 같은데... 어려워요.. T_T
Commented at 2007/05/30 11:09
비공개 덧글입니다.
Commented by sakuragi at 2007/05/30 15:26
멋쟁이, 문제의 의미가 명확하게 와닿지 않네요. cost(u,v)는 그 경로상의 이음선들 중에 가중치가 가장 큰 값으로 정의 한다는 것이, 단지 이미 정해진 가중치가 큰 값이라는 의미인지. 주어진 가중치들을 가지고 u와 v 상이에서 가장 가중치가 큰 경로를 구하라는 의미인지가 명확하지 않네요.
만약의 후자(u와 v 사이에서 가장 가중치가 큰 경로를 구하라는 의미)라면 결과적으로 모든 노드쌍에 대해서 최소 cost가 아닌 최대 cost를 구하게 됩니다. 모든 노드쌍에 대해 최소 cost를 구하려고 한다면 cost(u, v) 역시 그 경로 상의 최소 cost가 되야 하는게 아닐까요?

제 생각에는 그냥 지정된 그래프의 가중치, 즉 cost(u, v)는 u, v 사이의 최대값이라고 이미 정의 내린 상태에서, 큰값이다 작은 값이다에 연연하는게 아니고, 그래프에 있는 가중치를 그대로 이용해서 모든 경로에 최소값을 구하라는 의미인 것 같네요.

만약 이미 정의된 것이 큰값이다라는 얘기라면, 결과적으로는 이 소스 그대로 써도 될 것 같습니다. 아니라면 제가 느끼기에는 지금 써주신 문제의 내용이 완전히 상반되는 내용을 담고 있다고 생각되어서 다른 해결책을 찾지 못하겠네요.
Commented by heroant at 2010/03/29 13:15
배열 P의 메모리 해제는 free(P)만 해도 되나요?
*P의 모든 인덱스를 모두 메모리 해제 한 후에
free(P) 해줘야 하는 걸로 알고 있는데요...
Commented by sakuragi at 2010/04/06 18:11
그런가요? 제 실력이 미천해서 그런 부분에는 약합니다.
지적 감사합니다. ( __)
Commented by 후잉, at 2010/11/08 22:00
저 이거 해서 실행해봣는데 오류는 없는데 실행하면
쫌 이상하게 되요 ㅠㅠ 실행해보시고 틀린거 고쳐서
수정해주시면 안되까요? ㅠㅠ 알고싶어요,, 어디가 틀렸는지
Commented by sakuragi at 2010/11/09 18:33
실행도 안되는 소스를 올렸겠습니까? ^^
Commented by relilau at 2012/02/23 23:23
print_path가 이해가 안 됩니다...
설명 부탁드립니다. 왜 k+1을 P에다가 넣는지 등등... floyd 알고리즘 자체는 이해했지만 경로 출력하는 것이 이해가 되지 않다는 겁니다.
Commented by sakuragi at 2012/02/27 01:28
알고리즘(Foundatations of Algorithms Using C++ Pseudocode) - Neapolitan, Naimipour 저 / 도경구 역
이 책에 출력하는 부분의 알고리즘도 나오는지는 정확하게 기억이 나진 않습니다만,
가능하면 책을 한번 참고해서 보세요. 만약 책에 나오는 내용이면 자세히 설명되어 있을 겁니다(아마도).
Commented by 어려워 at 2013/12/03 18:09
실행 창 누르면 왜 텍스트 창에 제가 입력한 값이 아닌 이상한 841451 이런식의 숫자가 나오는지 모르겠어요
Commented by sakuragi at 2013/12/03 18:51
네, 저도 잘 모르겠네요.
Commented by ksm9781 at 2016/06/09 00:42
fopen부분에서 계속 No such file or directory 라고 에러가 떠요ㅠㅠㅠㅠ
소스를 그대로 복사했고, txt파일도 바탕화면에 저장해놓았는데..
따로 위치를 명시해줘야 하나요??ㅠㅠ

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶

:+: sakuragi's Steam :+: