ݺߣ

ݺߣShare a Scribd company logo
그래프와 그래프 알고리즘
(Graph & Graph Algorithm)
Wider planet Tech Talk
2015.10.22
조은우
그래프(Graph)란 무엇인가?
• 현상이나 사물들을 정점(Vertex)으로 표현하고,
정점들간에 관계를 간선(Edge)으로 표현한 것
• 도로 시스템
• 도시와 도시간의 항공편
• 트리(Tree)도 그래프의 한 종류
그래프 용어(1)
• 정점(Vertex)
: 정점, 꼭지점, 노드라고도 부르며, 그래프 알고리즘의 핵심
• 간선(Edge)
: 정점들 간의 관계를 나타냄.
간선이 방향을 가지면 유향 그래프(Directed Graph)
• 가중치(Weight)
: 한 정점에서 다른 정점으로 가는데 발생하는 비용
• 경로(Path)
: 간선으로 연결된 정점들을 순서대로 나열한 것
• 순환(Cycle)
: 유향 그래프에서 순환은 시작 정점과 마지막 정점이 같은 경로.
순환이 없는 그래프는 Acyclic graph.
유향그래프가 순환을 갖지 않으면 Directed acyclic graph
그래프 용어(2)
G = (V, E)
그래프 G는 N개의 정점의 집합 V와 간선들의 집합 E로 구성
코드로 추상화(ADT)한 그래프
• Graph 클래스: 비어있는 새 그래프 객체를 생성
• addVertex(vert): Vertex클래스의 인스턴스(vert)를 인자로
전달해서 그래프에 정점을 추가
• addEdge(fromVert, toVert, weight): 인접한 2개의 정점을
잇는 간선을 추가, 마지막 인자는 가중치(선택)
• getVertex(vertKey): vertKey 를 이름으로 갖는 정점을 찾음
• getVertices(): 그래프안에 있는 모든 정점들의 리스트를 반환
그래프의 표현
인접 행렬(Adjacency Matrix)
• 두개의 정점이 간선에 의해 서로
연결되어 있으면 인접하다
(adjacent)
• 정점v에서 정점w까지의 간선의 가
중치가 각 셀에 저장됨
그래프의 표현
인접 리스트(Adjacency List)
• 그래프의 모든 정점을 기준 리스트
로 유지하고, 각 정점들마다 인접
한 정점들을 리스트로 연결
• 인접행렬과 달리 존재하지 않는 간
선은 표현상에 나타나지 않음
인접 행렬(Adjacency Matrix)
장점
• 간단해서 이해하기 쉬우며, 간선의 존재여부를 즉각 알 수
있음
• 밀도가 아주 높은 그래프를 표현하기에 적합
단점
• 행렬을 준비하는 과정에서 정점의 개수(n)의 제곱만큼의 공
간과 시간이 필요함
• 밀도가 낮은 그래프는 빈 공간이 많이 생기기 때문에 적합
하지 않음
인접 리스트(Adjacency List)
장점
• 공간이 간선의 총 개수에 비례하는 양만큼만 필요하므로 낭비가 없음
• 밀도가 낮은 그래프를 표현하는데 적합
• 특정 정점을 기준으로 연결되어 있는 모든 간선을 쉽게 찾을 수 있음
단점
• 모든 정점에 간선이 존재하는 경우 리스트를 만드는데 많은 비용이
발생
• 정점들간에 간선의 유무를 확인하기 위해 리스트를 차례로 훓어야함
<?php
class Vertex
{
public function __construct($key)
{
$this->id = $key;
$this->connect_to = array();
}
public function addNeighbor($neighbor, $weight = 0)
{
$this->connect_to[$neighbor->getId()] = array('neighbor' => $neighbor, 'weight' =>
$weight);
}
public function getConnections()
{
return array_keys($this->connect_to);
}
public function getId()
{
return $this->id;
}
public function getWeight($neighbor)
{
return $this->connect_to[$neighbor->getId()]['weight'];
}
}
PHP 클래스로 정점의 인접 리스트 구현
4 5
3
6
단어 사다리 문제
(The Word Ladder Problem)
• ‘이상한 나라의 엘리스’의 작가 ‘루이스캐럴’
발명
• 시작 단어에서 도착 단어까지 한글자씩 바꿔
가면서 도달
• 각 단계마다 모두 의미가 있는 단어가 되어야
함
단어 사다리 그래프 만들기
• 각각의 단어들이 정점(Vertex)
• 무향 그래프(Undirected Graph)이며 가중치를 갖지 않음
• 한글자만 다른 두 단어는 서로 인접하여, 간선(Edge)으로 연결됨
단어 사다리 그래프 구현
1. 모든 정점들마다 다른 정점과 비교
• 단점: 비교하는 횟수는 단어 개수에 제곱
2. 단어의 한글자만 가린 라벨을 단 단어 버켓을 만들어 단어들
을 분류
단어 사다리 그래프 구현<?php
require_once('Graph.php');
function buildGraph($word_file)
{
$container = array();
$graph = new Graph();
$f = fopen($word_file, "r");
if ($f) {
while (($word = fgets($f)) !== false) {
$container = setBucketContainer(trim($word));
}
fclose($f);
} else {
throw new Exception('파일을 읽을 수 없습니다.');
}
$bucket_keys = array_keys($container);
foreach ($bucket_keys as $bucket) {
foreach ($container[$bucket] as $word1) {
foreach ($container[$bucket] as $word2) {
if ($word1 != $word2) {
$graph->addEdge($word1, $word2);
}
}
}
}
return $graph;
}
단어 사다리 그래프 구현(2)
function setBucketContainer($word)
{
$container = array();
$word_length = strlen($word);
for ($i = 0; $i < $word_length; $i++) {
$bucket = substr($word, 0, $i) . '_' . substr($word, $i + 1, ($word_length - 1) - $i);
if (array_key_exists($bucket, $container)) {
array_push($container[$bucket], $word);
} else {
$container[$bucket] = array($word);
}
}
return $container;
}
너비 우선 탐색
(Breath First Search)
• 탐색과정을 트리로 표현하면 이해하기 쉬움
• 루트(root)노드를 시작으로 같은 거리에 있는 자식 노드들을 탐색
• 부모 노드는 자식 노드들은 전임자(Predecessor)로 등록하고,
현재까지의 누적 거리를 저장
너비우선탐색(BFS) 구현function bfs($start_vertex)
{
$start_vertex->setDistance(0);
$start_vertex->setPredcessor(false);
$queue = array();
array_push($queue, $start_vertex);
while (count($queue) > 0) {
$current_vertext = array_shift($queue);
$neighbor = $current_vertext->getConnections();
foreach ($neighbor as $nbr_vertext) {
if ($nbr_vertext['neighbor']->getColor() == 'white') {
$nbr_vertext['neighbor']->setColor('gray');
$nbr_vertext['neighbor']->setDistance($current_vertext->getDistance() +
1);
$nbr_vertext['neighbor']->setPredecessor($current_vertext);
array_push($queue, $nbr_vertext['neighbor']);
}
}
$current_vertext->setColor('black');
}
}
bfs($word_radder->getVertex('fool'));
Github
$ git clone https://github.com/jonnung/word_ladder_graph
감사니다.

