1.1 从∑到∫:用循环理解求和与累积

副标题:程序员的数学急救包------无论你写Java、Python、Go还是Rust,看到∑就该想到一个for循环。

开篇灵魂拷问

翻开源码或者论文,你大概率会撞见这样的符号:

是不是瞬间头大?别慌。作为一个写了成千上万行循环的程序员,你有天然的优势------这些符号在代码世界里,全是循环和累加器

本节目标:让你今后见到∑、∏、∫、Δ,条件反射般地在大脑中生成一个for循环或者while循环的代码片段。本文提供 Java / Python / JavaScript / C++ / Rust / Go 六种语言实现,覆盖99%后端与算法工程师技术栈。

1. ∑:高级版的 for 循环累加器

1.1 数学定义

LaTeX 源码(可复制)

复制代码
\sum_{i=m}^{n} a_i = a_m + a_{m+1} + \dots + a_n

其中:

  • ii 是循环变量 (代码里的i

  • mm 是起始值range(m, n+1)start

  • nn 是终止值rangestop

  • aiai​ 是第 i 项表达式(循环体)

1.2 核心代码直译

语言 核心实现
Python total=0; for i in range(m, n+1): total+=a(i)
Java int total=0; for(int i=m; i<=n; i++) total+=a(i);
JavaScript let total=0; for(let i=m; i<=n; i++) total+=a(i);
C++ int total=0; for(int i=m; i<=n; ++i) total+=a(i);
Rust let mut total=0; for i in m..=n { total+=a(i); }
Go total:=0; for i:=m; i<=n; i++ { total+=a(i) }

1.3 面试题实战

题目 :计算 ,手写代码并分析时间复杂度。

解法一:循环法(直译数学符号)

Python

复制代码
def sum_of_squares_loop(n=100):
    s = 0
    for i in range(1, n+1):
        s += i * i
    return s

Java

复制代码
public class SumSquares {
    public static int sumLoop(int n) {
        int s = 0;
        for (int i = 1; i <= n; i++) {
            s += i * i;
        }
        return s;
    }
}

JavaScript

复制代码
function sumSquaresLoop(n = 100) {
    let s = 0;
    for (let i = 1; i <= n; i++) {
        s += i * i;
    }
    return s;
}

C++

复制代码
int sumSquaresLoop(int n = 100) {
    int s = 0;
    for (int i = 1; i <= n; ++i) {
        s += i * i;
    }
    return s;
}

Rust

复制代码
fn sum_squares_loop(n: i32) -> i32 {
    let mut s = 0;
    for i in 1..=n {
        s += i * i;
    }
    s
}

Go

复制代码
func sumSquaresLoop(n int) int {
    s := 0
    for i := 1; i <= n; i++ {
        s += i * i
    }
    return s
}
  • 时间复杂度:O(n)

  • 空间复杂度:O(1)

解法二:公式法(数学降维打击)

平方和公式:

LaTeX 源码(可复制)

复制代码
\sum_{i=1}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}

各语言一行代码实现:

Python

复制代码
def sum_of_squares_formula(n=100):
    return n * (n + 1) * (2 * n + 1) // 6

Java

复制代码
public static int sumFormula(int n) {
    return n * (n + 1) * (2 * n + 1) / 6;
}

JavaScript

复制代码
const sumSquaresFormula = n => n * (n + 1) * (2 * n + 1) / 6;

C++

复制代码
int sumFormula(int n) {
    return n * (n + 1) * (2 * n + 1) / 6;
}

Rust

复制代码
fn sum_squares_formula(n: i32) -> i32 {
    n * (n + 1) * (2 * n + 1) / 6
}

Go

复制代码
func sumSquaresFormula(n int) int {
    return n * (n + 1) * (2*n + 1) / 6
}
  • 时间复杂度:O(1)

  • 空间复杂度:O(1)

1.4 什么时候用循环,什么时候用公式?

