자료구조실습 수업

[Project - 기말 발표]

2022. 12. 4. 15:41
 
 
 
1. 문제 소개
 

→ 문제 소개

“쓰레기 집수 풀"에 모인 쓰레기들을 비워내기 위해 Kruskal 알고리즘을 이용해 최소 비용으로 모든 노드를 연결하기

사진 출처: https://www.theguardian.com/world/2019/nov/07/bubble-barrier-launched-to-keep-plastics-out-of-oceans

 

→ 기술 소개 및 문제 선정 이유

 바다의 플라스틱(쓰레기)는 3분의 2이상이 강이나 하천에서부터 흘러온다.

이를 막기 위해서는 더이상 쓰레기가 흘러 바다로 오지 않도록 하천 쪽에 쓰레기 수거망을 설치해야한다.

42,000kg의 쓰레기를 모을 수 있는 쓰레기 배를 여러대 곳곳에 배치한다. 또 그 옆쪽으로는 거품 장벽을 배치해 쓰레기 조각이 기포에 걸려 표면으로 밀려나고, 그곳에서 물살에 의해 집수 풀(쓰레기 배)로 운반될 수 있도록 하는 기술이다.

https://www.theguardian.com/world/2019/nov/07/bubble-barrier-launched-to-keep-plastics-out-of-oceans

이렇게 쓰레기 배(집수 풀)에 모인 쓰레기들은 모아서 다시 육지로 수거해 가야 한다.

이때 모든 노드를 최소 비용으로 거치며 모든 쓰레기 집수 풀의 쓰레기를 수거해갈 수 있도록 Kruskal 알고리즘을 사용했다.

원래는 다익스트라 알고리즘을 통해 해결하려 하였으나 다익스트라 알고리즘으로는 모든 노드를 통과할 수는 없기 때문에 Kruskal 알고리즘을 사용하게 되었다.

 

 

→ 사용할 언어

C++을 사용해 문제를 풀 예정이다.

 
 
 
2. 사용한 데이터 소개
 

→ 데이터 소개

쓰레기 소각장은 각 지역마다 있고 서울에도 양천구, 강남구, 노원구, 마포구에 있기 때문에 한 곳을 정해야 했다.

따라서 나는 노원구에 모일 쓰레기들만 고려할 예정이고, 노원구 근처의 쓰레기 집수 풀로부터 최단거리를 구했다.

데이터도 그 근처의 노원구, 성북구 등의 데이터만 사용하였다.

한 사람은 비교적 가까운 거리 안에서 사진을 찍었을 것이라고 가정하고 한 사람의 csv 파일의 경도 위도 값을 평균 내어 그 곳에 하나의 쓰레기 집수 풀을 설치한다고 생각하였다.

총 10명의 데이터를 사용했고, 그 값들을 평균 내어 위도 경도 값을 총 10개를 갖게 되었다.

이 열 곳에 각각 하나의 쓰레기 집수 풀이 있다고 가정했다.

따라서 열 곳에서의 최단 경로를 구하면 되는 것이다.

(1, 2, 4, 8, 11, 12, 14, 15, 16, 20 학생의 데이터를 사용하였다.)

 

 

→ 코드 설명

- 각 노드

각 노드는 임의로 0부터 9까지의 숫자를 매겨 0~9의 수를 가지도록 했다.

- 가중치 값

다익스트라 알고리즘을 사용하기 위해서는 가중치 값을 정해야 했다.

가중치 값은 두 지점을 이동하는데 걸리는 비용을 고려하기 위해 두 지점의 거리로 설정했다.

거리로 가중치를 설정하면 이동하는 데 걸리는 시간, 비용(기름값) 등을 모두 고려할 수 있기 때문이었다.

그냥 두 점 사이의 값을 계산하면 n.xx...형태의 매우 작은 값이 나왔기 때문에 결과값에 x10을 한 후 소수점은 제거해 주었다.

- 코드 

1단계) 입력할 가중치를 txt 파일에 저장해 이 파일을 불러와 그래프에 가중치 정보를 저장하도록 한다.

2단계) txt파일의 정보를 입력 받아 그래프 만들기

