Skip to content

5.3 函数的极限、连续与初等函数 ​

上一节讨论数列的极限,自变量只取离散的下标。这一节把极限搬到函数上:当 x 逼近某个点 a 时,f(x) 是否稳定地逼近某个值。函数极限是导数的前身(导数就是差商的极限),连续性是它的第一个应用(函数值等于极限值),而闭区间上连续函数的性质(介值定理、最值定理、一致连续)是后面所有存在性论证的基础。

连续性有三个层次,区别在于「误差控制是否与位置有关」。逐点连续在每个点单独给误差标准;一致连续用同一个误差标准管住整个区间;Lipschitz 连续进一步要求误差与自变量变化成正比。三个层次的关系是包含的:Lipschitz 蕴含一致连续,一致连续蕴含逐点连续,反之都不成立。区分它们的原因在于实际含义不同:逐点连续只保证「函数图像不断开」,一致连续保证「用有限精度的输入得到有限精度的输出」(数值计算的稳定性),Lipschitz 连续还给出误差的定量上界(用于收敛速度估计)。

本节最后一段内容处理连续性的定量刻画与数值实现。连续模 ω(δ) 把「误差控制」写成函数:跨度为 δ 的两点函数值之差的上确界。它的增长速度给出连续的强弱:Lipschitz 与 Hölder 函数的连续模按幂律增长,间断函数的连续模不随 δ 减小。同一段内容还包括极限计算中的数值陷阱:(1−cos⁡x)/x2 这类表达式在 x 很小时直接计算会损失全部有效数字,需要改写成稳定形式。

5.3.1 函数极限 ​

定义与语言 ​

设函数 f 在点 a 的某个去心邻域内有定义。称 x→a 时 f(x) 的极限为 L,若对任意 ϵ>0,存在 δ>0,使得当 0<|x−a|<δ 时

|f(x)−L|<ϵ

记作 limx→af(x)=L。与数列极限的 ε-N 定义相比,控制「靠近」的变量从下标 N 换成距离 δ,结构完全一致:任给误差标准,找到足够小的邻域。

单侧极限把 x 的接近方向分开:左极限 x→a−(a−δ<x<a)与右极限 x→a+。极限存在的充要条件是左右极限存在且相等。

无穷远与无穷大:x→+∞ 时 f(x)→L 的写法是把邻域换成 x>M;f(x)→+∞ 的写法是把误差标准换成任意大的界。

Heine 归结原则:limx→af(x)=L 的充要条件是对任何满足 xn→a(xn≠a)的数列,都有 f(xn)→L。这条原则把函数极限与数列极限连接起来,两个方向都有用:证明极限不存在时构造两个数列给出不同的极限值;证明极限存在时由数列极限的唯一性得到函数极限的唯一性。

运算法则与夹逼:两个函数极限的四则运算与数列版本相同;夹逼定理也相同:若在 a 的附近有 g(x)≤f(x)≤h(x) 且 g、h 的极限都是 L,则 f 的极限也是 L。常用的两个极限 limx→0sin⁡xx=1 与 limx→0(1+x)1/x=e 由夹逼与单调有界定理得到,它们是初等函数求导与展开的基础。

5.3.2 连续与间断点 ​

连续的定义 ​

称 f 在点 a 连续,若 limx→af(x)=f(a)。写成 ε-δ 语言:对任意 ϵ>0 存在 δ>0,当 |x−a|<δ 时 |f(x)−f(a)|<ϵ(注意这里去掉了 x≠a 的限制,连续的定义比极限多要求函数值参与)。

三种说法等价:f 在 a 连续;f 在 a 的左右极限都存在且等于 f(a);f 在 a 处满足 xn→a 时有 f(xn)→f(a)(归结原则)。

间断点按极限的行为分类。可去间断点:左右极限存在且相等,但函数值不等于极限值或函数在该点无定义(如 sin⁡xx 在 x=0,补充定义后连续)。跳跃间断点:左右极限存在但不相等(如符号函数在 x=0,左极限 −1、右极限 1)。第二类间断点:至少一个单侧极限不存在(如 sin⁡(1/x) 在 x=0,1x 在 x=0)。前两类合称第一类间断点。

连续函数的运算:连续函数的和、差、积、商(分母不为零处)仍连续;连续函数的复合仍连续。初等函数(幂函数、指数函数、对数函数、三角函数、反三角函数及其复合)在定义域内连续,这一条使大多数函数的连续性检查可以省略,也让求极限时可以直接代入(这是「直接代入」的合法性来源)。

