关于 π 的一些趣闻

2018 年 3 月 14 日 | Cormullion

虽然我们已经了解“阿基米德常数”几个世纪了,但我们直到 18 世纪才开始用希腊字母 π 来表示它。Patricia Rothman 建议 下面的页面可能包含了最早使用希腊字符表示这个概念的例子之一。它出自威廉·琼斯于 1706 年撰写并出版的一本数学教科书。

William Jones 1706

琼斯在不同的位置使用希腊字母来表示这个数字,并且就像他的前任一样,将其用作圆周或周长(希腊语 περιφέρεια)的代称,以及在图表中标注点,就像我们今天使用 PQ 一样。琼斯还将其称为范·塞伦数,以纪念荷兰数学家鲁道夫·范·塞伦,他花了大部分时间来计算这个值。(他计算出了 35 位小数,并要求将其刻在自己的墓碑上。)

琼斯和其他数学家一样,使用了大量的符号。他的《数学掌上概要,或数学新导论》已被数字化,可以 在这里 查看。这是一个了不起的成就,尤其是对于他的排版员来说,它可能会让你比平时更欣赏你接下来的 LaTeX 会话。

但是,虽然琼斯是最早使用 π 符号的人之一,但他没有足够的影响力去激励更多的追随者,而是在这个世纪后期,更著名的莱昂哈德·欧拉及其对 π 符号的使用,才更多地为后代确立了这个希腊字母的主要数学意义。

Euler 1746

(原始在线版 在这里。)

Julia 热情地拥抱了 Unicode 标准 enthusiastically,因此在代码中使用合适的希腊字母(以及其他 Unicode 字母)非常容易。例如,在 REPL 中,输入 \pi TAB 以插入 Unicode 字符 U+03C0

julia> π
π = 3.1415926535897...

你可以在表达式中随意使用它

@test kepler_solver.([π/4, π/6, 8π/3], 0) ≈ [π/4, π/6, 2π/3]

(尽管如果你愿意,pi 仍然可以使用)。在 Julia 0.7 版本中,π 和其他数学常数,例如欧拉数 ℯ (2.7182818284590),位于 Base.MathConstants 模块中。

与 Julia 中的其他操作一样,你通常可以看到魔术是如何实现的

julia> Base.REPLCompletions.latex_symbols["\\pi"]
"π"

更多关于 π 的内容

我们通常使用来自 Unicode 区块的希腊语和科普特语文本字形的 U+03C0 π(科普特语是由埃及人采用的一种基于希腊语的文字,用来取代象形文字),但还有其他主要用于数学用途的 π 符号。以下 Julia 代码片段试图展示所有小写的 Unicode π 符号

morepi = [
(0x3c0,    "\\pi",           "GREEK SMALL LETTER PI")
(0x213c,   "\\bbpi",         "DOUBLE-STRUCK SMALL PI") # v0.7
(0x1d6d1,  "\\mbfpi",        "MATHEMATICAL BOLD SMALL PI")
(0x1D70B,  "\\mitpi",        "MATHEMATICAL ITALIC SMALL PI")
(0x1d745,  "\\mbfitpi",      "MATHEMATICAL BOLD ITALIC SMALL PI")
(0x1d77f,  "\\bsanspi",      "MATHEMATICAL SANS-SERIF BOLD SMALL PI") # v0.7
(0x1d7b9,   "\\mbfitsanspi", "MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI")];

foreach(p -> println(" $(Char(p[1])) U+$(lpad(hex(p[1]), 5, "0")) $(rpad(p[2], 15)) $(p[3])"), morepi)

注释为 v0.7 的两个 Tab 补全已在 Julia 0.7 版本中添加。

你看到的输出将很大程度上取决于你的计算机、操作系统以及其他各种设置。这是一个很好的入门,让你开始探索奇妙的字体世界。例如,这是我今天(0.6.2 版)在 macOS 上使用 Terminal 应用程序在我的 Julia REPL 中执行此代码片段后看到的输出

Terminal app before

如你所见,并非所有 π 符号都已渲染。操作系统会在当前字体(我正在尝试使用 Fira Code)中查找 Unicode 字符 U+3c0U+213cU+1d6d1U+1d70bU+1d745U+1d77fU+1d7b9。对于每个找不到的字符,它会在其他激活的字体中搜索,寻找包含该代码点的符号的字体。操作系统不会检查找到的符号是否合适。

在上面的示例中,U+3c0 的希腊字母 π 已经在 Fira Code 中可用。\mitpiU+1d70b 的数学斜体 π)最终在微软的 Segoe UI Symbol 中找到,因此该字体被用于该字符。对于 \bbpi,双线 π 或黑板 π,Code2000 字体在 U+213c 处生成一个符号,该符号被适当地使用,尽管对我来说,它看起来更像一个倒过来的单线 epsilon,而不是任何类型的 π。其他的符号在任何活动的字体中都找不到,因此它们是用一个带有方框的问号显示的。

现在,如果我激活一个字体,例如 Asana-MathEverson MonoSTIX,或者其他一些具备数学功能的字体,操作系统会立即开始找到匹配项并更新显示,根据需要不断在字体之间切换以满足对 Unicode 代码点的需求

Terminal app after

安装 Asana Math 后,所有其他 π 都可以找到,现在双线小 π 看起来更像它应该代表的黑板书写字符,尽管在小字号下很难看到。我认为还有更好的设计