场景 推荐方法 原因
n很小(n < 1000) 循环法 代码直观,性能差异可忽略
n极大(n = 10^9) 公式法 O(1) vs O(n) 是天壤之别
通项公式极其复杂或不存在 循环法 数学没有闭式解,只能迭代
需要展示推导过程(面试) 先写循环,再优化成公式 体现从直观到优化的思维过程

面试官心理 :你直接甩出公式,他只看到你背了题;你先写出循环,再指出可以数学优化,他看到的是工程思维 + 数学素养

1.5 扩展:求和符号的更多用法

双重求和

LaTeX 源码(可复制)

复制代码
\sum_{i=1}^{m} \sum_{j=1}^{n} a_{ij} = \sum_{j=1}^{n} \sum_{i=1}^{m} a_{ij}

对应嵌套循环:

Python

复制代码
total = 0
for i in range(1, m+1):
    for j in range(1, n+1):
        total += a[i][j]
条件求和

Python

复制代码
sum(i for i in range(1, n+1) if i % 2 == 0)

2. ∏:高级版的 while 循环累乘器

2.1 数学定义

LaTeX 源码(可复制)

复制代码
\prod_{i=1}^{n} a_i = a_1 \times a_2 \times \dots \times a_n

2.2 核心代码对照

计算阶乘

Python

def factorial(n):

result = 1

for i in range(1, n+1):

result *= i

return result

Java

复制代码
public static long factorial(int n) {
    long result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
    }
    return result;
}

JavaScript

复制代码
function factorial(n) {
    let result = 1;
    for (let i = 1; i <= n; i++) result *= i;
    return result;
}

C++

复制代码
long long factorial(int n) {
    long long result = 1;
    for (int i = 1; i <= n; ++i) result *= i;
    return result;
}

Rust

复制代码
fn factorial(n: u64) -> u64 {
    (1..=n).product()
}

Go

复制代码
func factorial(n int) int {
    res := 1
    for i := 1; i <= n; i++ {
        res *= i
    }
    return res
}

2.3 注意陷阱:空积与空和

  • 空和 → 累加器初始化为0,循环0次返回0。

  • 空积 → 累乘器初始化为1,循环0次返回1。

这在边界条件处理时极其重要。

Python 示例

复制代码
def safe_sum(n):
    return sum(range(1, n+1))  # n=0 时返回0

def safe_product(n):
    result = 1
    for i in range(1, n+1):
        result *= i
    return result  # n=0 时返回1

3. ∫:当步长趋近于0时的求和

3.1 数学定义

积分 ∫abf(x)dx∫ab​f(x)dx 可以看作是把区间[a,b]切成无数个小矩形,求面积和

LaTeX 源码(可复制)

复制代码
\int_a^b f(x) dx \approx \sum_{k=0}^{N-1} f(a + k \cdot \Delta x) \cdot \Delta x

其中 ,N是划分份数。

3.2 代码实现:矩形法求定积分

计算(精确值为 1/3 ≈ 0.3333)

Python

复制代码
def integral(f, a, b, n=10000):
    dx = (b - a) / n
    total = 0.0
    for i in range(n):
        total += f(a + i * dx) * dx
    return total

f = lambda x: x**2
print(integral(f, 0, 1, 10000))  # 输出约0.33328333

Java

复制代码
import java.util.function.Function;

public static double integral(Function<Double, Double> f, double a, double b, int n) {
    double dx = (b - a) / n;
    double total = 0.0;
    for (int i = 0; i < n; i++) {
        total += f.apply(a + i * dx) * dx;
    }
    return total;
}

JavaScript

复制代码
function integral(f, a, b, n = 10000) {
    const dx = (b - a) / n;
    let total = 0;
    for (let i = 0; i < n; i++) {
        total += f(a + i * dx) * dx;
    }
    return total;
}

C++

复制代码
#include <functional>
double integral(std::function<double(double)> f, double a, double b, int n = 10000) {
    double dx = (b - a) / n;
    double total = 0.0;
    for (int i = 0; i < n; ++i) {
        total += f(a + i * dx) * dx;
    }
    return total;
}