5.3.3 闭区间上连续函数的性质 ​

闭区间上的连续函数满足三条基本定理,它们的证明都依赖紧性(有界闭集)与连通性(区间)。

最值定理:闭区间 [a,b] 上的连续函数取到最大值与最小值。证明思路是先证有界(用有限覆盖定理把每点的局部有界拼成整体有界),再证上确界被取到(由上确界的第二条性质取一串点,用 Bolzano-Weierstrass 提取收敛子列,由连续性得极限点的函数值等于上确界)。

介值定理:闭区间上的连续函数取到两个函数值之间的任何值。若 f(a)<c<f(b),则存在 ξ∈(a,b) 使 f(ξ)=c。证明用二分法:不断取中点,保留函数值跨越 c 的那一半区间,由区间套定理得到公共点,再由连续性得该点的函数值为 c。零点定理是 c=0 的特例,它是数值求根算法的理论基础(二分法、试位法都建立在它上面)。

反函数连续性:闭区间上严格单调的连续函数的反函数连续。单调性提供一一对应,紧性保证反函数的连续性。

一致连续(Cantor 定理):闭区间上的连续函数一致连续。这条结论把逐点连续升级为整体一致,证明依赖有限覆盖定理,也是「紧集上的连续函数一致连续」的特例。

5.3.4 连续模、Lipschitz 与 Hölder 条件 ​

一致连续的定义 ​

f 在集合 E 上一致连续,若对任意 ϵ>0,存在 δ>0(只依赖 ϵ,不依赖具体位置),使得对一切 x,y∈E 满足 |x−y|<δ 时 |f(x)−f(y)|<ϵ。

与逐点连续的差别在 δ 的选择:逐点连续允许每个点用自己的 δ,一致连续要求整个集合共用。反例说明这个差别是实质性的:f(x)=x2 在 \R 上逐点连续但不一致连续(同样大的 ϵ,x 越大需要的 δ 越小,取 x,y 相距 x2+1−x 就能让函数值差 1,而这个距离随 x 增大趋于零,不存在统一的 δ);f(x)=sin⁡(1/x) 在 (0,1] 上连续但不一致连续(摆动幅度不随距离减小)。

判定要点:在闭区间上,连续与一致连续等价(Cantor 定理);在开区间或无穷区间上,一致连续需要额外条件,常见的充分条件是导数有界(Lipschitz 条件)。

连续模 ​

把一致连续的量写成函数,得到连续模:

ω(δ)=sup{|f(x)−f(y)|:x,y∈E, |x−y|≤δ}

ω 随 δ 单调不减,ω(0+)=0 当且仅当 f 在 E 上一致连续。它对连续性的强弱给出定量分级:

f 满足 Lipschitz 条件(记为 f∈Lip(L)):|f(x)−f(y)|≤L|x−y|,此时 ω(δ)≤Lδ,增长指数为 1。

f 满足 Hölder 条件(指数 α∈(0,1]):|f(x)−f(y)|≤L|x−y|α,此时 ω(δ)=O(δα)。x 在 [0,1] 上是 α=1/2 的例子:ω(δ)=δ。

既不 Lipschitz 也不 Hölder 的一致连续函数存在,它们的连续模可以更慢(例如 1/ln⁡(1/δ) 类型的增长)。

不连续函数的连续模不趋于零:阶跃函数(x>0 时取 1,否则取 0)在 [−1,1] 上的 ω(δ)≡1,无论 δ 多小。

为什么要区分这三种条件

三个条件对应三种保证。逐点连续保证函数值不跳变,是理论分析的最低要求。一致连续保证「输入误差小则输出误差小」这件事在整个区域一致成立,这是数值计算的基本前提:只有一致连续的函数才能用有限精度输入得到有限精度输出,误差不随位置恶化。Lipschitz 条件给出误差的定量上界,用于分析算法的收敛速度:压缩映射原理的压缩常数就是 Lipschitz 常数,梯度下降的步长上界由梯度的 Lipschitz 常数决定。连续模是这三者的统一语言:ω(δ)≤Lδ1 是 Lipschitz,ω(δ)≤Lδα(α<1)是 Hölder,ω(δ)→0 是一致连续。

5.3.5 数值实验 ​

实验一:极限计算中的数值陷阱 ​

三个常见极限都可以解析求出,值分别是 1/2、1、1/2。直接代入的写法在 x 很小时会因两个接近的数相减而失去有效数字,改写之后精度恢复到机器精度水平。

python
import math

