2. 과제수행일지
조원소개
소 속 C1 조 조장 박태원
자료조사 김무경, 고충욱
프로그래밍 김정훈, 김종진
과제수행기
14일 16 시간
간
주 제
연구제목 최단경로 찾기
APSP 알고리즘을 통해 최단경로를 구하는 프로그램을 작성하고 그 원리를
연구배경
이해함으로서 실생활에 적용할 수 있다.
참 고 자 료
참고 서적 C 로 쓴 자료구조론 (저자 : 이석호 ) - 교보문고
http://adnoctum.tistory.com/165 : 다익스트라 알고리즘
http://en.wikipedia.org/wiki/Shortest_path_problem : SP(Shortest Path) 알고리즘
정리(영문)
참고 URL http://ko.wikipedia.org/wiki/%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-
%EC%99%80%EC%85%9C_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98
: 플로이드 와셜 알고리즘 소스
http://alg.pknu.ac.kr/wordpress/floyd-warshall-algorithm/ : 플로이드-와셜 알고리즘 개요
과제의 수행
첫째날 2012년 5월 29일 화요일
회의 주제 역할분담 및 토의
조장 : 박태원
자료 조사 : 김무경, 고충욱
회의 내용
프로그래밍 : 김정훈, 김종진
위와 같이 역할분담을 실시하였으며 각자가 최단거리 산출 알고리즘에 대해 조사한
3. 내용을 정리했습니다. 다익스트라 알고리즘을 이용해 문제를 해결할 수 있을지에
대한 이야기를 나눴습니다.
다익스트라 알고리즘
- 하나의 출발점에서 모든 도착점들 사이의 최단경로를 산출한다.
- O(n^2)의 형태를 가진다.
알고리즘 개요
1. 출발점으로 설정된 노드를 중심으로 인접한 노드들을 자식노드로 추가한다.
2. 새로 생겨난 자식노드들에 대해 인접한 노드들을 다시 아래쪽에 추가한다.
3. 트리 내부에 중복된 노드들이 존재할 경우, 출발점으로부터 가장 작은
weight 값을 가진 노드를 제외한 노드들을 삭제한다.
4. 더 이상 추가시킬 노드가 없을때까지 2 와 3 을 반복한다.
(단, 서로간에 사이클을 생성하는 노드간의 weight 값에 음수가 들어가면 작동하지
않을 수도 있다.)
다익스트라 알고리즘을 이용해 문제를 해결할 수 있을지에 대해 논의했지만
반성 APSP(All Pairs Shortest Path)와는 크게 연관이 없었던 이유로 다른 알고리즘에 대해
각자가 조사해 오기로 논의했습니다.
둘째날 2012년 5월 31일 목요일
회의주제 APSP(All Pairs Shortest Path), 플로이드-워셜 알고리즘
APSP 가 무엇인지에 대해 개념을 정리하고, 이를 해결할 방법을 찾는 것이 중점을
두고 조사한 결과를 조원들과 이야기하고 어떤 방법으로 프로그램을 작성할 수
있을지에 대해 논의했습니다.
APSP(All Pairs Shortest Path) : 모든 쌍에 대한 최단경로, n 개의 정점이 주어졌을 때
모든 정점의 쌍인 Vi – Vj(단, i ≠ j)가 가진 최단경로들을 이야기한다.
회의내용 플로이드-워셜 알고리즘
- 모든 노드에 대한 최단거리 패스를 구해낸다.
- O(n^3)의 형태를 하고 있으나 한번의 루프때마다 하는 일이 적어 실직적인 속도도
다른 알고리즘에 비해 빠른 경우가 많다.
알고리즘 소스
for(int k = 1; k <= N; k++){
4. for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
if(d[i][j] > d[i][k] + d[k][j]){
d[i][j] = d[i][k] + d[k][j];
b[i][j] = k;
}
}
}
}
- 모든 노드에서 다른 노드로 향하는 각각의 경로를 확인해나가며 새로운 경로(d[i][k]
+ d[k][j])가 기존의 경로(d[i][j])가 가지던 weight 값보다 작은 값을 가진 경로일 경우
이를 갱신해 나가는 형태를 띄고 있습니다. 새로운 경로가 기존의 경로보다
weight 값이 작은 경우 배열 b[i][j]에는 새로운 패스의 경유 노드인 k 가 저장됩니다.
플로이드-워셜 알고리즘의 소스의 내용을 따라 이차배열 형태의 방향그래프 지도를
만들고, 파일로부터 정보를 읽어 오는 방법에 대해서 조원들과 함께 논의했습니다.
플로이드-워셜 알고리즘을 통해 구해진 각 노드에 대한 최단경로들을 weight 값
반성 순으로 정렬한 후 출력하는 방법을 찾지 못했습니다. 이차배열에 각 노드 사이의
간선을 입력받는 방법과 함께 조사해 이를 해결할 방법을 찾기로 했습니다.
셋째날 2012년 6월 5일 화요일
회의주제 프로그램 초안 확인, 개량안 제시
#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); // 배열에 들어간 간선간의 weight 값을 출력
5. int** floyd(int* W[],int* P[], int n); // 플로이드최단경로알고리즘
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 AlgorithmnW 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("n"); // 입력받은 시작점과 끝점간의 최단경로를 출력
free(D); // 사용한 메모리를 해제
free(W);
6. 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) // 정해진 배열의 값을 출력
7. {
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]) {
8. 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;
}
2 차배열의 안에 저장된 각 노드사이의 최단거리를 저장할 수 있었지만 그 경로와
회의내용
최단패스의 weight 합을 출력할 방법을 찾기 위해 논의했습니다.
● 노드와 간선을 입력받을 시 입력받은 내용을 배열의 형태로 입력하지 못했음
● 노드간의 최단거리 패스 사이에 둘 이상의 노드가 들어가 있어도 하나 이상
문제점
출력되지 않음
● 각 노드의 최단거리 패스의 weight 값의 합을 구하지 못함.
결과 발표
#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); // 배열에 들어간 간선간의 weight 값을 출력
소스
int** floyd(int* W[],int* P[], int n); // 플로이드최단경로알고리즘
FILE* file; // 배열을 읽기 위한 파일의 식별자
int main()
{
9. 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 AlgorithmnW 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("n"); // 입력받은 시작점과 끝점간의 최단경로를 출력
free(D); // 사용한 메모리를 해제
free(W);
free(P);
fclose(file); // 열었던 파일을 닫아줌
return 0;
}
10. 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++) {
11. 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 의 값을 출력
12. //print_array(D, n); // K 의 변화에 따른 배열값을 출력
}
return D;
}
노드 K 를 경유하는 노드 A 와 B 의 연결을 찾는다.
↓
알고리즘 노드 A 와 노드 B 가 가진 기존의 연결이 가진 weight 값과 노드 K 를 경유하는
연결이 가진 weight 값을 비교해서 전자가 더 작은 값이 나온다면 최단거리 경로의
개요
weight 값을 갱신한다.
↓
그래프가 가진 모든 노드를 순회하며 위의 과정을 반복한다.
각각의 노드 쌍이 가진 최단경로의 거리 값을 구하는 부분은 구현 가능했지만
최단경로의 출력 부분을 거의 구현하지 못했습니다. 문제의 해결에 있어서 문제의
최종 반성
파악이 늦었던 점이 가장 큰 문제였다고 생각합니다. 이런 경우가 발생하는 것을
줄이기 위해서 앞으로 좀 더 문제파악에 노력을 기울여야겠다고 생각합니다.