โน๏ธ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | FAIL | download_stamp > now() - 6 MONTH | 7.5 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://sungwookyoo.github.io/algorithms/BellmanFord/ |
| Last Crawled | 2025-09-05 17:44:57 (7 months ago) |
| First Indexed | 2021-03-12 14:01:55 (5 years ago) |
| HTTP Status Code | 200 |
| Meta Title | Bellman-Ford and DAG Algorithms in Python - Wookโs Blog - Data is the new oil |
| Meta Description | Single source shortest path Algorithm |
| Meta Canonical | null |
| Boilerpipe Text | 1
2
3
4
5
6
7
8
9
import sys , random , string sys . path . append ( "/home/swyoo/algorithm/" ) from utils.verbose import visualize_graph , logging_time from utils.generator import randomString from collections import defaultdict from pprint import pprint from copy import deepcopy from typing import List , Tuple import numpy as np 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def generate_graph ( n , m , randrange : Tuple [ int , int ], verbose = False ): """ |V|: n, |E|: m """ # S = set(' '.join(string.ascii_lowercase).split()[:n])
S = set ( range ( n )) seen = set () edges = [] for _ in range ( m ): while True : # start = randomString(length=1, samples=list(S))
# end = randomString(length=1, samples=list(S - {start}))
while True : start , end = random . choices ( population = range ( n - 1 ), k = 2 ) if start != end : break if ( start , end ) not in seen : seen . add (( start , end )) break edges . append (( start , end , random . randint ( randrange [ 0 ], randrange [ 1 ]))) if verbose : visualize_graph ( edges , weighted = True ) graph = defaultdict ( list ) for i in S : graph [ i ] for u , v , w in edges : graph [ u ]. append (( v , w )) return graph , edges INF = 1e20 def g2m ( graph ): n , nodes = len ( graph . keys ()), sorted ( graph . keys ()) n2i = { k : v for k , v in zip ( nodes , range ( n ))} weights = [[ INF ] * n for _ in range ( n )] for i in nodes : weights [ n2i [ i ]][ n2i [ i ]] = 0 for i in nodes : for j , w in graph [ i ]: weights [ n2i [ i ]][ n2i [ j ]] = w return n2i , weights def hasNcycles ( weights , verbose = False ): n = len ( weights ) ans = deepcopy ( weights ) for k in range ( n ): for i in range ( n ): for j in range ( n ): ans [ i ][ j ] = min ( ans [ i ][ j ], ans [ i ][ k ] + ans [ k ][ j ]) # check if negative cycle exist
for i in range ( n ): if ans [ i ][ i ] < 0 : if verbose : print ( "negative cycle exists from node[{}] to node[{}]" . format ( i , i )) return True return False def generate_graph_no_neg_cycle ( n , m , randrange ): weights = graph = None while True : graph , edges = generate_graph ( n , m , randrange , verbose = False ) n2i , W = g2m ( graph ) if not hasNcycles ( W ): weights = deepcopy ( W ) return n2i , weights , graph , edges n , m = 5 , 7 n2i , weights , graph , edges = generate_graph_no_neg_cycle ( n , m , randrange = ( - 10 , 100 )) visualize_graph ( edges = edges , weighted = True ) graph 1
2
3
4
5
6
defaultdict(list,
{0: [(1, 48), (2, -9)],
1: [(2, 0)],
2: [(1, 72), (3, 26)],
3: [(1, 3), (2, 95)],
4: []})
๊ธฐ๋ณธ๊ฐ์ : no negative weight cycle(์๋ค๋ฉด False return) Dijkstraโs algorithm๊ณผ ๋ฌ๋ฆฌ Bellman Ford ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ์ค์น๊ฐ ์์์ธ ๊ฒฝ์ฐ์๋ ์ ์ฉ ๊ฐ๋ฅ.
์์ ๊ฐ์ค์น๊ฐ ์ฌ์ดํด(cycle)์ ์ด๋ฃจ๊ณ ์๋ ๊ฒฝ์ฐ์๋ ์๋ํ์ง ์๋๋ค. naive ํ๊ฒ ๊ทธ๋ํ ์ ์ ์๋งํผ ๊ทธ๋ํ ๋ด ๋ชจ๋ ์ฃ์ง์ ๋ํด edge relaxation ์ ์ํ ํ๋ค.
๊ทธ๋ฌ๋ฉด (negative weight cycle ์ด ์๋ค๋ ๊ฐ์ ํ์) ๋ชจ๋ ์ ์ ์ ๋งํผ์ relaxation ์ ๋์์๋, shortest path๋ฅผ ์ฐพ์ ์ ์๋ค. Pseudo Code 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Bellman ( G , s ) # shortest distance ๊ฐ์ ์ ์ฅํ array
let d [ 1 .. | G . V | ] be a new array # initialization
d [ k ] = INF for all k in G . V except for k == s d [ s ] = 0 # edge relaxations for all cases O(VE)
for i = 1 to | G . V | for ( u , v ) in G . E if d [ v ] > d [ u ] + w ( u , v ) d [ v ] = d [ u ] + w ( u , v ) # check whether eixist negative weight cycle
# negative weight cycle ๊ฐ ์๋ค๋ฉด edge relaxation์ ํ์๋
# shortest path distance๋ณด๋ค ์์ distance ๊ฐ ์กด์ฌ ํ ๊ฒ์ด๋ค.
for ( u , v ) in G . E if d [ v ] > d [ u ] + w ( u , v ) return False return d Time Complexity ๋ชจ๋ cases ์ ๋ํด edge relaxation์ ์ํํด์ผํ๋ฏ๋ก $T(n) =O(VE)$ c++ python Implementation graph ์ ์ฃ์ง์ ๋ณด์ธ edges ์ ๋
ธ๋์ ์ด ๊ฐ์ $n$ ๋ง ์๋ฉด ๋๋ค. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
INF = 1e20 n = len ( graph . keys ()) @ logging_time def bellman ( src , edges , n ): ans = [ INF ] * n ans [ src ] = 0 for _ in range ( n ): for i , j , w in edges : ans [ j ] = min ( ans [ j ], ans [ i ] + w ) for i , j , w in edges : if ans [ j ] > ans [ i ] + w : return False # detect negative weight cycle.
return ans 1
2
3
4
5
6
7
n , m = 5 , 7 n2i , weights , graph , edges = generate_graph_no_neg_cycle ( n , m , randrange = ( - 10 , 100 )) visualize_graph ( edges = edges , weighted = True ) pprint ( graph ) print ( edges ) print ( "after run bellman ford algorithm ..." ) bellman ( 0 , edges , n , verbose = True ) 1
2
3
4
5
6
7
8
9
10
defaultdict(<class 'list'>,
{0: [(2, 86), (3, 99)],
1: [(2, 79), (3, 23)],
2: [(3, 54), (0, 29)],
3: [(0, 0)],
4: []})
[(0, 2, 86), (0, 3, 99), (2, 3, 54), (2, 0, 29), (1, 2, 79), (3, 0, 0), (1, 3, 23)]
after run bellman ford algorithm ...
WorkingTime[bellman]: 0.01311 ms
1
[0, 1e+20, 86, 99, 1e+20]
Application It can be used to detect negative weight cycle like floyd warshall algorithm ! DAG Topological sort๋ฅผ ์ฌ์ฉํ์ฌ Bellman Ford ๋ฅผ ์ข๋ ๊ฐ์ ํ ๋ฐฉ์ ๊ธฐ๋ณธ๊ฐ์ : Topological sort๋ฅผ ์ฌ์ฉํด์ผํ๋ฏ๋ก DAG์ ๋ํด์๋ง ์ฌ์ฉ๊ฐ๋ฅ Bellman ford ์๊ณ ๋ฆฌ์ฆ์ naiveํ๊ฒ ๋ชจ๋ ๊ฐ๋ฅํ ๊ฒฝ์ฐ์ ์์ ๋ํด์ edge relaxation ์ ์ํํ์๋ค. DAG algorithm์ ์ข๋ ํจ์จ์ ์ด๊ฒ topolgical sort๋ฅผ ํ ์์์ ์ ์ ๋ฆฌ์คํธ์ ๋ํด์
adjacent list ๋ฅผ ๋ฐํ์ผ๋ก edge relaxation ์ ์ํ Pseudo Code 1
2
3
4
5
6
7
8
9
10
11
12
13
14
Bellman ( g , s ) let d [ 1 .. | G . V | ] be a new array # initialization
d [ k ] = INF for all k in G . V except for k == s d [ s ] = 0 # edge relaxations for a efficient way
L = TopoSort ( G ); for u in L for ( u , v ) in G . adj [ u ] if d [ v ] > d [ u ] + w ( u , v ) d [ v ] = d [ u ] + w ( u , v ) return d Topological sort๋ฅผ ํ List ์์ผ๋ก ์งํ๋๋ฏ๋ก $T(n) =O(V + E)$ c++ python Step1. Generate DAG and Topological Sort 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def toposort ( g : dict ): seen , finish = set (), set () topo = [] hascycle = False def dfs ( i ): nonlocal hascycle seen . add ( i ) for j , w in g [ i ]: if j not in seen : dfs ( j ) elif j not in finish : hascycle = True return topo . append ( i ), finish . add ( i ) for i in g . keys (): if i not in seen : dfs ( i ) return topo [:: - 1 ], hascycle toposort ( graph ) 1
2
3
4
5
6
7
8
def generate_graph_no_cycle ( n , m , randrange ): weights = graph = None while True : graph , edges = generate_graph ( n , m , randrange , verbose = False ) n2i , W = g2m ( graph ) if not toposort ( graph )[ 1 ]: weights = deepcopy ( W ) return n2i , weights , graph , edges 1
2
3
n2i , weights , graph , edges = generate_graph_no_cycle ( 6 , 10 , randrange = ( - 10 , 30 )) visualize_graph ( edges , weighted = True ) pprint ( graph ) 1
2
3
4
5
6
7
8
defaultdict(<class 'list'>,
{0: [(1, -10), (4, 24), (3, 0)],
1: [(4, 10), (3, -5)],
2: [(0, 29), (4, -6), (3, 1), (1, 21)],
3: [],
4: [(3, 15)],
5: []})
1
([5, 2, 0, 1, 4, 3], False)
Step 2. DAG Algorithm 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
INF = 1e20 @ logging_time def DAG ( src , g , n ): def toposort ( g : dict ): seen , finish , topo , hascycle = set (), set (), [], False def dfs ( i ): nonlocal hascycle seen . add ( i ) for j , w in g [ i ]: if j not in seen : dfs ( j ) elif j not in finish : hascycle = True return topo . append ( i ), finish . add ( i ) for i in g . keys (): if i not in seen : dfs ( i ) return topo [:: - 1 ], hascycle ans = [ INF ] * n ans [ src ] = 0 L , hascycle = toposort ( g ) for i in L : for j , w in g [ i ]: ans [ j ] = min ( ans [ j ], ans [ i ] + w ) return ans if not hascycle else False 1
2
3
4
5
n2i , weights , graph , edges = generate_graph_no_cycle ( 6 , 10 , randrange = ( - 10 , 30 )) visualize_graph ( edges , weighted = True ) pprint ( graph ) n = len ( graph . keys ()) DAG ( src = 0 , g = graph , n = n , verbose = True ) 1
2
3
4
5
6
7
8
9
defaultdict(<class 'list'>,
{0: [(1, 6)],
1: [],
2: [(1, 10), (3, -5), (4, -9), (0, -3)],
3: [(1, 8), (0, 28)],
4: [(3, -4), (1, 6), (0, -6)],
5: []})
WorkingTime[DAG]: 0.01574 ms
1
[0, 6, 1e+20, 1e+20, 1e+20, 1e+20]
1
2
for i in graph . keys (): print ( DAG ( src = i , g = graph , n = n , verbose = False )[ 0 ]) 1
2
3
4
5
6
7
[0, 6, 1e+20, 1e+20, 1e+20, 1e+20]
[1e+20, 0, 1e+20, 1e+20, 1e+20, 1e+20]
[-15, -9, 0, -13, -9, 1e+20]
[28, 8, 1e+20, 0, 1e+20, 1e+20]
[-6, 0, 1e+20, -4, 0, 1e+20]
[1e+20, 1e+20, 1e+20, 1e+20, 1e+20, 0]
Ballman Ford vs DAG ๋น๊ต๋ฅผ ์ํด์๋ ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋๋ฆฌ๊ธฐ ์ํ ์ ํ์ฌํญ์ ๋๋ค ๋ง์กฑํด์ผํ๋ค.
๋ฐ๋ผ์, cycle์ด ์๋ ๋ ๊ทธ๋ํ๋ฅผ ๋ง๋ ํ, ๋น๊ตํ๊ฒ ๋ค. 1
2
3
4
5
6
n2i , weights , graph , edges = generate_graph_no_cycle ( 300 , 400 , randrange = ( - 10 , 30 )) visualize_graph ( edges , weighted = True ) n = len ( weights ) ans1 = bellman ( 0 , edges , n , verbose = True ) ans2 = DAG ( src = 0 , g = graph , n = n , verbose = True ) assert ans1 == ans2 1
2
3
WorkingTime[bellman]: 28.24116 ms
WorkingTime[DAG]: 0.32878 ms
Reference [1] Floyd Warshall
[2] Topological Sort |
| Markdown | null |
| Readable Markdown | null |
| Shard | 143 (laksa) |
| Root Hash | 2566890010099092343 |
| Unparsed URL | io,github!sungwookyoo,/algorithms/BellmanFord/ s443 |