小名开开

在春天的光影里喘息

不同等级的歌曲在获得对应评价时获得的经验值:

Score Rating Lv.1 Lv.2 Lv.3 Lv.4 Lv.5 Lv.6 Lv.7 Lv.8 Lv.9 Lv.10
1000000 Exc 88 104 120 136 152 168 184 200 216 232
980000-999999 SSS 82 97 112 127 142 157 172 187 202 217
950000-979999 SS 77 91 105 119 133 147 161 175 189 203
900000-949999 S 71 84 97 110 123 136 149 162 175 188
850000-899999 A 66 78 90 102 114 126 138 150 162 174
800000-849999 B 60 71 82 93 104 115 126 137 148 159
700000-799999 C 55 65 75 85 95 105 115 125 135 145
500000-699999 D 49 58 67 76 85 94 103 112 121 130
0-499999 E 44 52 60 68 76 84 92 100 108 116
  • 同评价下不同等级的歌曲获得的经验值为等差增长数列;
  • 同等级下不同评价的歌曲获得的经验值为交替增长数列;
  • 各等级歌曲 Exc 的经验总是 E 的两倍,细分的小数点等级以取整计。

玩家各等级升级经验:

Lvl Exp Lvl Exp Lvl Exp Lvl Exp Lvl Exp
1 44 21 2505 41 7597 61 15329 81 25701
2 113 22 2697 42 7921 62 15785 82 26289
3 179 23 2895 43 8251 63 16247 83 26883
4 252 24 3100 44 8588 64 16716 84 27484
5 332 25 3312 45 8932 65 17192 85 28092
6 418 26 3530 46 9282 66 17674 86 28706
7 511 27 3755 47 9639 67 18163 87 29327
8 611 28 3987 48 10003 68 18659 88 29955
9 717 29 4225 49 10373 69 19161 89 30589
10 830 30 4470 50 10750 70 19670 90 31230
11 949 31 4721 51 11133 71 20185 91 31877
12 1075 32 4979 52 11523 72 20707 92 32531
13 1207 33 5243 53 11919 73 21235 93 33191
14 1346 34 5514 54 12322 74 21770 94 33858
15 1492 35 5792 55 12732 75 22312 95 34532
16 1644 36 6076 56 13148 76 22860 96 35212
17 1803 37 6367 57 13571 77 23415 97 35899
18 1969 38 6665 58 14001 78 23977 98 36593
19 2141 39 6969 59 14437 79 24545 99 37293
20 2320 40 7280 60 14880 80 25120 100 38000
  • 两等级间的经验差额 Dn=Expn+1-Expn 在 n≥2 时符合间隔为 7,7,6,7,7,6,7,6,7,6 的递增序列。
  • 即 Expn+10-Expn=830+66n, n≥2。E2~E11 为基准值。
  • 可以进一步推测,Exp1 原本应为 53,但为了确保任何一个新玩家哪怕是最初级的玩家也能立刻体验到升级效果,单独修正为 44。

玩家等级与体力值:

スタミナ = プレーヤーレベル + 49,即 Stamina 体力 = Player Level 玩家等级 + 49,初始 1 级 50 点体力。

  • 1-3 级歌曲,每次游玩消耗 20 点体力

  • 4-6 级歌曲,每次游玩消耗 25 点体力

  • 7-10 级歌曲,每次游玩消耗 30 点体力

  • 每 3 分钟恢复 1 点体力,直到达到体力上限;

  • 当用户升级时,会恢复体力上限等额的体力,此时可用体力会超过上限;

  • 当前体力不足以游玩指定歌曲时,系统会提示用户观看广告获得 50 点体力,当上限小于 79 时,可用体力可能会超过上限;

  • 观看广告获得 50 体力,这一行为本身有 1 小时的 CD,不影响其它情况下观看广告获得加成。

  • 使用 1 个可乐道具,或 50 个 jBlock 道具可以换取 50 点体力,无 CD,道具通过各类任务获得,也可以直接付费购买;

  • 使用 jBlock 在 plus 模式下购买的曲包可以无限次游玩,不消耗体力,但也不会获得经验值;

  • 使用 Skip 券可以跳过指定谱面的实际游玩过程,直接按该谱面的历史最高分结算奖励,也会消耗体力。该谱面必须之前有过演奏记录。节约时间用。

尽快升级

  • 由于体力上限与等级成正比,因此在游戏初期,尽快升级是提高游戏体验的要素之一。
  • 由于歌曲难度等级对应体力消耗不同,且歌曲难度对 Rating 有影响,因此同体力最多 Exp 的升级路线要根据个人能力调整。
  • 例如,都为 E 级,Lv.6 的 84/25 = 3.36 就大于 Lv.8 的 100/30 = 3.33。
  • 如果所有歌都能打相同 Rating,优势顺序为 10 9 6 8 7 5 3 4 2 1。如果不是,就需要按下表排列:
档位 效率比例与等级
≥ 7 10Exc (7.73) 10SSS (7.23) 9Exc (7.2)
≥ 6.5 10SS (6.77) 9SSS (6.73) 6Exc (6.72) 8Exc (6.67)
≥ 6 9SS (6.3) 6SSS (6.28) 10S (6.27) 8SSS (6.23) 7Exc (6.13) 5Exc (6.08) 3Exc (6)
≥ 5.5 6SS (5.88) 8SS (5.83) 9S (5.83) 10A (5.8) 7SSS (5.73) 5SSS (5.68) 3SSS (5.6)
≥ 5 6S (5.44) 4Exc (5.44) 8S (5.4) 9A (5.4) 7SS (5.37) 5SS (5.32) 10B (5.3) 3SS (5.25) 2Exc (5.2) 4SSS (5.08) 6A (5.04) 8A (5)
≥ 4.5 7S (4.97) 9B (4.93) 5S (4.92) 3S (4.85) 2SSS (4.85) 10C (4.83) 4SS (4.76) 6B (4.6) 7A (4.6) 8B (4.57) 5A (4.56) 2SS (4.55) 3A (4.5) 9C (4.5)
≥ 4 1Exc (4.4) 4S (4.4) 10D (4.33) 6C (4.2) 2S (4.2) 7B (4.2) 8C (4.17) 5B (4.16) 1SSS (4.1) 3B (4.1) 4A (4.08) 9D (4.03)
≥ 3.5 2A (3.9) 10E (3.87) 1SS (3.85) 7C (3.83) 5C (3.8) 6D (3.76) 3C (3.75) 8D (3.73) 4B (3.72) 9E (3.6) 1S (3.55) 2B (3.55)
≥ 3 7D (3.43) 5D (3.4) 4C (3.4) 6E (3.36) 3D (3.35) 8E (3.33) 1A (3.3) 2C (3.25) 7E (3.07) 5E (3.04) 4D (3.04) 1B (3) 3E (3)
≥ 2 2D (2.9)1C (2.75)4E (2.72)2E (2.6)1D (2.45)1E (2.2)

这里有个重要的点位是 10E(3.87)。E 是最低评价,摆烂也能拿。也就是说,对于效率升级的目标,即以相同体力获得最多经验经验值来说,即使你 1 级歌能打 SS,都不如开 10 级歌放那里不动等 0 分结算快。

コイン (Coin)

  • 1-3 级歌曲,每次游玩后奖励 3 个宝箱
  • 4-6 级歌曲,每次游玩后奖励 4 个宝箱,其中 1 个为劣质宝箱
  • 7-10 级歌曲,每次游玩后奖励 5 个宝箱,其中 2 个为劣质宝箱
  • 宝箱可能开出 20, 30, 50, 100, 200, 1000, 10000 等数额的金币。
  • 根据 Rating 不同,宝箱开出的金币数目不同,且有一定的随机性。
  • 在 Rating = E 的情况下,宝箱开出的金币绝大部分为 20, 30 两种。
  • 完成任务和日常签到也会获得金币、jBlock 和道具奖励,数额不等。

Hexo Next 开启 Mathjax

Hexo Next 主题默认支持 Mathjax 和 katex,以在博客文章中显示数学公式。其中,Mathjax 默认以『按需加载』的方式开着,只需要在 md 文件的头部区域添加 mathjax: true 即可激活该文的 Mathjax 支持。

1
2
3
4
5
---
layout: post
title: "Latex 简单语法总结"
mathjax: true # 开启 Mathjax
---

也可以在 _config.next.yml 配置文件中设置 math: ↴ every_page: true 为所有文章开启数学公式支持。

Latex 语法

Mathjax 不只支持 Latex,还支持 MathML 等语法。本着快速入门的实用主义角度,本文只记录一部分 Latex 语法。

Latex 的基本语法包括:

  • 开头结尾与键盘字符。
  • 格式控制,包括公式组合、上下标、分数、根式、矩阵等 。
  • 转义字符,包括希腊字符、数学符号、运算符、箭头、修饰符等。
  • 打印控制,包括字体、颜色、大小、对齐等。
  • 宏定义与扩展包,包括自定义命令、环境等。

