[TAOCP] 2.2.3 연결된 할당 - 위상정렬

47
[TAOCP] 2.2.3 연결된 할당 - 위상정렬 ohyecloudy http://ohyecloudy.com 아꿈사 http://cafe.naver.com/architect1.cafe 2011.04.23

Transcript of [TAOCP] 2.2.3 연결된 할당 - 위상정렬

Page 1: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

[TAOCP] 2.2.3 연결된 할당 - 위상정렬

ohyecloudy http://ohyecloudy.com

아꿈사 http://cafe.naver.com/architect1.cafe 2011.04.23

Page 2: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

부분순서

위상정렬

위상정렬 알고리즘 사용하는 자료구조

알고리즘

Page 4: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

이항 관계가 더 일반적 용어

책에 이항 관계에 대해 특별한 언급이 없다.

부분순서는 이항 관계 부분집합

Page 5: [TAOCP] 2.2.3 연결된 할당 - 위상정렬
Page 6: [TAOCP] 2.2.3 연결된 할당 - 위상정렬
Page 7: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

다시 책으로…

Page 8: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

부분순서 partial ordering

다음을 만족하는 이항관계

만일 𝑥 ≼ 𝑦이고 y ≼ 𝑧이면 𝑥 ≼ 𝑧. 추이성

만일 𝑥 ≼ 𝑦이고 y ≼ 𝑥이면 𝑥 = 𝑦. 반대칭성

𝑥 ≼ 𝑥. 반영성

Page 9: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

𝒙 ≺ 𝒚는 𝒙 ≼ 𝒚이고 𝒙 ≠ 𝒚

만일 𝑥 ≺ 𝑦이고 y ≺ 𝑧이면 𝑥 ≺ 𝑧. 추이성

만일 𝑥 ≺ 𝑦이면 𝑦 ⊀ 𝑥. 반대칭성

𝑥 ⊀ 𝑥. 비반영성

Page 10: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

부분순서

위상정렬

위상정렬 알고리즘 사용하는 자료구조

알고리즘

Page 11: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

위상 정렬 topological sorting

부분순서를 선형적인 순서로 배치

1

3

4

8

9

7

5

6

2

1 3 4 8 9 7 5 6 2

Page 12: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

부분순서

위상정렬

위상정렬 알고리즘 사용하는 자료구조

알고리즘

Page 13: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

+ 0 COUNT[k] TOP[k]

객체 k의 직접immediate 선행자 개수

객체 k의 직접 후행자 목록 시작을 가리키는 링크

※앞으로 표기를 간단히 한다. +, 0은 생략

Page 14: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

+ 0 SUC NEXT

객체 k의 직접 후행자

목록의 다음 항목

※앞으로 표기를 간단히 한다. +, 0은 생략

Page 15: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

2 1

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

7 ≺ 5, 3 ≺ 7, 9 ≺ 5

5 7 5

Page 16: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

왜 이런 구조?

알고리즘 단순화

COUNT 필드가 0인 노드 출력

직접 선행자가 없다

직접 후행자 COUNT 필드 감소

직접 선행자가 다 출력된 객체를 찾는다

Page 17: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

COUNT 필드가 0인 노드 검색

까다로움

대기열을 만듬

COUNT 필드가 0인 노드를 연결

COUNT 필드와 QLINK 필드 저장 위치는 같다

헷갈림을 방지하려고 QLINK로 표기

필드에 들어있는 값은 링크를 뜻함

Page 18: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

부분순서

위상정렬

위상정렬 알고리즘 사용하는 자료구조

알고리즘

Page 19: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

QLINK[0], COUNT[0]=QLINK[0], … COUNT[N]=QLINK[N]

P : 저장소 풀 안의 노드를 참조

F, R : 대기열 앞단과 뒷단을 가리키는 데 쓰는 정수 값

N : 출력해야 할 남은 객체 개수

Page 20: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

n 입력 받음.

foreach (1≤k≤n)

COUNT[k] ← 0, TOP[k] ← Λ

N ← n

Page 21: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

n = 9 가 입력됐다면 N = 9

Page 22: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

다음 𝑗 ≺ 𝑘를 얻는다.

없다면 T4로 고고

Page 23: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

𝑗 ≺ 𝑘에서

COUNT[k]를 1증가

P ⇐ AVAIL, SUC(P) ← k, NEXT(P) ← TOP[j], TOP[j] ← P

Page 24: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

N = 9 9 ≺ 2 가 입력됐다면

𝑗 ≺ 𝑘에서

COUNT[k]를 1증가

P ⇐ AVAIL, SUC(P) ← k, NEXT(P) ← TOP[j], TOP[j] ← P

Page 25: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

N = 9 9 ≺ 2 가 입력됐다면

𝑗 ≺ 𝑘에서

COUNT[k]를 1증가

P ⇐ AVAIL, SUC(P) ← k, NEXT(P) ← TOP[j], TOP[j] ← P

2

Λ

P

Page 26: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1

Λ

0 0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

0

Λ

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

N = 9 9 ≺ 2 가 입력됐다면

𝑗 ≺ 𝑘에서

COUNT[k]를 1증가

P ⇐ AVAIL, SUC(P) ← k, NEXT(P) ← TOP[j], TOP[j] ← P

2

Λ

Page 27: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 0

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

N = 9 9 ≺ 2, 3 ≺ 7, 7 ≺ 5, 5 ≺ 8, 8 ≺ 6 4 ≺ 6, 1 ≺ 3, 7 ≺ 4, 9 ≺ 5, 2 ≺ 8이 입력됐다면

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

