阅读视图

发现新文章,点击刷新页面。
🔲 ☆

一道初中数学极值题的多种解法:柯西不等式、几何法、函数法详解

By Long Luo

最近在短视频 App 里刷到不少中学数学题讲解,才发现现在的初中数学题,很多已经不只是“会算”这么简单了,背后往往还藏着不等式、函数、几何直觉等不同层面的思维训练。

当然,这也不算奇怪,几十年前的 IMO 试题放现在也就普通题。很多经典数学竞赛题,随着时间推移和教学资源普及,早已从高阶技巧逐渐变成了基础训练的一部分。

前几天我就反复刷到一道“求极值”的题,看起来不难,但这道题其实非常适合拿来练习数学直觉。

已知 \(x + y = 5\) ,求 \(\sqrt {x + 1} + \sqrt {y + 3}\) 的最大值。

看到这道题,首先想到的几何意义其实是将军饮马模型,但将军饮马问题求得是最小值问题。那最大值该怎么求呢?最大值的几何意义是什么呢?

这道题的有趣之处在于可以用很多完全不同的思路来解决,有的解法只需要代数变形和基本不等式,也可以使用几何视角、函数思想。

下面就道题为例,把几种典型解法都梳理一遍,也顺便重新锻炼一下自己的数学思维。

解法一:常规解法

\(y = 5 - x\) ,原式变成 \(\sqrt {x + 1} + \sqrt {8 - x}\) ,要保证根号有意义,所以 \(−1 \le x \le 8\)

\(S = \sqrt {x + 1} + \sqrt {8 - x}\) ,两边平方得 \(S^2 = (\sqrt{x + 1} + \sqrt{8 - x})^2\)

展开可得:

\[ \begin{aligned} S^2 & = (x + 1) + (8 - x) + 2 \sqrt {(x + 1)(8 - x)} \\ & = 9 + 2 \sqrt {(x + 1)(8 - x)} \\ & = 9 + 2 \sqrt {-x^2 + 7x + 8} \\ & = 9 + 2 \sqrt {-(x - \frac {7}{2})^2 + \frac {81}{4}} \end{aligned} \]

因此 \(S^2 \le 9 + 2 \times \dfrac {9}{2} = 18\) ,于是 \(S \le 3 \sqrt 2\) 当且仅当 \(x = \dfrac {7}{2}\) 时取等号。

故最大值为 \(3\sqrt2\)

🔲 ☆

Backpropagation: A Vector Calculus Perspective

The primary goal of backpropagation is to calculate the partial derivatives of the cost function CC with respect to every weight W\mathbf{W} and bias b\mathbf{b} in the network.

1. Notation (Matrix Form)

To facilitate a clean derivation using matrix calculus, we adopt the following conventions:

  • al\mathbf{a}^l: Activation vector of the ll-th layer (nl×1n_l \times 1).
  • zl\mathbf{z}^l: Weighted input vector of the ll-th layer (nl×1n_l \times 1), where zl=Wlal1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l.
  • Wl\mathbf{W}^l: Weight matrix of the ll-th layer (nl×nl1n_l \times n_{l-1}).
  • bl\mathbf{b}^l: Bias vector of the ll-th layer (nl×1n_l \times 1).
  • δl\boldsymbol{\delta}^l: Error vector of the ll-th layer, defined as δlCzl\boldsymbol{\delta}^l \equiv \frac{\partial C}{\partial \mathbf{z}^l}.
  • \odot: Hadamard product (element-wise multiplication).
  • σ(zl)\sigma'(\mathbf{z}^l): The derivative of the activation function applied element-wise to zl\mathbf{z}^l.

2. Derivation of the Equations

BP1: Error at the Output Layer

Goal: δL=aLCσ(zL)\boldsymbol{\delta}^L = \nabla_{\mathbf{a}^L} C \odot \sigma'(\mathbf{z}^L)

Derivation Steps:

  1. Apply the Chain Rule: The error δL\boldsymbol{\delta}^L represents how the cost CC changes with respect to the weighted input zL\mathbf{z}^L. Since CC depends on zL\mathbf{z}^L through the activations aL\mathbf{a}^L:

δL=CzL=(aLzL)TCaL\boldsymbol{\delta}^L = \frac{\partial C}{\partial \mathbf{z}^L} = \left( \frac{\partial \mathbf{a}^L}{\partial \mathbf{z}^L} \right)^T \frac{\partial C}{\partial \mathbf{a}^L}

  1. Compute the Jacobian: Because ajL=σ(zjL)a^L_j = \sigma(z^L_j) (each output depends only on its own input), the Jacobian matrix aLzL\frac{\partial \mathbf{a}^L}{\partial \mathbf{z}^L} is a diagonal matrix:

aLzL=diag(σ(z1L),,σ(znL))\frac{\partial \mathbf{a}^L}{\partial \mathbf{z}^L} = \text{diag}(\sigma'(z^L_1), \dots, \sigma'(z^L_n))

  1. Result: Multiplying a diagonal matrix by a vector is equivalent to the Hadamard product:

δL=aLCσ(zL)\boldsymbol{\delta}^L = \nabla_{\mathbf{a}^L} C \odot \sigma'(\mathbf{z}^L)

BP2: Propagating Error to Hidden Layers

Goal: δl=((Wl+1)Tδl+1)σ(zl)\boldsymbol{\delta}^l = ((\mathbf{W}^{l+1})^T \boldsymbol{\delta}^{l+1}) \odot \sigma'(\mathbf{z}^l)

Derivation Steps:

  1. Link Consecutive Layers: We express the error at layer ll in terms of the error at layer l+1l+1 using the multivariate chain rule:

δl=Czl=(zl+1zl)TCzl+1=(zl+1zl)Tδl+1\boldsymbol{\delta}^l = \frac{\partial C}{\partial \mathbf{z}^l} = \left( \frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l} \right)^T \frac{\partial C}{\partial \mathbf{z}^{l+1}} = \left( \frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l} \right)^T \boldsymbol{\delta}^{l+1}

  1. Differentiate the Linear Transformation: Recall zl+1=Wl+1al+bl+1\mathbf{z}^{l+1} = \mathbf{W}^{l+1} \mathbf{a}^l + \mathbf{b}^{l+1} and al=σ(zl)\mathbf{a}^l = \sigma(\mathbf{z}^l). Applying the chain rule to find zl+1zl\frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l}:

zl+1zl=zl+1alalzl=Wl+1diag(σ(zl))\frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l} = \frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{a}^l} \cdot \frac{\partial \mathbf{a}^l}{\partial \mathbf{z}^l} = \mathbf{W}^{l+1} \cdot \text{diag}(\sigma'(\mathbf{z}^l))

  1. Transpose and Simplify: Substitute back and use the property (AB)T=BTAT(AB)^T = B^T A^T:

δl=(Wl+1diag(σ(zl)))Tδl+1=diag(σ(zl))(Wl+1)Tδl+1\boldsymbol{\delta}^l = \left( \mathbf{W}^{l+1} \cdot \text{diag}(\sigma'(\mathbf{z}^l)) \right)^T \boldsymbol{\delta}^{l+1} = \text{diag}(\sigma'(\mathbf{z}^l)) (\mathbf{W}^{l+1})^T \boldsymbol{\delta}^{l+1}

  1. Final Form:
    Convert the diagonal matrix multiplication to a Hadamard product:

δl=((Wl+1)Tδl+1)σ(zl)\boldsymbol{\delta}^l = ((\mathbf{W}^{l+1})^T \boldsymbol{\delta}^{l+1}) \odot \sigma'(\mathbf{z}^l)

BP3: Gradient for Biases

Goal: Cbl=δl\frac{\partial C}{\partial \mathbf{b}^l} = \boldsymbol{\delta}^l

Derivation Steps:

  1. Chain Rule via Intermediate zz:

Cbl=(zlbl)TCzl\frac{\partial C}{\partial \mathbf{b}^l} = \left( \frac{\partial \mathbf{z}^l}{\partial \mathbf{b}^l} \right)^T \frac{\partial C}{\partial \mathbf{z}^l}

  1. Compute Local Gradient: From zl=Wlal1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l, we see that bl\mathbf{b}^l is added directly to the weighted sum. Thus, zlbl\frac{\partial \mathbf{z}^l}{\partial \mathbf{b}^l} is the Identity matrix I\mathbf{I}.

  2. Conclusion:

Cbl=ITδl=δl\frac{\partial C}{\partial \mathbf{b}^l} = \mathbf{I}^T \boldsymbol{\delta}^l = \boldsymbol{\delta}^l

BP4: Gradient for Weights

Goal: CWl=δl(al1)T\frac{\partial C}{\partial \mathbf{W}^l} = \boldsymbol{\delta}^l (\mathbf{a}^{l-1})^T

Derivation Steps:

  1. Element-wise Approach: Consider a single weight wjklw^l_{jk} connecting neuron kk in layer l1l-1 to neuron jj in layer ll:

Cwjkl=Czjlzjlwjkl=δjlzjlwjkl\frac{\partial C}{\partial w^l_{jk}} = \frac{\partial C}{\partial z^l_j} \frac{\partial z^l_j}{\partial w^l_{jk}} = \delta^l_j \frac{\partial z^l_j}{\partial w^l_{jk}}

  1. Solve for Partial Derivative: Since zjl=mwjmlaml1+bjlz^l_j = \sum_m w^l_{jm} a^{l-1}_m + b^l_j, the derivative with respect to wjklw^l_{jk} is simply akl1a^{l-1}_k. Therefore, Cwjkl=δjlakl1\frac{\partial C}{\partial w^l_{jk}} = \delta^l_j a^{l-1}_k.

  2. Vectorize as an Outer Product: The collection of all such derivatives δjlakl1\delta^l_j a^{l-1}_k for all j,kj, k forms the Outer Product of the error vector δl\boldsymbol{\delta}^l and the input activation vector al1\mathbf{a}^{l-1}:

CWl=δl(al1)T\frac{\partial C}{\partial \mathbf{W}^l} = \boldsymbol{\delta}^l (\mathbf{a}^{l-1})^T

🔲 ☆

Backpropagation: A Vector Calculus Perspective

The primary goal of backpropagation is to calculate the partial derivatives of the cost function CC with respect to every weight W\mathbf{W} and bias b\mathbf{b} in the network.

1. Notation (Matrix Form)

To facilitate a clean derivation using matrix calculus, we adopt the following conventions:

  • al\mathbf{a}^l: Activation vector of the ll-th layer (nl×1n_l \times 1).
  • zl\mathbf{z}^l: Weighted input vector of the ll-th layer (nl×1n_l \times 1), where zl=Wlal1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l.
  • Wl\mathbf{W}^l: Weight matrix of the ll-th layer (nl×nl1n_l \times n_{l-1}).
  • bl\mathbf{b}^l: Bias vector of the ll-th layer (nl×1n_l \times 1).
  • δl\boldsymbol{\delta}^l: Error vector of the ll-th layer, defined as δlCzl\boldsymbol{\delta}^l \equiv \frac{\partial C}{\partial \mathbf{z}^l}.
  • \odot: Hadamard product (element-wise multiplication).
  • σ(zl)\sigma'(\mathbf{z}^l): The derivative of the activation function applied element-wise to zl\mathbf{z}^l.

2. Derivation of the Equations

BP1: Error at the Output Layer

Goal: δL=aLCσ(zL)\boldsymbol{\delta}^L = \nabla_{\mathbf{a}^L} C \odot \sigma'(\mathbf{z}^L)

Derivation Steps:

  1. Apply the Chain Rule: The error δL\boldsymbol{\delta}^L represents how the cost CC changes with respect to the weighted input zL\mathbf{z}^L. Since CC depends on zL\mathbf{z}^L through the activations aL\mathbf{a}^L:

δL=CzL=(aLzL)TCaL\boldsymbol{\delta}^L = \frac{\partial C}{\partial \mathbf{z}^L} = \left( \frac{\partial \mathbf{a}^L}{\partial \mathbf{z}^L} \right)^T \frac{\partial C}{\partial \mathbf{a}^L}

  1. Compute the Jacobian: Because ajL=σ(zjL)a^L_j = \sigma(z^L_j) (each output depends only on its own input), the Jacobian matrix aLzL\frac{\partial \mathbf{a}^L}{\partial \mathbf{z}^L} is a diagonal matrix:

aLzL=diag(σ(z1L),,σ(znL))\frac{\partial \mathbf{a}^L}{\partial \mathbf{z}^L} = \text{diag}(\sigma'(z^L_1), \dots, \sigma'(z^L_n))

  1. Result: Multiplying a diagonal matrix by a vector is equivalent to the Hadamard product:

δL=aLCσ(zL)\boldsymbol{\delta}^L = \nabla_{\mathbf{a}^L} C \odot \sigma'(\mathbf{z}^L)

BP2: Propagating Error to Hidden Layers

Goal: δl=((Wl+1)Tδl+1)σ(zl)\boldsymbol{\delta}^l = ((\mathbf{W}^{l+1})^T \boldsymbol{\delta}^{l+1}) \odot \sigma'(\mathbf{z}^l)

Derivation Steps:

  1. Link Consecutive Layers: We express the error at layer ll in terms of the error at layer l+1l+1 using the multivariate chain rule:

δl=Czl=(zl+1zl)TCzl+1=(zl+1zl)Tδl+1\boldsymbol{\delta}^l = \frac{\partial C}{\partial \mathbf{z}^l} = \left( \frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l} \right)^T \frac{\partial C}{\partial \mathbf{z}^{l+1}} = \left( \frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l} \right)^T \boldsymbol{\delta}^{l+1}

  1. Differentiate the Linear Transformation: Recall zl+1=Wl+1al+bl+1\mathbf{z}^{l+1} = \mathbf{W}^{l+1} \mathbf{a}^l + \mathbf{b}^{l+1} and al=σ(zl)\mathbf{a}^l = \sigma(\mathbf{z}^l). Applying the chain rule to find zl+1zl\frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l}:

zl+1zl=zl+1alalzl=Wl+1diag(σ(zl))\frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{z}^l} = \frac{\partial \mathbf{z}^{l+1}}{\partial \mathbf{a}^l} \cdot \frac{\partial \mathbf{a}^l}{\partial \mathbf{z}^l} = \mathbf{W}^{l+1} \cdot \text{diag}(\sigma'(\mathbf{z}^l))

  1. Transpose and Simplify: Substitute back and use the property (AB)T=BTAT(AB)^T = B^T A^T:

δl=(Wl+1diag(σ(zl)))Tδl+1=diag(σ(zl))(Wl+1)Tδl+1\boldsymbol{\delta}^l = \left( \mathbf{W}^{l+1} \cdot \text{diag}(\sigma'(\mathbf{z}^l)) \right)^T \boldsymbol{\delta}^{l+1} = \text{diag}(\sigma'(\mathbf{z}^l)) (\mathbf{W}^{l+1})^T \boldsymbol{\delta}^{l+1}

  1. Final Form:
    Convert the diagonal matrix multiplication to a Hadamard product:

δl=((Wl+1)Tδl+1)σ(zl)\boldsymbol{\delta}^l = ((\mathbf{W}^{l+1})^T \boldsymbol{\delta}^{l+1}) \odot \sigma'(\mathbf{z}^l)

BP3: Gradient for Biases

Goal: Cbl=δl\frac{\partial C}{\partial \mathbf{b}^l} = \boldsymbol{\delta}^l

Derivation Steps:

  1. Chain Rule via Intermediate zz:

Cbl=(zlbl)TCzl\frac{\partial C}{\partial \mathbf{b}^l} = \left( \frac{\partial \mathbf{z}^l}{\partial \mathbf{b}^l} \right)^T \frac{\partial C}{\partial \mathbf{z}^l}

  1. Compute Local Gradient: From zl=Wlal1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l, we see that bl\mathbf{b}^l is added directly to the weighted sum. Thus, zlbl\frac{\partial \mathbf{z}^l}{\partial \mathbf{b}^l} is the Identity matrix I\mathbf{I}.

  2. Conclusion:

Cbl=ITδl=δl\frac{\partial C}{\partial \mathbf{b}^l} = \mathbf{I}^T \boldsymbol{\delta}^l = \boldsymbol{\delta}^l

BP4: Gradient for Weights

Goal: CWl=δl(al1)T\frac{\partial C}{\partial \mathbf{W}^l} = \boldsymbol{\delta}^l (\mathbf{a}^{l-1})^T

Derivation Steps:

  1. Element-wise Approach: Consider a single weight wjklw^l_{jk} connecting neuron kk in layer l1l-1 to neuron jj in layer ll:

Cwjkl=Czjlzjlwjkl=δjlzjlwjkl\frac{\partial C}{\partial w^l_{jk}} = \frac{\partial C}{\partial z^l_j} \frac{\partial z^l_j}{\partial w^l_{jk}} = \delta^l_j \frac{\partial z^l_j}{\partial w^l_{jk}}

  1. Solve for Partial Derivative: Since zjl=mwjmlaml1+bjlz^l_j = \sum_m w^l_{jm} a^{l-1}_m + b^l_j, the derivative with respect to wjklw^l_{jk} is simply akl1a^{l-1}_k. Therefore, Cwjkl=δjlakl1\frac{\partial C}{\partial w^l_{jk}} = \delta^l_j a^{l-1}_k.

  2. Vectorize as an Outer Product: The collection of all such derivatives δjlakl1\delta^l_j a^{l-1}_k for all j,kj, k forms the Outer Product of the error vector δl\boldsymbol{\delta}^l and the input activation vector al1\mathbf{a}^{l-1}:

CWl=δl(al1)T\frac{\partial C}{\partial \mathbf{W}^l} = \boldsymbol{\delta}^l (\mathbf{a}^{l-1})^T

🔲 ☆

扔几个骰子,怎么算出期望?——拼多多校招笔试算法题的数学故事

By Long Luo

在上一篇文章我们剖析了 拼多多 2020 年校招笔试算法题中第一题: 多多的魔术盒子 ,今天来挑战下其中的第 4 题:骰子期望 1 ,题目如下:

骰子期望

\(n\) 个骰子,第 \(i\) 个骰子有可能投掷出 \(X_i\) 种等概率的不同的结果,数字从 \(1\)\(X_i\) 。所有骰子的结果的最大值将作为最终结果。求最终结果的期望。

  • 输入描述:
    • 第一行一个整数 \(n\) ,表示有 \(n\) 个骰子。( \(1 \le n \le 50\) )
    • 第二行 \(n\) 个整数,表示每个骰子的结果数 \(X_i\) 。( \(2 \le X_i \le 50\) )
  • 输出描述:
    • 输出最终结果的期望,保留两位小数。
  • 输入例子 \(1\) :
    • 2
    • 2 2
  • 输出例子 \(1\) :
    • 1.75

要解答这道题,我们需要先从脑海里把中学数学知识捡起来,弄清楚什么是期望 2

在概率论和统计学中,一个离散性随机变量的数学期望是试验中每次可能的结果乘以其结果概率的总和:

\[ \operatorname {E} [X] = \sum _{i=1}^{\infty }x_{i}\,p_{i} \tag{1} \label{1} \]

具体到这道题示例 \(1\) ,很明显 \(2\) 个骰子只能取到 \(1\) 或者 \(2\) 个值:

  1. 假设这 \(2\) 个骰子取到的最大值为 \(1\) ,那么这 \(2\) 个骰子都只能选择 \(1\) ,概率为: \(\dfrac {1}{2} \times \dfrac {1}{2} = \dfrac {1}{4}\)

  2. 假设这 \(2\) 个骰子取到的最大值为 \(2\) ,那么存在 \(2\) 种可能,要么都取 \(2\) 或者 \(2\) 个骰子中有一个骰子投出了 \(2\) ,其概率为: \(\dfrac {1}{2} \times \dfrac {1}{2} + \dfrac {1}{2} \times 1 = \dfrac {3}{4}\)

那么期望为: \(\dfrac {1}{4} \times 1 + \dfrac {3}{4} \times 2 = 1.75\)

对于骰子数少的时候还可以枚举,如果骰子数量很多呢?用上述方法就会遇到困难,比如有 \(N\) 个骰子,最大值为 \(M\) ,那么骰子结果为 \([1, 2, \dots, M]\) ,如何计算每个结果的概率值呢?

直接算当然是可行的,但是如果骰子数量很多的话,计算会非常繁琐,所以有没有更简单的方法呢?

🔲 ☆

拼多多校招笔试算法题:一行公式搞定“多多的魔术盒子”

By Long Luo

众多IT大厂中拼多多虽然工作强度很大,但在给钱方面非常大方。大厂给的钱多,但要求也高。下面就来挑战拼多多 2020 年校招笔试算法题 1 中第一题:多多的魔术盒子 2 ,看看难度如何。

多多的魔术盒子

多多鸡有 \(N\) 个魔术盒子(编号 \(1 \sim N\) ),其中编号为 \(i\) 的盒子里有 \(i\) 个球。多多鸡让皮皮虾每次选择一个数字 \(X\) ( \(1 \le X \le N\) ),多多鸡就会把球数量大于等于 \(X\) 个的盒子里的球减少 \(X\) 个。 通过观察,皮皮虾已经掌握了其中的奥秘,并且发现只要通过一定的操作顺序,可以用最少的次数将所有盒子里的球变没。

那么请问聪明的你,是否已经知道了应该如何操作呢?

  • 时间限制:

    • C/C++ 1秒,其他语言 2 秒
  • 空间限制:

    • C/C++ 256M,其他语言 512M
  • 输入描述:

    • 第一行,有 \(1\) 个整数 \(T\) ,表示测试用例的组数。 ( \(1 \le T \le 100\) )
    • 接下来 \(T\) 行,每行 \(1\) 个整数 \(N\) ,表示有 \(N\) 个魔术盒子。 ( \(1 \le N \le 1,000,000,000\) )
  • 输出描述:

    • \(T\) 行,每行 \(1\) 个整数,表示要将所有盒子的球变没,最少需要进行多少次操作。
  • 输入例子 1 :

    1
    2
    3
    4
    3
    1
    2
    5

  • 输出例子 1 :

    1
    2
    3
    1
    2
    3

最少的操作次数该怎么做?

根据题意,我们关键是要找到最少次数这个方法,那如何操作才能使用最少次数呢?

当面对复杂问题时,我们需要从简单情况入手,分析其中规律,找到突破口。

  1. \(N = 1\) 时,显然选择 \(X = 1\) ,需要 \(1\) 次操作;
  2. \(N = 2\) 时,可以先选择 \(X = 1\) ,再选择 \(X = 2\) ,需要 \(2\) 次操作;
  3. \(N = 3\) 时,只有先选择 \(X = 2\) ,再选择 \(X = 1\) ,最少需要 \(2\) 次操作;
  4. \(N = 4\) 时,可以先选择 \(X = 2\) 或者 \(X = 3\) ,最少需要 \(3\) 次操作;

通过分析发现,每次操作之后,球的数量都会动态变化。如果每次都选择中间的数字,这样每次操作之后,如果 \(N\) 为奇数的话,可以变成 \(2\) 个对称相同的数组, \(N\) 为偶数的话,则 \(2\) 个数组中元素值会相差 \(1\) ,再选择元素值更多的数组进行消除,这样可以实现操作次数最少

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static int leastTimes_power(int n) {
int ans = 1;

for (int i = 0; i <= Math.sqrt(n); i++) {
if (Math.pow(2, i) <= n) {
ans = i;
} else {
break;
}
}

return ans + 1;
}

为什么每次选取中间的数字进行操作次数最少呢?下面我们就来严谨的证明下!

🔲 ☆

斯特林公式(Stirling's Formula):我一个阶乘表达式,怎么就和圆扯上关系了呢?

By Long Luo

在科研和工程领域中,阶乘( \(\textit{Factorial}\) )有着广泛的应用。在概率论中,阶乘是计算排列( \(\textit{Permutation}\) )和组合( \(\textit{Combination}\) )时不可或缺的;在物理中,计算粒子系统的状态数以及大型系统的统计分布都要用到阶乘;在计算机中,阶乘则用于图论和组合优化问题。

大家都知道“指数爆炸”这个值,因为指数增长是非常迅猛的。其实阶乘增长要远远快于指数增长,如下图 1 所示为不同算法复杂度增长情况。随着 \(n\) 的增大,阶乘的计算复杂度迅速上升,当处理大 \(n\) 时,计算 \(n!\) 会变得极其复杂且耗时。例如 \(5! = 120\) ,而 \(50! \approx 3.04 \times 10^{64}\) ,这已经是一个非常庞大的数字!直接使用递归方法去求解 \(n!\) 的时间复杂度是 \(O(n)\) ,对于较大的 \(n\) 来说很容易栈溢出。在实际应用中,往往需要计算大数的阶乘,即使目前最先进的计算机去处理极大数的阶乘时,也会面临需要巨大的计算及存储资源消耗问题。

图1. 不同算法时间复杂度 Time Complexity Comparison

人们迫切需要找到一种可以快速计算阶乘的方法,在 18 世纪初期,苏格兰数学家詹姆斯·斯特林( \(\textit{James Stirling}\) )提出了斯特林公式 。斯特林公式( \(\textit{Stirling's Approximation}\) )提供了一种近似计算阶乘的方法,特别适用于大 \(n\) 的情况,其标准形式为:

\[ n! \approx {\sqrt {2\pi n}} \,\left( {\frac {n}{e}} \right )^n \tag{1.1} \label{1.1} \]

其对数形式为:

\[ \ln (n!) \approx n \ln n - n \]

这个公式最早是亚伯拉罕·棣莫弗( \(\textit{Abraham de Moivre}\) )在研究二项分布时,为了解决大数阶乘问题时发现的,其形式为:

\[ n! \approx C n^{n + \frac {1}{2}}e^{-n} \tag{1.2} \label{1.2} \]

其中 \(C\) 为某个常量值,斯特林证明了公式中 \(C = \sqrt {2 \pi}\) ,于是冠名权就给了斯特林。

斯特林公式可以大大简化阶乘的计算,特别是当 \(n\) 很大时,它提供了一个非常精确的近似值。斯特林公式使得复杂的阶乘计算可以通过较为简单的幂函数、指数函数和根号运算来完成。相比于直接计算阶乘,它极大地减少了计算量,是大数问题中不可或缺的工具。

斯特林公式中集合了圆周率 \(\pi\) 和自然常数 \(e\) ,这 \(2\) 个数学中最重要的常数,十分独特且具有美感。因为 \(e\) 意味着连续增长,而阶乘就是连续自然数的相乘。而出现 \(\pi\) 的时候,就要问自己 “Where is the circle?”,那么阶乘是如何和几何中的圆扯上关系了呢?

关于斯特林公式的证明,常见的证明是对数形式的证明或者利用伽马函数( \(\textit{Gamma Function}\) )来证明,这里将介绍一种从零开始更易理解的推导方式。

🔲 ☆

我爱做题:2010年江西高考理科数学压轴题

By Long Luo

知乎上有个问题是高考数学最后一题可以有多难? 1,而公认史上最难高考数学题就是 2008 年江西高考理科数学压轴题。2005 - 2014 年是江西自主命题,而江西卷也以题难计算量大著称,尤其是数学和理综。陶平生老师 是 2008 - 2011 年江西高考数学命题组长,参与了 2005 - 2011 年江西高考命题,他出江西数学卷时,是江西30多万学生被支配的恐惧!

作为一名来自十八线农村做题家,高考时赶上了江西自主命题,在考场上也体会到了数学和理综卷题目居然没做完没思路的恐惧!中学时没能感受到数学的乐趣,最近几年看了一些数学书之后,重新拾起了数学的乐趣,经常找些数学题来训练下我的思维,今天就来挑战一下 2010 年江西高考理科数学压轴题:

  1. (本小题满分14分) 证明以下命题:
  1. 对任意正整数 \(a\) ,都存在正整数 \(b, c\)\(b < c\) )使得 \(a^2, b^2, c^2\) 为等差数列.
  2. 存在无穷多互不相似的三角形 \(\triangle_n\) ,其边长 \(a_n, b_n, c_n\) 为正整数且 \(a_n^2, b_n^2, c_n^2\) 成等差数列.

这道题其实是数论( \(\textit{Number theory}\) ) 2 背景,除了搞竞赛的同学,谁学过数论呢?这种构造性( \(\textit{Constructive proof}\) ) 3 题目,没有接触过类似题目的话,根本不知道如何下手。要是我在考场上也会一脸懵圈,甚至在几年前我也是一筹莫展,不过现在倒是有勇气挑战这类题目了!网上关于这道题的参考答案太简略了,主要是如何找到答案的构造不清楚。这道题真是一道绝妙的题,我花了比较长时间来思考这道题,下面详细描述我的解题思路。

第一问

根据题意,正整数 \(a, b, c\)\(b < c\) ,满足 \(a^2, b^2, c^2\) 为等差数列,即:

\[ b^2 - a^2 = c^2 - b^2 \Rightarrow 2b^2 = a^2 + c^2 \]

观察上式,我们可以得到 \(2\) 个推论:

  1. \(a < b < c\)
  2. \(a\)\(c\) 要么都是偶数,要么都是奇数。

因为 \(a\) 为任意正整数,那么就从最简单的开始,不妨设 \(a = 1\) ,则有:

  1. \(c = 1\) , 得 \(b = 1\) ,与 \(a < b < c\) 矛盾;
  2. \(c = 3\) , 得 \(b = \sqrt {5}\) ,与 \(b\) 为正整数矛盾;
  3. \(c = 5\) , 得 \(b = \sqrt {13}\) ,与 \(b\) 为正整数矛盾;
  4. \(c = 7\) , 得 \(b = 5\) ,满足条件。

简单猜测实验得到一组要求的值: \(a = 1\) , \(b = 5\) , \(c = 7\)

再设 \(a = 2\) ,同理有:

  1. \(c = 4\) , 得 \(b = \sqrt {10}\) ,与 \(b\) 为正整数矛盾;
  2. \(c = 6\) , 得 \(b = 2 \sqrt {5}\) ,与 \(b\) 为正整数矛盾;
  3. \(c = 8\) , 得 \(b = \sqrt {34}\) ,与 \(b\) 为正整数矛盾;
  4. \(c = 10\) , 得 \(b = 2 \sqrt {13}\) ,与 \(b\) 为正整数矛盾;
  5. \(c = 12\) , 得 \(b = \sqrt {74}\) ,与 \(b\) 为正整数矛盾;
  6. \(c = 14\) , 得 \(b = 10\) ,满足条件。

通过实验又得到一组要求的值: \(a = 2\) , \(b = 10\) , \(c = 14\)

综合 \(a\) 为奇数和偶数的情况,猜想:

对于 \(\forall a = n \in \mathbb{N}^+\) ,令 \(b = 5a\) , \(c = 7a\) ,易得:

\[ 2 \cdot (5n)^2 = n^2 + (7n)^2 \]

故命题(1)得证。

🔲 ⭐

为什么 2024 年会有 366 天?

By Long Luo

2024 年很快就要过去了,就在今天,我们脚下的地球已经以每秒约 30 公里的速度飞快越过了近日点,飞驰在围绕着太阳的椭圆轨道上。当 2025 年新年钟声敲响时,对于位于银河系第三旋臂边缘的这颗蓝色行星来说,不过是围绕着一颗黄矮星完成了一次再普通不过的公转,正如之前的 40 多亿次一样。而对于这颗行星上的碳基生物来说,不同的生物感受大不一样,这一刻却意味着对过去 366 个日夜 的告别与总结,也承载着对未来的期待与梦想。

和 2023 年不一样的是,我们在 2024 年要经历 366 个日夜,因为 2024 年是闰年。小学时,课本和老师都告诉我们一365 天,但是闰年却是 366 天,这多出来的一天就是 2 月 29 日,在英语中叫做 \(\textit{Leap Day}\) 。四年一闰,百年不闰,四百年再闰,这是闰年的规则。后来学习编程时,判断某一年是不是闰年也是常见的编程练习题。在学习之余,你有没有想过,为什么闰年的规则要这么奇怪呢?背后的原因是什么呢?

小学读书时,就想 2 月份很委屈, 1 月 和 3 月都是 31 天,2 月却只有 28 或者 29 天,为什么 2 月这么特别呢?为什么有的月份是 31 天,有的月份是 30 天呢?但我的老师并没有讲清楚为什么,因为当时我的老师们也不清楚为什么,我也一直到大学里读了一本天文学书才知道了这个问题的答案。

为什么 2024 年 2 月有 29 天?要回答这个问题,我们需要穿越历史的迷雾,回顾人类文明史,才能找到答案。

逝者如斯夫,不舍昼夜!

《鲁滨逊漂流记》中的鲁滨逊流落到荒岛之后第一时间就是竖起了一根大柱子,用刀子在立柱上刻上凹口当作日历。一方面是因为鲁滨逊是基督徒需要做礼拜,另外一方面也是为了记录时间。我们的现代生活是离不开钟表的,如果没有钟表来量化时间的话,我们的工作生活将失去秩序。当然鲁滨逊在荒岛上也只能过着“日出而作,日落而息”的农业社会生活,无法精确的安排工作和生活。

“朝菌不知晦朔,蟪蛄不知春秋”,我们智人的寿命足够长,不像朝生暮死的蜉蝣,也不似春生夏死的寒蝉,可以目睹很多生命的诞生、成长以及消亡,感受时间的流逝。“逝者如斯夫,不舍昼夜!”,时间的洪流永远奔涌向前,然而虽然以我们生命的长度可以跨越四季与年轮,但是依然无法触及时间的尽头。

寄蜉蝣于天地,渺沧海之一粟。哀吾生之须臾,羡长江之无穷。当然我们现在知道时间并不是均匀流逝的,也不能脱离物质而存在,但在足够宏大的尺度上,时间在均匀的流逝着。

虽然你可能没有意识到,但是我们一直都在用着天然的时钟,它们就位于我们头顶的星空。这些时钟都足够精准,地球自转的每日误差在毫秒级别,月球公转和地球公转上百年也仅有几毫秒的差别。

“日出东南隅,照我秦氏楼。”,新的一天又开始了;残月如弓,新月如眉,满月如镜,周而复始;“未觉池塘春草梦,阶前梧叶已秋声。”,四季轮回时,我们知道新的一年又来临了。

🔲 ⭐

数学之美:几何视角下的高斯积分(Gaussian Integral)

By Long Luo

从之前的文章 正态分布(Normal Distribution)公式为什么长这样?从最小二乘法到正态分布:高斯是如何找到失踪的谷神星的? ,我们使用了 \(2\) 种不同的方法最终得到了如下公式 \((1)\) 所示的误差的概率密度函数 ( \(\text{Probability Density Function}\) ) :

\[ f(x) = \mathrm{e}^{-cx^2}, \, c > 0 \tag{1} \label{1} \]

其函数图像如下图 1 所示的钟形曲线 ( \(\text{Bell Curve}\) ) :

图1. 钟形曲曲线

在概率论中,我们需要保证上图 1 中 \(f(x)\)\(x\) 轴围成的面积是 \(1\) , 即:

\[ \int_{- \infty}^{+ \infty} f(x) \mathrm{d}x = 1 \tag{2} \label{2} \]

最终我们得到了正态分布 ( \(\text{Normal Distribution}\) ) 的公式如下所示:

\[ f(x) = {\frac {1}{\sigma {\sqrt {2 \pi }}}}\;e^{-{\frac {\left(x - \mu \right)^{2}}{2 \sigma ^{2}}}} \tag{3} \label{3} \]

上式中有一个 \(\pi\) ,用费曼( \(\text{Richard Feynman}\) )的话来说,当我们看到一个公式中存在 \(\pi\) 时,我们都要问自己“Where is the cycle?”。我们知道公式 \(\eqref{3}\) 中的归一化系数 \(\dfrac {1}{\sigma {\sqrt {2 \pi }}}\) 是为了保证 \(f(x)\) 下的面积为 \(1\) ,出现 \(\pi\) 是因为高斯积分 ( \(\text{Gaussian Integral}\) ) 的结果为 \(\sqrt{\pi}\)

那么什么是高斯积分呢?高斯积分和圆有什么关系呢?

🔲 ⭐

正态分布(Normal Distribution)公式为什么长这样?

By Long Luo

相信大家或多或少都听过六西格玛( \(\text{6 Sigma}\) ) 1 这个词,六西格玛是指生产的产品中, \(99.99966\%\) 的产品是没有质量问题的,即只有 \(3.4ppm\) 的不良率。

假如一家工厂生产某型号零件,零件的长度要求是 \(100mm\) ,允许的标准差是 \(0.1mm\) 。根据 \(6 \sigma\) 原则,零件规格允许的偏差范围是: \(100 \pm 6 \times 0.1 = 100 \pm 0.6\)

这意味着,零件长度超过 \(100.6mm\) 或低于 \(99.4mm\) 的概率是非常低的,约为 \(0.00034\%\) 。如果工厂每天生产 100 万个零件,只允许有 \(3.4\) 个零件会超出 \(6 \sigma\) 的范围,几乎可以忽略不计。因此,生产过程是极其稳定和可靠的,达到了六西格玛水平。

那么 \(6 \sigma\)\(3.4ppm\) 的不良率来自哪里呢?

学过中学数学都知道,在正态分布( \(\text{Normal Distribution}\) ) 2 中, \(68.27\%\) 的数据位于平均值的一个标准差内, \(95.45\%\) 位于两个标准差内, \(99.73\%\) 位于三个标准差内,这也是著名的 68-95-99.7 Rule 3 ,如下图 1 所示:

图1. 68-95-99.7 Rule

什么是正态分布?

数据可以用不同的方式“分布”,比如数据可以向左散布的多一些,也可以向右散布的多一些,或者分布的乱七八糟,如下图 2 - 4 所示,

图2. 数据偏向左散布
图3. 数据偏向右散布
图4. 数据随机分布

但数据经常会集中在一个中心值的附近,而不向左或右偏斜,像一个钟形,如下图 5 所示。

图5. 数据正态分布

正态分布,又称高斯分布( \(\text{Gaussian Distribution}\) ),是一种重要的概率分布,数学王子高斯 4 在正态分布的研究和应用上做出了巨大贡献。有很多日常现象都符合这种分布,如人的身高、考试成绩等。正因为它几乎无处不在,所以叫 \(\text{Normal Distribution}\) 。德国曾经发行的一款 10 马克的纸币上就印着高斯和正态分布曲线,如下图 6 所示。

图6. 高斯和正态分布曲线
🔲 ⭐

浮点数

By Long Luo

挖坑

假如你知道浮点数的话,你就知道为什么了!

按照 IEEE 754 浮点数标准 制定的 浮点数运算法则,

🔲 ☆

为什么 2024 年会有 366 天?

By Long Luo

2024 年很快就要过去了,就在今天,我们脚下的地球已经以每秒约 30 公里的速度飞快越过了近日点,飞驰在围绕着太阳的椭圆轨道上。当 2025 年新年钟声敲响时,对于位于银河系第三旋臂边缘的这颗蓝色行星来说,不过是围绕着一颗黄矮星完成了一次再普通不过的公转,正如之前的 40 多亿次一样。而对于这颗行星上的碳基生物来说,不同的生物感受大不一样,这一刻却意味着对过去 366 个日夜 的告别与总结,也承载着对未来的期待与梦想。

和 2023 年不一样的是,我们在 2024 年要经历 366 个日夜,因为 2024 年是闰年。小学时,课本和老师都告诉我们一365 天,但是闰年却是 366 天,这多出来的一天就是 2 月 29 日,在英语中叫做 \(\textit{Leap Day}\) 。四年一闰,百年不闰,四百年再闰,这是闰年的规则。后来学习编程时,判断某一年是不是闰年也是常见的编程练习题。在学习之余,你有没有想过,为什么闰年的规则要这么奇怪呢?背后的原因是什么呢?

小学读书时,就想 2 月份很委屈, 1 月 和 3 月都是 31 天,2 月却只有 28 或者 29 天,为什么 2 月这么特别呢?为什么有的月份是 31 天,有的月份是 30 天呢?但我的老师并没有讲清楚为什么,因为当时我的老师们也不清楚为什么,我也一直到大学里读了一本天文学书才知道了这个问题的答案。

为什么 2024 年 2 月有 29 天?要回答这个问题,我们需要穿越历史的迷雾,回顾人类文明史,才能找到答案。

逝者如斯夫,不舍昼夜!

《鲁滨逊漂流记》中的鲁滨逊流落到荒岛之后第一时间就是竖起了一根大柱子,用刀子在立柱上刻上凹口当作日历。一方面是因为鲁滨逊是基督徒需要做礼拜,另外一方面也是为了记录时间。我们的现代生活是离不开钟表的,如果没有钟表来量化时间的话,我们的工作生活将失去秩序。当然鲁滨逊在荒岛上也只能过着“日出而作,日落而息”的农业社会生活,无法精确的安排工作和生活。

“朝菌不知晦朔,蟪蛄不知春秋”,我们智人的寿命足够长,不像朝生暮死的蜉蝣,也不似春生夏死的寒蝉,可以目睹很多生命的诞生、成长以及消亡,感受时间的流逝。“逝者如斯夫,不舍昼夜!”,时间的洪流永远奔涌向前,然而虽然以我们生命的长度可以跨越四季与年轮,但是依然无法触及时间的尽头。

寄蜉蝣于天地,渺沧海之一粟。哀吾生之须臾,羡长江之无穷。当然我们现在知道时间并不是均匀流逝的,也不能脱离物质而存在,但在足够宏大的尺度上,时间在均匀的流逝着。

虽然你可能没有意识到,但是我们一直都在用着天然的时钟,它们就位于我们头顶的星空。这些时钟都足够精准,地球自转的每日误差在毫秒级别,月球公转和地球公转上百年也仅有几毫秒的差别。

“日出东南隅,照我秦氏楼。”,新的一天又开始了;残月如弓,新月如眉,满月如镜,周而复始;“未觉池塘春草梦,阶前梧叶已秋声。”,四季轮回时,我们知道新的一年又来临了。

🔲 ⭐

跟上Go演进步伐,你只需要关注这几件事儿

本文永久链接 – https://tonybai.com/2024/09/30/how-to-keep-up-with-go-evolution

Go语言以其简洁、高效和强大的特性赢得了众多开发者的青睐。与许多主流编程语言有着明确的演进Roadmap或下一个版本spec不同,Go的演进过程更加独特、灵活与开放。这种看起来不那么正式和严肃的演进方式却也能让Go快速响应开发者的需求,同时保持语言的稳定性和一致性。

作为一名Go开发者,跟上Go的演进步伐,甚至是参与到这个激动人心的过程中来,不仅能让你更好地利用语言的新特性,还能帮助你更深入地理解Go的设计哲学。

但很多Go开发者只知道每年Go有两次的大版本Release,并通过大版本的Release Notes来了解Go的演进。这无可厚非,但对于那些想及时跟进Go演进的Gopher来说,光有一年两次的Release Notes还是不够的的,很难及时跟进Go的演进决策。

但如果直接到Go语言项目的issue中去翻阅,面对Go丰富的社区讨论和频繁的更新,你可能会感到无从下手。别担心,本文将为你指明方向,让你只需关注几个关键点,就能轻松跟上Go的演进步伐。

1. 开发计划早知道

Go的版本规划具有很高的灵活性。每个Go 1.x版本在开发前,Go语言项目相关人员都会在golang-dev讨论组上发布一个帖子,这个帖子通常的标题为”Planning Go 1.x”,例如”Planning Go 1.23″,如下图:

很多contributor,无论是Go团队的,还是外部贡献者的,会在该帖子下面留下自己的plan(注意:这些plan中的特性可不一定会在最终的版本中发布),然后等main tree开放后,就会将已经准备完毕的cl(changelist) merge到main tree中去,或开始提交cl,等待Go团队或社区的开发者进行评审。

当然对于Go 1.x这样的大版本,Go团队会在github建立专门的milestone跟踪,大家也可以在对应的milestone中看到该版本带来的新特性等,下图是目前正在积极开发的Go 1.24版本里程碑

通过查看这些Plan或定期查看Go 1.x里程碑,你可以提前了解Go的发展方向,为新版本的到来做好准备。

当然如果要了解那些更早的Go演进的决策,我们还得关注和跟踪下面的Proposal Project看板和三个关键的issue。

2. Proposal Project看板和三个关键的Issue

Go在早期并没有规范的proposal提案流程,更多是由Rob Pike、Robert Griesemer等三个Go语言之父,外加Ian Taylor和Russ Cox讨论确定,这一状态在Russ Cox建立明确的Go proposal提案流程后结束,提案流程是Go团队审查提案并决定接受或拒绝提案的过程。Russ Cox在提案流程中明确了Go项目的开发过程是设计驱动(design-driven)的,必须首先对语言、库或工具的重大更改进行讨论(包括Go语言项目主仓库和所有golang.org/x仓库中的API更改,以及对go command的命令行更改),并在实现这些设计之前进行正式记录。

Go团队目前使用Proposal Project看板和GitHub Issues来追踪语言的演进,下面我们来看看这个看板和值得关注的三个Issue。

2.1 Proposal Project看板

Proposal Project看板是Go团队跟踪proposal的全局视图,当然要理解该看板,我们需要先来简单看看Go的proposal流程以及每个提案的生命周期是怎样的。

Go Proposal流程并不复杂,可以概括为下面这个示意图:

该流程图展示了Go提案流程的几个主要步骤:

  • 任何人都可以作为提案作者,在Go项目上创建一个简短的issue来描述提案。
  • Go团队成员以及任何Go社区成员在issue上进行初步讨论,由一组人组成的Go提案审核委员会决定是接受提案、拒绝提案,还是需要进一步的设计文档。
  • 如果需要进一步的设计文档,提案作者会撰写一个详细的设计文档。
  • 在设计文档的评论减少/收敛后(意见趋于一致后),由Go提案审核委员会会进行最终讨论,决定接受或拒绝提案。

Go提案审查委员会使用GitHub项目看板来跟踪提案的状态并管理提案的生命周期(如下图所示):

该看板针对每个提案issue设置了几个生命周期状态:

  • Incoming:新提交的提案
  • Active:正在积极讨论的提案
  • Likely Accept / Likely Decline:可能被接受或拒绝的提案
  • Accepted / Declined:已被接受或拒绝的提案
  • Hold:需要设计修订或需要几周或更长时间才能获得附加信息的提案,这类提案一旦准备就绪,还会回到Active状态

了解了上述Go提案与审核流程,再看下面的几个关键Issue就容易多了。

2.2 proposal: review meeting minutes(33502)

该issue于2019年8月创建,其创建者为前Go团队技术负责人Russ Cox。这是目前Go语言项目最核心的追踪Issue,它记录了Go提案审查会议的纪要,通常每周更新一次(如下图所示):

我们看到内容包括:

  • 发布当周已经决策为Accepted和Declined的proposal列表
  • 后续Likely Accept和Likely Decline的proposal列表
  • 正处于Active讨论的proposal列表
  • 当前处于Hold状态的proposal列表

和Go提案看板不同,该issue是对提案Issue的状态变更的记录,Gopher可以第一时间看到每周Go提案的状态更新。

由于Russ Cox已经辞去了Go团队技术负责人的头衔,从2024年9月下旬开始,Go团队新的技术负责人Austin Clements将继续主持提案审核会议,并更新该Issue。

除了Review meeting minutes这个重要的issue外,还有两个issue值得我们关注,通过它们,我们可以及时了解到Go编译器和运行时的演进以及Go语法特性的演进。

2.3 Go compiler and runtime meeting notes(43930)

Go编译器和运行时团队定期(大约每周)召开会议,讨论Go编译器和运行时的后续开发和演进事宜,该会议是Google Go团队的内部会议,但Go团队觉得Go社区有必要了解这个会议上的一些讨论议题、过程与会议结论,从而知道Go编译器和运行时团队正在以及将要做什么。

于是前Go团队成员Jeremy Faller于2021年1月创建了该Issue,向Go社区发布Go编译器和运行时的最新演进动向。

之前Go编译器和运行时团队的负责人是Austin Clements,如今是CherryMui

2.4 spec: language change review meeting minutes(33892)

编译器和运行时之外,Gopher最关心的就是Go语法的演进以及Go语言规范的变更,这个事儿是由Go语言之父之一的Robert Griesemer亲自抓的。在2019年8月,Robert Griesemer就建立了跟踪Go语法变化的issue,当然最初是要跟踪Go2的演进,后来Go泛型落地后,Go2彻底融入了Go1,该issue也就变成了跟踪Go语法演进的Issue。Robert Griesemer主持的Go语言变更审查会议每月举行一次,并将会议讨论的记录发布到该Issue上。

3. Discussion与Russ Cox博客

关于Go语言演进的动向,还有两个渠道可以关注,一个是Go团队在github repo上发起的discussionRuss Cox在2021年7月启用了discussion,旨在寻找一个地方来扩大许多人可能想要参与的讨论。当前,该discussion仅针对非常有限的事项添加讨论,并且只有少数Go核心团队的人才有发起discussion的权限。一些在前几个版本的重要语言特性变化以及标准库的变化,都在这里进行了充分的讨论,比如loopvar语义修正自定义iterator开启标准库major版本更新的math/rand/v2以及gonew工具等。

另外一个则是Russ Cox的博客,作为Go项目团队前技术负责人,作为Rob Pike的接班人,Russ Cox很好地完成了承上启下的作用,并为Go的演进和发展确立了演进框架、方法以及方向。Russ Cox经常在自己的博客上先“憋大招,做铺垫”!最典型的就是vgo,也就是go module的前身,在短短几周内Russ Cox在博客上发表了7篇关于vgo的设计思路文章,为后来Go module的落地奠定了基础,至此基本上不再有Gopher抱怨Go依赖管理了。Russ Cox现已辞去Go技术负责人的头衔,后续是否还能在他的博客上看到Go相关的新特性的设计,让我们拭目以待!

4. 小结

在快速发展的技术环境中,Go语言以其独特的演进方式和灵活的开发计划,吸引了越来越多的开发者。本文介绍了如何及时有效地跟踪Go的演进的方法,包括关注大版本开发计划、Proposal Project看板和关键的issue,帮助Gopher及时了解语言的新特性与设计决策。通过参与讨论和关注Go团队的动态,开发者不仅能掌握最新的语言更新,还能深入理解Go的设计哲学和发展方向。希望每位Gopher都能抓住这些资源,与Go语言共同成长,提升自己的开发技能。


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

© 2024, bigwhite. 版权所有.

🔲 ⭐

数学之美:几何视角下的高斯积分(Gaussian Integral)

By Long Luo

从之前的文章 正态分布(Normal Distribution)公式为什么长这样?从最小二乘法到正态分布:高斯是如何找到失踪的谷神星的? ,我们使用了 \(2\) 种不同的方法最终得到了如下公式 \((1)\) 所示的误差的概率密度函数 ( \(\text{Probability Density Function}\) ) :

\[ f(x) = \mathrm{e}^{-cx^2}, \, c > 0 \tag{1} \label{1} \]

其函数图像如下图 1 所示的钟形曲线 ( \(\text{Bell Curve}\) ) :

图1. 钟形曲曲线

在概率论中,我们需要保证上图 1 中 \(f(x)\)\(x\) 轴围成的面积是 \(1\) , 即:

\[ \int_{- \infty}^{+ \infty} f(x) \mathrm{d}x = 1 \tag{2} \label{2} \]

最终我们得到了正态分布 ( \(\text{Normal Distribution}\) ) 的公式如下所示:

\[ f(x) = {\frac {1}{\sigma {\sqrt {2 \pi }}}}\;e^{-{\frac {\left(x - \mu \right)^{2}}{2 \sigma ^{2}}}} \tag{3} \label{3} \]

上式中有一个 \(\pi\) ,用费曼( \(\text{Richard Feynman}\) )的话来说,当我们看到一个公式中存在 \(\pi\) 时,我们都要问自己“Where is the cycle?”。我们知道公式 \(\eqref{3}\) 中的归一化系数 \(\dfrac {1}{\sigma {\sqrt {2 \pi }}}\) 是为了保证 \(f(x)\) 下的面积为 \(1\) ,出现 \(\pi\) 是因为高斯积分 ( \(\text{Gaussian Integral}\) ) 的结果为 \(\sqrt{\pi}\)

那么什么是高斯积分呢?高斯积分和圆有什么关系呢?

🔲 ⭐

智能控制系统的灵魂:深入研究 PID 控制器的算法逻辑

By Long Luo

PID 算法 s是自动控制领域中很重要的算法。

\[ u(t) = K_Pe(t) + K_I \int e(t) \mathrm{d}t + K_D \frac{\mathrm{d}e(t)}{\mathrm{d}t} \]

Simple PID Controller

非常简单的 PID 算法在线互动式模拟器,传送门 →

PID Algorithm

之前这个是 PID v1.0 版本,最近重构了代码,增加了一些新功能:

  1. 增加机器人速度 \(v\) 及加速度 \(a\) 显示;
  2. 增加 2 个图表展示 PID X 轴方向及 Y 轴方向的 P、I、D \(3\) 个分量随时间变化显示;
  3. 之前代码将时间及速度固定了,但这不符合实际,增加随 \(dt\) 变化积分和微分项;

pid_track

❌