开头结尾与键盘字符

$ ... $ 包裹的内容会被 Mathjax 重新改写为 行内公式,比如 \$F=ma\$ 就会被自动渲染为 $F=ma$,行内公式后续文字会在同一行继续。用 $$ ... $$ 包裹的内容称为 行间公式 或者 块内公式,公式在文章中是独立的块,\$\$E=mc^2\$\$ 会被渲染为 $$E=mc^2$$ 文字将被断开换行并形成上下围绕。

对于英文字母以及一些键盘上的符号,Mathjax 也会适当地重新渲染,$+ - * / = > < ( ) [ ]$ 等均会被渲染为数学符号。因此像 F=ma 这样的公式,直接前后套上 $,变为 \$F=ma\$ 就是 Latex 表达式了。

格式控制:初步

从上面我们还注意到,c^2 经过 Mathjax 渲染后变成了 $c^2$。没错,^ 就是上标控制符,将紧随其后的字符变为上标。同理,_ 就是下标控制符。\$C_4^1 * C^2_4\$ 将渲染为 $C_4^1 * C^2_4$。上下标没有先后顺序,都会附着到前一个全高字符上。

当表达『24 选 4』时,\$C_24^4\$ 却被渲染为了 $C_24^4$。规则是严格执行的,但结果却不是我们想要的。这时候需要用到花括号 {} 来控制上下标的范围,用 \$C_{24}^4\$ 就能正确渲染为 $C_{24}^4$ 了。

{} 『分组符』不光能用于上下标,其用途如名字所言,是将若干 latex 表达式定义为一个组,以一个整体与表达式中其它部分作用。比如 \$1^{3^{5^{7^9}}}\$ $\Rightarrow$ $1^{3^{5^{7^9}}}$,就是先将 7^9 视为整体,作为 5 的上标,再以三者的组合作为整体,作为 3 的上标,以此类推直到完成这个叠幂公式。你甚至可以反过来,比如 \${{{1^3}^5}^7}^9\$ $\Rightarrow$ ${{{1^3}^5}^7}^9$。分组符的灵活应用是大型公式的基础之一。

注意看两种不同的叠幂公式的渲染效果,有什么不同?为什么?

$C_{24}^4$ 可以展开为 $(24 \cdot 23 \cdot 22 \cdot 21)/(1 \cdot 2 \cdot 3 \cdot 4)$。即通项公式 $C_n^k = \frac{1 \times 2 \times \dots \times k}{n (n-1) (n-2) (n-k+1)}$,其 Latex 表达式为 \$ C_n^k = \frac{1 \times 2 \times \dots \times k}{n (n-1) (n-2) (n-k+1)} \$

从这里我们开始入门大型公式。上述分数的表达式是 $\frac{分子}{分母}$,在 \frac 后面,分子和分母的位置是固定的,分子在前,分母在后,与上下标有所不同。大括号 {} 则继续发挥分组的作用。在分子分母两组内部,我们又用了 \times 作为乘号,\dots 作为三点省略号。Mathjax 会自动识别空格,因此我们不必担心空格的问题,在源代码上添加空格可以使阅读与编辑更方便,有时也能避免一些 latex 的编译错误。

在初步掌握格式控制后,就可以展开阅读以下表格了:

控制符 作用 效果 控制符 作用 效果
^ 上标 2^3 $\Rightarrow$ $2^3$ _ 下标 C_2 $\Rightarrow$ $ C_2$
{ } 分组 1^{3^5} $\Rightarrow$ $1^{3^5}$ \quad 宽度 1em 的空格$^*$ 1 \quad 2 $\Rightarrow$ $1\quad 2$
\frac 上下分式 \frac 1 2 $\Rightarrow$ $\frac 1 2$ \over 上下分式 1 \over 2 $\Rightarrow$ $ {1\over2}$
\sqrt 根号 \sqrt[3]{27} $\Rightarrow$ $\sqrt[3]{27}$ \binom 组合数 \binom{4}{24} $\Rightarrow$ $\binom{4}{24}$
\left( 左大括号$^*$ \left( \frac{4}{24} \right) $\Rightarrow$ $\left( \dfrac4{24} \right)$ $\Leftrightarrow$ \right) 右大括号 <反例> (\frac{4}{24}) $\Rightarrow$ $( \dfrac4{24} )$
\left[ 左大方括号 \left[ \frac{4}{24} \right] $\Rightarrow$ $\left[ \dfrac4{24} \right]$ $\Leftrightarrow$ \right] 右大方括号 <反例> [\frac{4}{24}] $\Rightarrow$ $[ \dfrac4{24} ]$
\left\{ 左大方括号 \left\{ \frac{4}{24} \right\} $\Rightarrow$ $\left\{\dfrac4{24}\right\}$ $\Leftrightarrow$ \right] 右大方括号 <反例> \{\frac{4}{24}\} $\Rightarrow$ $\{\dfrac4{24}\}$

* 为兼容源代码的可读性,Latex 会忽略普通空格。因此在公式中需要添加额外空格时要用 \quad。后续排版环节会介绍更多距离控制符。
* 在所属代码块内,以最大上下范围为准,自动调整括号大小。

转义字符与修饰符号

在数学中我们会用到很多字符,比如圆周率 π,积分符号 ∫,无穷大 ∞,等等。归功于 unicode 的普及,我们可以直接从输入法的特殊字符输入,或者其它查码表的方式来输入这些字符,Mathjax 也都是支持的。但 Latex 出身甚早,自身也提供了一套转义字符以输入这些特殊字符,显示效果也更好。

希腊字符