More Related Content

그래프(Graph)와 그래프 알고리즘(단어사다리 문제 해결)

  • 1. 그래프와 그래프 알고리즘 (Graph & Graph Algorithm) Wider planet Tech Talk 2015.10.22 조은우
  • 2. 그래프(Graph)란 무엇인가? • 현상이나 사물들을 정점(Vertex)으로 표현하고, 정점들간에 관계를 간선(Edge)으로 표현한 것 • 도로 시스템 • 도시와 도시간의 항공편 • 트리(Tree)도 그래프의 한 종류
  • 3. 그래프 용어(1) • 정점(Vertex) : 정점, 꼭지점, 노드라고도 부르며, 그래프 알고리즘의 핵심 • 간선(Edge) : 정점들 간의 관계를 나타냄. 간선이 방향을 가지면 유향 그래프(Directed Graph) • 가중치(Weight) : 한 정점에서 다른 정점으로 가는데 발생하는 비용 • 경로(Path) : 간선으로 연결된 정점들을 순서대로 나열한 것 • 순환(Cycle) : 유향 그래프에서 순환은 시작 정점과 마지막 정점이 같은 경로. 순환이 없는 그래프는 Acyclic graph. 유향그래프가 순환을 갖지 않으면 Directed acyclic graph
  • 4. 그래프 용어(2) G = (V, E) 그래프 G는 N개의 정점의 집합 V와 간선들의 집합 E로 구성
  • 5. 코드로 추상화(ADT)한 그래프 • Graph 클래스: 비어있는 새 그래프 객체를 생성 • addVertex(vert): Vertex클래스의 인스턴스(vert)를 인자로 전달해서 그래프에 정점을 추가 • addEdge(fromVert, toVert, weight): 인접한 2개의 정점을 잇는 간선을 추가, 마지막 인자는 가중치(선택) • getVertex(vertKey): vertKey 를 이름으로 갖는 정점을 찾음 • getVertices(): 그래프안에 있는 모든 정점들의 리스트를 반환
  • 6. 그래프의 표현 인접 행렬(Adjacency Matrix) • 두개의 정점이 간선에 의해 서로 연결되어 있으면 인접하다 (adjacent) • 정점v에서 정점w까지의 간선의 가 중치가 각 셀에 저장됨
  • 7. 그래프의 표현 인접 리스트(Adjacency List) • 그래프의 모든 정점을 기준 리스트 로 유지하고, 각 정점들마다 인접 한 정점들을 리스트로 연결 • 인접행렬과 달리 존재하지 않는 간 선은 표현상에 나타나지 않음
  • 8. 인접 행렬(Adjacency Matrix) 장점 • 간단해서 이해하기 쉬우며, 간선의 존재여부를 즉각 알 수 있음 • 밀도가 아주 높은 그래프를 표현하기에 적합 단점 • 행렬을 준비하는 과정에서 정점의 개수(n)의 제곱만큼의 공 간과 시간이 필요함 • 밀도가 낮은 그래프는 빈 공간이 많이 생기기 때문에 적합 하지 않음
  • 9. 인접 리스트(Adjacency List) 장점 • 공간이 간선의 총 개수에 비례하는 양만큼만 필요하므로 낭비가 없음 • 밀도가 낮은 그래프를 표현하는데 적합 • 특정 정점을 기준으로 연결되어 있는 모든 간선을 쉽게 찾을 수 있음 단점 • 모든 정점에 간선이 존재하는 경우 리스트를 만드는데 많은 비용이 발생 • 정점들간에 간선의 유무를 확인하기 위해 리스트를 차례로 훓어야함
  • 10. <?php class Vertex { public function __construct($key) { $this->id = $key; $this->connect_to = array(); } public function addNeighbor($neighbor, $weight = 0) { $this->connect_to[$neighbor->getId()] = array('neighbor' => $neighbor, 'weight' => $weight); } public function getConnections() { return array_keys($this->connect_to); } public function getId() { return $this->id; } public function getWeight($neighbor) { return $this->connect_to[$neighbor->getId()]['weight']; } } PHP 클래스로 정점의 인접 리스트 구현 4 5 3 6
  • 11. 단어 사다리 문제 (The Word Ladder Problem) • ‘이상한 나라의 엘리스’의 작가 ‘루이스캐럴’ 발명 • 시작 단어에서 도착 단어까지 한글자씩 바꿔 가면서 도달 • 각 단계마다 모두 의미가 있는 단어가 되어야 함
  • 12. 단어 사다리 그래프 만들기 • 각각의 단어들이 정점(Vertex) • 무향 그래프(Undirected Graph)이며 가중치를 갖지 않음 • 한글자만 다른 두 단어는 서로 인접하여, 간선(Edge)으로 연결됨
  • 13. 단어 사다리 그래프 구현 1. 모든 정점들마다 다른 정점과 비교 • 단점: 비교하는 횟수는 단어 개수에 제곱 2. 단어의 한글자만 가린 라벨을 단 단어 버켓을 만들어 단어들 을 분류
  • 14. 단어 사다리 그래프 구현<?php require_once('Graph.php'); function buildGraph($word_file) { $container = array(); $graph = new Graph(); $f = fopen($word_file, "r"); if ($f) { while (($word = fgets($f)) !== false) { $container = setBucketContainer(trim($word)); } fclose($f); } else { throw new Exception('파일을 읽을 수 없습니다.'); } $bucket_keys = array_keys($container); foreach ($bucket_keys as $bucket) { foreach ($container[$bucket] as $word1) { foreach ($container[$bucket] as $word2) { if ($word1 != $word2) { $graph->addEdge($word1, $word2); } } } } return $graph; }
  • 15. 단어 사다리 그래프 구현(2) function setBucketContainer($word) { $container = array(); $word_length = strlen($word); for ($i = 0; $i < $word_length; $i++) { $bucket = substr($word, 0, $i) . '_' . substr($word, $i + 1, ($word_length - 1) - $i); if (array_key_exists($bucket, $container)) { array_push($container[$bucket], $word); } else { $container[$bucket] = array($word); } } return $container; }
  • 16. 너비 우선 탐색 (Breath First Search) • 탐색과정을 트리로 표현하면 이해하기 쉬움 • 루트(root)노드를 시작으로 같은 거리에 있는 자식 노드들을 탐색 • 부모 노드는 자식 노드들은 전임자(Predecessor)로 등록하고, 현재까지의 누적 거리를 저장
  • 17. 너비우선탐색(BFS) 구현function bfs($start_vertex) { $start_vertex->setDistance(0); $start_vertex->setPredcessor(false); $queue = array(); array_push($queue, $start_vertex); while (count($queue) > 0) { $current_vertext = array_shift($queue); $neighbor = $current_vertext->getConnections(); foreach ($neighbor as $nbr_vertext) { if ($nbr_vertext['neighbor']->getColor() == 'white') { $nbr_vertext['neighbor']->setColor('gray'); $nbr_vertext['neighbor']->setDistance($current_vertext->getDistance() + 1); $nbr_vertext['neighbor']->setPredecessor($current_vertext); array_push($queue, $nbr_vertext['neighbor']); } } $current_vertext->setColor('black'); } } bfs($word_radder->getVertex('fool'));
  • 18. Github $ git clone https://github.com/jonnung/word_ladder_graph

