news 2026/10/23 18:58:43

分治算法学不透?画图吃透最近点对问题与Python实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分治算法学不透?画图吃透最近点对问题与Python实现

学算法这么多年,我有一个很深的体会:有些代码你盯着看一百遍,觉得每一步都懂,可一旦合上书,让你自己把思路讲给别人听,却像隔着一层雾,怎么都说不清楚。“最近点对问题”的分治法就是非常典型的一个。你知道思路大概逃不开“分割、递归、合并”,也见过网上那份常见的解法代码,但为什么合并的时候只需要看中间那条窄窄的条带?为什么说最多检查几个点就够了?分治的复杂度到底是怎么从 O(n²) 降下来的?很多人其实没有真正想明白。

这篇文章我想用一种特别直观的方式把这个算法讲透——画图。不是“画图辅助理解”,而是把图画到位之后,整个算法的正确性自己就在纸上跑起来了。适合三类人看:正在学《算法设计与分析》的本科生,准备面试时想系统梳理分治套路的人,以及那些代码能跑但心里总觉得差点什么的同学。我会把算法原理、常数级检查点的严格证明、可直接复现的 Python 实现,以及我在实际写代码时踩过的几个隐蔽的坑,一次性掰开讲清楚。

1. 为什么最近点对问题值得画图画到底

1.1 暴力解法为什么撑不住

问题本身的描述很简单:平面上给定 n 个点,任意两个点之间有一条欧氏距离,请你找出距离最短的那一对点。输入是若干个 (x, y) 坐标,输出是那一对点的坐标和它们之间的距离。

最朴素的做法不需要任何思考:两层循环枚举所有点对,每对一个算一次距离,记录最小值。代码十行不到。但它的复杂度是 O(n²),n 到一万就要算一亿次距离,n 到十万就是一百亿次,随便一个正经一点的工程数据集都能把这种写法直接拖垮。所以我们需要更聪明的策略。

分治法的宏观思路,用一句话概括就是:把点集按 x 坐标切成左右两半,分别递归求两半内部的最小点对距离,再考虑“一个点在左半、一个点在右半”的跨界点对能不能打破当前纪录。这个思路本身谁都能说出来,但真正的难点全部集中在最后那步“跨界合并”上。

1.2 几何问题要用几何直觉

我见过很多同学卡在这一章,卡住的共同原因都是同一个:他们试图用纯代数推导去理解合并步骤,硬推那个“为什么只需要检查常数个点”。这其实是把简单问题复杂化了。最近点对问题本质上是一个平面几何问题,几何问题就该用几何的方式去处理。把图画对了,很多证明步骤甚至可以直接在图上“看见”,而不是在公式里推出来。

这也是我这篇文章坚持把“画图表示”放在标题里的原因。我用第六感打个比方:暴力解法就像在一个小区里挨家挨户敲门找一个人,分治法则是先通过地图把小区切成两个片区,再决定哪些区域基本不可能有答案,直接划掉不看。地图是从哪来的?就是画图画出来的。

1.3 画图要画到什么程度

很多初学者理解的“画图”,就是在白板上随手画几个点,然后草草画一条竖线,比自己假装看懂了,或者画给老师看。这远远不够。我建议要做到下面这个程度:

  • 画完竖线分割之后,能把左右两个子集的点分别圈出来。
  • 递归返回之后,能把左右各自的最小点对用线段连出来。
  • 做合并之前,能准确画出那条 2d 宽度的中间条带区域,并且能把条带里的点重新按 y 坐标从上到下排个序。
  • 最后能把那个“为什么最多检查常数个点”的 d×2d 矩形画出来,还能把矩形内部切成若干小格子,用抽屉原理说明格子里的点不可能密集。

每一张图画完,对应的算法部分在你脑子里的印象就会深一个档次。下面几节,我就带着你一张一张画。

2. 一图看懂分治主框架:从全局到局部的递归拆分

2.1 第一张图:用一条竖线把点集劈成两半

拿到点集之后,第一步是把所有点按 x 坐标从小到大排序,然后取中位数位置的那个点的 x 值作为分割线。这里的“中位数”不是算术平均值,而是排序后处在中间位置的那个点的 x 坐标。为什么不能随便找一条线?因为如果左右两部分点数量严重不均衡,递归的层数就会变深,复杂度会退化。用中位数能保证每次左右各约 n/2 个点,递归树是平衡的。