Rust

复制代码
fn integral<F>(f: F, a: f64, b: f64, n: usize) -> f64
where
    F: Fn(f64) -> f64,
{
    let dx = (b - a) / n as f64;
    (0..n).map(|i| f(a + i as f64 * dx) * dx).sum()
}

Go

复制代码
func integral(f func(float64) float64, a, b float64, n int) float64 {
    dx := (b - a) / float64(n)
    total := 0.0
    for i := 0; i < n; i++ {
        total += f(a+float64(i)*dx) * dx
    }
    return total
}

3.3 精度与性能的权衡

划分份数 n 近似值 误差 计算时间
10 0.285 ~0.048 极快
100 0.32835 ~0.005 很快
10,000 0.333283 ~0.00005
1,000,000 0.333333 极小 较慢

经验法则:对于平滑函数,n=10,000 已足够应对大多数工程场景。

3.4 扩展:蒙特卡洛积分思想

除了均匀切分矩形,还可以随机扔点------这就是蒙特卡洛方法(下册第8章将详细展开)。核心思想

Python 示例

复制代码
import random

def monte_carlo_integral(f, a, b, n=10000):
    total = 0.0
    for _ in range(n):
        x = random.uniform(a, b)
        total += f(x)
    return (b - a) * total / n

4. Δ:差分,就是前后相减

4.1 数学定义

LaTeX 源码(可复制)

复制代码
\Delta x_i = x_i - x_{i-1}

在时间序列、信号处理、物理模拟中到处都是Δ。

4.2 各语言实现

Python

复制代码
def diff(arr):
    return [arr[i] - arr[i-1] for i in range(1, len(arr))]

JavaScript

复制代码
const diff = arr => arr.slice(1).map((v, i) => v - arr[i]);

C++

复制代码
#include <vector>
std::vector<int> diff(const std::vector<int>& arr) {
    std::vector<int> res;
    for (size_t i = 1; i < arr.size(); ++i)
        res.push_back(arr[i] - arr[i-1]);
    return res;
}

Rust

复制代码
fn diff(arr: &[i32]) -> Vec<i32> {
    arr.windows(2).map(|w| w[1] - w[0]).collect()
}

Go

复制代码
func diff(arr []int) []int {
    res := make([]int, len(arr)-1)
    for i := 1; i < len(arr); i++ {
        res[i-1] = arr[i] - arr[i-1]
    }
    return res
}

4.3 二阶差分与导数的联系

二阶差分:

LaTeX 源码(可复制)

复制代码
\Delta^2 x_i = \Delta(\Delta x_i) = x_i - 2x_{i-1} + x_{i-2}

Python 实现

复制代码
def second_diff(arr):
    return diff(diff(arr))

这与离散函数的"二阶导数"对应,在数值分析中用于近似曲率。

5. 综合实战:用循环思维拆解复杂公式

来看一个真实的公式------均方误差 MSE

LaTeX 源码(可复制)

复制代码
MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2

5.1 四步拆解法演示(预告1.2节)

  1. 识符号:∑∑ 求和,yiyi​ 真实值,y^iy^​i​ 预测值。

  2. 找结构:求和内部是差的平方,求和后除以n。

  3. 代数值:假设 y = [2,4,6],y_hat = [1,5,5]。

  4. 写代码

Python 循环版

复制代码
def mse_loop(y_true, y_pred):
    n = len(y_true)
    total = 0
    for i in range(n):
        total += (y_true[i] - y_pred[i]) ** 2
    return total / n

Python 向量化版(NumPy)

复制代码
import numpy as np
def mse_np(y_true, y_pred):
    return np.mean((np.array(y_true) - np.array(y_pred)) ** 2)

Java 版

复制代码
public static double mse(double[] yTrue, double[] yPred) {
    double sum = 0.0;
    for (int i = 0; i < yTrue.length; i++) {
        double diff = yTrue[i] - yPred[i];
        sum += diff * diff;
    }
    return sum / yTrue.length;
}