double-struck pi

一些乏味的实验表明,搜索在某种程度上是按字母顺序进行的,因为如果 Asana Math 不可用,操作系统会继续尝试 Code2000,然后是 Deja Vu,然后是 Everson,然后是 FreeSans,等等。这至少是我认为在当前的 macOS 上使用我的特定设置时会发生的情况。我怀疑在你那里会不同。类似(但不同)的事情也会发生在你的浏览器中。

顺便说一下,这些 π 的替代符号,例如 \mitpi,不会计算为 3.14...,因此你可以像威廉·琼斯一样将它们用作通用符号。例如,如果你处理素数,你可以使用其中一个来表示 素数计数函数。或者你也可以用以下内容来让自己迷惑

julia> 𝜋 = 3
julia> 2𝜋
6

音标符号 Ⓟ

并非所有字体都包含适合的希腊字母 π,位于 U+03C0 处。一些昂贵的字体,例如 Gotham,在 U+03c0 处提供带圆圈的 P 符号,而不是 π。这是“音标”符号(或“音响录制”符号),通常位于 U+2117(位于 U+24C5 的符号也使用),它类似于版权符号 ©,但用于录音。这背后的故事是,特别是在 Unicode 标准化之前,字体公司有时会偏向实用性而不是正确性。根据 David Berlow 的说法

我们在 Font Bureau 了解用户的操作方式,因此我们在一个字形(数学 π)的插槽中放了一个带圆圈的 p,这是一个“必备”字形,用户很难即兴创建,因为即使他们需要它,它也存在于地球上每台计算机的 Symbol 字体中。

π 的设计

我们可以使用 Julia 来查找所有不同的 π 设计吗?我第一次尝试解决这个挑战使用的是 Fontconfig.jl 来生成已安装字体的列表,以及 Luxor.jl 来将它们绘制在表格中。它至少让我们看到了各种可用的设计,并说明了一些问题。提供零宽度字形的字体会被跳过,这意味着表格最终会比最初计划的要小。

using Fontconfig, Luxor

function buildfontlist()
    fonts = []
    for font in Fontconfig.list()
        families = Fontconfig.format(font, "%{family}")
        for family in split(families, ",")
            push!(fonts, family)
        end
    end
    filter!(f -> !ismatch(r".LastResort|Agenda|Topaz|Bodoni Ornaments|System",
        f), fonts)
    return sort(unique(fonts))
end

function tabulatepi()
    fonts = buildfontlist()
    ncols = 25
    nrows = convert(Int, ceil(length(fonts))) ÷ ncols
    @svg begin
        background("ivory")
        setopacity(1)
        t = Table(nrows, ncols, 30, 25)
        sethue("black")
        cellnumber = 1
        for n in 1:length(fonts)
            fontface(fonts[n])
            te = textextents("π")
            if te[3] > 0.0
                fontsize(18)
                text("π", t[cellnumber], halign=:center)
                setfont("Lucida-Sans", 3)
                settext(fonts[n], t[cellnumber] + (0, isodd(cellnumber) ? 6 : 10), halign="center")
                cellnumber += 1
            end
        end
    end 800 1200
end

tabulatepi()

你可能不需要像我一样手动删除字体列表中的奇怪东西,比如 Bodoni Ornaments 或 Topaz...

tabulating pi - PNG fallback

如果我手动选择候选字体,而不是使用这个自动生成的字体列表,效果会更好:有太多来自各种系统和语言特定字体的“默认”设计,它们只是用一个基本设计填充了插槽,而不是根据字体的主题来解释形状。而且 fontconfig 生成的字体列表并没有查看所有字体库,因此它是一个不完整的列表。

以下是一些更有趣的设计:圆润的 Cooper Black 和 Tiffany Heavy,极简的 Verdana 和 Tahoma(看起来几乎像 “n” 字?),古怪的 Gill “Kayo” Sans,以及愚蠢的 Chalkduster。

piversity

我喜欢 Dalliance 的小号版本;它很好地体现了老派风格,这里的“老派”指的是基于 1799 年的原始设计,这距离欧拉去世不久。

平均 π

我想知道“π 的平均值是多少?”,或者“如果所有 π 同时显示,它会是什么样子?”。使用与之前相同的字体列表生成方式,我运行了以下代码

function textstroke(s, pos, action)
    @layer begin
        translate(pos)
        te = textextents(s)
        move(-te[3]/2, te[4]/2)
        textpath(s)
        tp = pathtopoly()
        poly.(tp, action, close=true)
    end
end

function accumulatepi()
    fonts = buildfontlist()
    @png begin
        background("midnightblue")
        sethue("lightgoldenrod2")
        setline(0.2)
        fontsize(560)
        setopacity(0.3)
        for n in 1:length(fonts)
            fontface(fonts[n])
            te = textextents("π")
            if te[3] > 0.0
                textstroke("π", O, :stroke)
            end
        end
    end
end
accumulatepi()

accumulating pi

对于一个基于许多其他字体的平均形状的完整字体,请查看 Averia.

我还没有找到一种方法来列出所有包含完整 Unicode π 符号集的字体。理论上我认为我可以使用 Freetype.jl 检查字体,看看它是否包含特定字符字形的代码点。也许我会在 2019 年 3 月 14 日报告任何进展……