在图上,你看到的是一张坐标纸,上面散布着若干黑点,中间一条竖直虚线把点群一分为二。左半边大约 n/2 个点,右半边大约 n/2 个点。注意:这条线画在哪个 x 值上,不是靠肉眼“差不多居中”,而是靠排序之后精确取出来的中位数。这里画图多精确都不为过。

然后问题就变成三个子问题:左半内部的最近点对、右半内部的最近点对、以及跨越了这条虚线的一左一右两个点组成的点对。前两个是原问题在规模减半后的拷贝,所以我们递归去解。第三个是合并时单独需要处理的。

2.2 第二张图:递归返回时画出左右各自的答案

对左右两半分别递归求解,这个“递归”在图上怎么理解?你可以想象成把刚才那张大图放大镜一分为二,每一半各自重复一遍“切分、递归、合并”的过程,直到某一半的点很少(不超过 3 个),直接暴力求解就算完了。

递归逐层返回时,你的图上会出现一些短线。假设左半内部已经找到了距离最短的一对点,就在这两个点之间连一条红色线段,记这段距离为 dL。右半内部同理,连一条蓝色线段,距离为 dR。取两者较小的那个作为 d = min(dL, dR),把对应的点对记为当前全局最优。

这里有一个关键直觉:如果跨界点对之间的距离想要比 d 还小,那么这两个点各自的 x 坐标距离分割线都不能太远。为什么?因为两点之间的欧氏距离一定不小于它们在 x 方向上的投影距离。换句话说,如果其中一个点在左半,距离分割线超过了 d,那它就算贴着分割线,到另一个点的 x 方向距离也已经 ≥ d,实际距离必然大于等于 d。距离分割线超过 d 的点,根本没资格参与“破纪录”的竞争。

2.3 第三张图:中间那条 2d 宽的灰色条带

把上述推理用到图上,就是第三张图:以你画的那条虚线为中心,左左右右各留出宽度 d,整个区域就是一条宽度为 2d 的竖直条带。画完之后你会发现,只有落在条带内部的点,才可能和另一侧的点构成比 d 更短的距离。条带外面的点,无论怎么配对,x 方向距离已经超了,可以直接无视。

这一步把“跨界点对问题”从全平面缩减到一条细长的条带里,范围小了很多。但条带里面仍然可能有很多点,如果直接 O(n²) 两两比较,分治就没有意义了。所以问题继续收窄:条带里的点需要按 y 坐标从上到下排序,然后从下往上扫描每个点,只检查它“上方”距离足够近的几个邻居。这个扫描过程就是我们下一节要重点讲的“6 点”(严格说是常数个点)的秘密。

3. 中间的“致命6点”:合并层为什么只需要看这么多点

3.1 第四张图:d×2d 的矩形窗口

假设条带里的点已经按 y 坐标升序排列好了。我们从最下面的点开始,逐个往上处理。对于当前点 p,我们真正需要担心的是:有没有另一个点 q,它也在条带里,且满足下面两个条件:

  • q 的 y 坐标大于 p 的 y 坐标(我们只要向后看就行,前面的已经处理过了)
  • q 与 p 的 y 坐标差小于 d(因为如果 y 差超过了 d,两点距离必然更大,没有希望破纪录)

把这两个条件画到图上,就是以 p 为底边中点、向上延伸的一个矩形:水平方向宽度为 d,竖直方向高度为 2d。等一下,为什么宽度是 d 而不是 2d?因为 p 本身一定落在条带里,它距离分割线的 x 方向距离小于 d。而 q 也在条带里。两点的 x 坐标差可能最大是多少?考虑 p 在最左边边缘、q 在最右边边缘这种极端情况,x 方向距离小于 2d,这个矩形宽应该是 2d 才保险。对,这里要格外仔细,你画图的时候把这个矩形画成宽 2d、高 2d 也说得通。但如果你用“只看 y 差小于 d 的上方点”这个扫描逻辑,实际需要覆盖的矩形宽度应该是多少?想想看。

稳妥起见,我们以 p 为基准,画一个宽 2d、高 2d 的正方形区域放在 p 的上方,再把这个正方形划分成 4×4 的 16 个小正方形,每个小正方形边长为 d/2。任何一个小正方形里如果出现了两个点,这两个点之间的距离最多是 d/√2,严格小于 d,这就和“d 是左右递归结果里已经不存在更近点对”矛盾了。所以每个小正方形里最多只能有一个点。p 所在的这一行有 4 个格子,p 正上方一直到顶还有 12 个格子,减掉 p 自己占的 1 个,理论上最多 15 个点。这还不是我们想要的结论。所以要继续画图。

