Surreal Numbers

First Post:

Last Update:

Word Count:
2.4k

Read Time:
9 min

Page View: loading...

本文可以视为 weilycoder 阅读 On Numbers and Games 的笔记。

构造

为任意满足条件的两个 Surreal Number(超现实数,以下简称“数”)构成的集合,则 为一个数。

规定:

约定

  • 定义 表示全体数构成的类(class)。
  • ,则使用 表示 中的元素; 表示 中的元素。对于 ,也记作
  • 表示

定义

  • 的定义

  • 的定义

  • 的定义

  • 的定义

  • 的定义

    以下是一个意义更明确的写法,但是不严谨:

    有时也使用 表示两个数相乘。

由于等号 的特殊性,需要指出,这里的等号意义比“相等”要弱一些,具体表现在,我们可以构造一种运算 ,使得 不等价。

因此,我们规定,定义变量的时候,使用 表示赋值/定义;使用 表示两个数的 分别相等(称为全等/恒等,递归定义,这时若 ,则显然可以将式子中的一切 替换为 )。

code

这里使用 Python(3.13) 编写了一个简单的 Surreal Number 的类,对象不可变,支持了 __ge__, __le__, __add__, __neg__, __sub__, __mul__);可以使用 is 运算判断两个数全等。

由于 Python hashable 对象的相关 feature,定义 __eq__ 会导致一些冲突,而定义 __eq__is)会导致歧义;故不定义 __eq__ 方法。

方便验证一些数的大小关系。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# pylint: disable=line-too-long,invalid-name

"""Surreal Numbers"""

from __future__ import annotations

from functools import lru_cache
from typing import Iterable


class SurrealNumber:
"""A surreal number represented by two sets of surreal numbers."""

aliases: dict[SurrealNumber, str] = {}
No: dict[tuple[frozenset[SurrealNumber], frozenset[SurrealNumber]], SurrealNumber] = {}

L: frozenset[SurrealNumber]
R: frozenset[SurrealNumber]

def __new__(
cls,
left: Iterable[SurrealNumber] = (),
right: Iterable[SurrealNumber] = (),
name: str = "",
) -> SurrealNumber:
"""Create a new surreal number or return an existing one."""
key = (frozenset(left), frozenset(right))
if not all(isinstance(x, SurrealNumber) for x in key[0]):
raise TypeError("Left set must contain only surreal numbers.")
if not all(isinstance(x, SurrealNumber) for x in key[1]):
raise TypeError("Right set must contain only surreal numbers.")
if key not in cls.No:
instance = super().__new__(cls)
instance.L, instance.R = key
cls.No[key] = instance
if name != "":
cls.No[key].set_alias(name)
return cls.No[key]

def set_alias(self, name: str) -> None:
"""Set an alias for the surreal number."""
if self in self.aliases:
raise ValueError(f"Alias already exists for {self}")
self.aliases[self] = name

@lru_cache(maxsize=1 << 24)
def __ge__(self, other: SurrealNumber) -> bool:
"""Check if this surreal number is greater than or equal to another."""
return not any(right <= other for right in self.R) and not any(left >= self for left in other.L)

@lru_cache(maxsize=1 << 24)
def __le__(self, other: SurrealNumber) -> bool:
"""Check if this surreal number is less than or equal to another."""
return other >= self

@lru_cache(maxsize=1 << 24)
def __add__(self, other: SurrealNumber) -> SurrealNumber:
"""Add two surreal numbers."""
return SurrealNumber(
{xL + other for xL in self.L} | {self + yL for yL in other.L},
{xR + other for xR in self.R} | {self + yR for yR in other.R},
)

@lru_cache(maxsize=1 << 24)
def __neg__(self) -> SurrealNumber:
"""Negate the surreal number."""
return SurrealNumber({-x for x in self.R}, {-x for x in self.L})

@lru_cache(maxsize=1 << 24)
def __sub__(self, other: SurrealNumber) -> SurrealNumber:
"""Subtract two surreal numbers."""
return self + (-other)

@lru_cache(maxsize=1 << 24)
def __mul__(self, other: SurrealNumber) -> SurrealNumber:
"""Multiply two surreal numbers."""
L1 = {xL * other + self * yL - xL * yL for xL in self.L for yL in other.L}
L2 = {xR * other + self * yR - xR * yR for xR in self.R for yR in other.R}
R1 = {xL * other + self * yR - xL * yR for xL in self.L for yR in other.R}
R2 = {xR * other + self * yL - xR * yL for xR in self.R for yL in other.L}
return SurrealNumber(L1 | L2, R1 | R2)

@lru_cache(maxsize=1 << 24)
def to_string(self, alias: bool = True) -> str:
"""Return a string representation of the surreal number."""
if alias and self in self.aliases:
return self.aliases[self]
left_str = ", ".join(sorted(x.to_string(alias) for x in self.L))
right_str = ", ".join(sorted(x.to_string(alias) for x in self.R))
return f"{{{left_str} | {right_str}}}"

def __repr__(self) -> str:
"""Return a string representation of the surreal number."""
return self.to_string()

def __str__(self) -> str:
"""Return a string representation of the surreal number."""
return self.to_string()

超限归纳

不加说明地引入“超限归纳法”。