3단계) Kruskal 알고리즘을 실행하여 모든 노드 연결하기

    -> 인접 행렬로 가중치 그래프를 만드는 class 를 작성한다.

    -> 위의 그래프를 만들기 위해 노드, 가중치 값을 입력받는 class 를 작성한다.

    -> Kruskal 알고리즘에서는 cycle이 있는지 검사해야 하므로

        이를 위한 VertexSets class 를 작성한다.

    -> Kruskal 알고리즘에서 힙 정렬 개념을 사용해 노드를 정렬하고

        노드들을 최소 비용으로 연결하므로 이를 위한 HeapNode, MinHeap class 를 작성한다.

    -> main 함수를 통해 파일을 읽게 한 후 그래프를 만들어

         Kruskal 알고리즘을 실행하도록 한다.

#include <iostream>
#include <fstream>
using namespace std;
#define MAX_VTXS 100
#define INF 9999              // INF이면 간선없음

// 가중치 그래프를 표현하는 클래스
class AdjMatGraph {
protected:
	int    size;
	char   vertices[MAX_VTXS];
	int    adj[MAX_VTXS][MAX_VTXS];
public:
	AdjMatGraph() { reset(); }
	char getVertex(int i) { return vertices[i]; }
	int  getEdge(int i, int j) { return adj[i][j]; }
	void setEdge(int i, int j, int val) { adj[i][j] = val; }
	bool isEmpty() { return size == 0; }
	bool isFull() { return size >= MAX_VTXS; }

	void reset() {
		size = 0;
		for (int i = 0; i < MAX_VTXS; ++i)
			for (int j = 0; j < MAX_VTXS; ++j)
				setEdge(i, j, 0);
	}
	void insertVertex(char name) {
		if (!isFull()) vertices[size++] = name;
		else cout << "ERROR : 그래프 정점 개수 초과" << endl;
	}
	void insertEdge(int u, int v) {
		setEdge(u, v, 1);
		setEdge(v, u, 1);
	}
	void display() {
		cout << size << endl;
		for (int i = 0; i < size; ++i) {
			cout << getVertex(i) << " ";
			for (int j = 0; j < size; ++j)
				cout << getEdge(i, j) << "\t";
			cout << endl;
		}
	}
};

// 가중치 그래프를 표현하는 클래스
class WGraph : public AdjMatGraph {
public:
	void insertEdge(int u, int v, int weight) {
		if (weight > INF) weight = INF;
		setEdge(u, v, weight);
	}
	bool hasEdge(int i, int j) { return (getEdge(i, j) < INF); }
	void load(string filename) {
		ifstream fp(filename);
		if (fp.is_open()) {
			int n, val;
			fp >> n;              // 정점의 전체 개수
			for (int i = 0; i < n; i++) {
				char str[80];
				int val;
				fp >> str;        // 정점의 이름
				insertVertex(str[0]);        // 정점 삽입
				for (int j = 0; j < n; j++) {
					fp >> val;    // 간선 정보
					insertEdge(i, j, val);     // 간선 삽입          
				}
			}
		}
		else cout << "File can not be found !" << endl;
		fp.close();
	}
};

// 힙에 저장할 노드 클래스
#define MAX_ELEMENT	200
// 정점 집합 클래스 (Union-Find 연산)
class VertexSets {
	int parent[MAX_VTXS];	// 부모 정점의 id
	int nSets;				// 집합의 개수
public:
	VertexSets(int n) : nSets(n) {
		for (int i = 0; i < nSets; i++)
			parent[i] = -1;			// 초기에 모든 정점이 고유의 집합에 속함
	}
	bool isRoot(int i) { return parent[i] < 0; }// -1이면 root
	int findSet(int v) {	    				// v가 속한 집합을 찾아 root 반환
		while (!isRoot(v)) v = parent[v];		// v가 속한 집합의 루트를 찾음
		return v;
	}
	void unionSets(int s1, int s2) {	// 집합 s1을 집합 s2와 합침
		parent[s1] = s2;  				// s1의 parent를 s2로 설정
		nSets--;          				// 2개의 집합을 합쳐서 집합 개수는 1 감소
	}
};

class HeapNode {
	int	key;    // Key 값: 간선의 가중치
	int	v1;     // 정점 1
	int	v2;     // 정점 2
public:
	HeapNode(int k = 0, int u = 0, int v = 0) : key(k), v1(u), v2(v) { }
	void setKey(int k) { key = k; }
	void setKey(int k, int u, int v) {
		key = k;   v1 = u;  v2 = v;
	}
	int getKey() { return key; }
	int getV1() { return v1; }
	int getV2() { return v2; }
	void display() {
		cout << "\t" << "(" << v1 << "-" << v2 << ") -- " << key << endl;
	}
};
class MinHeap {
	HeapNode node[MAX_ELEMENT];
	int size;
public:
	MinHeap() : size(0) { }
	bool isEmpty() { return size == 0; }
	bool isFull() { return size == MAX_ELEMENT - 1; }