xs = [1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-16]
print("(1 - cos x)/x^2 的两种算法(极限为 0.5):")
print(f"{'x':>8} {'直接计算':>22} {'相对误差':>12} {'稳定算法':>22} {'相对误差':>12}")
for x in xs:
    direct = (1.0 - math.cos(x)) / (x * x)
    stable = 2.0 * math.sin(x / 2.0) ** 2 / (x * x)
    print(f"{x:>8.0e} {direct:>22.6e} {abs(direct - 0.5) / 0.5:>12.3e} "
          f"{stable:>22.6e} {abs(stable - 0.5) / 0.5:>12.3e}")

print("(e^x - 1)/x 的两种算法(极限为 1):")
for x in [1e-8, 1e-12, 1e-16]:
    direct = (math.exp(x) - 1.0) / x
    stable = math.expm1(x) / x
    print(f"  x = {x:.0e}: 直接计算 = {direct:.6e}(误差 {abs(direct - 1):.3e}), "
          f"expm1 = {stable:.6e}(误差 {abs(stable - 1):.3e})")

print("(sqrt(1+x) - 1)/x 的两种算法(极限为 0.5):")
for x in [1e-8, 1e-12, 1e-16]:
    direct = (math.sqrt(1.0 + x) - 1.0) / x
    stable = 1.0 / (math.sqrt(1.0 + x) + 1.0)
    print(f"  x = {x:.0e}: 直接计算 = {direct:.6e}(误差 {abs(direct - 0.5):.3e}), "
          f"共轭化简 = {stable:.6e}(误差 {abs(stable - 0.5):.3e})")

输出:

text
(1 - cos x)/x^2 的两种算法(极限为 0.5):
       x                   直接计算         相对误差                   稳定算法         相对误差
   1e-02           4.999958e-01    8.333e-06           4.999958e-01    8.333e-06
   1e-04           5.000000e-01    6.077e-09           5.000000e-01    8.333e-10
   1e-06           5.000445e-01    8.890e-05           5.000000e-01    8.338e-14
   1e-08           0.000000e+00    1.000e+00           5.000000e-01    0.000e+00
   1e-10           0.000000e+00    1.000e+00           5.000000e-01    0.000e+00
   1e-16           0.000000e+00    1.000e+00           5.000000e-01    0.000e+00
(e^x - 1)/x 的两种算法(极限为 1):
  x = 1e-08: 直接计算 = 1.000000e+00(误差 6.077e-09), expm1 = 1.000000e+00(误差 5.000e-09)
  x = 1e-12: 直接计算 = 1.000089e+00(误差 8.890e-05), expm1 = 1.000000e+00(误差 5.000e-13)
  x = 1e-16: 直接计算 = 0.000000e+00(误差 1.000e+00), expm1 = 1.000000e+00(误差 0.000e+00)
(sqrt(1+x) - 1)/x 的两种算法(极限为 0.5):
  x = 1e-08: 直接计算 = 5.000000e-01(误差 3.039e-09), 共轭化简 = 5.000000e-01(误差 1.250e-09)
  x = 1e-12: 直接计算 = 5.000445e-01(误差 4.445e-05), 共轭化简 = 5.000000e-01(误差 1.250e-13)
  x = 1e-16: 直接计算 = 0.000000e+00(误差 5.000e-01), 共轭化简 = 5.000000e-01(误差 0.000e+00)

三组数据的形态一致。x 从 10−2 减到 10−6 时直接计算的误差没有单调下降,反而在 10−6 处回升到 8.9×10−5,原因是 cos⁡x 与 1 的差落在机器精度的量级(x=10−6 时 cos⁡x−1≈−5×10−13,只有约三位有效数字),相减之后剩下的有效数字很少。x=10−8 及更小时,cos⁡x 在双精度下等于 1,相减结果为零,相对误差 100%。改写后的算法全部达到机器精度:1−cos⁡xx2=2sin2⁡(x/2)x2 把相减移到了 sin 内部(sin 在零点附近没有抵消),ex−1x 用 expm1(直接计算 ex−1 的库函数,内部做了补偿),1+x−1x=11+x+1 用共轭化简把减法换成了加法。

同一类陷阱在统计学与优化里也常见:方差计算中的 E[X2]−(E[X])2、逻辑回归的 log⁡(1+e−z)、交叉熵中的 log⁡p(p 接近 1 时),都有对应的稳定形式(Welford 递推、log1p、-expm1)。

实验二:一致连续的数值判定 ​

