从市场带回的一篮东西
阿玛拉和爷爷从市场回到家,带回一个大篮子。里面有香蕉和柠檬、苹果和草莓,还有一根黄瓜、一个梨、一个橙子和一根胡萝卜。
“我们把东西都收拾好吧,”爷爷说,“可是每样东西该放在哪儿呢?”
要收拾好,阿玛拉需要一条规则——一种决定每样东西该放在哪里的方法。数学家一直在做这样的事。他们把东西归在一起,组成一组组对象。
给篮子里的东西分类
自己试着给篮子里的东西分类吧。先按颜色分。然后重新开始,再按形状分。
市场分类游戏
从篮子里选一样东西,再选它应该放进的盒子。
篮子
从篮子里选一样东西。
你注意到装蓝色东西的盒子了吗?什么也没有放进去。它还算一组吗?继续读下去就知道了!
重要的想法
定义— 一组对象
一组对象(简称“一组”)就是我们放在一起来考虑的一些东西。一组对象里的东西,叫作它的成员。
当某样东西在一组对象里时,我们说它属于这一组。香蕉属于黄色东西这一组。苹果不属于。
定义— 同一组
当两组对象的成员完全相同时,它们就是同一组。成员按什么顺序排列,都没有关系。
定义— 空组
一个成员也没有的一组对象,叫作空组。装蓝色东西的盒子是空的——但它仍然是一组!
我们一起来练习
例— 它属于这一组吗?
这里有一组对象:🍎 🍌 🍐。
- 🍌 是成员吗?是——它就在那里。
- 🍋 是成员吗?不是——仔细看看。里面没有柠檬。
例— 相同,还是不同?
健二的盒子里有 🍎 和 🍌。露西娅的盒子里有 🍌 和 🍎。
它们的成员相同,只是顺序不同。所以它们是同一组。
例— 有几个?
数一数这一组:🍊 🍋 🍎 🍓。每样东西只摸一次,同时说一个数:一、二、三、四。一共有 4 个成员。
现在换成从草莓开始数。结果还是 4!
为什么数出来的结果总是一样
数数的时候,我们把每样东西和恰好一个数词配对:一、二、三、四。每样东西得到一个数词,没有哪个数词用两次。
如果从别的地方开始数,配对方式就不同了——但用掉的数词还是同样那些,因为东西的个数和原来一样多。没有添加任何东西,也没有拿走任何东西。
给大人看的 · 这个想法将发展成什么
“数数就是配对”这一想法,是数学中一个深刻思想的种子:两组对象的成员个数相同,当且仅当它们的成员可以一一配对。很久以后,正是这种配对的想法,让数学家能够比较无穷集合的大小。
Gideon 的是非游戏
Gideon 喜欢那些明明白白为真或明明白白为假的说法。看看黄色东西这一组:🍌 🍋。下面每个说法是真还是假?
- “香蕉属于黄色组。”——真。
- “黄色组的每个成员都是长的。”——假!柠檬是圆的。只要有一个打破规则的例子,就足以说明这个说法是假的。
- “蓝色组里有一个成员。”——假。蓝色组是空的。
给大人看的 · 在证明检验器 Lean 中表达同样的想法
像 Lean 这样的计算机证明助手可以检验这类说法。下面,一篮东西被写成 Lean 的一个列表。Lean 检验了芒果属于这个篮子、篮子里有 3 个成员,以及没有任何东西属于空列表。
/-- There is a mango in the basket: it is the very first thing in the list. -/
theorem mango_in_basket : "mango" ∈ basket :=
List.Mem.head ["orange", "banana"]/-- The basket holds three pieces of fruit. Lean counts them itself, so `rfl` is enough. -/
theorem basket_count : basket.length = 3 := rfl/-- The empty list has no members at all. -/
theorem nothing_in_empty : ∀ x : String, x ∉ ([] : List String) := by
intro x h
-- A proof of `x ∈ []` would have to come from one of the ways of building membership,
-- but none of them produces an empty list, so there is no case left to consider.
cases h列表会记住顺序,所以 Lean 认为列表 [1, 2] 与 [2, 1] 不同——尽管它们的成员完全相同。忘掉顺序的一组对象,就是数学家所说的集合,也就是下一课的主题。
/-- For lists, order matters: `[1, 2]` and `[2, 1]` are different lists.
`decide` asks Lean to compare the two lists element by element. -/
theorem order_matters_for_lists : ([1, 2] : List Nat) ≠ [2, 1] := by decide/-- …but `[1, 2]` and `[2, 1]` have exactly the same members. As *sets* they would be equal;
as *lists* they are not (see `order_matters_for_lists`). -/
theorem same_members : ∀ x : Nat, x ∈ [1, 2] ↔ x ∈ [2, 1] := by
intro x
-- Being in a list means being equal to one of its entries:
-- `x ∈ [1, 2]` unfolds to `x = 1 ∨ x = 2`, and `x ∈ [2, 1]` to `x = 2 ∨ x = 1`.
simp only [List.mem_cons, List.not_mem_nil, or_false]
-- "or" does not care about order.
exact Or.commMargaret 数市场的篮子
给大人看的 · Margaret 的程序
这个 Python 程序把从市场买回的一篮东西按种类和颜色分类,并数出每一堆有几个。点击运行,就能在浏览器中试一试。它还会尝试一篮 6 样东西的全部 720 种顺序。这只检验了这一个篮子;上面的解释告诉我们,它为什么对每个篮子都成立。
"""Sorting and counting a market basket.
A *collection* here is a basket of items. Two mangoes are two different items
that happen to share a name, so the basket is a list (order and repeats are
recorded), not a set. We sort the basket by a property, count each group, and
check that the counts do not depend on the order in which the items were
picked.
Runs unchanged in the browser (Pyodide) using only the standard library.
"""
from collections import Counter
from itertools import permutations
from math import factorial
import textwrap
#: The basket, in the order the items were picked. Each item is
#: (name, kind, colour). Repeated names are separate items.
BASKET = (
("mango", "fruit", "yellow"),
("okra", "vegetable", "green"),
("banana", "fruit", "yellow"),
("lime", "fruit", "green"),
("carrot", "vegetable", "orange"),
("mango", "fruit", "yellow"),
("aubergine", "vegetable", "purple"),
("orange", "fruit", "orange"),
("lime", "fruit", "green"),
("yam", "vegetable", "brown"),
)
#: Positions of each property inside an item tuple.
PROPERTIES = {"name": 0, "kind": 1, "colour": 2}
def value(item, prop):
"""Return the value of property ``prop`` ("name", "kind" or "colour") of an item."""
try:
return item[PROPERTIES[prop]]
except KeyError:
raise ValueError(f"unknown property {prop!r}; expected one of {sorted(PROPERTIES)}") from None
def sort_by(items, *props):
"""Return a new list of the items sorted by the given properties, in priority order.
The original collection is not changed. With no properties, items are sorted
by (name, kind, colour) as plain tuples.
"""
if not props:
return sorted(items)
return sorted(items, key=lambda item: tuple(value(item, p) for p in props))
def group_by(items, prop):
"""Group items by a property: return {value: [items with that value]} with keys sorted."""
groups = {}
for item in items:
groups.setdefault(value(item, prop), []).append(item)
return {key: groups[key] for key in sorted(groups)}
def count_by(items, prop):
"""Count how many items have each value of ``prop``: {value: count}, keys sorted."""
counts = Counter(value(item, prop) for item in items)
return {key: counts[key] for key in sorted(counts)}
def distinct_values(items, prop):
"""Return the set of different values of ``prop`` that occur in the collection."""
return {value(item, prop) for item in items}
def counts_same_in_every_order(items, prop):
"""Check every ordering of ``items``: do the group counts by ``prop`` ever change?
Returns ``(orders_checked, all_same)``. The number of orders is n!, so this
is only practical for small collections (n <= 8 or so).
"""
items = list(items)
reference = count_by(items, prop)
checked = 0
for order in permutations(items):
checked += 1
if count_by(order, prop) != reference:
return checked, False
return checked, True
def format_counts(counts):
"""Format {value: count} as 'a 2, b 3' with the total in brackets."""
parts = ", ".join(f"{key} {n}" for key, n in counts.items())
return f"{parts} (total {sum(counts.values())})"
def main():
basket = list(BASKET)
print(f"A market basket of {len(basket)} items, in the order they were picked:")
print(" " + ", ".join(value(item, "name") for item in basket))
print()
print("Sorted by kind, then colour, then name:")
print(f" {'kind':<10} {'colour':<8} name")
for item in sort_by(basket, "kind", "colour", "name"):
print(f" {value(item, 'kind'):<10} {value(item, 'colour'):<8} {value(item, 'name')}")
print()
by_kind = count_by(basket, "kind")
by_colour = count_by(basket, "colour")
print("Counts by kind: " + format_counts(by_kind))
print("Counts by colour: " + format_counts(by_colour))
sums = " + ".join(str(n) for n in by_kind.values())
print(f"Every item lands in exactly one group, so the groups add up: {sums} = {len(basket)}.")
names = distinct_values(basket, "name")
print(f"Different names: {len(names)} (two mangoes are two items with one name).")
print()
small = basket[:6]
n_orders = factorial(len(small))
print(f"Does order matter? Take the first {len(small)} items and try all {len(small)}! = {n_orders} orders:")
for prop in ("kind", "colour"):
checked, same = counts_same_in_every_order(small, prop)
print(f" counts by {prop:<6} identical in all {checked} orders: {same}")
print()
print(textwrap.fill(
"Epistemic status: the counts above are exact, and all "
f"{n_orders} orders of the 6-item basket were checked, which proves "
"order-independence for that basket only. Why it holds for every finite "
"collection (counting pairs items with 1, 2, 3, ...) is explained in the "
"lesson, not by this program.",
width=78,
))
if __name__ == "__main__":
main()轮到你了
慢慢来。你随时都可以要一个提示。
1. 一组对象
热身人类思维这里有一组对象:🍎 🍌 🍐。下面哪一个是这一组的成员?
2. 一组对象
热身人类思维这一组有几个成员?🍊 🥕 🍋 🍎 🍐
3. 一组对象
核心人类思维健二的盒子里有 🍎 🍌 🍐。露西娅的盒子里有 🍐 🍎 🍌。它们是同一组吗?
4. 一组对象
核心人类思维装蓝色水果的盒子是空的。空组有几个成员?
5. 一组对象
核心形式化Gideon 的游戏:这一组是 🍌 🍋 🥒。下面的说法是真还是假?“这一组的每个成员都是黄色的。”
6. 一组对象
挑战人类思维给大一点的孩子:从苹果(apple)、香蕉(banana)、草莓(strawberry)和柠檬(lemon)中,写出由红色东西组成的一组。请使用代码中显示的英文单词,用逗号分隔,例如 {kiwi, plum}。
容易让人犯迷糊的地方
误解
“空盒子不算一组。”——不,它算!空组是一个真正的组,只是没有成员而已。数学家觉得它非常有用。
误解
“如果我把东西挪来挪去,就会得到不同的一组。”——挪动成员改变的是顺序,而不是这一组本身。成员相同,就是同一组。
误解
“大的东西算得更多。”——一个大椰子和一颗小草莓,都各自只算一个成员。
继续探奇
能把一个盒子放进另一个盒子里吗?一个只装着一个空盒子的盒子里,到底有什么?如果你好奇,就沿着这条小径走一走吧——它完全是可选的。
探奇小径 · 可选 · 从不评分Boxes inside boxesWhat is inside a box that contains only an empty box? Is it empty? And can every description you can think of make a collection?踏上小径一组对象将通向哪里
在下一课中,一组对象会得到一个“大人的”名字:集合。数学家用花括号来写集合,比如 ,并把空集写作 。
“两组对象的成员相同,它们就是同一组”这条规则,是关于集合最重要的规则之一。它叫作外延原理。
给大人看的 · 分类规则就是函数
把每样东西都恰好送进一个盒子的分类规则,就是函数(映射)的一个例子——这是第 4 课的主题。函数,以及函数的复合方式,最终会通向范畴论。年幼的学习者不需要知道这些词;会分类就够了。
很久以前,世界各地的人们怎样数数
人们数一组组东西,已经有非常久远的历史了。
- 在非洲南部,有一根刻着刻痕的狒狒骨头——莱邦博骨(Lebombo bone)——距今已有四万多年。一些研究者认为,这些刻痕是用来计数的记号,不过没有人能确定。
- 在南美洲的安第斯山脉,印加的记录员使用奇普(khipu):在绳子上打结,用来记录人口、牲畜和物品的数量。
- 许多文化用手指、脚趾、石子和绳结来计数。英语单词 calculate(计算)来自拉丁语中表示“小石子”的词。
给大人的延伸阅读
- Paul R. Halmos,Naive Set Theory(1960)——一本写给成人、经典而易读的集合入门书。中译本名为《朴素集合论》。
- 许多免费教育网站上都能找到适合幼儿的分类和数数活动,例如 NRICH 的早期教育栏目(nrich.maths.org,英文)。
可选的基督教默想 · 每一个都重要
这是一段来自基督教传统的默想,这一传统启发了 The Lumenary。它不属于数学内容,你完全可以跳过。
在耶稣讲的一个比喻中,一个牧人有一百只羊。其中一只走失了,他就察觉到了——因为他一只一只地认识自己的羊群——于是出去寻找它(路加福音 15:3–7)。在这个故事里,数数是一种关怀:这一组中的每个成员都很重要。数学教给我们同样的专注习惯,而每一位学习者,无论身在何处,都很重要。