3.2 第五张图:把矩形切成小格,看清“每格唯一”

我们换一种更经典的分格方法,直接得到干净的常数结论。考虑以 p 为左下角,向上画一个宽 d、高 2d 的矩形窗口。把这个窗口横着切成 2 列、竖着切成 4 行,得到 8 个边长 d/2 的小方格子。现在从抽屉原理的角度数一数:窗口内如果存在两个点落在同一个小格子里,它们的最远距离是格子对角线的长度,也就是 d/√2,小于 d,这不可能。所以每个小格子里最多只有一个点。

p 自己在窗口的左下角某个位置。真正能被 p“看见”的候选点,是这个 8 格窗口内部的其他点。8 个格子减掉 p 占的一个,最多只剩 7 个点。也就是说,对于任意一个点 p,它在条带内只需要向后检查最多 7 个点,就足够保证不会漏掉任何更短的点对。这就是“检查常数个点”的来历。

现在你可能会问:很多教材和博客明明写的是“最多检查 6 个点”,你这怎么是 7?这里我解释一下:6 和 7 的差别来自网格划分方式和边界条件的细节处理,本质上两种证明都得出了同一个关键结论——最多只需要检查常数个点,这个常数不随 n 变化。至于到底是 6 还是 7,对算法的正确性和复杂度没有任何影响。你在图上画出 8 格窗口,实际数出来多少就是多少,不用纠结教材之间的数字差异。

3.3 为什么“常数个”这么值钱

这一节的核心,是把“最多检查 7 个点”和算法整体复杂度挂钩。条带里总共有 O(n) 个点,每个点检查 O(1) 个后继点,所以合并整个条带需要 O(n) 的时间。如果我们每次合并时对条带按 y 排序的代价暂时不计,那分治的递归式就是 T(n) = 2T(n/2) + O(n),根据主定理,结果是 O(n log n)。这个复杂度比 O(n²) 提升了好几个档次,n 是十万时两者的差距要按百万倍算。

你可以这样记住:分治法把“全平面找最近点对”这个大搜索,压缩成了“窄条带里的线性扫描”。窄条带本身是 O(n) 大小,扫描时每个点又不看太多邻居,整体就变成了 O(n log n)。如果哪一步的图画清晰了,这个复杂度的每一步几乎都是可视的。

4. 画图驱动实现:把几何直觉翻译成可运行代码

4.1 递归边界该怎么处理

有了前面的图,实现时每一步要做什么就很清楚了。我直接给一份完整可运行的 Python 实现,你对照着第二、三节的图来读,会发现每一段代码都对应纸上的某一块图形。

递归边界:当子问题里只剩 1 个点时,不存在点对,返回无穷大;剩 2 个点时,返回这两点的距离和点对;剩 3 个点时,直接暴力枚举 3 对点。为什么边界设在 3?因为当 n 是 2 或 3 时,暴力枚举的点对数量很少,比继续分割再合并还省事,而且还能避开后面合并时条带过窄、候选点不足的麻烦。

下面是完整代码:

import math def dist(p, q): return math.hypot(p[0] - q[0], p[1] - q[1]) def brute_force(pts): """点数很少时直接暴力求解""" n = len(pts) best = math.inf best_pair = None for i in range(n): for j in range(i + 1, n): d = dist(pts[i], pts[j]) if d < best: best = d best_pair = (pts[i], pts[j]) return best, best_pair def closest_pair(points): """分治法入口:先按 x 坐标排序""" pts = sorted(points, key=lambda p: (p[0], p[1])) return _closest(pts) def _closest(px): n = len(px) if n == 1: return math.inf, None if n == 2: return dist(px[0], px[1]), (px[0], px[1]) if n == 3: return brute_force(px) # 用中点索引切分,注意分割线的 x 坐标取右半部分最左点的 x mid = n // 2 mid_x = px[mid][0] dl, pair_l = _closest(px[:mid]) dr, pair_r = _closest(px[mid:]) if dl < dr: d, pair = dl, pair_l else: d, pair = dr, pair_r # 合并:只收集离分割线 x 方向距离小于 d 的点 strip = [p for p in px if abs(p[0] - mid_x) < d] # 按 y 坐标排序后,从上到下扫描 strip.sort(key=lambda p: p[1]) for i in range(len(strip)): for j in range(i + 1, len(strip)): # 如果 y 方向差已经 >= d,后面的点更不可能更近 if strip[j][1] - strip[i][1] >= d: break dd = dist(strip[i], strip[j]) if dd < d: d = dd pair = (strip[i], strip[j]) return d, pair