Page 28: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

출력 대기열을 QLINK 필드를 통해 연결

R ← 0, QLINK[0] ← 0

foreach (1≤k≤n)

if COUNT[k] == 0 then QLINK[R] ← k, R ← k

F ← QLINK[0]

Page 29: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 0

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 0, R = 0, N = 9

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT R ← 0, QLINK[0] ← 0

foreach (1≤k≤n)

if COUNT[k] == 0 then QLINK[R] ← k, R ← k

F ← QLINK[0]

Page 30: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 0

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, R = 1, N = 9

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT R ← 0, QLINK[0] ← 0

foreach (1≤k≤n)

if COUNT[k] == 0 then QLINK[R] ← k, R ← k

F ← QLINK[0]

QLINK 구분이 쉽게 숫자에 밑줄

Page 31: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, R = 9, N = 9

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT R ← 0, QLINK[0] ← 0

foreach (1≤k≤n)

if COUNT[k] == 0 then QLINK[R] ← k, R ← k

F ← QLINK[0]

Page 32: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 9, N = 9

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT R ← 0, QLINK[0] ← 0

foreach (1≤k≤n)

if COUNT[k] == 0 then QLINK[R] ← k, R ← k

F ← QLINK[0]

Page 33: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 9, N = 9

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT R ← 0, QLINK[0] ← 0

foreach (1≤k≤n)

if COUNT[k] == 0 then QLINK[R] ← k, R ← k

F ← QLINK[0]

COUNT가 0인 것들 중 가장 먼저 나온 숫자가

F에 들어간다.

COUNT가 0인 숫자들은 QLINK로 이어져 있음

1 → 9

Page 34: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

F 출력

if F == 0

goto T8

else

N ← N – 1, P ← TOP[F]

Page 35: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 9, N = 9

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

F 출력, if (F == 0) { goto T8 } else { N ← N – 1, P ← TOP[F] }

출력 : 1

Page 36: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 9, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

F 출력, if (F == 0) { goto T8 } else { N ← N – 1, P ← TOP[F] }

출력 : 1

P

Page 37: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 1 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 9, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

F 출력, if (F == 0) { goto T8 } else { N ← N – 1, P ← TOP[F] }

출력 : 1

P 이 단계에서만 출력이 이루어짐

출력한 숫자 직접 후행자를 P로 지정하고 계속 진행

뒤에 단계에서 다음에

출력할 숫자를 F에 넣는다.

Page 38: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

loop

if P == Λ

goto T7

else

COUNT[SUC(P)] = COUNT[SUC(P)] – 1,

if COUNT[SUC(P)] == 0

QLINK[R] ← SUC(P), R ← SUC(P)

P ← NEXT(P)

Page 39: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 0 0 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 9, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

loop { if (P == Λ) {goto T7}

else { COUNT[SUC(P)]--,

if (COUNT[SUC(P)] == 0) {QLINK[R] ← SUC(P), R ← SUC(P)}

P ← NEXT(P) }

출력 : 1

P

Page 40: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 3 0 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 3, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

출력 : 1

P

loop { if (P == Λ) {goto T7}

else { COUNT[SUC(P)]--,

if (COUNT[SUC(P)] == 0) {QLINK[R] ← SUC(P), R ← SUC(P)}

P ← NEXT(P) }

Page 41: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 3 0 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 3, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

출력 : 1

P

loop { if (P == Λ) {goto T7}

else { COUNT[SUC(P)]--,

if (COUNT[SUC(P)] == 0) {QLINK[R] ← SUC(P), R ← SUC(P)}

P ← NEXT(P) }

Page 42: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 3 0 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 1, R = 3, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

출력 : 1

P

loop { if (P == Λ) {goto T7}

else { COUNT[SUC(P)]--,

if (COUNT[SUC(P)] == 0) {QLINK[R] ← SUC(P), R ← SUC(P)}

P ← NEXT(P) }

출력한 숫자의 직접 후행자들 COUNT를 감소

(COUNT는 직접 선행자 개수)

1 ≺ 3, 1 ≺ 5 가 있고 1을 출력했다면 3, 5의

직접 선행자 개수를 줄인다

출력한 숫자 뒷정리라 생각하면 됨

Page 43: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

T1. 초기화

T2. 다음 관계

T4. 0들을 찾는다

T3. 관계 기록

T5. 대기열 앞단 출력

T8. 공정 끝

T6. 관계들 삭제

T7. 대기열에서 제거

F ← QLINK[F], goto T5

Page 44: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 3 0 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 3, R = 3, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

출력 : 1

P

F ← QLINK[F], goto T5

Page 45: [TAOCP] 2.2.3 연결된 할당 - 위상정렬

1 3 0 1 2 2

Λ

1 2 9

k

COUNT[k]

TOP[k]

1 2 3 4 5 6 7 8 9

QLINK[0] = 1, F = 3, R = 3, N = 8

8

Λ

5 7

Λ

6

Λ

8

Λ

4 6

Λ

3

Λ

2

Λ

5

Λ

SUC

NEXT

출력 : 1

P

F ← QLINK[F], goto T5

앞 단계에서 직접 선행자가 0인 숫자를 QLINK로 연결

여기선 QLINK를 따라가며 다음에

출력할 숫자를 F에 넣는다

QLINK는 따라 가는거니 헷갈리지 말 것.

Page 46: [TAOCP] 2.2.3 연결된 할당 - 위상정렬
Page 47: [TAOCP] 2.2.3 연결된 할당 - 위상정렬