一致连续的直接检验是观察「跨度固定为 h 时的最大函数值差」是否随 h→0 趋于零。两组数据:x 在 [0,1] 上(一致连续)与 sin⁡(1/x) 在 [10−3,1] 上(连续但不一致连续)。

python
import math
import numpy as np

def sup_diff(f, lo, hi, h, n=40000):
    x = np.linspace(lo, hi - h, n)
    return float(np.max(np.abs(f(x + h) - f(x))))

print("f(x) = x^2 在区间上的最大斜率(近似 Lipschitz 常数):")
for b in [1.0, 10.0]:
    x = np.linspace(0, b, 100001)
    slope = np.max(np.abs((x[1:] ** 2 - x[:-1] ** 2) / (x[1:] - x[:-1])))
    print(f"  [0, {b:.0f}]: 最大斜率 = {slope:.6f}(理论值 2b = {2 * b:.1f})")

print("f(x) = sqrt(x) 在 (0, 1] 上的最大斜率(随区间缩小):")
for lo in [1e-2, 1e-4, 1e-6]:
    x = np.linspace(lo, 1.0, 200001)
    slope = np.max(np.abs((np.sqrt(x[1:]) - np.sqrt(x[:-1])) / (x[1:] - x[:-1])))
    print(f"  从 {lo:.0e} 起的最大斜率 = {slope:.3e}(理论值 1/(2 sqrt(lo)) = "
          f"{1 / (2 * math.sqrt(lo)):.1e})")

print("一致连续的判别:sup |f(x+h) - f(x)| 随 h 的变化")
print(f"{'h':>10} {'sqrt(x) 在 [0,1]':>22} {'sin(1/x) 在 [0.001,1]':>26}")
for h in [1e-2, 1e-3, 1e-4, 1e-5]:
    a = sup_diff(np.sqrt, 0.0, 1.0, h)
    b = sup_diff(lambda t: np.sin(1.0 / t), 1e-3, 1.0, h)
    print(f"{h:>10.0e} {a:>22.6e} {b:>26.6e}")

输出:

text
f(x) = x^2 在区间上的最大斜率(近似 Lipschitz 常数):
  [0, 1]: 最大斜率 = 1.999990(理论值 2b = 2.0)
  [0, 10]: 最大斜率 = 19.999900(理论值 2b = 20.0)
f(x) = sqrt(x) 在 (0, 1] 上的最大斜率(随区间缩小):
  从 1e-02 起的最大斜率 = 4.999e+00(理论值 1/(2 sqrt(lo)) = 5.0e+00)
  从 1e-04 起的最大斜率 = 4.939e+01(理论值 1/(2 sqrt(lo)) = 5.0e+01)
  从 1e-06 起的最大斜率 = 2.899e+02(理论值 1/(2 sqrt(lo)) = 5.0e+02)
一致连续的判别:sup |f(x+h) - f(x)| 随 h 的变化
         h        sqrt(x) 在 [0,1]       sin(1/x) 在 [0.001,1]
     1e-02           1.000000e-01               1.989633e+00
     1e-03           3.162278e-02               1.998970e+00
     1e-04           1.000000e-02               1.986319e+00
     1e-05           3.162278e-03               1.990829e+00

三组数据对应三个结论。

Lipschitz 常数的估计。f(x)=x2 在 [0,b] 上的最大差商是 2b(在区间右端附近取得),数值结果 1.999990 与 19.999900 与理论值相符(偏差来自网格采样,网格越密越接近)。这个最大差商给出 Lipschitz 常数,也是「导数有界蕴含 Lipschitz」这条结论的数值形态。

区间位置对 Lipschitz 性的影响。x 在 (0,1] 上的最大差商是 12x,在 x 接近 0 时无界。数值上从 10−2 处的 5.0 增到 10−4 处的 49.4,再到 10−6 处的 289.9(采样原因使最后一项低于理论值 500,因为网格步长 5×10−6 已经与 10−6 同量级)。结论是 x 在含 0 的区间上不满足 Lipschitz 条件,但它仍然一致连续(由 Cantor 定理,[0,1] 闭区间上连续)。

一致连续的判定数据。x 的列显示 sup 差按 h 下降:h 缩小 100 倍,sup 差缩小 10 倍(1.0×10−1→3.2×10−2→1.0×10−2→3.2×10−3),趋于零,与一致连续相符,同时也印证了 Hölder 指数 1/2。sin⁡(1/x) 的列显示 sup 差稳定在 2 附近,不随 h 下降,与不一致连续相符。需要说明的是数值判定有边界:h 继续减小到 10−6 以下时,sin⁡(1/x) 的摆动周期在区间左端已经短于 h,可分辨的最大差值受采样密度限制而失真,而且从理论上看,当 h 小于 2×(10−3)2 时,区间 [10−3,1] 内不再存在能跨越完整振荡的两点。一致连续的判断最终依赖理论论证(闭区间上的连续函数必一致连续),数值实验的作用是给出直观与量级。