假设一个性质 满足:,则任意数 都满足性质

二元形式为,若 满足:,则任意数 满足

没有归纳起点 的原因留给读者思考。

博弈

我们同样允许表达式 ,这里 不满足

由于这种形式在非公平博弈中被引入,我们称它为博弈(game)。

(值得指出的是,公平博弈属于非公平博弈,因此 Nim 数也可以使用博弈表述)。

显然博弈也应该满足超限归纳法。

创造日

由于数的定义是递归进行的,因此每个数的构造必须依赖曾经构造的数。但当我们还没有构造任何一个数的时候,我们所能使用的数集只有

因此,我们在最开始的时候只能构造 ,我们将它称为 。我们不妨称这是第 天。

接下来,我们可以使用 作为 中的元素,因此可以构造出 ,分别称它们为 ,并称这时为第 天。

如此不断进行下去,第 天构造的所有数中, 是最大的一个,而 是最小的一个。

由于允许无限集合的出现,这个过程可以持续到第 天,并可以继续下去。由此可以看到,Surreal Numbers 的数量至少和序数同样多,这样 不能成为一个集合(set)而只能成为一个类(class)。

Surreal Numbers

一些构造

可以验证,上述命名符合我们使用实数的习惯()。

这里给出一个可以简化比较的引理:

  • ,则
  • ,则

诸运算性质和全序域

若无说明,以下定理适用于全体博弈。

比较运算律

等号的自反性

Proof
  1. 将定义中的 使用 替换,发现一定有 ,因此 不成立。
  2. 同理可证。
  3. ,我们已经证明不存在 且不存在 ,因此
  4. 由等号的定义得。

不等号的传递性

Proof

因为 ,因此不存在 ,由归纳假设,不存在 ,同理得不存在 ,因此命题成立。

的推论很多,下面列出一部分:

Proof
  • :将 换成 即可;
  • :由 得;
  • :反证,不成立由 得矛盾;
  • :显然是 的子集;
  • :同上;
  • :由 ,又 导出矛盾;
  • :与 类似;
  • 的子集;
  • 的子集;
  • :反证法;
  • :反证法;
  • :由 得。

尽量选用了大于号形式的结果,如果将 轮换,可以得到小于号形式的结果(就像 )。

不等号的连接性

这条定理需要用到 ,因此对于博弈不适用。

Proof
  1. 已经证明 ,故只需证明 。根据定义,它成立当且仅当 。若前者成立,根据归纳假设,有 。而后者根据 不成立。
  2. ,则 ,即

因此,全序

加法运算律

Proof

意味着 是加法单位元,这也是我们将 称为 的原因。

减法(相反数)运算律

Proof

是显然的:


注意 中的符号不是

,则存在 ,即 ,但是后者不成立,因为根据归纳假设,有 。同理 不成立。故

顺便,后文中将 记为

等式/不等式的基本性质(加法消去律)

Proof

,则不会有

根据归纳假设,等价于不会有 ,因此


,则有

此时如果 成立,则

以上的每个式子都将导出矛盾。

综上所述,

这条定理也有不少推论,简单列一下:

顺便,请注意 ,例如 ,这里

加法的封闭性

Proof

由于不存在 ,因此不存在 ,故


由于 ,因此


容易得到

同时

根据这些定理, 运算下构成全序交换群

乘法运算律

容易证明但证明较繁,故略。

值得指出的是, 连接的定律是由于 使用等号连接。

乘法的封闭性、等式的基本性质与排序不等式

中不等式严格当且仅当条件中两个不等式均严格。

原文没有为 标注 ,但根据证明可以推断,且不满足时可以举出反例。

另外,其实 不需要均满足 ,但是我懒得讨论了,毕竟乘法对于博弈的用途不大。

Proof

首先,我们将 记为

考虑证明 ,只需要

如果 ,有

上式需要

,则

这需要

根据归纳假设 ,以上的 个条件应该全部成立,故 成立。


考虑 ,这需要

根据乘法定义

利用归纳假设 并移项:

为例,其等价于 ,即 ,根据归纳假设 ,后者成立。


考虑 ,注意到 的情况都可以从 得到,因此只需要考虑

既然 ,则 。以前者为例, 可以从 得到,其中后者是归纳假设。

总之,我们只需要考虑

这等价于

这样,已经证明全部的数构成全序环

推论

Proof

应用 即可。

乘法逆元

对于 ,定义

式中的 同样只取正数。

请注意 是递归定义的,甚至在定义中使用了 的左右项,这可以理解为 的左右项是由旧的项递推生成的。我们保证了 ,这可以看作是递归定义的起点。

另外, 定义为 (按照习惯)。


以上

Proof

简便起见,令

观察到 的项的定义满足:

这里 是比 “更早”定义的项; 的非 项。

上式可以写成

这表明,若 满足 ,则 也满足,而起点 满足


显然没有


的项的形式为 ,后者可以写为


易得。

至此,已经证明 是一个全序域

另外需要指出的是,通常语境下,group)、ring)、field)均用于描述 集合set)而不是 class)。原文将前三个术语进行了首字母大写,来和通常语义区分,这里统一注明。


更进一步地,Clive Bach 发现了平方根的定义,对于非负数 及其正项,有

另外,Martin Kruskal 指出, 的项也可以写成