Editor's Notes

  • #3: 어떤 현상이나 사물들간에 관계를 표현할때 정점과 정점들 사이를 잇는 간선으로 표현하는 자료구조를 '그래프'라고 합니다. 실생활에서 그래프로 표현할 수 있는 상황은 '도로 시스템', '항공편', '인터넷 연결'등이 있겠습니다. 현재 보시는 슬라이드에 있는 작은 그래프 그림과 위 실생활 예를 함께 생각해보면 바로 이해가 가실 것입니다. 지난 섹션에 '조정희'님이 발표 해주신 트리도 그래프의 한 종류 입니다. 차이점이 있다면 트리는 자료들간에 계층 관계를 표현하기 위한 것이고, 그래프는 원소들간에 관계를 표현하기 위한 자료 구조입니다.  바로 뒤에 용어 설명에서 나오지만 그래프만이 순환 이라는 구조를 갖고 있을 수 있습니다.
  • #7: 컴퓨터에서 그래프를 표현하는 방법을 알아보겠습니다. 그래프 표현법에는 크게 행렬을 이용하는 방법과 리스트를 이용하는 방법이 있습니다. 그중에서 가장 쉬운 방법중에 하나인 인접행렬을 이용하는 방법입니다. 그래프의 모든 정점들은 행렬의 행과 열로 표현되는데요, 정점 v0 에서 정점v1까지의 간선은 행v0과 열v1이 교차되는 셀에 가중치 5가 저장되는 것입니다.
  • #8: 인접리스트는 각 정점에 인접한 정점들을 리스트로 표현하는 방법 입니다. 그래프의 모든 정점을 기준 리스트로 유지하고, 각 정점들마다 인접한 정점들을 리스트로 연결합니다. 인접행렬과 달리 존재하지 않는 간선은 당연히 리스트로 표현되지 않습니다.
  • #9: 인접 행렬의 장점은 간단해서 이해하기 쉬우며, 노드와 노드간에 모든 연결을 한눈에 쉽게 알아볼 수 있습니다. 하지만 행렬을 준비하기 위해 정점의 개수의 제곱에 해당하는 행렬이 필요하며, 그만큼의 시간도 필요합니다. 그리고 간선의 개수가 적은, 즉 밀도가 낮은 그래프를 인접행렬로 표현하면 빈공간이 많이 생기게 됩니다. 예를들어 100만개의 정점을 정점을 가진 그래프가 200만개의 간선을 갖는다면 행렬의 총 셀은 1조개가 되지만 고작 200만개의 셀에만 가중치 값이 채워지기 때문에 공간 낭비가 심합니다. 따라서 인접행렬은 밀도가 아주 높은 그래프를 표현하기에 적합합니다.
  • #10: 인접행렬과 반대로 존재하는 간선만 리스트로 표현되기 때문에 공간낭비가 없습니다. 따라서 밀도가 낮은 그래프를 표현하는데 적합하다고 할 수 있습니다.  그리고 인접행렬이 전체적인 관계를 살펴보기 용이 하다면 인접리스트는 특정 정점을 대상으로 연결된 모든 간선을 쉽게 찾아볼 수 있다는 측면이 있습니다. 하지만 밀도가 높은 그래프의 경우 이런 부분도 단점으로 작용하는데요. 정점들간에 간선의 유무를 확인하기 위해 리스트를 쭉 훓어야 한다는 단점이 있습니다.
  • #13: 그래프로 단어간의 관계를 표현 해보겠습니다. 단어 사다리 그래프는 각각의 단어는 정점이 됩니다. 그리고 정점들 사이에 간선은 방향을 갖지 않는 무향 그래프 입니다. 그리고 가중치도 갖지 않습니다. 단어간에 간선은 서로 한글자씩 다르면 인접하다고 할 수 있습니다.
  • #14: 이 문제를 해결하기 위해 가장 먼저 떠올릴 수 있는 방법은 각 정점마다 다른 정점과 모두 비교하는 것 입니다. 하지만 단어의 개수가 엄청 많다면 이 방법도 많은 부하를 주게 됩니다. 또 한가지 방법은 단어의 한글자만 언더바로 가린 라벨을 가진 큰 단어 버켓을 만들어 단어들을 분류한 후 차례차례 비교하는 것입니다. 이것을 코드로 구현해 보도록 하겠습니다.
  • #15: 한줄에 한개의 단어가 있는 파일을 한줄씩 읽어들입니다. 단어 사다리의 사전 조건대로 모든 단어의 글자수는 같습니다.  단어의 첫글자부터 순서대로 블라인드, 즉 언더바로 치환한 문자를 키(key)로 사용합니다. 그 키를 컨테이너 배열 변수의 키로 단어를 추가 합니다. 모든 단어가 버켓으로 분리된 컨테이너에 저장이 되면, 이제 해당 버켓들을 하나씩 뒤져가며 그래프 객체의 간선으로 추가합니다.
  • #17: 이제 만들어진 그래프를 본격적으로 탐색하는 그래프 알고리즘을 알아보도록 하겠습니다. 너비우선탐색은 그래프를 탐색하기 위한 가장 쉬운 알고리즘 중에 하나 입니다.  특정 정점 S로부터 연결된 모든 정점들을 찾기 위해 간선들을 탐색합니다.  여기서 주목할 점은 S로부터 거리가 K+1 인 정점들을 찾기 전에 모든 K인 모든 정점들을 탐색한다는 것 입니다. 직관적인 이해를 돕기 위해 트리로 표현해보는게 가장 좋습니다.  이해를 돕기 위해 정점들의 색상은 흰색과 회색, 그리고 검정색을 사용해서 너비우선탐색이 진행되는 동안의 변화를 확인할 수 있습니다. 최초에 모든 정점들은 흰색으로 초기화 됩니다. 그리고 방문하게 되는 정점은 회색이 되고, 더이상 인접한 흰색 정점이 없을때는 검정색으로 표현합니다.