这段代码很短,但已经有非常多的细节值得展开讲。分割线那条竖线,我取的是 px[mid][0],也就是右半部分索引位置在那里的那个点的 x 坐标。条带筛选用的是严格小于 d 的 abs(x - mid_x) < d。这里为什么用“严格小于”而不是“小于等于”?因为如果两点到分割线的 x 距离恰好等于 d,那它们的实际距离至少是 d,不可能打破当前纪录,所以不需要纳入候选。这一点画图的时候很容易验证:条带边界上的点,就算成对出现,也最多打平,不会突破。

4.2 合并扫描的循环为什么可以提前 break

内层循环有很多同学看不懂,问为什么 y 差 ≥ d 就可以 break。原因很简单:strip 已经按 y 升序排好了,所以对于固定 i,随着 j 增大,strip[j][1] 只会越来越大,y 方向的差值单调递增。一旦某个 j 的 y 差 ≥ d,后续所有 j 的 y 差只会更大,距离只会更远,继续循环纯属浪费时间,直接跳出内层循环。

这也是“检查常数个点”在代码层面的体现:因为 y 差一旦到 d 就必须停,能进入内层循环体继续比较的点,一定只有 y 坐标差不超过 d 的那少数几个。结合上一节的 8 格窗口论证,这个数量有上界。所以内层循环在数据量很大的时候,摊下来每个点只多做几次距离计算,不会让复杂度飙升。

4.3 一个完整的小例子,纸笔跟着跑一遍

给你一组我能手工算清楚的小数据,你可以自己在纸上画一画、验一验:

点集:(0,0), (1,1), (2,4), (3,1), (4,6)。

按 x 排序后还是这 5 个点。中位索引是 2,分割线 x = 2。左半是 (0,0) 和 (1,1),递归暴力求出最近点对距离 √2 ≈ 1.414。右半是 (3,1) 和 (4,6),暴力求出距离 √10 ≈ 3.162。所以当前 d = √2。条带筛选条件是 |x - 2| < 1.414,于是 (1,1) 和 (3,1) 被选中。按 y 排序后是 (1,1)、(3,1)。检查这对点距离是 2,大于 1.414,不更新。最终答案是 (0,0) 和 (1,1),距离 √2。把这个过程对着图走一遍,比看十遍代码都管用。

4.4 复杂度优化:预排序按 y 归并的思路

我在上面给出的简洁代码里,合并时对 strip 做了一个 sort,这是 O(k log k),k 是条带内点数。每一层递归都这么干,总复杂度是 O(n log² n),虽然比暴力的 O(n²) 好了很多,但和理论最优 O(n log n) 还有差距。

优化思路也不难理解:与其每层都重新排序,不如在入口处先对点集按 x 排序,同时维护一份按 y 排序的列表。递归过程中,按 x 排序的列表正常切分,按 y 排序的列表也跟着切分,合并时用归并的方式把两个子列表按 y 合并回一份有序列表,这样合并这一层的成本就从排序降为线性归并。于是递归式重新变成 T(n) = 2T(n/2) + O(n),主定理直接给出 O(n log n)。实际工程中,如果 n 是十万量级,这份优化能明显感知到;如果只是几万点,简洁版也完全够用,没必要为了常数因子徒增代码复杂度。

5. 排错实录:画图踩过的三个坑与验证方法

5.1 坑一:分割线的 x 取错导致条带漏点

我第一次给学生调这段代码时,遇到的第一个诡异问题就是:明明暴力解法能找到更近的点对,分治结果却漏掉了。后来我让他把每一层递归的分割线画出来,才发现问题出在分割线 x 的取值上。有人习惯取 px[mid] 和 px[mid-1] 两个 x 的平均值,也有人直接取左半最右点的 x,还有人图省事直接取了索引 mid 的 x 但没意识到这时一半点可能都在右半。