Rust 版

复制代码
fn mse(y_true: &[f64], y_pred: &[f64]) -> f64 {
    y_true.iter()
        .zip(y_pred.iter())
        .map(|(t, p)| (t - p).powi(2))
        .sum::<f64>() / y_true.len() as f64
}

6. 符号→代码映射总表

数学符号 代码本质 Python Java Go Rust
∑∑ 累加循环 sum(...) sum(...) for(int i..) 对于(智 i..) for i:=.. 对于 i:=.. (m..=n).sum() (M..=n).sum()
∏∏ 累乘循环 math.prod 数学.prod for for (m..=n).product() (m..=n).product()
∫∫ 循环累加×dx for + dx 对于 + dx for + dx 对于 + dx for + dx 对于 + dx map().sum() map().sum()
ΔΔ 相邻差 arr[i]-arr[i-1] 同上 同上 windows(2) Windows(2)

课后习题

基础题(必做)

题1:将下列数学表达式翻译成Python函数。

提示:裂项相消法可得解析解 ​,但请先用循环实现。

LaTeX 源码

复制代码
S = \sum_{k=1}^{50} \frac{1}{k(k+1)}

面试题(大厂高频)

题2 (类似LeetCode 268):给定一个包含n个不同数字的数组,数字范围是0到n,找出缺失的那个数。要求分别用循环求和法异或法实现,并分析复杂度。

思路引导:0到n的和公式为 n(n+1)/2n(n+1)/2,减去数组实际和即为缺失数。

题3 :不使用math.factorial,用while循环实现一个函数approx_e(n_terms),根据泰勒展开式 计算自然常数e的近似值,项数为n_terms

题4 (性能挑战):分别用循环法和公式法计算 ,用timeit对比时间,分析原因。

本节小结

掌握内容 检验标准
∑ 符号 能写循环实现任意求和,知道何时用公式优化
∏ 符号 能写累乘,注意空积为1
∫ 符号 能用矩形法近似计算简单积分
Δ 符号 能计算序列差分
多语言迁移 至少能用两种语言实现本节核心函数

下节预告:1.2 希腊字母速查表 + 公式阅读实战------我们将拿Transformer论文开刀,当场拆解多头注意力公式,并给出四步拆解法的完整演示。

本文配套代码已上传至 GitHub 仓库 self-learners-league/beauty-of-operators-and-formulas,各语言实现见 volume1/chapter1/1.1_sigma_pi_integral/ 目录。习题答案将在专栏完结后统一发布在附录D。


本文为《算符与公式之美·高级卷(上册)》第1章第1节内容,版权所有,未经授权禁止转载。

相关推荐
有一个好名字6 小时前
Claude Code 50+命令全解析
python
liliangcsdn6 小时前
LLM如何与mcp server交互示例
linux·开发语言·python
Lupino6 小时前
拯救迷失的荧光溶解氧传感器:从“三无”到“复活”的全记录
python
维齐洛波奇特利(male)6 小时前
@Pointcut(“execution(* com.hdzx..*(..))“)切入点与aop 导致无限循环
java·开发语言
色空大师6 小时前
【日志文件配置详解】
java·logback·log4j2·日志
xcjbqd06 小时前
SQL中视图能否嵌套存储过程_实现复杂自动化报表逻辑
jvm·数据库·python
郝学胜-神的一滴6 小时前
[简化版 GAMES 101] 计算机图形学 04:二维变换上
c++·算法·unity·godot·图形渲染·unreal engine·cesium
ZC跨境爬虫6 小时前
海南大学交友平台开发实战day7(实现核心匹配算法+解决JSON请求报错问题)
前端·python·算法·html·json
迷藏4946 小时前
**发散创新:基于角色与属性的混合权限模型在微服务架构中的实战落地**在现代分布式系统中,
java·python·微服务·云原生·架构