diff --git a/app/content/posts/20240506-biconnected-cmp-zh-tw.mdx b/app/content/posts/20240506-biconnected-cmp-zh-tw.mdx new file mode 100644 index 0000000..e864548 --- /dev/null +++ b/app/content/posts/20240506-biconnected-cmp-zh-tw.mdx @@ -0,0 +1,98 @@ +--- +title: 雙連通元件與關節點 +description: 這兩個重要概念在問題中的運用。 +slug: 20240506-biconnected-cmp +lang: zh-tw +date: 2024-05-06 +type: Post +tags: + - math + - computer_science +--- + +一個關節點是一種當被剔除時,會造成原有的圖形裂成兩個(包含)以上的圖。如果一個圖沒有任何關節點,它便是一個雙連通圖, +一個有極多點的雙連通圖被稱為雙連通元件。找到關節點可以順便找到雙連通子圖,所以我們需要有個快速的求法。 + +## 基本演算法 + +我們可以在每次剔除一個節點時跑一次 DFS(深度優先搜尋)。 + +``` +nodes = 1000 +visited = [0, ..., 0] +algorithm DFS(now, removed): + visited[now] = true + for n in now.adjs: + if n == removed: + continue + DFS(n, removed) + +algorithm get_components(): + aps = [] + og_components = 0 + for i from 0 to nodes: + if visited[i] is false: + components = components + 1 + DFS(now, -1) + for i from 0 to nodes: -- 剃除i + visited = [0, ..., 0] + components = 0 + for j from 0 to nodes: + if i == j: + continue + if visited[i] is false: + components = components + 1 + DFS(now, i) + if components > og_components: + aps = aps + 1 + return aps +``` + +## Tarjan 演算法 + +不過上述演算法有些嚴重錯誤,其中最大是它的低效率問題,因此在 1973 年,John Hopcroft 與 Robert Tarjan 想出了一個新的演算法, +它運作的方法是找到每個節點的 DFS 尋找時間,並使用一個特別的值*lowpoint*來判斷我們可不可以用比原本節點更短的路徑走到另一個節點, +為了補足沒有反邊的問題(在 DFS 樹中連回到另一個上層的邊)。如果一個節點$N$是關節點,那它必須滿足下列條件: + +1. 如果$N$是 DFS 樹的樹根,則$N$必須有至少兩個子節點。 +2. 如果$N$不是樹根,則至少有一個$N$的子節點沒有回到$N$的祖先的反邊。 + +``` + +nodes = 1000 +time = 0 +visited = [] +discovery = [] +lowpoint = [] +aps = {} -- 集合 +algorithm DFS(now, parent): + children = 0 + time = time + 1 + visited[now] = true + discovery[now] = lowpoint[now] = time + + for next in now.adjs: + if visited[next] == false: + children = children + 1 + DFS(next, now) + lowpoint[now] = min(lowpoint[now], lowpoint[next]) + if parent == -1 and lowpoint[next] >= discovery[now]: + aps = aps or {now} -- 或集 + else: -- 之前就找過 + lowpoint[now] = min(lowpoint[now], discovery[next]) + + if parent == -1 and children >= 2: + aps = aps or {now} + +algorithm Tarjan(): + for i from 0 to nodes: + DFS(i, -1) + return aps + +``` + +這演算法以線性時間$O(V + E)$執行,非常快。 + +## 真實世界應用 + +關節點是一個網路中最重要的點,因為刪除它們意味著多個彼此分開的圖,在社交網路上,它們代表的是一群使社交網路對人而言有間接或直接共鳴的重要人物。 diff --git a/app/content/posts/20240506-biconnected-cmp.mdx b/app/content/posts/20240506-biconnected-cmp.mdx new file mode 100644 index 0000000..3d97a75 --- /dev/null +++ b/app/content/posts/20240506-biconnected-cmp.mdx @@ -0,0 +1,104 @@ +--- +title: Biconnected Component & Articulation Point +description: The usage of these two important concept in problems. +slug: 20240506-biconnected-cmp +lang: en +date: 2024-05-06 +type: Post +tags: + - math + - computer_science +--- + +An articulation point is a vertex that when removed, can result in two or more disconnected graphs. When a graph does +not have any articulation points, it is called a biconnected graph. Such a graph with the maximum possible nodes +is called a biconnected component. Finding these articulation points can directly lead to the discovery of biconnected +subgraphs; thus, we need a way to do this quickly. + +## Naive Algorithm + +We can run an algorithm that uses dfs to visit the entire graph every time we remove a vertex. + +``` +nodes = 1000 +visited = [0, ..., 0] +algorithm DFS(now, removed): + visited[now] = true + for n in now.adjs: + if n == removed: + continue + DFS(n, removed) + +algorithm get_components(): + aps = [] + og_components = 0 + for i from 0 to nodes: + if visited[i] is false: + components = components + 1 + DFS(now, -1) + for i from 0 to nodes: -- removes i + visited = [0, ..., 0] + components = 0 + for j from 0 to nodes: + if i == j: + continue + if visited[i] is false: + components = components + 1 + DFS(now, i) + if components > og_components: + aps = aps + 1 + return aps +``` + +## Tarjan's Algorithm + +However, the above algorithm evidently has some fatal flaws, with the biggest one being its inefficiency, so in 1973, +John Hopcroft and Robert Tarjan devised a new algorithm. This algorithm works by taking note of the discovery time +of a node, and use a special value called _lowpoint_ to deduce if we can travel to the node faster than using a +specific point to bypass the lack of back edges (edges that are connected back to the upper layers of a DFS tree). +If node $N$ is an articulation point, then it must satisfy the following: + +1. If $N$ is a root in a DFS tree, then $N$ must have at least two children. +2. If $N$ is not a root, then at least one of $N$'s children can't have a backedge back to the ancestors of $N$. + +``` + +nodes = 1000 +time = 0 +visited = [] +discovery = [] +lowpoint = [] +aps = {} -- a non-duplicating set +algorithm DFS(now, parent): + children = 0 + time = time + 1 + visited[now] = true + discovery[now] = lowpoint[now] = time + + for next in now.adjs: + if visited[next] == false: + children = children + 1 + DFS(next, now) + lowpoint[now] = min(lowpoint[now], lowpoint[next]) + if parent == -1 and lowpoint[next] >= discovery[now]: + aps = aps or {now} -- or union + else: -- already discovered + lowpoint[now] = min(lowpoint[now], discovery[next]) + + if parent == -1 and children >= 2: + aps = aps or {now} + +algorithm Tarjan(): + for i from 0 to nodes: + DFS(i, -1) + return aps + +``` + +This algorithm runs in linear time with its time complexity asymptote $O(V + E)$, which is lightning fast. + +## Real Applications + +Finding articulation points can be useful for finding the most important points in a network, since with the removal +of them comes two or more disconnected networks. They can also be used in social networks to find the most important +people for keeping the social network immediately/vicariously relevant to an existing group of people.