实验三:连续模的估计 ​

连续模把「误差控制」写成关于跨度的函数。对三类函数计算 ω(δ) 并估计它的增长指数。

python
import math
import numpy as np

def modulus(f, h, lo=0.0, hi=1.0, n=200000):
    x = np.linspace(lo, hi - h, n)
    return float(np.max(np.abs(f(x + h) - f(x))))

funcs = [("x^2", lambda t: t ** 2, 0.0, 1.0),
         ("sqrt(x)", np.sqrt, 0.0, 1.0),
         ("阶跃(x > 0 取 1)", lambda t: np.where(t > 0, 1.0, 0.0), -0.5, 0.5)]
for name, f, lo, hi in funcs:
    print(f"{name} 在 [{lo}, {hi}] 上的连续模:")
    prev = None
    for h in [1e-1, 1e-2, 1e-3, 1e-4, 1e-5]:
        w = modulus(f, h, lo, hi)
        if prev is None:
            slope_txt = ""
        else:
            slope_txt = f", 局部斜率 = {math.log(w / prev[1]) / math.log(h / prev[0]):.3f}"
        print(f"  delta = {h:.0e}: omega = {w:.6e}{slope_txt}")
        prev = (h, w)

输出:

text
x^2 在 [0.0, 1.0] 上的连续模:
  delta = 1e-01: omega = 1.900000e-01
  delta = 1e-02: omega = 1.990000e-02, 局部斜率 = 0.980
  delta = 1e-03: omega = 1.999000e-03, 局部斜率 = 0.998
  delta = 1e-04: omega = 1.999900e-04, 局部斜率 = 1.000
  delta = 1e-05: omega = 1.999990e-05, 局部斜率 = 1.000
sqrt(x) 在 [0.0, 1.0] 上的连续模:
  delta = 1e-01: omega = 3.162278e-01
  delta = 1e-02: omega = 1.000000e-01, 局部斜率 = 0.500
  delta = 1e-03: omega = 3.162278e-02, 局部斜率 = 0.500
  delta = 1e-04: omega = 1.000000e-02, 局部斜率 = 0.500
  delta = 1e-05: omega = 3.162278e-03, 局部斜率 = 0.500
阶跃(x > 0 取 1) 在 [-0.5, 0.5] 上的连续模:
  delta = 1e-01: omega = 1.000000e+00
  delta = 1e-02: omega = 1.000000e+00, 局部斜率 = -0.000
  delta = 1e-03: omega = 1.000000e+00, 局部斜率 = -0.000
  delta = 1e-04: omega = 1.000000e+00, 局部斜率 = -0.000
  delta = 1e-05: omega = 1.000000e+00, 局部斜率 = -0.000

三类函数的局部斜率分别稳定在 1.000、0.500、0.000。x2 的斜率 1 与 Lipschitz 条件相符(ω(δ)=2δ−δ2≈2δ);x 的斜率 1/2 与 Hölder 指数 1/2 相符(ω(δ)=δ);阶跃函数的斜率 0 表示 ω 不随 δ 衰减,函数不连续。用局部斜率估计增长指数是数值分析中的常用手法:把 ω(δ) 与 δ 取对数后做线性拟合,斜率就是指数,这一操作在数值求积的误差阶、迭代法的收敛阶、优化算法的收敛速率的实验测定中反复出现。

5.3.6 本节小结 ​

从极限到连续的三级阶梯

本节把数列极限推广到函数极限,再引出连续性的三层结构。函数极限用 ε-δ 定义,Heine 归结原则把它与数列极限连通,两侧的运算法则与夹逼定理与数列版本一致。连续要求极限值等于函数值,间断点按单侧极限的存在与相等情况分成可去、跳跃、第二类三种。闭区间上的连续函数有三条基本结论:最值定理(取到最大最小值)、介值定理(取到中间值,零点定理是特例,也是数值求根的基础)、一致连续(Cantor 定理)。一致连续与逐点连续的差别在于误差标准是否与位置有关,闭区间上两者等价,开区间或无穷区间上需要额外条件。连续模 ω(δ) 给出定量分级:x2 的局部斜率 1.000(Lipschitz)、x 的 0.500(Hölder 指数 1/2)、阶跃函数的 0.000(不连续),局部斜率的大小直接对应当 δ 减小时误差控制的性质。数值上还要注意极限计算的稳定性:(1−cos⁡x)/x2 与 1+x−1 这类表达式在 x 很小时直接计算会失去全部有效数字(x=10−8 时误差 100%),改写为 2sin2⁡(x/2)/x2 与 1/(1+x+1) 之后恢复到机器精度,这类改写(消除接近数相减)是数值实现中的标准动作。