还有更隐蔽的:如果点集里有大量 x 坐标相同的点,分割线 x 附近会挤着很多点。此时条带筛选用 abs(p[0] - mid_x) < d,只要 d 比这些点之间的间距大,它们就会被同时纳入条带,扫描的时候两两检查,程序依然正确。但如果把分割线取成了左右两个点的 x 均值,而中间根本没有点落在这个均值上,就有可能出现一个本来应该被条带覆盖的跨界点和那根虚拟分割线之间 x 距离略大于或小于 d,导致边界处漏判。画图时最容易发现这个问题:你画完条带后,用两根竖线标记边界,再看看最接近边界的那几个点是否真的都在条带里,如果在边界外却又和另一侧的点形成了更短距离,那就是分割线取错了。

5.2 坑二:条带没有按 y 排序,顺序混乱导致漏解

有一次我拿到一份看起来完全正确的实现,跑随机数据也能对上,但一压到大数据就总差一点点。我也没有怀疑排序,直到我故意在代码里写了一个按 x 排序的 bug,并把 strip 打印出来看,才发现原来的代码在合并前把 strip 按 x 坐标又排了一遍,导致 y 扫描完全失效。这个错误特别容易发生在从别的语言翻译代码时,因为 Python 的 sort 用起来太顺手,一不当心就把 sort key 写成了 p[0]。

正确做法是 strip.sort(key=lambda p: p[1]),然后从下往上扫。画图时,你需要把条带区域单独拎出来,把里面的点按 y 坐标从低到高写上序号,1、2、3……然后你就能直观看到扫描顺序:序号小的先处理,每个点只往后看序号更大的邻居。只要遵循这个顺序,“当前点只看上方”就不会漏掉任何点对,因为任意一对点总有一个是下方那个,它会负责检查上方的那个。

5.3 坑三:浮点数比较和大数初始值

最近点对问题涉及开方运算,距离都比较的是浮点数。这里有几个常见的坑:一是初始值用很大的数,但不能用整数 999999,位置坐标一变大就直接爆炸,务必用 math.inf;二是浮点数计算时,距离接近 d 但实际相等的情况非常常见,代码里直接用 < 判断就行,不用为“恰好相等”做特殊处理,因为就算漏掉一个距离完全相等的点对,返回值也不会有问题;三是随机生成坐标时,如果允许重复点,可能出现距离为 0 的点对,这会让 d 变成 0,条带宽度变成 0,后续扫描只剩一个点,程序照样能返回距离 0,逻辑是对的,但你可能被 0 宽度条带吓一跳。

验证方法就是我常说的“暴力对拍”:随机生成几百组数据,每组几百到几千个点,同时用暴力解法和分治解法跑,比较结果。如果分治解法有任何一处逻辑错误,对拍几乎必现。这就是画图之外最有力的调试武器。

6. 进阶:从平面到高维,以及我画图时的一些心得

6.1 高维空间为什么不能直接套“检查常数个点”

最近点对问题在二维平面上有非常优美的分治解法,但如果你把这个思路直接搬到三维,就会发现“最多检查常数个点”不成立了。三维的情况,条带换成一个厚度为 2d 的薄片,薄片里需要检查的候选点数量会随着 d 的变小而激增,因为这个薄片可以容纳的点变多了。更高维的空间就更不用说了,分治合并那一步的候选数量会迅速膨胀,常数不再是常数。所以高维最近点对通常用其他方案,比如 KD 树这类空间索引结构,或者近似算法。

这样对比着学很有好处:正是因为二维版的常数结论来自平面几何的特殊性质,它才显得珍贵。你画图的时候能清楚看到“二维的格子填充”这个论证在三维里复制不了,因为你无法用一层薄片分隔足够多的候选点。这种理解只有在图面上才特别直观。

6.2 真实应用场景里,这个算法解决什么问题

最近点对不是一道单纯的作业题。在图形学里,碰撞检测经常需要快速判断很多物体之间有没有靠得太近的;在计算几何里,它是很多空间聚类算法的基础步骤;在分子结构比较里,也需要快速找出两堆原子之间最近的距离。这类场景有一个共同特点:数据量动辄上万,点分布在低维空间里,而且你只关心“最接近的那一对”,而不是所有接近的配对。分治法在这种场景下就是高效且简单的选择。

如果你只是处理几十万个点,又不想引入 KD 树这种结构,分治法完全可以胜任。实测下来,n = 10 万随机点,Python 简洁版大概在一秒多跑完,暴力法基本要等几分钟。这个差距在真实项目的实时性要求下,就是能不能用的区别。

