惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
大猫的无限游戏
大猫的无限游戏
爱范儿
爱范儿
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Register - Security
The Register - Security
IT之家
IT之家
博客园_首页
Microsoft Security Blog
Microsoft Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
I
InfoQ
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
博客园 - Franky
C
Check Point Blog
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
美团技术团队
The Cloudflare Blog
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
The GitHub Blog
The GitHub Blog
D
DataBreaches.Net
Google DeepMind News
Google DeepMind News
V
V2EX
aimingoo的专栏
aimingoo的专栏
GbyAI
GbyAI
G
Google Developers Blog
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42
罗磊的独立博客
量子位
MongoDB | Blog
MongoDB | Blog
Last Week in AI
Last Week in AI
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
D
Docker
人人都是产品经理
人人都是产品经理

hsfzxjy 的博客

解决 VSCode + CMake + MSVC 编译器信息乱码的问题 使用 3090 部署 1.58bit 动态量化版 DeepSeek R1 671b 如何在 VS Code DevContainer 中配置 HTTP 代理 如何在跳板机背后的服务器上使用 VS Code Remote - Containers Cohesive Digests for Ints and Floats Rust 中的隐匿概念 —— Place(位置) 美术馆 一尺之槌,日取其半,1075日而竭 老生常谈:使用 Cloudflare 自选 IP 加速站点访问 辩义 State、Nation 与 Country 将 Base64 编码的数据快速转换为 Uint8Array 折腾 NPU·第1章 —— 搭建 Level Zero 开发环境 折腾 NPU·第0章 —— Intel NPU 概述与 Level-Zero 新增域名 monad.run CSS 中为特定字符设置不同字体 Arbitary Lifetime Transmutation via Rust Unsoundness Manacher 回文计数算法 硬卧 Go Fact: Zero-sized Field at the Rear of a Struct Has Non-zero Size Display *big.Rat Losslessly and Smartly in Golang 代码的仪式 Building Electron From Scratch 中式亲属称谓研究之一:构建半群 Some Notes on Kotlin Coroutines Git sparse-checkout and partial clones for Mega-Repos 辩义“封建” Diving from the CUDA Error 804 into a bug of libnvidia-container Modern Cryptography, GPG and Integration with Git(hub) Move the Root Partition of Ubuntu A New Programmer Kicks a Roadblock Git-based Dependencies in Dart and Go Reversy Naming 人类一败涂地 Invalid Golang Pointers Can Bite You Even If You Don't Dereference Side Project(副业) A Flaw of Promoting Complex Trait Bounds in Rust Initialize Process Pool Worker with Individual Value Rust - Python FFI From Scratch [Extending Hexo For My Site] Part 1 [Extending Hexo For My Site] Part 0 Debug a 'torch.tensor(1).cuda()' hanging 不自由的互联网 Retrieve Contents over HTTP without curl or wget [Unravelling mocona] Part 1 - Verbosity or Anti-Pattern [Unravelling mocona] Part 0 - Preface Understanding pickle in Python Rough Notes on Deploying Vaultwarden & NextCloud Bookmarks 语言狂热者与实用主义者 Demystify the randomness in CUDA kernels Performant Bulk Mutations in IndexedDB Auto Rebuild .pyx Files with pyximport Cython and Threads Obtain a Random Available TCP Port with Bash Information Theory: KL Divergence Information Theory: Entropy and Mutual Information 铁板烧 西郊线 Proof of the Gumbel Max Trick Option::as_ref Rc, RefCell and Interior Mutability Visualizing Correlation 三月十日杂感 三月一日杂感 二月十一日杂感 一月二十六日杂感 SS Configuration 一月七日杂感 四月·病 Haskell 笔记:State Monad Haskell 笔记:Monad 引论 Haskell 笔记:Applicative Haskell 笔记:Category Theory and Functor Haskell 笔记:data, type, newtype Haskell 笔记:folds 使用 Aria2 在 Ubuntu 中下载百度云资源 从伪并行的 Python 多线程说起 一个 Reentrant Error 引发的对 Python 信号机制的探索和思考 Linux 文件权限 HSFZMUN 4.0 部署小记 午后雨·科大 最后的雨夜·广州 揭秘·变态的平方根倒数算法 神坑·Python 装饰类无限递归 Python“黑魔法”之 Encoding & Decoding Ubuntu 重新映射键盘布局 为什么我要翻墙 Python“黑魔法”之 Generator Coroutines 数学美 之 判断线段相交的最简方法 除夕杂感 17 行代码实现的简易 Javascript 字符串模板 Python“黑魔法”之 Meta Classes 诗集 生活,需要被“发现” 家书·十八岁成人礼 炫技?还是需求? 【译】响应式图片的现状 【译】“为什么有这么多的编程语言?” Wisecity 商赛总结——也谈前端自动化测试 记一次 DoS 诈骗网站的经历 那一年,我们望向星空
Dijkstra 算法的延伸
2024-01-22 · via hsfzxjy 的博客