连续函数最重要的一条性质是「局部可以用直线近似」,这就是下一节的微分学:导数把函数在一点附近的行为压缩成一个数,中值定理与 Taylor 公式把这个数推广到整个区间的估计,而本节的一致连续与 Lipschitz 条件正是微分学结论(导数有界)的连续语言表述。

练习题 ​

第 1 题 概念推导 ​

(a) 用 ε-δ 定义证明 limx→3(2x−1)=5。(b) 用 Heine 归结原则证明 limx→0sin⁡1x 不存在。(c) 证明:若 f 在 [a,b] 上满足 Lipschitz 条件,则 f 在 [a,b] 上一致连续;举例说明反过来不成立。

参考答案

(a) 任给 ϵ>0,需要找 δ>0 使 0<|x−3|<δ 时 |(2x−1)−5|<ϵ。计算 |(2x−1)−5|=|2x−6|=2|x−3|,因此取 δ=ϵ/2 即可:当 |x−3|<δ 时 2|x−3|<2δ=ϵ。结论成立。

(b) 由归结原则,只需构造两个趋于零的数列使函数值趋于不同的极限。取 xn=1nπ,则 xn→0 且 sin⁡1xn=sin⁡(nπ)=0→0;取 yn=12nπ+π/2,则 yn→0 且 sin⁡1yn=sin⁡(2nπ+π/2)=1→1。两个子列的极限不同,由唯一性知极限不存在(这里两个单侧极限都不存在,属于第二类间断点)。

(c) 设 |f(x)−f(y)|≤L|x−y| 对一切 x,y∈[a,b] 成立。任给 ϵ>0,取 δ=ϵ/L(L>0;L=0 时常函数,结论显然),则当 |x−y|<δ 时 |f(x)−f(y)|≤L|x−y|<ϵ。δ 只依赖 ϵ,与位置无关,因此一致连续。

反例:f(x)=x 在 [0,1] 上一致连续(闭区间上连续,由 Cantor 定理),但不满足 Lipschitz 条件。证明不满足:取 x=0、y=t,则 |t−0|t−0=1t 在 t→0+ 时无界,不存在有限的 L 使不等式对一切 x,y 成立。实验二的数值形态:从 10−2 起的最大差商 5.0,从 10−6 起的最大差商约 289.9(理论值 500),随区间贴近零点而增大。

第 2 题 计算推理 ​

(a) 求 limx→01−cos⁡2xx2 与 limx→0tan⁡x−sin⁡xx3。(b) 判断下列函数在 x=0 处的间断点类型:sin⁡xx、|x|x、xsin⁡1x(补充定义 f(0)=0 后是否连续)。(c) 估计 f(x)=x3 在 [0,1] 上的连续模增长指数。

参考答案

(a) 第一式用倍角公式 cos⁡2x=1−2sin2⁡x:

1−cos⁡2xx2=2sin2⁡xx2=2(sin⁡xx)2→2

第二式提出 tan⁡x(或通分):

tan⁡x−sin⁡xx3=sin⁡x(1−cos⁡x)x3cos⁡x=sin⁡xx⋅1−cos⁡xx2⋅1cos⁡x→1⋅12⋅1=12

两式都用到了基本极限 sin⁡xx→1 与 1−cos⁡xx2→12。

(b) sin⁡xx 在 x=0 无定义但左右极限都等于 1:可去间断点,补充定义 f(0)=1 后连续。|x|x 在 x=0 的左极限 −1、右极限 1:跳跃间断点(第一类)。xsin⁡1x 在 x=0 无定义,但 |xsin⁡1x|≤|x|→0,因此左右极限都是 0:可去间断点,补充定义 f(0)=0 后连续。需要注意的是这个函数在 x=0 附近的振荡虽然无界频繁,但振幅被 |x| 压住,与 sin⁡1x 的情形不同。