6.3 可视化工具与画图的小技巧

我自己的习惯是先用方格纸手画,或者用平板上的手写笔记画,把条带和网格的几何关系真的落在纸上;确认无误了,再用 Python 的绘图库把整个递归过程可视化成图,直接看到每一层的分割线、条带、候选点。画图代码不必写得很复杂,核心就是散点图加几条竖线,再标出当前最优的那条连线。可视化有个额外的好处:你能看到递归合并时条带里实际进去多少个点,通常远远不到 7 个,这对建立常数级的直觉非常有帮助。

画图的时候有一个我一直提醒自己的原则:不要只画“成功路径”的图,也要把“候选区域外的点”画出来,用灰色标记它们被排除的原因。灰色点存在感越强,你对“为什么这些点不用看”的理解就越刻骨铭心。很多教程只画条带里的点,把被排除的点藏掉了,这反而让读者丢掉了一半的直觉。

6.4 最后一点经验之谈

用画图驱动学算法这件事,最初是我在准备一次分享时被逼出来的。当时我需要把一个算法给刚入门的人讲明白,代码讲了两轮,大家还是处于“好像懂了,又好像没懂”的状态。后来我换了个讲法,先画图,画完图再写代码,效果立竿见影。从此我养成了一个习惯:遇到一个平面几何算法,先别急着写循环,先问自己能不能把要处理的对象画出来,画不出来说明你没想透,画出来了代码基本就是照着图翻译。

最近点对的代码实现,在网上一搜一大把,但能真正说清楚“为什么合并时只看常数个点”的并不多。如果你按这篇文章的思路,先把分割图画出来,再把条带图画出来,最后把 d×2d 矩形网格画出来,你会发现这个算法留下的印象比别人直接甩给你十段代码都深。找一个点集,拿张纸,从第一步开始画吧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 6:48:31

从申报书到可运行原型:一份工程化拆解指南

简介&#xff1a;本资源为2018年浙江省大学生科技创新活动计划&#xff08;新苗人才计划&#xff09;项目申报书&#xff0c;面向高校学生与指导教师&#xff0c;聚焦「基于人脸识别的智能家居控制系统设计」这一应用基础研究课题。申报书完整呈现了项目概述、目标意义、实施路…

作者头像 李华
网站建设 2026/10/19 15:19:39

开源本地 AI 智能体 OpenClaw ,部署常见报错修复汇总

&#x1f4cc; 说明 本文基于 OpenClaw 版本展开讲解&#xff0c;全程采用图形化、可视化的交互模式。整合包已内置全部运行依赖&#xff0c;即便是普通用户&#xff0c;也能轻松复现完整的部署流程。 ✨核心亮点&#xff1a; 全程可视化图形交互界面&#xff0c;自动补齐全部运…

作者头像 李华
网站建设 2026/10/18 2:37:55

知识图谱与维基数据:GEO背后的实体信任技巧

AI在决定引用谁之前&#xff0c;会先查一件事——这个品牌在知识图谱里是不是一个“定义清晰、有第三方背书的实体”。如果你的品牌在维基数据或Google知识图谱里没有记录&#xff0c;或者各平台描述不一致&#xff0c;AI的第一反应是降低信任&#xff0c;而不是尝试理解你。 下…

作者头像 李华
网站建设 2026/10/20 10:15:09

分光计实验报告避坑指南:从三轴共面到折射率计算

简介&#xff1a;这份大学物理实验报告文档面向理工科学生及物理实验初学者&#xff0c;聚焦分光计的结构认知与操作训练&#xff0c;重点解决三棱镜顶角测量中仪器调整与读数计算的难点。内容完整覆盖实验目的、仪器清单、自准法与反射法两种测量原理&#xff0c;并详细拆解分…

作者头像 李华
网站建设 2026/10/21 11:53:31

强化学习三大算法DQN/PPO/SAC手写实现与调试指南

简介&#xff1a;本资源是一套面向Python开发者与人工智能学习者的强化学习实践教程&#xff0c;聚焦深度强化学习算法的代码实现与原理说明&#xff0c;适用于高校学生、算法工程师及希望从理论迈向工程落地的进阶学习者。压缩包共384个文件&#xff0c;主体为105个Python源码…

作者头像 李华