小写希腊字符 Latex 转义 Mathjax 效果 大写希腊字符 Latex 转义 Mathjax 效果 Unicode
α \alpha $\alpha$ Α \Alpha $\Alpha$ x0391
β \beta $\beta$ Β \Beta $\Beta$ x0392
γ \gamma $\gamma$ Γ \Gamma $\Gamma$ x0393
δ \delta $\delta$ Δ \Delta $\Delta$ x0394
ϵ \epsilon $\epsilon$ Ε \Epsilon $\Epsilon$ x0395
ζ \zeta $\zeta$ Ζ \Zeta $\Zeta$ x0396
η \eta $\eta$ Η \Eta $\Eta$ x0397
θ \theta $\theta$ Θ \Theta $\Theta$ x0398
ι \iota $\iota$ Ι \Iota $\Iota$ x0399
κ \kappa $\kappa$ Κ \Kappa $\Kappa$ x039a
λ \lambda $\lambda$ Λ \Lambda $\Lambda$ x039b
μ \mu $\mu$ Μ \Mu $\Mu$ x039c
ν \nu $\nu$ Ν \Nu $\Nu$ x039d
ξ \xi $\xi$ Ξ \Xi $\Xi$ x039e
ο \omicron $\omicron$ Ο \Omicron $\Omicron$ x039f
π \pi $\pi$ Π \Pi $\Pi$ x03a0
ρ \rho $\rho$ Ρ \Rho $\Rho$ x03a1
σ \sigma $\sigma$ Σ \Sigma $\Sigma$ x03a3
τ \tau $\tau$ Τ \Tau $\Tau$ x03a4
υ \upsilon $\upsilon$ Υ \Upsilon $\Upsilon$ x03a5
ϕ \phi $\phi$ Φ \Phi $\Phi$ x03a6
χ \chi $\chi$ Χ \Chi $\Chi$ x03a7
ψ \psi $\psi$ Ψ \Psi $\Psi$ x03a8
ω \omega $\omega$ Ω \Omega $\Omega$ x03a9
可以看到,希腊字母的转义就是它的英语名称前加反斜杠 `\`。大写希腊字母则首字母大写。同时也会注意到,不少大写的 Mathjax 渲染效果并不是西腊字符,而是将源码染红了。

标红是因为 Mathjax 认为它是错误代码。标红错误代码有助于作者顺利进行排查,比如 $\fracc{k}{n(n-k)}$ 很容易便能看出来是 \frac 拼写错误。但为何 $\Alpha$ 是错误的呢?原因如下:

[引用] There is no uppercase Alpha, Beta etc. defined in LATEX2ε because it looks the same as a normal roman A, B.

由于历史原因,Latex 以『形状相似』为由,没有定义几个与英文相似的大写希腊字母,包括 Α Β Ε Ζ Τ 等,遇到就报错,建议你用英文字母代替这几个大写希腊字符。所以 Mathjax 也跟着报错了。仔细想来,只是用于公式排版倒也差不多说得过去。只是极端节省到这地步,我的心情如下述评论:

[引用] The fact that they actually bothered to make a capital tau symbol surprises me ...

这种情况下,如果有必要输入正确的大写希腊字符,例如某些可能会被复制的场合。可以直接用输入法的特殊字符输入,或者用 Latex 后续版本扩展出来的 \unicode{} 方法,例如 \$\unicode{x0391} \neq \text{A}\$ $\Rightarrow$ $\unicode{x0391}\neq \text{A}$。

数学符号

Latex 定义了大量的符号,下表只列出了一小部分。大部分符号用不到就是不需要,知识领域没覆盖那部分。这里 有一份号称完整的符号表,不过比起查书,问问 ChatGPT 也许更直接。

效果 Latex 效果 Latex 效果 Latex 效果 Latex
$\infty$ \infty $\aleph$ \aleph $\because$ \because $\therefore$ \therefore
$\approx$ \approx $\neq$ \neq $\equiv$ \equiv $\mid$ \mid
$\oplus$ \oplus $\pm$ \pm $\parallel$ \parallel $\nparallel$ \nparallel
$\times$ \times $\div$ \div $\forall$ \forall $\exists$ \exists
$\cup$ \cup $\cap$ \cap $\subset$ \subset $\supset$ \supset
$\in$ \in $\notin$ \notin $\sum$ \sum $\prod$ \prod
$\partial$ \partial $\int$ \int $\iint$ \iint $\oint$ \oint
$\geq$ \geq $\leq$ \leq $\gg$ \gg $\ll$ \ll
$\to$ \to $\gets$ \gets $\mapsto$ \mapsto $\leadsto$ \leadsto
$\uparrow$ \uparrow $\upuparrows$ \upuparrows $\Uparrow$ \Uparrow $\downarrow$ \downarrow
$\updownarrow$ \updownarrow $\leftrightarrow$ \leftrightarrow $\twoheadrightarrow$ \twoheadrightarrow $\xtwoheadrightarrow{abc}$ \xtwoheadrightarrow{abc}
$\curvearrowleft$ \curvearrowleft $\curvearrowright$ \curvearrowright $\circlearrowleft$ \circlearrowleft $\circlearrowright$ \circlearrowright
$\square$ \square $\triangle$ \triangle $\perp$ \perp $\angle$ \angle
$\lim$ \lim $\log$ \log $\sin$ \sin $\cos$ \cos

特殊转义符

我们使用 $ ... $$$ ... $$ 来包裹 Latex 表达式,用 {} 来分组包裹,用 ^_ 作为上下标。那么当我们需要在公式中直接显示这几个字符时,就需要额外添加转义符了。

转义字符 Latex 表示法 Mathjax 效果 说明
$ _ { } \\$ \_ \{ \} \\{ \\_ \\\$ \\} $\Rightarrow$ $ \{ \_ \$ \}$ 普通地添加 \ 前缀即可,{} 大括号本身也是公式常用字符。
^ \verb!^! $^*$ 3\verb!^!3=27 $\Rightarrow$ $3\verb!^!3=27$ \verb 可能在一些复杂公式中与其它控制符冲突。
^ ^\wedge 3^\wedge3=27 $\Rightarrow$ $3^\wedge3=27$ 只是像,不是同一个字符。但参考上文,像有时就够了。
\ \backslash C:{\backslash}Windows $\Rightarrow$ $ C:{\backslash}Windows$ 这里用到 { } 是因为要与 Windows 这个单词分开。

* verb 是 verbatim 的简写,verbatim text 意为原始文本。\verb 的语法较为特殊,首先以 \verb 开头,然后紧接着一个界定符,这个界定符可以是 ! | # 等各种符号任选,只需要与正文不冲突即可,再继续输入正文,最后再以相同的界定符结尾。两个界定符之间的内容将被原样输出,不会被 Mathjax 渲染。界定符甚至可以是数字,比如 \$\verb03^30\$ 将输出 $\verb03^30$,前后两个 0 成为了首尾界定符,但唯独不能是字母,因为会和 verb 本身形成新的单词短语导致解析错误,\$\verba3^3a\$ $\Rightarrow$ $\verba3^3a$。

修饰符号

效果 Latex 效果 Latex 效果 Latex 效果 Latex
$\hat{abc}$ \hat{abc} $\widehat{abc}$ \widehat{abc} $\bar{abc}$ \bar{abc} $\overline{abc}$ \overline{abc}
$\vec{abc}$ \vec{abc} $\overrightarrow{abc}$ \overrightarrow{abc} $\dot{abc}$ \dot{abc} $\ddot{abc}$ \ddot{abc}
$\tilde{abc}$ \tilde{abc} $\widetilde{abc}$ \widetilde{abc} $\overbrace{abc}^n$ \overbrace{abc}^n $\underbrace{abc}_n$ \underbrace{abc}_n

修饰符号的使用类似于函数,用 {} 将修饰对象包裹起来,修饰符号的位置总是相对于修饰对象进行调整,而与修饰对象在整个公式中的位置无关。

从大型运算符到修饰命令

我们在前面已经见到了 \sum \prod \int 等大型运算符,这些符号在公式中往往还配有上下限、积分定义域等算子与脚标。Mathjax 会自动根据行内或块内的场景来调整算子的大小和脚标的相对位置。比如 \$\sum_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6\$ 将渲染为 $\sum_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6$,而相同的公式在块内渲染为:

$$\sum_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6$$

可以看到,$_{i=1}$ 与 $^n$ 的位置随着不同的条件自动调整了。这种自动调整的机制在大型公式中非常有用,我们只需要关心公式的结构即可。但如果一定要坚持特定的格式,也可以用 \limits 和 \nolimits 来强制调整上下限的位置。比如 \$\sum\limits_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6\$ 将渲染为 $\sum\limits_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6$。而\$\$\sum\nolimits_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6\$\$ 将渲染为:

$$\sum\nolimits_{i=1}^n i^2 = \frac {n(n+1)(2n+1)} 6$$

\limits\nolimits 没有自己的符号,但可以修饰很多带脚标的大形运算符,强制调整其形式,且只能用在运算符上。如果你用在别处,Mathjax 会报错。比如: \$C\limits_4^2\$ $\Rightarrow$ $C\limits_4^2$,报错了。我们期望的是 4 出现在 C 的下方,正确的实现方式是用下面的 \underset 命令:

修饰命令
作用
效果
\limits \nolimits 强制上下限以指定场景显示 上文已述
\substack{} 创建一个多行块,整体通常作为上下标 \sum*{\substack{0<i<3\\\\0<j<5}}a*{ij} $\Rightarrow$ $\sum_{\substack{0<i<3\\0<j<5}}a_{ij}$
\mathop{} 将 { }内的内容整体视作一个大型运算符 \mathop{\sum\sum}\limits_{i \neq j} (i *j) $\Rightarrow$ $\mathop{\sum\sum}\limits_{i \neq j} (i *j)$
\mbox{} { } 内的文本不会断行,可能导致溢出 \mbox{ ... very long contnet ... } $\Rightarrow$ $$\mbox{Very very very very very very very long content in the mbox and there is no line break. }$$
\sideset{}{}\prod 在 \prod 的左右边分别添加指定内容 \sideset{_1^2}{_3^4}\prod $\Rightarrow$ $\sideset{_1^2}{_3^4}\prod$
\overset{A}{B} 在内容 B 的上方添加内容 A 2Cu+2Al\overset{\triangle}{=}2Au+Cl_2\uparrow $\Rightarrow$ $2Cu+2Al\overset{\triangle}{=}2Au+Cl_2\uparrow$
\underset{A}{B} 在内容 B 的下方添加内容 A \underset{probability}{P}(X=x) = \frac{1}{x}^2 $\Rightarrow$ $\underset{probability}{P}(X=x) = \frac{1}{x}^2$

多行公式组:矩阵,表格及其它

equation 与 cases

经过前面的铺垫,我们可以开始对公式进行组合了。公式组的基本形式是:

1
2
3
4
5
6
\begin{equation}
\begin{cases}
x + y = 35 \\
2x + 4y = 94
\end{cases}
\end{equation}

效果如下:
\begin{equation}
\begin{cases}
x + y = 35 \\
2x + 4y = 94
\end{cases}
\end{equation}

  • 多行公式组不需要以 $ ... $ 包裹,但需要以 \begin{env} \end{env} 单独的首尾声明作为公式起止。env 则是由 latex 预先定义的一些环境,比如上面的 equation 就是一个环境。
  • equation 环境的作用是将包裹的所有内容视作一个公式全局自动编号,编号的位置在公式组的最右侧。而 cases 环境的作用是为包裹的所有内容添加左大括号,形成一个公式组。
  • 公式组内部仍可以用其它的公式控制符,比如 \frac \sum \int 等。
  • 公式组内部通常都有多行,需要用 \\ 来换行。Latex 源码的回车换行和空格一样会在渲染时忽略,多行 latext 公式组在源码上仍然可以只写成一行,\\begin{equation}\\begin{cases}x+y=35\\\\2x+4y=94\end{cases}\end{equation} 的效果是一样的,但合理书写可以让源码可读性及可编辑性更高。
  • \begin{env} \end{env} 是可以嵌套的,在上面的例子中,我们用了两个环境,在 equation 环境内部嵌套了 cases 环境,效果是将鸡兔同笼的两个二元一次方程合并成组,并做了全局编号。

align 与 align*

align 的作用是将每一个包裹的公式对齐,比较适合于表现演算过程。要注意的是,align 并不会自动以 $=$ 对齐,而是要用 & 指定竖向对齐位置。比如:

1
2
3
4
5
6
\begin{align}
f(x) &= x^4 + 4 \\
&= x^4 + 4x^2 + 4 - 4x^2 \\
&= (x^2 + 2)^2 - (2x)^2 \\
&= (x^2 + 2x + 2)(x^2 - 2x + 2)
\end{align}

得到:
\begin{align}
f(x) &= x^4 + 4 \\
&= x^4 + 4x^2 + 4 - 4x^2 \\
&= (x^2 + 2)^2 - (2x)^2 \\
&= (x^2 + 2x + 2)(x^2 - 2x + 2)
\end{align}

可以看到,每个公式后面都加上了全局编号,如果不想要的话,可以用 align* 来代替 align,效果如下:

\begin{align*}
f(x) &= x^4 + 4 \\
&= x^4 + 4x^2 + 4 - 4x^2 \\
&= (x^2 + 2)^2 - (2x)^2 \\
&= (x^2 + 2x + 2)(x^2 - 2x + 2)
\end{align*}

同样的情况也适用于 equation,即无编号公式 equation*,不再演示。

matrix 矩阵与省略号

matrix 环境用于矩阵排版,例如:

1
2
3
4
5
\begin{matrix}
a_1 & b_1 & c_1 \\
a_2 & b_2 & c_2 \\
a_3 & b_3 & c_3
\end{matrix}

\begin{matrix}
a_1 & b_1 & c_1 \\
a_2 & b_2 & c_2 \\
a_3 & b_3 & c_3
\end{matrix}
同样是用 & 来进行上下对齐。上述渲染结果相比于常见的矩阵缺少了左右 $[ ]$,可以使用上文的 \left[ \right] 命令来添加:

1
2
3
4
5
6
7
$$\left[
\begin{matrix}
a_1 & b_1 & c_1 \\
a_2 & b_2 & c_2 \\
a_3 & b_3 & c_3
\end{matrix}
\right]$$ % 当内容放在 \begin \end 之外时,需要用 $ 或 $$ 包裹

$$
\left[
\begin{matrix}
a_1 & b_1 & c_1 \\
a_2 & b_2 & c_2 \\
a_3 & b_3 & c_3
\end{matrix}
\right]
$$

也可以用 pmatrix bmatrix Bmatrix vmatrix Vmatrix 等环境来代替 matrix,效果如下:
$$ \text{pmatrix:}\begin{pmatrix} a_1 & b_1 \\ a_2 & b_2 \end{pmatrix}, \text{bmatrix:}\begin{bmatrix} a_1 & b_1 \\ a_2 & b_2 \end{bmatrix}, \text{Bmatrix:}\begin{Bmatrix} a_1 & b_1 \\ a_2 & b_2 \end{Bmatrix},\text{vmatrix:}\begin{vmatrix} a_1 & b_1 \\ a_2 & b_2 \end{vmatrix}, \text{Vmatrix:}\begin{Vmatrix} a_1 & b_1 \\ a_2 & b_2 \end{Vmatrix}$$

另外还有一个 smallmatrix 环境,用于在行内排版小矩阵,如下: \$\left(\\begin{smallmatrix}a_1&b_1\\\\a_2&b_2\end{smallmatrix}\right)\$ $\Rightarrow$ $\left(\begin{smallmatrix} a_1 & b_1 \\ a_2 & b_2 \end{smallmatrix}\right)$,但它没有 \smallBmatrix 等变化形式,需要括号时用普通的即可。

当矩阵很大时,会使用省略号来代替部分内容:

1
2
3
4
5
6
\begin{pmatrix}
a_{11} & a_{12} & \cdots & a_{1n} \\
a_{21} & a_{22} & \cdots & a_{2n} \\
\vdots & \vdots & \ddots & \vdots \\
a_{m1} & a_{m2} & \cdots & a_{mn}
\end{pmatrix}

\begin{pmatrix}
a*{11} & a*{12} & \cdots & a*{1n} \\
a*{21} & a*{22} & \cdots & a*{2n} \\
\vdots & \vdots & \ddots & \vdots \\
a*{m1} & a*{m2} & \cdots & a_{mn}
\end{pmatrix}

我们上文用过 \dots,此处又使用了 \cdots \vdots \ddots 等命令来输出省略号,可以认为前缀字母 c = center,v = vertical,d = diagonal,分别表示水平、垂直、对角线方向。另外还有 \ldots,l = lower 表示靠近底线的省略号。

array

array 可以起到与 matrix 一致的效果,但可以容纳更多的修饰或控制符使得表现效果更丰富,当然也意味着更多的 latex 代码量和复杂度。因为可以添加横竖线作为表格边框,array 一般用来表现表格:

1
2
3
4
5
6
7
\begin{array}{c|lcr}
n & 左对齐 & 居中对齐 & 右对齐 \\
\hline % 横线不占用行高,不必添加 \\ 换行
1 & \pi & rad & 355/113 \\
2 & e & {\left(1+\frac{1}{n}\right)}^n & \ln x \\
3 & i & \sqrt{-1} & sin(\theta)+icos(\theta)
\end{array}

效果:

$$
\begin{array}{c|lcr}
n & 左对齐 & 居中对齐 & 右对齐 \\
\hline
1 & \pi & rad & 355/113 \\
2 & e & {\left(1+\frac{1}{n}\right)}^n & \ln x \\
3 & i & \sqrt{-1} & sin(\theta)+icos(\theta)
\end{array}
$$

代码首行 \begin{array}{c|lcr} 中的 {c|lcr} 部分是 array 环境的参数,用于指定每一列的对齐方式,c = center,l = left,r = right,| 表示添加竖线作为列分隔符,可以不添加也可以添加多条。在内容部分,用 & 来对齐各列,用 \\ 来换行。用 \hline 添加横线,会自动置入两行之间,不影响文本的行高,也不必添加 \ 换行。

{array}也同样可以配合 \left{ \right} 来构造方程组,一些形式上要求齐次对齐的方程组,使用 array 会比较方便:

1
2
3
4
5
6
7
8
$$\left\\{
\begin{array}{lrrrrr}
f_1(x) = & & & & x & +1 \\
f_2(x) = & & & 8x^2 & +5x & +2 \\
f_3(x) = & & 3x^3 & +7x^2 & +2x & +9 \\
f_4(x) = & 9x^4 & +3x^3 & & +4x & +6
\end{array}
\right.$$ % 有 \left\{ 时,必须要有对应的右括号闭合。但可以用 \right. 放一个不显示内容的右括号。

$$
\left\{
\begin{array}{lrrrrr}
f_1(x) = & & & & x & +1 \\
f_2(x) = & & & 8x^2 & +5x & +2 \\
f_3(x) = & & 3x^3 & +7x^2 & +2x & +9 \\
f_4(x) = & 9x^4 & +3x^3 & & +4x & +6
\end{array}
\right.
$$

公式引用

\tag \label 与 \eqref

公式引用分为被引用,和引用别人的。虽然使用 \begin{equation} 等环境可以自动编号,但却无法直接根据该编号引用对应的公式。在 Latex 中引用公式,必须满足两个条件:

  1. 被引用的公式必须是块公式,行内公式不能被引用。
  2. 被引用的公式必须显式地声明一个 label,用于引用的锚点,语法为 \label{labelname}。

公式也可以用 \tag{equationname} 来修改默认的全局编号名,但并非必须。

1
2
3
4
5
\begin{equation}
\label{ef}
\tag{Euler's Formula} % 自定义公式名,非必须
e^{i\pi} + 1 = 0
\end{equation}

\begin{equation}
\label{ef}
\tag{Euler's Formula}
e^{i\pi} + 1 = 0
\end{equation}

在上面的例子中,我们为欧拉公式添加了一个 label ef。如果我们想要引用这个公式,可以使用 \eqref{ef} 命令,引用时则不限于是否是块公式,也不限于是否有 \tag{tagname},例如:

\$e^{i\theta}\overset{\\eqref{ef}}{=}cos\theta+isin\theta\$ $\Rightarrow$ $e^{i\theta}\overset{\eqref{ef}}{=}cos\theta+isin\theta$。

可以看到,引用的锚点是 \label{labelname} $\Rightarrow$ \\eqref{ labelname},而在 \eqref 处显示的则是由 \tag{equationname} 设定的公式名称。labelname 可以是几乎任意字符串,在各种教程中经常出现的 \label{eq:1} 主要是为了方便管理,取『equation No.1』之意。上面的 ef 也是 Euler's Formula 的缩写。使用 \label 的好处在于,当我们在后续编辑排版时,即使公式的编号变了,引用的地方也会自动更新,因为引用的锚点是固定的。这也是为什么要分别定义 \label 和 \tag 的原因。

\ref 与 \href

在完整的 latex 中,\ref 与 \eqref 稍有不同,后者是前者子集。但对于 Mathjax 这种限于网页展示的场合,两者并没有什么不同。根据 这里 的说明,\ref 还能引用外链,但我没有尝试成功,可能是 Mathjax 的限制。本来,在 Markdown 博客中引用外链有更简单的方式:[文字](链接地址),但如果非得拧巴一下,也可以用 \href{链接地址}{文字} 来实现:

$\href{https://www.kaikai.men/latex-syntax-mathjax/}{本文地址}$ $\Rightarrow$ $\href{https://www.kaikai.men/latex-syntax-mathjax/}{本文地址}$

打印控制

Latex 会自动调整公式的大小和排版,但有时候我们也需要适当地调整一下字体字号、空隙间距等,以达到更好的排版效果。这里介绍一些常用的控制命令。

空格间距

Latex 源码
效果
说明
ab $ab$ ab 间无空格
a b $a b$ a b 间有空格,无效
a\ b $a\ b$ 使用 \ 转义的空格,有效
a\  b $a\ b$ 第二空格未转义,无效
a\ \ b $a\ \ b$ 两个\ 转义的空格,有效
a\quad b $a\quad b$ quad 为 1em,一般相当于三个空格
a\qquad b $a\qquad b$ qquad 为 2em
a\,b $a\,b$ 半个空格
a\:b $a\:b$ 2/3 个空格
a\;b $a\;b$ 5/6 个空格
a\!b $a\!b$ 负向半个空格,常用于绘制长等号,如: =\!=\!= $\Rightarrow$ $=\!=\!=$

行间距

公式组的多行之间通过 \\\\ 分隔,在分隔符后面可以添加下方行距,允许小数点,如下:

1
2
3
4
5
\begin{cases}
x + y = 35 \\[15pt]
2x + 4y = 94 \\[1.5em]
x = 23, y = 12
\end{cases}

\begin{cases}
x + y = 35 \\[15pt]
2x + 4y = 94 \\[1.5em]
x = 23, y = 12
\end{cases}

字形字号

字形 Latex 效果 字体 Latex 效果 字体 Latex 效果
Roman 正体 \rm E $\rm E$ Italic 斜体 \it E $\it E$ Bold 黑体 \bf E $\bf E$
Calligraphic 草书 \cal E $\cal E$ Fraktur 尖角体 \frak E $\frak E$ Blackboard Bold 黑板粗体 \Bbb E $\Bbb E$
Sans-serif 无衬线体 \sf E $\sf E$ Typewriter 等宽 \tt E $\tt E$ Script 花体 \scr E $\scr E$
字号 Latex 效果 字号 Latex 效果 字号 Latex 效果
tiny \tiny E $\tiny E$ scriptsize \scriptsize E $\scriptsize E$ small \small E $\small E$
normalsize \normalsize E $\normalsize E$ large \large E $\large E$ Large \Large E $\Large E$
LARGE \LARGE E $\LARGE E$ huge \huge E $\huge E$ Huge \Huge E $\Huge E$

字体颜色

Mathjax 使用 \color{colorname} 命令来设置字体颜色,colorname 可以是预定义的颜色名,也可以是 RGB 值。比如 \$\color{red} E\$ $\Rightarrow$ $\color{red} E$。预定义的颜色名参考 这里,摘抄一部分如下:

$\color{aqua}{aqua}$ $\color{black}{black}$ $\color{blue}{blue}$ $\color{blueviolet}{blueviolet}$ $\color{brown}{brown}$ $\color{burlywood}{burlywood}$ $\color{cadetblue}{cadetblue}$ $\color{chocolate}{chocolate}$ $\color{coral}{coral}$ $\color{cornflowerblue}{cornflowerblue}$ $\color{crimson}{crimson}$ $\color{cyan}{cyan}$ $\color{darkblue}{darkblue}$ $\color{darkcyan}{darkcyan}$ $\color{darkgoldenrod}{darkgoldenrod}$ $\color{darkgray}{darkgray}$ $\color{darkgreen}{darkgreen}$ $\color{darkkhaki}{darkkhaki}$ $\color{deeppink}{deeppink}$ $\color{deepskyblue}{deepskyblue}$ $\color{dimgrey}{dimgrey}$ $\color{firebrick}{firebrick}$ $\color{forestgreen}{forestgreen}$ $\color{fuchsia}{fuchsia}$ $\color{gainsboro}{gainsboro}$ $\color{gold}{gold}$ $\color{goldenrod}{goldenrod}$ $\color{gray}{gray}$ $\color{green}{green}$ $\color{greenyellow}{greenyellow}$ $\color{grey}{grey}$ $\color{hotpink}{hotpink}$ $\color{indianred}{indianred}$ $\color{indigo}{indigo}$ $\color{lightblue}{lightblue}$ $\color{lightcoral}{lightcoral}$ $\color{lightsalmon}{lightsalmon}$ $\color{lightseagreen}{lightseagreen}$ $\color{lightskyblue}{lightskyblue}$ $\color{lime}{lime}$ $\color{limegreen}{limegreen}$ $\color{magenta}{magenta}$ $\color{maroon}{maroon}$ $\color{navy}{navy}$ $\color{olive}{olive}$ $\color{orange}{orange}$ $\color{orangered}{orangered}$ $\color{orchid}{orchid}$ $\color{peru}{peru}$ $\color{pink}{pink}$ $\color{plum}{plum}$ $\color{purple}{purple}$ $\color{red}{red}$ $\color{salmon}{salmon}$ $\color{silver}{silver}$ $\color{teal}{teal}$ $\color{thistle}{thistle}$ $\color{tomato}{tomato}$ $\color{turquoise}{turquoise}$ $\color{violet}{violet}$ $\color{yellow}{yellow}$ $\color{yellowgreen}{yellowgreen}$

如果使用 RGB,格式为 \color{#rrggbb} 或缩写的 \color{#rgb},其中 rgb 为 0-f 的十六进制数。

这里有一个新的注意点,在下一节中介绍。

宏定义与扩展包

Mathjax 不支持宏与扩展。Latex 本身提供了很多扩展功能,但 Mathjax 仅仅是一个 JS 库,它并不是完整的 Latex。如果需要使用这些功能,需要在 Latex 编译器中进行。

在这里记一笔,是因为在搜索某个效果的 Latex 语法时,往往会遇到一些使用扩展功能的回答,那些回答不适用于 Mathjax,需要注意。

与 Markdown 和 Hexo 的冲突处理

这个博客是 Hexo 搭建,加载了 Hexo Next 主题。使用 Markdown 语法在文本文件中编写博客,由 Hexo 将其编译为 HTML 文件,而 Mathjax 则在 HTML 文件中渲染 Latex 公式。而 Hexo 本身也有自己的模板语法,这使得我们编写的内容,需要经过 Hexo 编译,Markdown 编译,再由 Mathjax 渲染,最后才能在浏览器中显示。这样的多重编译过程,使得我们在写文章时,需要额外考虑一些情况。

Hexo 处理

使用 {% raw %} ... {% endraw %} 避免 Hexo 模板语法带来的问题。所有 {% raw %} ... {% endraw %} 两者之间的内容都不会被 Hexo 进一步解释,而是进入 Markdown 解释器层进行下一步处理。

在使用 \color{#rrggbb} 语法时就会遇到相关问题,{# 会被 Hexo 识别,但又不属于合法的模板语法,导致 Hexo 编译失败报错。相似的情况还出现在 {} 的嵌套场景下,连续的 {{{ 也会被视为错误的 Hexo 模板语法。因此,我们需要用 {% raw %} ... {% endraw %} 来避免这些问题。

Markdown 处理

Markdown 语法中,\ 也是转义字符,用于输出特殊字符,比如 \# $\Rightarrow$ # ,\* $\Rightarrow$ * 等。因为 #、*、_ 等字符在 Markdown 中是有意义的(用于标题、列表、强调等),因此需要用 \ 转义。所以为了在 Markdown 处理完以后还能保留 \,我们在写文章时就需要有意地写成 \\\\。

此外,Markdown 本身也会有意地忽略多余的空格,因此如果需要将多个空格传递给 html,需要使用 &nbsp; 来转义。所幸 &nbsp; 只要隔开连续的空格,每个空格包括 &nbsp; 都会保留,因此可以与空格交替写来省点事。

Mathjax 处理

在经过 Markdown 编译后,原始文章已经变成了 HTML 文件。此时 Mathjax 会搜索全文中的 Latex 相关语法,将其渲染为对应的公式。但是由于 Mathjax 也将 \$ 等符号视作特殊字符,因此仍然会再进行一次转义。比如 HTML 中如果有一个『\\\$』,会被 Mathjax 认为是 \$ 符号的转义符,因此会消除 \\,单显示一个 $ 符号。

与此相似的,还有为了在 Mathjax 渲染后还能正常显示 \\\\ (两个反斜杠),我们在源文件中需要写成惊人的八条反斜杠:\\\\\\\\\\\\\\\\。在 Markdown 编译时,每两条 \\ 会被解释为一条有效的反斜杠,因此在 HTML 文件中会生成连续的四条反斜杠。而 Mathjax 会在 HTML 呈现的内容基础上再作渲染,再一次将每两条反斜杠编译为一条有效的反斜杠,因此最终显示的是两条反斜杠。

如果能及时注意到 Mathjax 是在页面的 HTML 上再次渲染,就能很好地理解这个过程。

其它信息

本文到这里就差不多尾声了。Mathjax 是为网页提供 latex 公式渲染的 JS 库,并不拥有 latex 的全部功能。Latex 有很多全页排版、宏定义与扩展包,包括自定义命令、环境等功能,在 Mathjax 里是没有的。

本文的目的是为了介绍 Mathjax 的基本语法,以及与 Markdown 和 Hexo 的冲突处理。如果需要更多的功能,可以参考下面的参考资料。

Mathjax 的官方文档:https://docs.mathjax.org/en/latest/index.html

Mathjax 的官方示例:https://docs.mathjax.org/en/latest/web/start.html#mathjax-cdn

Latex 编辑器:TEXMaker

MathJax basic tutorial and quick reference

LaTeX Grammar List @rollpie

在博客访问者无法直连 Disqus 时,有个 DisqusJS 的劣化替代方案可供博主使用。方案内置在 Hexo Next 主题中,在正确配置第三方反向代理后,博客可向游客展示评论,但游客无法参与讨论。

这种方案的效果如本文底部评论区所示。该方案需要在三个地方进行配置,分别是 Disqus 服务提供方(需科学上网)、代理端(本文为 Cloudflare 云函数),以及 Hexo Next 配置文件(本地),没有先后顺序。

Disqus

注册 Disqus 账号。注册后,点击首页的『GET STARTED』,并选择『I want to install Disqus on my site』选项。

下一页,只有 Shortname 会在后续的配置中用到,其余随便选。
下一页,付费页面下拉选择 Basic 免费计划。
下一页,出现 Installation 界面就可以关闭了,因为 Hexo Next 已经内置,不需要手动安装。左侧菜单其它的审核策略等选项都可以后续有需要再设置。

然后 注册 Disqus API 应用
第一页,都随便填,Label 填个自己能看懂的名字,方便后续管理。网址格式要正确。填完以后点『Register my application』
下一页,Settings 下的 Domains 填上博客网址,不用加 http/https 协议前缀。Disqus 后续会用来匹配 referer。填完后点『Save Changes』
保存以后切换到『Details』页面,把 Public Key 的一长串字符复制下来,后续会用到。

Cloudflare

假设你已经有了 Cloudflare 账号,并且有一个由 Cloudflare 管理的域名。

登录 Cloudflare 云函数平台,Login。
下一页,点击右上角『Create a Worker』,下一页点击『创建 Worker』,下一页起个名 worker name 后续管理要用,点右下角部署。
下一页,『配置 Worker』。
下一页,右上角『快速编辑』,复制下面代码,粘贴后点右上角『保存并部署』。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
addEventListener('fetch', event => {
event.respondWith(proxy(event));
});

async function proxy(event) {
const getReqHeader = (key) => event.request.headers.get(key);

let url = new URL(event.request.url);
url.hostname = "disqus.com";

let parameter = {
headers: {
'Host': 'disqus.com',
'User-Agent': getReqHeader("User-Agent"),
'Accept': getReqHeader("Accept"),
'Accept-Language': getReqHeader("Accept-Language"),
'Accept-Encoding': getReqHeader("Accept-Encoding"),
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0'
}
};

if (event.request.headers.has("Referer")) {
parameter.headers.Referer = getReqHeader("Referer");
}

if (event.request.headers.has("Origin")) {
parameter.headers.Origin = getReqHeader("Origin");
}

return fetch(new Request(url, event.request), parameter);
}
// 代码来自 [idawnlight](https://github.com/idawnlight/disqusjs-proxy-cloudflare-workers)

下一页,点击仪表上 『Custom Domains』下面 『0』右边的『查看』,随后点击右上角『添加自定义域』。
建议填入 disqus.<你自己拥有的域名>,点击『保存』。这是 cloudflare worker domain,后续会用到。
如果你没有自定义域名,也可以用默认的。默认的在仪表上『预览』位置处,格式为 <workername>.<你的cloudflare 帐号名>.worker.dev

点击『添加自定义域』,Cloudflare 的配置完成。需要等几分钟生效。期间可以先配置 Hexo Next。

Hexo Next

编辑 _config.next.yml 文件,找到 comments disqus disqusjs 三个配置项,修改内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
comments:
style: tabs
active: disqusjs # 这里用 disqusjs 代替了 disqus
storage: true
lazyload: false
nav:
disqusjs: # 这里用 disqusjs 代替了 disqus
text: Load DisqusJS
order: -1
# Disqus
disqus:
enable: false # 这里改为 false,关闭原始的 Disqus
shortname:
count: true
# DisqusJS
disqusjs:
enable: true # 这里改为 true,启用 DisqusJS
api: https://<cloudflare worker domain>/api/ # 这里填 Cloudflare 云函数的域名,注意还要加上 /api/ 后缀
apikey: <API key> # 这里填 Disqus 提供的 public key
shortname: <shortname> # 这里填 Disqus 注册时的 shortname

验证

本地,或编译后在服务器端打开页面,都可以看到评论区。自行留言后断开科学上网,刷新页面看有没有更新即可。或者用 Cloudflare 的云函数计数是否增加也可以。

在 CPU 提升为 N200 后,Surface Go 4 似乎成为了一款不错的 Galgame 设备。当然现在还有另一个选择 Steamdeck,但是 Steamdeck 也有屏幕、风扇、系统兼容性等方面的劣势。尤其是在屏幕方面,差别非常大。总之先列举一下配置作参考。

Surface 系列

名称 配置 重量 厚度 CPU分数
Surface Go 奔腾 G4415Y HD615 8G 128/256G 1800*1200 522g 8.3mm 1591
Surface Go 2 奔腾 G4425Y HD615 8G 128G 1920*1280 544g 8.3mm 1696
Surface Go 2 酷睿 M3 8100Y HD615 8G 128/256G 1920*1280 544g 8.3mm 2878
Surface Go 3 奔腾 G6500Y HD615-23EU@900M 8G 128G 1920*1280 544g 8.3mm 3022
Surface Go 3 酷睿 i3 10100Y HD615-24EU@1G 8G 128/256G 1920*1280 544g 8.3mm 2935
Go 4 Business 酷睿 N200 UHD 8G 64/128/256G UFS 1920*1280 521g 8.3mm 5455

Steamdeck 系列

名称 配置 重量 厚度 屏幕
Steamdeck 7nm Zen2 8CU 16G 64G-1T 669g 49mm 7寸 1280 × 800
Steamdeck OLED 6nm Zen2 8CU 16G 512G-1T 640g 49mm 7.4寸 1280 × 800@90 HDR

对比

两者还是有挺大区别的,前几代 Surface Go 的 CPU 性能基本已经脱离主流了,毕竟被动散热,上架就是同时代低端性能,跑 E-mote/Live2D 都存在一定性能问题。手头的 Surface Go 一代跑 Steam 客户端本身都显得很勉强。4 代的 N200 CPU 性能其实也是本代低端,但毕竟刚上市还过得去。

所以针对 Galgame,Surface 的主要且唯一巨大的优势就是屏幕,无风扇算个小优,系统优势不大,Deck 也可以装 Win 切换。

而 Steamdeck 毕竟是游戏机,能跑的都能跑,只是屏幕差距对 Galgame 来说是个硬伤,性能则显得相对冗余。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
sudo fdisk -l                   # 列举设备或分区
sudo blkid # 查看硬盘 UUID,只列举物理储存设备
sudo fdisk /dev/sda # 互动式硬盘分区

sudo mkfs.ext4 /dev/sda # 格式化整个硬盘
sudo mkfs.ext4 /dev/sda1 # 仅格式化第一个分区
sudo e2label /dev/sda momoda # 给硬盘命名为 momoda
sudo e2label /dev/sda # 查看硬盘名字
ls /dev/disk/by-label/momoda # 以硬盘名字指向硬盘,方便硬盘位置变动后正常挂载

sudo mount /dev/sda ~/momoda # 挂载硬盘
sudo umount /dev/sda # 卸载硬盘

sudo chown $USER:$USER ~/momoda # 修改硬盘访问权限

# 挂载 windows 共享文件夹:命令,远端路径(Windows端),本地路径,用户名,密码,编码,文件权限,文件夹权限
sudo mount.cifs //<主机IP或主机名>/<共享目录> ~/<本地映射目录> -o username=<Win主机设置的共享用户名>,password=<Win主机设置的共享密码>,iocharset=utf8,file_mode=0777,dir_mode=0777

sudo apt install smartmontools # smartctl 命令属于 smartmontools 包
sudo smartctl -a /dev/sda # 查看硬盘健康状态
sudo hdparm -I /dev/sda # 查看硬盘健康状态(另一种方式)

dos2unix /path/to/file # 将文件从 windows 格式转换为 unix 格式,可用 * 通配
unix2dos /path/to/file # 将文件从 unix 格式转换为 windows 格式,可用 * 通配
1
2
# 开机自动挂载硬盘
nano /etc/fstab
1
2
# <device>                <mountpoint>        <file system> <options> <dump> <fschk>
/dev/disk/by-label/momoda /home/kaikai/momoda ext4 defaults 0 0

关于 fstab 的参数说明:https://www.redhat.com/sysadmin/etc-fstab

树莓派 4B 相比于 3B/3B+ 等 3 系有很多改进,但主要的是内存从 1G 加到了顶配版 8G,网卡也从百兆升级到了千兆,使得树莓派 4B 有了作为下载机的实用性。

系统直接使用官方的 Raspberry Pi。BT 软件一开始尝试使用 qBittorrent 的命令行版本 qBittorrent-nox,开启服务用 web UI 进行远程控制。但实际使用后发现在高压力下,qBittorrent-nox 因为内存占用巨大而频繁被杀进程,然后由系统服务重启,再被杀,再重启,最差时每两三分钟就被杀一次。报错日志看得我心惊肉跳。频繁异常重启也导致了文件丢失、下载进度丢失以及网站封禁等情况,其配置中关于内存限制的参数完全无用,搜索后发现官方开发组把很多反馈,包括内存控制、磁盘碎片、重启丢文件等都甩锅给 libtorrent。遂放弃。

对 qBittorrent 的评价又下降了。之前在 windows 下也是因为 qBittorrent 的缺陷(数量巨大的硬盘碎片)而放弃的。

一番周折后,最终改用了 Transmission 作为 BT/PT 服务器端(transmission-daemon),开启 RPC 以允许 Web UI 和第三方客户端连接,Samba 作为局域网文件共享服务器的方案。并自行编写了 RSS 下载脚本以实现 RSS 订阅自动下载。

Transmission

安装与文件目录连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 安装
sudo apt update
sudo apt install transmission-daemon

# 获得配置文件的权限
sudo chown -R $USER:$USER /etc/transmission-daemon

# 设置配置文件软链接
sudo mkdir -p /home/$USER/.config/transmission-daemon/
sudo ln -s /etc/transmission-daemon/settings.json /home/$USER/.config/transmission-daemon/
sudo chown -R $USER:$USER /home/$USER/.config/transmission-daemon/

## 挂载 USB 外接硬盘并设置目录权限
sudo mkdir -p /home/$USER/Momoda # Momoda 是硬盘挂载目录
sudo mount /dev/sda1 /home/$USER/Momoda # 挂载硬盘
sudo chown -R $USER:$USER /home/$USER/Momoda # 设置权限
mkdir -p /home/$USER/Momoda/BT # 建立下载目录

修改系统服务设置

1
2
3
4
5
6
7
8
9
10
# 修改启动脚本
sudo nano /etc/init.d/transmission-daemon
>> 在 nano 编辑器中,修改 USER=<YOURUSERNAME> 为你的用户名,<Ctrl+S> 保存 <Ctrl+X> 退出。

# 修改服务配置
sudo nano /etc/systemd/system/multi-user.target.wants/transmission-daemon.service
>> 在 nano 编辑器中,继续修改 User=<YOURUSERNAME> 为你的用户名,<Ctrl+S> 保存 <Ctrl+X> 退出。

# 重新加载服务
sudo systemctl daemon-reload

修改配置文件

Trasmission 会在停止服务时自动保存配置文件,运行服务时修改会被覆盖,必须停机修改。

1
2
3
4
# 停止服务
sudo systemctl stop transmission-daemon
# 编辑配置文件
nano ~/.config/transmission-daemon/settings.json
1
2
3
4
5
6
7
8
9
10
11
12
13
// 部分参数后续可以在 web UI 中再次修改,但当前另一些参数只能在配置文件中修改。
{
"download-dir": "/home/kaikai/Momoda/BT", // 设置下载目录,kaikai 替换为你的用户名
"rpc-whitelist-enabled": true, // 只允许白名单内的 IP 访问
"rpc-whitelist": "192.168.*.*", // 局域网白名单,如果公网要访问则为 *.*.*.*
"rpc-authentication-required": false, // 因为只是家用,不需要密码。若公网访问则设置为 true,之后下两条有效
"rpc-user-name": <username>, // 独立的用于 webUI/RPC 登录的用户名,自己设置,和本文其它用户名无关
"rpc-password": <password>, // 独立的用于 webUI/RPC 登录的密码,transmission 会自动修改内容加密
"umask": 2, // 设置下载文件的权限
"preallocation": 1, // 设置预分配磁盘空间方法,0 No 1 Fast 2 Full
"watch-dir-enabled": true, // 开启监视目录,将种子文件放入监视目录,transmission 会自动开始下载
"watch-dir": "/home/kaikai", // 设置监视目录路径
}

(可选)替换默认的 webui 为 Flood-for-transmission

1
2
3
4
5
6
7
8
# 下载
wget https://github.com/johman10/flood-for-transmission/releases/download/latest/flood-for-transmission.zip
# 解压
unzip flood-for-transmission.zip
# 备份原来的 webui
sudo mv /usr/share/transmission/web /usr/share/transmission/web.bak
# 替换
sudo cp -r flood-for-transmission /usr/share/transmission/web

还有 Transmission-web-controlTransmissionioc 等多个选择。当然默认的也很好。
另外还有独立的客户端比如 Transmission Remote GUI,如果有多个下载服务器的话用客户端更好。

重启 transmission-daemon 服务

1
2
3
4
# 启动服务
sudo systemctl start transmission-daemon
# 查看服务状态
sudo systemctl status transmission-daemon

远程访问

1
2
# 查看树莓派的 IP 地址
ip a

在浏览器中输入 http://<树莓派的ip>:9091 即可访问 webUI,如果设置了 "rpc-authentication-required": true 则会跳出认证输入框。使用 RPC 远程客户端也是类似设置。

RSS 订阅

Transmission 并不支持 RSS 订阅,所以我自己写了个脚本,定期检查指定的 RSS,并通过 RPC 接口发送 torrent-add 消息给 Transmisson。https://github.com/kaikaiiiiiii/rss-feed-down 。特色在于支持 PAC 代理读取 RSS。

1
2
3
4
5
6
7
git clone https://github.com/kaikaiiiiiii/rss-feed-down
cd rss-feed-down
npm install

node ./rss.js
# 在 rss.js 中修改下载目录 savepath = <path>
# 在 rssFeeds.txt 修改订阅源,格式为 <name>,<url>,<useProxy>

通过 crontab 定时执行脚本,实现 RSS 订阅更新并自动下载 torrent 文件。

Samba

安装与配置

1
2
3
sudo apt update
sudo apt install samba samba-common-bin
sudo nano /etc/samba/smb.conf

smb.conf 文件有一堆内容,拉到最下面,修改或添加以下内容:

1
2
3
4
5
6
7
[Pi]                        # 虚拟共享文件夹的名字,后续访问时的路径为 \\<树莓派的ip>\Pi
path = /home/kaikai/Momoda # Pi 虚拟文件夹对应的本地目录
writeable=Yes # 可以写,即可以上传文件,同时也可以删除文件
create mask=0777 # 新建文件的权限
directory mask=0777 # 新建文件夹的权限
public=no # 不是公共文件夹,ananymous 不能访问
# 编辑完成后按 Ctrl+S 保存,Ctrl+X 退出

添加 samba 的授权用户

1
2
# kaikaiHome 为独立的用于 samba 登录的用户名,和本文其它用户名无关
sudo smbpasswd -a kaikaiHome

随后系统会提示输入密码,输入两次并确认

重启 samba 服务

1
sudo systemctl restart smbd

在 Windows 的 <映射网络驱动器> 或其它可以用 smb/cifs 的程序 app 输入 \\<树莓派的ip>\Pi 并输入 kaikaiHome 的用户名和密码即可访问。

同上,这是 Sony Ericsson MW600 索尼爱立信 MW600 蓝牙耳机说明书。下载文件

  1. 关机状态下长按开机键 ⏼ 不放经过开机阶段继续按住进入配对模式,使用滑动条选择覆盖哪个旧配对。
  2. MW600 连接的通话设备和音乐设备是独立切换的,分别通过长按 ⏯ 或 📞 选择。
  3. 在待机模式下长按 ⏯ 选择设备时,存在第四项 FM 收音机功能。FM 为自带功能不需要蓝牙连接其它设备。
状态 按键 操作 备注
待机模式 长按 ⏯ 长按选择音乐播放配对设备 使用滑动条选择设备列表
待机模式 长按 📞 长按选择通话配对设备 使用滑动条选择设备列表
待机模式 📞 启动手机语音助手 部分电脑设备也有语音助手
待机模式 📞📞 重拨
待机模式 启动当前音乐/视频播放 部分设备会自动启动默认音乐播放器
播放状态 暂停当前音乐/视频播放
来电状态 📞 短按接听,长按拒绝接听
通话状态 📞 短按挂断,长按切换第二通话 另有耳机发声 / 手机发声切换功能
FM 模式 ⏮ ⏭ 长按搜索,短按微调 屏幕显示当前频率
所有状态 ⊂====⊃ 感应滑动条调整音量 设备列表时选择配对设备
配对状态 📞⏼ 同时长按两键 重新设置耳机,清空配对设备

如果您的浏览器不支持 PDF 文件,请下载文件查看。

买了个韶音,发现说明书不好找,就放这里了。下载文件

  1. 关机状态下长按音量 ⊕ 不放开机后继续按住进入配对模式。
  2. 配对状态下同时长按耳机键与音量 ⊕ 键开启双重配对。
  3. 配对状态下同时长按耳机键与音量 ⊖ 键关闭双重配对。
  4. 配对状态下同时长按耳机键、音量 ⊕ 键与音量 ⊖ 键恢复出厂设置,恢复后需要关机再开。
耳机键(多功能键) 状态 操作
播放/暂停音乐 待机 单击
拨打上一次通话 待机 双击
唤醒语音助手 待机 长按
下一首音乐 音乐播放 双击
上一首音乐 音乐播放 三击
接听电话 来电 单击
拒接电话 来电 长按
挂断电话 通话中 单击
挂断当前电话并切换到第二个通话 通话中 长按
切换系统语言 配对状态 双击

如果您的浏览器不支持 PDF 文件,请下载文件查看。

顺便测试 HTML 插入 PDF 的方法,不需要 hexo-pdf 插件。

1
<embed src="../attach/2023/11/Shokz-OpenMove.pdf" width="100%" height="850px"  type="application/pdf" />

咨询了 chatGPT,还有稍复杂的方式:

1
2
3
<object data="../attach/2023/11/Shokz-OpenMove.pdf" type="application/pdf" width="100%" height="850px">
<p>如果您的浏览器不支持 PDF 文件,请<a href="../attach/2023/11/Shokz-OpenMove.pdf">下载文件</a>查看。</p>
</object>

在 PSN 会员费又一次涨价后,我重新审视了光盘租赁的经济性。在过去不多的几次租光盘经历中,我发现自己其实并没有节约多少钱。于是做了一些主观分析,尝试厘清这个问题。

一,实际支出主要来自于光盘降价和背刺

虽然老猎人等二手盘商家的买卖差价都只有 10-15 元,但实际上,由于每张光盘在手上存留的时间都很长,光盘二手价格的下降才是真正的实际支出。尽管没有明确计算(因为有几张盘目前还在手上承受着掉价损失),但大致估计下来每张盘要损失 100-200 左右。

存留时间长带来的另一个风险是同游戏在各数字渠道的赠送或打折。比如《底特律:化身为人》《战神4》《尼尔:自动人形》《十三机兵防卫圈》等都是在光盘租赁期间,甚至尚未开始游玩时,就在 PSN XGPU 等会免或入库。光盘租赁成了无意义的支出,同时还得承担一笔会免大降价损失,俗称『背刺』。数字游戏的大幅折扣或赠送冲击光盘价格是市场的常态,实质是同一商品的渠道间竞争,但真轮到自己头上了还是很不好受。

二,引出问题:为什么会存留时间较长

闲余时间有限,而可选择的游戏却很多,有限的时间分摊给了不同的游戏。尤其是暴雪全家桶等长消耗类游戏占用了相当一块时间。

和直觉不同的是,虽然游戏总量很多,但每个单机游戏实际分配到的时间不会减少,因为通关时间是相对固定的。代之的是游戏完成周期不断地拉长,甚至可能断断续续持续几年,也使得光盘留存时间不断延长。很多故事剧情驱动的游戏更需要完整的时间段和精神舒适的状态才能游玩,相比于几分钟一局的消耗类游戏,在选择上也不占优势,更加长了通关周期。

另外,XGPU 等订阅服务的兴起、多年积累的 PSN 会免库、Steam 屡次大促积累的游戏库、Epic 坚持不懈的游戏赠送,使得排队列表越来越长,进一步稀释了光盘游戏的时间密度。

光盘租赁的固有缺陷,需要收发快递、换盘费事保管麻烦、资金占用等,虽然也都是劣势,却并不是导致存留时间长的原因,甚至是反向因素。其中唯与存留时长正相关的只有『计划赶不上变化』一项,买的时候计划着要玩,两天以后快递到了,却没时间玩,就变成了无效留存。

三,还有其它因素

  1. 游戏口味的宽泛减弱了对游戏库的二次筛选,增大了平均游戏周期。

  2. 对游戏新作时效性需求不高,不追求第一时间体验。

    新作发售时光盘版与数字版价格一致。如果第一时间购买,那么无论后续掉价几何,全价光盘版可以二手出售就仍比全价数字版有价差优势。但迄今为止追求第一时间玩到的单机游戏并不多。当游戏进入折扣周期后,光盘版价格与数字版价格变得互有高低,这就回到本文开头的情况了——因为存留周期掉价和背刺,我没从租光盘上省下什么钱。

  3. 其它生活因素的时间挤压。

    生活中的所有闲余时间都是相互挤占的,副业加班阅读学习运动健身动漫娱乐等等,都在分摊不多的时间。当生活休闲中加入新要素,游戏时间分配就变得更加捉襟见肘。

四,为什么重新考虑光盘租赁

目前的花费和实际消耗的时间不匹配。

PSN 和 XGP 费用在不断上涨,低价区限制越发严格,数字游戏的成本在变高,Epic 送的游戏越来越水,游戏总时间也在缩减。会员费交了不用,游戏买了不玩,光盘租了不还,都是浪费。在有限的时间下,几个游戏渠道不再是互为补充,而变成了相互冲突的状态。光盘租赁在大作上的相对价格优势重新凸显,比如《只狼》至今数字版最大折扣也就 50%,和光盘租赁的最坏支出差不多。另外如前文所述,如果减除 PSN 和 XGP,游戏库缩小,光盘游戏的存留时间也有可能减缩,以降低光盘租赁的平均成本。

缩减游戏库的纠结在于,停用 PSN 和 XGP 除了失去两个游戏库外,还会失去联网功能,同样会影响到一些游戏的体验。而另一方面,光盘的优势也需要一定的条件。一是其它渠道挤兑要少,因此排除第一方大作。二是通关周期短,因此不能是长消耗类游戏。三是游戏要优质,优先级高,且单价高,口碑好,市场稳定,最好是次新大作。

选择同时意味着放弃。

五,总不能什么都要吧

方案一,彻底数字化,PSNP 二档和 XGPU 二选一或二选二,以帐号共享进一步降低成本。主机选数字版,游戏以库存为主,大作等 50off 或随缘等入库,真有垂涎的就全价购入。

方案二,单平台化。放弃 XGPU,XSX 出售。只开 PSN 二档并出售 Home 位,PS5 购买光驱版。大作用光盘作为补充。

方案三,买块好显卡,放弃主机,保留 PC + SteamDeck。一别两宽,各生欢喜。

方案四,我全都要。


2024-11-27 edit:

目前实践现状是方案二+方案三,PSN 二档已经提供了足够的时间消耗,并没有租赁光盘,买光驱版主要是考虑二手保值问题。

SteamDeck 主要是出门/过年回家使用,同时也是一个现成的 Linux 机,比虚拟机方便且完善。

PC 处理各种杂事,偶尔暴雪全家桶。

0%