(c) 对 x3 取 x=0、y=s,差商为 s1/3/s=s−2/3,随 s→0 发散,因此不是 Lipschitz。由 Hölder 形式 |x3−y3|≤|x−y|1/3(可由 |a3−b3|≥|a−b|3 对非负数推出),指数 α=1/3,连续模 ω(δ)=δ1/3。数值上把 δ 从 10−3 减到 10−6,ω 从 0.1 变到 0.01,局部斜率约为 1/3,可以用实验三的代码验证(把函数换成 np.cbrt 或 lambda t: np.sign(t) * np.abs(t) ** (1/3))。

第 3 题 代码验证 ​

用本节代码做三组实验并解释:(a) 把实验一中 (1−cos⁡x)/x2 的 x 范围扩到 10−20,观察直接计算的输出;(b) 在实验二里把 sin⁡(1/x) 的定义域改为 [10−1,1] 与 [10−6,1],观察一致连续的数值判定如何随定义域变化;(c) 在实验三里对 f(x)=x4 与 f(x)=x4 估计连续模的局部斜率。

参考答案

(a) x=10−20 时直接计算的输出是 0.0(相对误差 100%),与 x=10−8 时相同;稳定算法输出 0.5。原因是双精度下 cos⁡x 对任何 |x|<10−8 都舍入为 1.0,相减得到精确的零。这个现象说明直接计算在 x 小到一定程度后输出的是完全错误的结果(恒为零),而不是一个精度较差的结果,并且无法通过观察输出发现问题(没有异常值或警告),只能靠改写公式或做量纲分析避免。实践中的检查手段是:对同一表达式用两种不同写法计算并比较,或者用高精度库(mpmath 一类)在小范围内对照。

(b) 定义域对判定影响明显。取 [10−1,1]:sin⁡(1/x) 的最大频率在左端点 x=10−1(周期 2πx2≈0.063),取 h=10−2 时 sup 差已经接近 2(跨越完整振荡),说明不一致连续;取 [10−6,1]:左端点的周期是 2π×10−12,远小于任何实用的 h,可分辨的区域内函数看起来平滑,h=10−2 时 sup 差由区间中部的振荡决定,仍然接近 2(因为 x≈0.1 附近的振荡尺度与 h 匹配)。结论:只要定义域的左端点趋于零,无论从哪个尺度看,总能找到与 h 匹配的振荡区域使 sup 差不衰减;数值实验的「分辨率」取决于采样密度,判定仍依据理论(sin⁡(1/x) 在 (0,1] 上不可能一致连续,因为取 xn=12nπ+π/2、yn=12nπ 时 xn−yn→0 而函数值差恒为 1)。

(c) x4 的局部斜率为 1.000(Lipschitz,ω(δ)=2δ 量级,因为 |x4−y4|≤4max|x|3|x−y|≤4δ 在 [0,1] 上),x4 的局部斜率为 0.250(Hölder 指数 1/4)。数据形态与实验三相同,只是斜率数值不同。这条练习说明连续模的增长指数由函数在「最陡处」的局部行为决定:x4 的导数有界(斜率 1),x4 在零点附近的导数发散(斜率 1/4),指数 0.250 正好等于 1/4。

第 4 题 综合应用 ​

某数值程序需要计算函数 g(t)=et−1−tt2(在 t=0 处的极限是 1/2,来自 Taylor 展开)。输入 t 的量级从 1 到 10−15,程序要求相对误差不超过 10−10。请给出实现方案:如何改写表达式使所有量级的输入都达到精度要求、如何验证、以及在 t 小于机器精度相关尺度时应当如何处理;再说明这个问题与本节「一致连续」概念的关联。

参考答案

改写:直接写 (np.exp(t) - 1 - t) / t**2 在 t 很小时会连续经历两次抵消:et−1 已经损失有效数字,再减去 t 又损失一次,t=10−8 时结果可能只剩几位有效数字,t=10−16 时结果为零。改写分两段:t 较大时(例如 |t|>10−3)用 (np.expm1(t) - t) / t**2(expm1 处理第一次抵消);t 较小时用幂级数 g(t)=12+t6+t224+⋯,例如取到 t3 项:

g(t)≈12+t6+t224+t3120

当 |t|≤10−3 时,被截掉的项量级为 t4/720≈10−15,相对误差远小于 10−10,满足要求。分段阈值取 10−3 是因为两段在阈值处都达到 10−13 以上的相对精度,重叠区间保证过渡平滑。

验证:三条手段。其一,与高精度计算对照:用 mpmath 或 decimal 模块在 t∈{10−1,10−3,10−6,10−10,10−15} 上算出参考值,比较相对误差;其二,用 Taylor 展开独立核对小 t 段(两段实现互不依赖);其三,检查函数在 t→0 的极限是否稳定收敛到 0.5(用一系列递减的 t 观察输出,若在小 t 处输出突然变为 0 或出现随机波动,说明抵消未消除)。