我们知道 Dijkstra 算法是一个高效的单源最短路径(SSSP)算法,本文将不再赘述他的细节。但同时,Dijkstra 也是一个动态规划算法。Dijkstra 算法的正确性源自无负边权图的若干性质。如果一个问题本身也满足这些性质,那么即使它不是一个图论最短路径问题,也可以使用 Dijkstra 算法解决。那么,这些性质是什么呢?

SSSP 问题的延伸

我们可以从更加抽象的层次描述 SSSP 问题。设想有一个函数 $W: [n] \times [n] \times \mathbb{R}^+ \mapsto \bar{\mathbb{R}}$,对 $\forall g, g_1, g_2 \in \mathbb{R}^+, g_1 < g_2, i, j \in [n], i \neq j$ 满足以下性质

$$\begin{align} W(i, i, g) &= 0, \\ W(i, j, g) &\geq 0, \\ W(i, j, g_1) - W(i, j, g_2) &\leq g_2 - g_1 \label{eq:bound} \end{align}$$

$W$ 可以看成一个广义的权值函数,带有一个额外的自变量 $g$。在实际问题中 $g$ 可以是“已走过的距离”等额外参数。而 $\eqref{eq:bound}$ 约束了 $W$ 随着 $g$ 最多只能以线性速度衰减,这对后面的证明是十分必要的。注意 $W$ 的值域是扩充实数域,为了保持一致性,我们额外规定 $\infty - \infty = 0$。

另一方面,我们记 $[n]$ 中元素构成的子序列的集合为 $\mathcal{U}$

$$\mathcal{U} = \{(a_1, a_2, \ldots, a_k)|k \in \mathbb{N}, a_1, \ldots, a_k \in [n]\}$$

$\mathcal{U}$ 中元素我们用加粗小写字母 $\mathbf{a}, \mathbf{b}$ 表示,$|\mathbf{a}|$ 为 $\mathbf{a}$ 的长度,$a_i$ 为 $\mathbf{a}$ 的第 $i$ 个元素,$\mathbf{a}_{q}$ (如 $\mathbf{a}_{\leq k}$)为 $\mathbf{a}$ 中下标满足条件 $q$ 的元素构成的子序列。同时,我们还定义 $||$ 为序列拼接运算符。有了以上准备,现在我们定义路径长度函数 $L: \mathcal{U} \mapsto \bar{\mathbb{R}}$:

$$ \begin{align} L(\mathbf{a}) &= W(1, a_1, 0), &\text{if } |\mathbf{a}| = 1; \\ L(\mathbf{a}) &= L(\mathbf{a}_{\leq k-1}) + W(a_{k-1}, a_k, L(\mathbf{a}_{\leq k-1})), &\text{if } |\mathbf{a}| = k \geq 2. \end{align} $$

现在我们要求解另一个函数 $D: [n] \mapsto \mathbb{R}$,其定义如下

$$ \begin{align} D(u) = \min_{\mathbf{a} \in \mathcal{U}, |\mathbf{a}| \leq n} L(\mathbf{a}||u) \label{eq:D} \end{align} $$

上述这个问题可以用 Dijkstra 算法求解。在 SSSP 中,$W(i, j, g)$ 即为 $i$ 到 $j$ 的边权,$W(i, j, g)$ 是关于 $g$ 的常函数,$D(i)$ 则是 $i$ 到源点的最短路径。

延伸问题的求解

和 Dijkstra 算法类似,我们维护一个试探性数组 $p: [n] \in \mathbb{R}$。初始时,$p(i) \geq D(i), \forall i$。同时我们维护一个“已求解”的集合 $S$,记其补集 $T = [n] - S$。我们将经过 $n$ 次迭代,逐渐将 $S$ 填满。而在每次迭代开始前,我们会通过某种“更新”机制使 $p$ 满足如下条件:

