[论文阅读报告] Faster Algorithms for Approximate Distance Oracles and All-Pairs Small Stretch Paths, FOCS '06
本篇文章介绍三个最短路近似算法,分别是带权无向图 (2,ω)(2,ω) 和 stretch 22 近似的 ˜O(m√n)~O(m√n) 算法以及 stretch 33 近似的 O(min(m√n,n2))O(min(m√n,n2)) 时间、O(n32)O(n32) 空间做法。其中 ωω 表示最短路上最长边的长度,(2,ω)(2,ω) 表示对每一对 (u,v)(u,v),d(u,v)≤2δ(u,v)+ωu,vd(u,v)≤2δ(u,v)+ωu,v;O(n32)O(n32) 空间代表着每次查询 d(u,v)d(u,v) 时可以仅通过查询这些信息快速回答。
˜O(m√n)~O(m√n) 的 stretch 22 近似算法是 ˜O(n2.032)~O(n2.032) 的 stretch 22 近似算法的一项关键材料。
如果我们把原先的无权图上的 (2,1)(2,1) 算法迁移过来,得到的近似算法也是多加了一条边权值的 22 近似,但是这与 (2,ω)(2,ω) 不同,因为这一条边并不一定在最短路上。
令 pS(v)pS(v) 表示 SS 中离 vv 最近的点(如果有多个,选择任意一个),令 δ(v,S)δ(v,S) 表示 vv 到 SS 的距离。
给定 SS,我们可以通过 Dijkstra 在 O(m+nlogn)O(m+nlogn) 时间内对每个 vv 算出 pS(v),δ(v,S)pS(v),δ(v,S)。
我们在无权图上的 (2,1)(2,1) 近似中使用到了这样的做法:估计 uu 到 vv 的最短路时,我们只考虑一侧达到最优,用 min(δ(u,pS(u))+δ(pS(u),v),δ(u,pS(v))+δ(pS(v),v))min(δ(u,pS(u))+δ(pS(u),v),δ(u,pS(v))+δ(pS(v),v)) 作为答案。接下来的若干做法将会沿用这一想法,不过挖掘更多性质,以在近似效果上有所不同。
可能有人会想,在原先的算法中,我们使用了 δ(u,x)+δ(x,v)δ(u,x)+δ(x,v) 来估计 δ(u,v)δ(u,v),正确性证明类似于 δ(u,x)≤12(δ(u,v)+1),δ(x,v)≤δ(x,u)+δ(u,v)⇒δ(u,x)+δ(x,v)≤2δ(u,v)+1δ(u,x)≤12(δ(u,v)+1),δ(x,v)≤δ(x,u)+δ(u,v)⇒δ(u,x)+δ(x,v)≤2δ(u,v)+1,这是否对 δ(x,v)δ(x,v) 来说太不公平了?因为 δ(u,x)δ(u,x) 和 δ(x,v)δ(x,v) 的位置理应是对等的。答案是,如果我们想要得到更快的做法,就只能利用一边的信息,因为利用两边的信息依赖于 (min,+)(min,+) 矩阵乘法。不过需要指出的是,即使 (min,+)(min,+) 矩阵乘法没有 O(n2.9999)O(n2.9999) 的做法,我们还是能将这个问题做到 O(n2.5)O(n2.5),这说明 n×nn×n 的 (min,+)(min,+) 矩阵乘法对其并没有约束力。
一个更严谨的结果是,如果我们想要在最短路近似上做到优于 22 的近似比,那么 0101 矩阵乘法不难于它。如果我们想要计算 A×BA×B,其中 A,BA,B 为 n×m,m×kn×m,m×k 的 0101 矩阵,那么,建一张 n+m+kn+m+k 个点的图,第一排 nn 个点,第二排 mm 个点,第三排 kk 个点,前两排之间的邻接矩阵为 AA,后两排之间的邻接矩阵为 BB,第一排与第三排、同一排之间不练边。那么如果第一排的 ii 到第三排的 jj 距离为 22,A×BA×B 的 (i,j)(i,j) 结果为 11,当结果为 00 时,第一排的 ii 到第三排的 jj 距离至少为 44。因此如果我们能够区分 i,ji,j 的距离是 22 还是 ≥4≥4,我们就能够解决 0101 矩阵乘法。
如果我们仔细考虑这里的情况,当我们估计 δ(u,v)δ(u,v) 时,使用第二排的中间点 xx,那么如果 δ(u,x)=1,δ(x,v)=3δ(u,x)=1,δ(x,v)=3,我们并不能区分这是因为选择了不恰当的 xx,还是因为 u,vu,v 之间的距离就是 >2>2,除非我们选上所有的 xx,也即直接计算了矩阵乘法。此时 δ(x,v)≤δ(x,u)+δ(u,v)δ(x,v)≤δ(x,u)+δ(u,v) 是紧的。
小于 22 近似比的算法都是难的,而 stretch 33 和 (2,ω)(2,ω) 近似已经做到了 ˜O(n2)~O(n2),因此剩下的唯一的问题便是 stretch 22 是否也能做到 ˜O(n2)~O(n2)。目前仍然没有人知道。
我们先把之前已有的算法列出来:
算法 0.1 (带权图上的 ˜O(n73)~O(n73) stretch 33 近似) 对图 (V,E)(V,E),令
对每个 DD 跑完整的 Dijkstra,得到 δ(D×V)δ(D×V)。 对于 u∈D∨v∈D∨v∈ball(u)u∈D∨v∈D∨v∈ball(u),d(u,v)=δ(u,v)d(u,v)=δ(u,v)。 否则,考虑 w∈ball(u)∩Dw∈ball(u)∩D,d(u,v)=δ(u,w)+δ(w,v)d(u,v)=δ(u,w)+δ(w,v)。 这是一个 stretch 33 近似。 时间复杂度为 ˜O(nb2+n3b)~O(nb2+n3b),令 b=n23b=n23,最终时间复杂度为 ˜O(n73)~O(n73)。 |
算法 0.2 (无权图上的 (2,1)(2,1) 近似) 对图 (V,E)(V,E),令
用 BFS 在 (V,Ei∪E∗)(V,Ei∪E∗) 上计算 δi(Di−1×V)δi(Di−1×V),1≤i≤k1≤i≤k,时间复杂度为 ˜O(n2didi−1)~O(n2didi−1)。 考虑将 d(u,v)=min1≤i≤k{δi(u,u′i)+δi(u′i,v),δi(u,v′i)+δi(v′i,v)}d(u,v)=min1≤i≤k{δi(u,u′i)+δi(u′i,v),δi(u,v′i)+δi(v′i,v)} 作为答案,其中 u′i=argminw∈Diδi(u,w),v′i=argminw∈Diδi(w,v)u′i=argminw∈Diδi(u,w),v′i=argminw∈Diδi(w,v),也即只考虑一侧。
因此这是一个 (2,1)(2,1) 近似。 时间复杂度为 ˜O(n2)~O(n2)。 |
(2,ω)(2,ω) 近似算法的想法和上面两个算法都有联系。首先,它为了做到 ˜O(n2)~O(n2),只能考虑将一侧达到最优,同时他的近似比证明类似于 stretch 33 中 ballball 的概念,即,保证一个邻域的最短路的精确值,其他的点用邻域的边界上的关键点转移,相较于 stretch 33 它的优化点在于,我们发现,我们不需要为每一个 uu 设立一个 ball(u)ball(u),这个属性可以下放到边上。这是本篇论文最重要的概念,某种意义上是唯一的优化点。
定义 集合 ES(v)ES(v) 为所有边权小于 δ(v,S)δ(v,S) 的与 vv 相邻的边,如果 v∈S,ES(v)=∅v∈S,ES(v)=∅。令 ES=⋃v∈VES(v)ES=⋃v∈VES(v)。
引理 对于 δ(u,v)<δ(u,pS(u))δ(u,v)<δ(u,pS(u)) 的所有 vv,(V,ES)(V,ES) 保留了 (u,v)(u,v) 的一条最短路。(V,ES∪E(pS(u)))(V,ES∪E(pS(u))) 保留了 (u,pS(u))(u,pS(u)) 的最短路,其中 EE 表示全部邻边。
这不难通过归纳法证明。这条性质与我们所需要的性质是一致的。
引理 如果我们将图中每个点以 qq 的概率选入 SS 中(0<q<10<q<1),那么 E[|ES|]=O(n/q)E[|ES|]=O(n/q)。
证明 我们分每个点考虑。如果 pS(v)pS(v) 是 E(v)E(v) 中的点,那么边权小于这一条边的会被加入,这是一个几何分布,因此期望加入 O(1/q)O(1/q)。对于其他情况,直接放缩到加入整个 E(v)E(v),这样若 |E(v)|=d|E(v)|=d,它对期望的贡献是 d(1−q)dd(1−q)d,这个函数关于 dd 的最大值是 O(1/q)O(1/q) 的。或者,可以直接观察到的是,在第一种情况中期望加入的点数与 vv 的度数无关,因此可以加入许多虚点,直到虚点的个数足以表达第二种情况中加入点数的期望,整体的 O(1/q)O(1/q) 总是不会变的。
这非常符合我们的预期。原先我们需要 nb2nb2 来得到 ball(∗)ball(∗),现在我们只需要线性时间来得到 ESES。接下来要做的便是把上述两个算法照搬。
算法 1 (带权图上的 ˜O(n2)~O(n2) 的 (2,ω)(2,ω) 近似) 令 S0=V⊇S1⊇…⊇Sk−1⊇Sk=∅S0=V⊇S1⊇…⊇Sk−1⊇Sk=∅,其中 SiSi 通过将 Si−1Si−1 中的每个元素以 |S|−1/k|S|−1/k 的概率加入得到。令 k=⌈logn⌉k=⌈logn⌉。 对 0≤i<k0≤i<k,
将 min0≤i<k(δ(u,pi(u))+ˆδ(pi(u),v),δ(v,pi(v))+ˆδ(pi(v),u))min0≤i<k(δ(u,pi(u))+^δ(pi(u),v),δ(v,pi(v))+^δ(pi(v),u)) 作为 u,vu,v 的答案。 令 jj 满足 (u,v)(u,v) 最短路上所有的边 PuvPuv 在 ESj+1ESj+1 中但至少有一条边不在 ESjESj 中,由于 S0=V,Sk=∅S0=V,Sk=∅,ES0=∅,ESk=EES0=∅,ESk=E,这样的 jj 一定存在。假设这一条边为 (x,y)(x,y)。根据引理,我们有 δ(u,pj(u))≤δ(u,y)δ(u,pj(u))≤δ(u,y) 和 δ(v,pj(v))≤δ(v,x)δ(v,pj(v))≤δ(v,x),因此 δ(u,pj(u))+δ(v,pj(v))≤d(u,v)+ω(x,y)δ(u,pj(u))+δ(v,pj(v))≤d(u,v)+ω(x,y)。不失一般性地假设 δ(u,pj(u))≤δ(v,pj(v))δ(u,pj(u))≤δ(v,pj(v)),则有 δ(u,pj(u))≤12(d(u,v)+ω(x,y))δ(u,pj(u))≤12(d(u,v)+ω(x,y))。 由于 ESj⊆ESj+1ESj⊆ESj+1,(pj(u),u)(pj(u),u) 的最短路将会在 (ESj+1∪E(s))(ESj+1∪E(s)) 中被保留。因此有 ˆδ(pj(u),v)≤δ(pj(u),u)+δ(u,v)^δ(pj(u),v)≤δ(pj(u),u)+δ(u,v)。 整理后有 δ(u,pj(u))+ˆδ(pj(u),v)≤2δ(u,v)+ωuvδ(u,pj(u))+^δ(pj(u),v)≤2δ(u,v)+ωuv。 因此这是一个 (2,ω)(2,ω) 近似。 E|Si|=n1−i/kE|Si|=n1−i/k,因此每一层的 Dijkstra 总复杂度均为 ˜O(n2+1/k)~O(n2+1/k)。当 k=lognk=logn 时,总复杂度为 ˜O(n2)~O(n2)。 |
将其与 stretch 33 的算法作比较,我们发现在 stretch 33 中,ballball 是一个单侧的信息,算法中并没有考虑 ball(u),ball(v)ball(u),ball(v) 如何覆盖 (u,v)(u,v) 的最短路,因此只能得到一倍而非半倍的 δ(u,p(u))δ(u,p(u))。
令 ballj(u):={x∈V∣δ(v,x)<δ(v,Sj)}ballj(u):={x∈V∣δ(v,x)<δ(v,Sj)}。若边 (x,y)(x,y) 满足 x,y∈ballj(u)x,y∈ballj(u),则 (x,y)∈ESj(x,y)∈ESj。
在 (2,ω)(2,ω) 的算法中,我们分了许多层,这是因为我们要用两个 ballball 覆盖 PuvPuv。Puv⊆ESj+1Puv⊆ESj+1 要求关键点更疏松,而 δ(u,pj(u))+δ(v,pj(v))≤d(u,v)+ω(x,y)δ(u,pj(u))+δ(v,pj(v))≤d(u,v)+ω(x,y) 要求关键点更密集。具体地,
-
如果 ballj(u)∩ballj(v)≠∅ballj(u)∩ballj(v)≠∅,Puv⊆ESjPuv⊆ESj,但是 δ(u,pj(u))+δ(v,pj(v))δ(u,pj(u))+δ(v,pj(v)) 得不到保证。
-
否则,如果存在 z∈Puv,z∉ballj(u)∪ballj(v)z∈Puv,z∉ballj(u)∪ballj(v),则 δ(u,pj(u))+δ(v,pj(v))≤δ(u,z)+δ(z,v)=δ(u,v)δ(u,pj(u))+δ(v,pj(v))≤δ(u,z)+δ(z,v)=δ(u,v),但此时 PuvPuv 不一定在 ESjESj 中。
-
否则,一定是 ballj(u),ballj(v)ballj(u),ballj(v) 不交地覆盖了 PuvPuv,令 (x,y)(x,y) 满足 x∈ballj(u),y∈ballj(v)x∈ballj(u),y∈ballj(v)。当且仅当是这种情况时,上述的算法只能做到 δ(u,v)≤2d(u,v)+ω(x,y)δ(u,v)≤2d(u,v)+ω(x,y)。
此时,我们可以令 jj 继续向下,直到 ballj′(u),ballj′(v)ballj′(u),ballj′(v) 不再覆盖 PuvPuv 上所有的点了,那么 PuvPuv 除了一条边 (x,y)(x,y) 都在 ESj′+1ESj′+1 中,而此时 δ(u,pj(u))+δ(v,pj(v))≤δ(u,v)δ(u,pj(u))+δ(v,pj(v))≤δ(u,v),这样我们就做到了 22 近似。
算法 2 (带权图上的 ˜O(m√n)~O(m√n) 的 22 近似) 令 S0=V⊇S1⊇…⊇Sk−1⊇Sk=∅S0=V⊇S1⊇…⊇Sk−1⊇Sk=∅,k=⌈logn⌉k=⌈logn⌉。 对 0≤i<k0≤i<k,
将 min0≤i<k(δ(u,pi(u))+ˆδ(pi(u),v),δ(v,pi(v))+ˆδ(pi(v),u))min0≤i<k(δ(u,pi(u))+^δ(pi(u),v),δ(v,pi(v))+^δ(pi(v),u)) 作为 u,vu,v 的答案。 令 jj 满足 (u,v)(u,v) 最短路上所有的边 PuvPuv 在 ESj+1ESj+1 中但至少有一条边不在 ESjESj 中,假设这一条边为 (x,y)(x,y),那么 x∈ballj(u),y∉ballj(u)x∈ballj(u),y∉ballj(u),前者根据引理是显然的,后者是因为 ω(x,y)≥d(x,pj(x))⇒d(u,x)+ω(x,y)≥d(u,x)+d(x,pj(x))≥d(u,pj(u))ω(x,y)≥d(x,pj(x))⇒d(u,x)+ω(x,y)≥d(u,x)+d(x,pj(x))≥d(u,pj(u))。因此 ballball 与 EE 是双向对应的关系。
因此这是一个 22 近似。 如果我们将图中每个点以 qq 的概率选入 SS 中(0<q<10<q<1),则 ∀u,E[ball(u)]≤1/q∀u,E[ball(u)]≤1/q。 若 xx 出现在了 ballj(u)ballj(u) 中共 t(x)t(x) 次,那么计算 ball(u)ball(u) 以及枚举 yy 的复杂度均为 O(∑x∈Vt(x)deg(x))O(∑x∈Vt(x)deg(x))。易知 {u∣x∈ballj(u)}={u∣δ(u,x)<δ(u,Sj)}{u∣x∈ballj(u)}={u∣δ(u,x)<δ(u,Sj)},称其为 CSj(x)CSj(x)。 定理 给定 qq(0<q<10<q<1),可以在期望 O(m/qlogn)O(m/qlogn) 的时间内找到一个大小为 O(nqlogn)O(nqlogn) 的集合 SS,使得 ∀x∈V,|CS(x)|≤1/q∀x∈V,|CS(x)|≤1/q。 令 SiSi 的构造如下:先找到大小为 O(√n)O(√n) 的集合 SS,满足 ∀x∈V,|CS(x)|≤√nlogn∀x∈V,|CS(x)|≤√nlogn,再构造 A0=V⊇A1⊇A2⊇…⊇AkA0=V⊇A1⊇A2⊇…⊇Ak,其中 AiAi 通过将 Ai−1Ai−1 中的每个元素以 n−1/2kn−1/2k 的概率加入得到。最终令 Si=Ai∪SSi=Ai∪S。这样 |Ai||Ai| 主导了集合大小,因此每两层之间的集合大小比例也得到了保证。 此时计算 SS 的复杂度为 O(m√n)O(m√n),计算 ballball 的复杂度为 O(m√n)O(m√n),枚举 yy 的复杂度为 ˜O(m√n)~O(m√n),运行最后一层 SkSk 的复杂度为 ˜O(m√n)~O(m√n)。因此总复杂度为 ˜O(m√n)~O(m√n)。 |
我们现在证明上文关于 C(x,S)C(x,S) 的定理。
定义 CA(x)={v∈V∣δ(v,x)<δ(v,A)},ballA(v)={x∈V∣δ(v,x)<δ(v,A)}CA(x)={v∈V∣δ(v,x)<δ(v,A)},ballA(v)={x∈V∣δ(v,x)<δ(v,A)}.
x∈ballA(v)⇔v∈CA(x)x∈ballA(v)⇔v∈CA(x),因此
而对于右式,我们只要以 q′q′ 的概率将 w∈Ww∈W 选入 AA 中,它就 ≤n/q′≤n/q′。因此我们可以很直接地得到 Ev∈VE[∑w∈W|CA(w)|]≤1/qEv∈VE[∑w∈W|CA(w)|]≤1/q。问题在于这是一个平均意义的结果。
令 W0=V,A0=∅W0=V,A0=∅。Wi={w∈V∣|CAi(w)|>n/q}Wi={w∈V∣|CAi(w)|>n/q},即不满足条件的所有 ww。每次以 4q4q 的概率将 w∈Wiw∈Wi 加入 Ai+1Ai+1。则有
根据 Markov 不等式,有 1212 的概率有
而 Wi+1⊆WiWi+1⊆Wi,因此
因此每次有 1212 的概率 |Wi||Wi| 减半。我们因此只需要 lognlogn 轮便能让 Wi=∅Wi=∅。期望加入了 8nqlogn8nqlogn 个点。
也就是说,考虑那些没有满足条件的 WiWi 和一个 x∈Vx∈V,如果最初有许多 w∈Wiw∈Wi 满足 x∈CA(w)x∈CA(w),现在考虑离 xx 最近的加入了 AA 的那个 w∗w∗,在它之后的所有 ww 都把 xx 从 C(w)C(w) 中剔除掉了,因此降速是根号的。
对于 33 近似,时间上做到 O(n2)O(n2) 是简单的,因为 (2,ω)(2,ω) 近似已经是一个 33 近似了。但是想要做到 O(n3/2)O(n3/2) 空间查询,我们发现,第 ii 层的空间为 O(n|Si|)O(n|Si|),每一层的空间利用并不均匀。因此我们把前若干轮用另一种算法解决。选择一个大小为 n−12n−12 的集合 SS,计算 ballS(u)ballS(u),如果 v∈ballS(u)∨u∈ballS(v)v∈ballS(u)∨u∈ballS(v),我们可以利用一个双层哈希表 O(1)O(1) 回答 δ(u,v)δ(u,v),其他情况通过令 S0=SS0=S 解决。
算法 2 (带权图上的 O(min(m√n,n2))O(min(m√n,n2)) 时间、O(n32)O(n32) 空间 33 近似) 将图中每个点以 n−12n−12 的概率加入 SS 中。 对所有的 u∈Vu∈V,计算 ballS(u)ballS(u)。 令 S0=S⊇S1⊇…⊇Sk−1⊇Sk=∅S0=S⊇S1⊇…⊇Sk−1⊇Sk=∅,其中 SiSi 通过将 Si−1Si−1 中的每个元素以 |S|−1/k|S|−1/k 的概率加入得到。k=⌈log|S|⌉k=⌈log|S|⌉。 对 0≤i<k0≤i<k,
若 v∈ballS(u)v∈ballS(u),使用双层哈希表 O(1)O(1) 查询 δ(u,v)δ(u,v),否则将 min0≤i<k(δ(u,pi(u))+ˆδ(pi(u),v),δ(v,pi(v))+ˆδ(pi(v),u))min0≤i<k(δ(u,pi(u))+^δ(pi(u),v),δ(v,pi(v))+^δ(pi(v),u)) 作为 u,vu,v 的答案。 令 jj 满足 (u,v)(u,v) 最短路上所有的边 PuvPuv 在 ESj+1ESj+1 中但至少有一条边不在 ESjESj 中,如果不存在这样的 jj,说明 Puv⊆ES0Puv⊆ES0,因此 v∈ballS(u)v∈ballS(u)。假设这一条边为 (x,y)(x,y)。根据引理,我们有 δ(u,pj(u))≤δ(u,y)δ(u,pj(u))≤δ(u,y) 和 δ(v,pj(v))≤δ(v,x)δ(v,pj(v))≤δ(v,x),因此 δ(u,pj(u))+δ(v,pj(v))≤d(u,v)+ω(x,y)δ(u,pj(u))+δ(v,pj(v))≤d(u,v)+ω(x,y)。不失一般性地假设 δ(u,pj(u))≤δ(v,pj(v))δ(u,pj(u))≤δ(v,pj(v)),则有 δ(u,pj(u))≤12(d(u,v)+ω(x,y))δ(u,pj(u))≤12(d(u,v)+ω(x,y))。 由于 ESj⊆ESj+1ESj⊆ESj+1,(pj(u),u)(pj(u),u) 的最短路将会在 (ESj+1∪E(s))(ESj+1∪E(s)) 中被保留。因此有 ˆδ(pj(u),v)≤δ(pj(u),u)+δ(u,v)^δ(pj(u),v)≤δ(pj(u),u)+δ(u,v)。 整理后有 δ(u,pj(u))+ˆδ(pj(u),v)≤2δ(u,v)+ωuv≤3δ(u,v)δ(u,pj(u))+^δ(pj(u),v)≤2δ(u,v)+ωuv≤3δ(u,v)。 因此这是一个 33 近似。 计算 ballS(u)ballS(u) 的复杂度为 O(m√n)O(m√n),为了加速,我们可以对于每个 xx,将所有出边 (x,v)(x,v) 按照 δ(v,S)−ω(x,v)δ(v,S)−ω(x,v) 排序。从每个 uu 开始 BFS 到 xx 时,我们取出所有满足 δ(u,x)+ω(x,v)<δ(v,S)δ(u,x)+ω(x,v)<δ(v,S) 的 vv,这是序列的一个前缀,而这同时是 v∈CS(u)v∈CS(u) 的充要条件。而所有满足这个条件的 vv,由于 ω(x,v)<δ(v,S)ω(x,v)<δ(v,S),复杂度不超过 E[|ballS(u)||ES(u)|]≤E|ballS(u)|2E[|ballS(u)||ES(u)|]≤E|ballS(u)|2,后者等于 ∑n−1i=1i2(1−√n)i√n≤2n∑n−1i=1i2(1−√n)i√n≤2n。因此复杂度为 O(min(m√n,n2))O(min(m√n,n2))。 总时间复杂度为 O(min(m√n,n2))O(min(m√n,n2)),空间复杂度为 O(n3/2)O(n3/2)。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!