	HeapNode& getParent(int i) { return node[i / 2]; }
	HeapNode& getLeft(int i) { return node[i * 2]; }
	HeapNode& getRight(int i) { return node[i * 2 + 1]; }

	// 삽입 함수
	void insert(int key, int u, int v) {
		if (isFull()) return;
		int i = ++size;
		while (i != 1 && key < getParent(i).getKey()) {
			node[i] = getParent(i);
			i /= 2;
		}
		node[i].setKey(key, u, v);
	}
	// 삭제 함수
	HeapNode remove() {
		if (isEmpty()) return NULL;

		HeapNode root = node[1];
		HeapNode last = node[size--];

		int parent = 1;
		int	child = 2;

		while (child <= size) {
			if (child < size
				&& getLeft(parent).getKey() > getRight(parent).getKey())
				child++;

			if (last.getKey() <= node[child].getKey()) break;

			node[parent] = node[child];
			parent = child;
			child *= 2;
		}
		node[parent] = last;
		return root;
	}
};
class WGraphMST : public WGraph {
public:
	void Kruskal() {     // kruskal의 최소 비용 신장 트리 프로그램
		MinHeap heap;
		// 1. 오름차순정렬 (heap sort)
		for (int i = 0; i < size - 1; i++)
			for (int j = i + 1; j < size; j++)
				if (hasEdge(i, j))
					heap.insert(getEdge(i, j), i, j); // 모든 간선 삽입
		VertexSets set(size);		// size개의 집합을 만듦
		int edgeAccepted = 0;		// 선택된 간선의 수
		while (edgeAccepted < size - 1) { 	// 4.(n-1)개의 edge가 삽입될때까지
			HeapNode e = heap.remove(); // 2.가장 작은 edge 선택
			int uset = set.findSet(e.getV1()); // v1이 속한 집합의 루트 반환
			int vset = set.findSet(e.getV2()); // v2가 속한 집합의 루트 반환
			if (uset != vset) {          // 3.사이클 생기지 않으면 MST삽입
				cout << "간선 추가 : " << getVertex(e.getV1()) << " - " << getVertex(e.getV2()) << " (비용 : " << e.getKey() << ")" << endl;
				set.unionSets(uset, vset);       // 두개의 집합을 합함.
				edgeAccepted++;
			}
		}
	}
};

int main() {

	WGraphMST g;
	g.load("fin_graph.txt");
	cout << "MST 크루스칼 알고리즘" << endl;
	g.Kruskal();

	return 0;
}

 

 

3. 결과 및 효과(유형 효과, 또는 무형 효과)
 

→ 코드 실행 결과

모든 노드를 최소 비용으로 연결할 수 있었다.

 

→ 유형 효과

최소한의, 필요한 거리만큼만 이동해 쓰레기를 수거함으로써 교통비, 이동 비용 등이 절감 된다.

Kruskal 알고리즘을 이용했기 때문에 최소 비용으로 움직일 수 있는 효율적인 방법이다.

 

 

→ 무형 효과

새로운 기술들을 도입하며 자라나는 미래의 개발자에게 창의력을 자극할 수 있다.

IT시대답게 IT기술로 환경 문제를 해결할 수 있다.

하천의 쓰레기들이 해양으로 흘러 가 해양쓰레기가 되어 더 큰 문제 발생하는 것을 막을 수 있다.

(여기서 더 큰 문제라 하면 해양쓰레기로 인한 자연 생물 파괴, 환경 오염 등을 해결하는 데 들어가는 비용을 아낄 수 있다는 의미)

 

 

 

 

 

(발표자료)

FIN.pdf
0.17MB

 

 

 

 

'자료구조실습 수업' 카테고리의 다른 글

[13주차 과제]  (0) 2022.12.04
[12주차 과제]  (0) 2022.11.27
[9주차 과제]  (0) 2022.11.06
[MID] 중간 프로젝트  (0) 2022.10.16
[4주차 과제]  (0) 2022.10.02