$$ \begin{align} p(i) &= D(i), &\forall i \in S; \\ p(i) &= \min_{j \in S} \left(D(j) + W(j, i, D(j)\right)), &\forall i \in [n].\label{eq:ST} \end{align} $$

在每次迭代中,我们从 $T$ 中取出

$$\begin{align}u = \arg\min_{i \in T}d(i)\label{eq:u}\end{align}$$

,并认为 $i$ 已经“求解完成”,将其加入 $S$。为什么可以这么做呢?我们可以用以上的条件证明其正确性。

利用反证法,如果说 $u$ “还未求解完成”,也就是此时 $p(u) > D(u)$。由 $\eqref{eq:D}$ 中 $\mathbf{a}$ 的取值集合有限可知,$\exists \mathbf{a}, |\mathbf{a}|=k$,使得 $D(u) = L(\mathbf{a}||u)$。我们讨论两种情况:

第一种,如果 $\mathbf{a} \subset S$,

$$ \begin{align*} D(u) &< p(u) & 利用 \eqref{eq:ST}\\ &\leq D(a_k) + W(a_k, u, D(a_k)) & 利用 D(a_k) \leq L(\mathbf{a}) 以及 \eqref{eq:bound}\\ &\leq D(a_k) + W(a_k, u, L(\mathbf{a})) + L(\mathbf{a}) - D(a_k) \\ &= W(a_k, u, L(\mathbf{a})) + L(\mathbf{a}) & 利用 D(u) = L(\mathbf{a}||u)\\ &= D(u) \end{align*} $$

矛盾!

第二种,如果诸 $\mathbf{a}$ 中有不属于 $S$ 的元素,假设 $a_j \in T$ 是其中下标最小者。则有

$$ \begin{align*} p(u) &> D(u) \\ &= L(\mathbf{a}_{<j}||a_j||\mathbf{a}_{>j}||u) \\ &\geq L(\mathbf{a}_{<j}||a_j) \\ &= L(\mathbf{a}_{<j}) + W(a_{j-1}, a_j, L(\mathbf{a}_{<j})) &利用 D(a_{j-1}) < L(\mathbf{a}_{<j})\\ &= L(\mathbf{a}_{<j}) + W(a_{j-1}, a_j, D(a_{j-1})) + D(a_{j-1}) - L(\mathbf{a}_{<j}) \\ &= W(a_{j-1}, a_j, D(a_{j-1})) + D(a_{j-1}) & 利用 a_{j-1} \in S 及 \eqref{eq:ST}\\ &\geq p(a_j) \end{align*} $$

这与 $u$ 的选取矛盾!故综上,如此选出的 $u$ 一定是“已经求解完成的”。

选出了 $u$ 后,我们需要以 $u$ 为基点,更新 $Y$ 中其他元素的 $d$ 值。具体而言,对于 $\forall j \in T$,我们作如下更新

$$p(j) \gets \min(p(j), D(u) + W(u, j, D(u)))$$

我们可以证明这样的更新方式符合 $\eqref{eq:ST}$ 的要求

$$ \begin{align*} &\min\left\{p(j), D(u) + W(u, j, D(u))\right\} \\ = &\min\left\{\min_{w \in S}\{D(w)+W(w, j, D(w))\}, D(u, W(u, j, D(u)))\right\} \\ = &\min_{w \in S \cup \{u\}}\{D(w)+W(w, j, D(w))\} \end{align*} $$

由此可知,Dijkstra 的改进算法可以解决这个延伸问题。

应用

前面说过,SSSP 是延伸问题的一个特殊情况。此外,本文的算法还可以用来解决一些特殊的最短路径问题。比如在 LeetCode 2577. Minimum Time to Visit a Cell In a Grid 中,图的边权不是一个常数,而是随着当前已走的距离(或时间)增加而减少,而且减少的幅度不超过线性速度。可以验证,这道题的条件满足上面的约束,因此也可以用 Dijkstra 算法来解决。


作者:hsfzxjy
链接:
许可:CC BY-NC-ND 4.0.
著作权归作者所有。本文不允许被用作商业用途,非商业转载请注明出处。