Transitive Closure算法笔记

2023-10-17 13:58

本文主要是介绍Transitive Closure算法笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

楔子

把一張圖想像成道路地圖,把圖上的點想像成地點,把圖上的邊想像成道路。現在我們在意的是:由某一點開始,走過 N 條道路後,可以到達哪些點?

最簡單的莫過於走過零條道路的情況了,哪裡都去不了。至於走過一條道路的情況,可以到達起點附近的點。

【註:讀過 Shortest Path 章節的讀者,應該很快就會聯想到:由一點到另一點最少需要走過幾條道路。但是這和此處所言不同。】

 N 很大時,各位可能會想到用 Graph Traversal來試試看──可是路線是環的話就沒轍了。環經過同一個點很多次,而 Graph Traversal 只能拜訪一個點一次。

Transitive Closure

中文譯作「遞移閉包」。一張圖的 Transitive Closure 是一張圖,用來紀錄由一點能不能走到另一點的關係,如果能走到,則兩點之間以邊相連。

要找出一張圖的 Transitive Closure ,也就是要找出圖上每一個點,走過一條、兩條、…… 、無限多條道路之後,會到達圖上哪些點。

然而,一張圖上最多只有 V 個點,要從一點走到另一點,走 V-1 條道路之內一定到得了,否則不論走多少條都一定到不了。另外還要考慮從一點走過 V 個邊回到原點的情況(經過圖上所有點的環)。

因此,要找出一張圖的 Transitive Closure ,只要找出圖上每一個點,走過一條、兩條、……  V 條道路之後,會到達圖上哪些點就可以了。

Transitive Closure: 一個普通的演算法

經過其他點

有個簡單的想法:如果一張圖上,由 i 點可以走到某一個 k 點、這個 k 點又可以走到 j 點,那麼就可以由 i點走到 j 點。

窮舉所有可能的 k 點,就可以判斷出由 i 點到 j 點是否相通了!

只要再計算一下圖上每一個 i 點和每一個 j 點,就可以判斷出圖上各點是否相通了。不過這樣只能找出走過兩條道路的情況。

p2(i, j) = p1(i, 0) && p1(0, j) || ... || p1(i, 9) && p1(9, j)pN(i, j):由i點能不能走到j點。N是走過的道路數目。

兩條加一條就是三條,三條加一條就是四條。走過三條道路、走過四條道路等等的情況,可以以逐次加一條道路的方式,慢慢累積而得。

pN+1(i, j) = pN(i, 0) && p1(0, j) || ... || pN(i, 9) && p1(9, j)pN(i, j):由i點能不能走到j點。N是走過的道路數目。

經過其他點 v.s. 矩陣相乘

點到 k 點, k 點到 j 點,窮舉所有 k 點──其實就和矩陣乘法的規則一樣。如果把一張圖儲存成 adjacency matrix ,那麼直接拿這張圖的 adjacency matrix 自己乘上自己,並且把加法改成 OR 運算,乘法改成 AND 運算,相乘的結果就是走過兩條道路的情況了!

同理,走過兩條道路的矩陣,再乘上一次原圖的adjacency matrix ,就會成為走過三條道路的情況。如此一來,若要求走過 N 條道路的情況,就是原圖的adjacency matrix  N 次方。

一個普通的演算法

現在回頭談 Transitive Closure 要怎麼求。既然一張圖的 Transitive Closure 只需要檢查一條、兩條、…… 條道路,所以一張圖的 Transitive Closure 就是此圖的adjacency matrix  1 次方、 2 次方、……  V 次方,然後統統 OR 起來就對了。

時間複雜度

矩陣相乘需要 O(V^3) (還可以更快)。矩陣的 V次方可藉由 Divide and Conquer  O(logV) * O(V^3) = O(logV * V^3) 求得。(請參考本站文件「 Divide and Conquer  Fast Exponentiation 」)

Transitive Closure: Warshall's Algorithm

Warshall's Algorithm

Warshall 不用「走過幾條道路」的觀點來思考,反而是用「中繼點」的觀點來思考:如果一張圖上可以由 i點開始,走過一些中繼點,最後走到 j 點,那麼就可以由 i 點走到 j 點。這和上一個章節的「經過其他點」的概念類似。

中繼點有可能是第 0 點、第 1 點、…… 、等等。中繼點也不見得只有一點。儘管聽起來很複雜,不過Warshall 卻利用 Divide and Conquer ,分割原問題成容易解決的小問題了!

Warshall 把由 i 點到 j 點的路線分成兩種:經過第 0點的、未經過第 0 點的。接著分別遞迴下去,再分為經過第 1 點的、未經過第 1 點的,如此不斷分下去,直到最後一點。

tc(i, j, k) = tc(i, k, k+1) && tc(k, j, k+1) || tc(i, j, k+1)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^經過第k點                    沒有經過第k點tc(i, j ,k):紀錄由i點走不走的到j點,當中繼點遞迴到第k點的時候。

找出一張圖的 Transitive Closure

使用 Dynamic Programming 紀錄每個小問題的答案。另外,由於計算順序以及 OR 運算的關係,造成記憶體可以重複使用,只需要開二維陣列。請見程式碼:

  1. bool map[9][9]; // 一張圖,資料結構為adjacency matrix。
  2. bool tc[9][9];  // Transitive Closure
  3.  
  4. void warshall()
  5. {
  6.     // 先把原圖複製到要進行DP計算的陣列
  7.     for (int i=0i<9i++)
  8.         for (int j=0j<9j++)
  9.             tc[i][j] = map[i][j];
  10.  
  11.     for (int k=0k<9k++) // 嘗試每一個中繼點
  12.         for (int i=0i<9i++) // 計算每一個i點與每一個j點
  13.             for (int j=0j<9j++)
  14.                 if (tc[i][k] && tc[k][j])
  15.                     tc[i][j] = true;
  16. }

徵求練習題。

这篇关于Transitive Closure算法笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/225846

相关文章

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1