极小 t 的处理:当 t 小于约 10−8 时(exp 的抵消尺度),幂级数段直接给出 g≈1/2+t/6,其中 t/6 项已经小于 10−8,输出实际上等于 0.5,这是正确结果(极限值),不是错误。当 t 小到与次正规数或机器精度边界相当时,使用者应当明确问题的物理量纲:如果 t 的来源是某个差值,应当回到上游消除那次相减(例如用 log1p、expm1 计算 t 本身),而不是在下游修补。

与一致连续的联系:这个问题是「连续模与数值稳定性」的具体形态。g 作为 t 的函数在 [−1,1] 上连续且可微,导数有界(|g′|≤1 量级),因此 Lipschitz 连续,理论上误差与输入误差成正比。数值实现的困难不来自函数本身的性质,而来自表达式的写法:直接写法在 t 小时的有效计算区间被压缩到机器精度的若干倍,等价于把 Lipschitz 常数在数值上放大到 1016 的量级。改写之后,数值行为与理论行为一致(输入误差 10−16 对应输出误差 10−15 量级),这正是数值计算中「按连续模的尺度选择实现方式」的含义。

常见错误 ​

错误 1 · 用「代入特殊点或数列」代替极限的存在性证明

原因:计算极限时用几个点或几个数列的数值结果代替证明,会漏掉极限不存在的情形。limx→0sin⁡1x 沿 xn=1nπ 观察到的值恒为 0,沿 yn=12nπ+π/2 观察恒为 1,只用一个数列会得出错误结论。同样的错误出现在连续性的验证中:用少数几个采样点确认「函数值接近极限值」,无法排除可去或跳跃间断。

解决:用 Heine 归结原则时说明「对任意数列都成立」,或者用 ε-δ 定义给出 δ 的构造。数值验证可以作为发现手段,但结论要回到定义或定理;检查极限是否存在时,主动构造两类数列(分别落在不同的振荡相位或不同的侧)来测试。

错误 2 · 混用逐点连续与一致连续

原因:两者的差别不显眼,但在数值与算法分析中影响很大。x2 在 \R 上逐点连续但不一致连续,f(x)=1/x 在 (0,1] 上逐点连续但不一致连续;把「逐点连续」当作「输入误差小则输出误差小」的保证会导致错误的误差估计(例如在开区间上引用 Cantor 定理)。反过来,在闭区间上要求额外的条件(例如证明 Lipschitz)也是多余的,闭区间上连续必一致连续。

解决:记住判定表:闭区间上连续等价于一致连续;开区间或无穷区间上需要额外条件(导数有界、连续模趋于零、极限存在且有限)。写误差估计时明确使用的条件与所在区间,不要把在一个区间上成立的结论跨区间使用。

错误 3 · 在数值代码中直接写有抵消的表达式

原因:(1−cos⁡x)/x2、(1+x−1)/x、(ex−1)/x 这类表达式在小量输入下会失去全部有效数字(实验一的数据:x=10−8 时前两个的直接计算相对误差分别是 100% 与 3×10−9,x=10−16 时都变成 100%)。这类错误的隐蔽性在于小 x 时输出可能是精确的零而不是乱码,不触发任何异常,只有与参考值比较才能发现。

解决:识别「两个接近的数相减」的结构并改写:1−cos⁡x 用 2sin2⁡(x/2);ex−1 用 expm1;log⁡(1+x) 用 log1p;1+x−1 用共轭化简;log⁡(1+e−z) 用 logaddexp。改写之后用小量级输入做回归测试(x=10−4,10−8,10−12),把输出与解析极限比较。

错误 4 · 把间断点分类与可去性判断混为一谈

原因:左右极限存在且相等(可去间断点)与函数在该点连续是两个不同的判断。sin⁡xx 在 x=0 处的左右极限都是 1,若直接认为「极限存在所以该点连续」就错了:函数在该点无定义,需要补充定义才连续。类似地,|x|x 的左右极限都存在(分别是 −1 与 1),但函数值不存在,且左右极限不相等,属于跳跃间断点。

解决:判断连续性按三步走:函数在该点是否有定义;左右极限是否存在且相等;函数值是否等于极限值。三步都满足才连续。分类时先看单侧极限是否存在(区分第一类与第二类),再比较两侧是否相等(区分可去与跳跃)。