English

Most introductions to computer science treat mathematics as background preparation — things to get through before the real subject starts. Discrete mathematics resists this framing. It is not borrowed from mathematics for computational purposes; it is the layer at which mathematical thinking and computational thinking coincide. The questions discrete mathematics asks — can this claim be proven? how many objects of this kind exist? what is the structure of this recursive thing? — are precisely the questions that computer science asks about algorithms, data structures, and programs. Learning discrete mathematics is not preparation for CS; it is the activity of beginning to think like a computer scientist in the rigorous sense.

The content is a coalition of topics that do not obviously belong together: propositional and predicate logic, set theory, mathematical induction, combinatorics and counting, graph theory, number theory, recurrences. What unites them is a shared underlying activity — constructing precise arguments about finite or countable structures — and a shared purpose: making computational claims precise enough to prove rather than merely plausible enough to believe. The programmer who knows that mergesort runs in O(n log n) time because they were told so has mathematical literacy. The programmer who can derive it from a recurrence, prove the recurrence correct by induction, and explain why no comparison-based sort can do better has mathematical understanding. Discrete mathematics is what produces the second kind of practitioner.

Prerequisites: Programming (§2.1) is useful for concreteness but not strictly required. This section is otherwise a starting point.

How Mathematicians Learned to Count the Uncountable, and Why That Matters for Code

The topics that make up discrete mathematics did not start as a unified subject. They accumulated over centuries, each developed independently to answer its own pressing questions, and they were assembled into a CS curriculum only when it became clear that computation kept bumping into the same mathematical structures wherever it turned.

Combinatorics — the mathematics of counting — is the oldest strand. Its modern form traces to the correspondence between Blaise Pascal and Pierre de Fermat in 1654, prompted by a gambling problem: how to divide stakes fairly when a game of chance is interrupted before completion. The question required counting how many ways a game could end, which required systematic methods for enumerating possibilities without listing them all. Pascal’s triangle had appeared in various forms in Chinese and Arab mathematics centuries earlier, but the 1654 correspondence produced the first clear framework for thinking about combinations, permutations, and probability as mathematical objects with definite properties. The subject developed through the eighteenth and nineteenth centuries in parallel with probability theory, and by the twentieth century had become essential for any domain that needed to reason about large collections of objects without examining each one individually. Algorithm analysis is almost entirely combinatorics: the question “how many operations does this algorithm perform?” is a counting question, and answering it rigorously requires the apparatus that Pascal and his successors built.

Graph theory entered through a puzzle. In 1736, Leonhard Euler was asked whether it was possible to walk through the Prussian city of Königsberg crossing each of its seven bridges exactly once and returning to the starting point. Euler proved it was impossible by abstracting the problem: replace the landmasses with vertices and the bridges with edges, and ask whether the resulting graph has an Eulerian circuit. The answer depends only on the structure of the graph — specifically, whether every vertex has even degree — and has nothing to do with the geography of Königsberg. This move — replacing a concrete problem with an abstract structure, reasoning about the structure, and translating the answer back — is the defining move of discrete mathematics, and Euler performed it for the first time in this paper. Graph theory grew from this beginning into one of the most broadly applicable branches of mathematics, and computer science is probably its largest contemporary consumer. Networks of every kind — the internet, social graphs, dependency graphs in software, state machines in compilers — are graphs, and the algorithms that operate on them draw directly on graph-theoretic results that trace back to Euler’s bridge problem.

Mathematical logic arrived from a different direction and with more urgency. In the nineteenth century, mathematicians became aware that the informal reasoning they used to prove theorems was insufficiently rigorous to guarantee the absence of contradictions. Euclid’s geometry, the foundation of rigorous mathematics for two millennia, turned out to rest on unstated assumptions. Set theory, developed by Georg Cantor in the 1870s to handle infinite collections rigorously, produced paradoxes when the same informal reasoning was applied to it — Bertrand Russell’s paradox in 1901 showed that the “set of all sets that do not contain themselves” was logically inconsistent. The response was a century-long effort to put mathematics on rigorous logical foundations: axiom systems for set theory (Zermelo-Fraenkel in 1908 and 1922), formal systems for logic (Frege’s Begriffsschrift in 1879, Russell and Whitehead’s Principia Mathematica in 1910-1913), and the metatheoretical study of what formal systems could and could not prove. This project produced Gödel’s incompleteness theorems in 1931, Turing’s analysis of computability in 1936, and the foundations of computer science itself.

The connection to computer science is not incidental. The logical apparatus developed for mathematical rigor turned out to be precisely the apparatus needed to reason about programs. A program is a formal object — it has syntax and semantics that can be defined precisely, and claims about its behavior can be stated and proved within a logical system. The precondition-postcondition style of reasoning that Hoare introduced in 1969 is Aristotelian logic applied to programs. Type systems are logical systems. Compilers use formal grammars, themselves a product of the logic-and-foundations tradition. Every tool for reasoning about program correctness is a descendant of the nineteenth-century project to make mathematics rigorous.

Number theory, developed from antiquity through Fermat, Euler, and Gauss, was for most of its history the most practically useless branch of mathematics — Gauss called it the “queen of mathematics” partly because it served no earthly purpose. This changed dramatically in 1976, when Diffie and Hellman proposed public-key cryptography and showed that the security of communication could rest on number-theoretic hardness assumptions: the difficulty of factoring large integers, the difficulty of computing discrete logarithms. Rivest, Shamir, and Adleman’s RSA algorithm the following year made these ideas concrete. Suddenly, number theory that Gauss had pursued for its beauty alone became load-bearing infrastructure for the internet. The shift was not an accident of history but a structural feature of the subject: number theory studies properties of integers that are easy to state and extraordinarily hard to compute, which is exactly what cryptography requires.

Mathematical induction — the technique for proving claims about all natural numbers by proving a base case and showing the claim propagates — is older than most of the topics above, appearing in recognizable form in the writings of Blaise Pascal and in the work of the Italian mathematician Francesco Maurolico in the sixteenth century. Its application to computer science was not just useful but structurally necessary: programs are recursive structures, and the natural way to reason about all inputs to a recursive program is by induction on the structure of the recursion. When Hoare, Dijkstra, and their contemporaries developed the systematic study of program correctness in the late 1960s, they reached immediately for induction as the primary tool. The connection between mathematical induction and program correctness is tight enough that structural induction — the generalization of induction to recursively defined objects — is now one of the standard techniques for proving programs correct. The fit is so natural that it is sometimes surprising to remember that induction was developed centuries before the first program was written.

The formalization of all these strands into a CS curriculum happened primarily in the 1960s and 1970s, as theoretical computer science established itself as a discipline. Donald Knuth’s The Art of Computer Programming (1968 onward) demonstrated that algorithm analysis required sophisticated combinatorics and set the standard for the mathematical depth the field expected. The theory of computation work of Turing, Church, Cook, and Karp showed that complexity theory required logic, combinatorics, and deep results about the structure of computation. Compiler design required formal language theory, itself a branch of the logic and computability tradition. By 1980, the set of mathematical topics that CS practitioners needed was clear enough to teach as a unified subject, and discrete mathematics textbooks began appearing in university CS programs.

What this history makes clear is that discrete mathematics is not an artificial collection assembled for pedagogical convenience. Each strand grew from real mathematical problems, the strands are connected by genuine conceptual relationships, and computer science is one of the disciplines that draws most deeply on all of them. The stability of the subject — its content has changed little in decades while the rest of CS has transformed repeatedly — reflects the stability of the mathematical structures that computation keeps returning to.

Proof, Structure, and the Discipline of Precision

The subject organizes itself around three problems, each of which appears throughout theoretical and practical computer science.

Finite Arguments About Infinite Domains

Many CS claims range over infinite sets: an algorithm is correct on all inputs, a data structure maintains its invariant after every operation, a protocol cannot deadlock regardless of timing. These claims cannot be verified by testing — the input space is infinite — and yet they must be established with certainty, because software deployed at scale will encounter inputs that no test anticipated.

Mathematical induction is the primary tool. The logic is clean: if a claim holds for the smallest case (say, n = 0), and if the claim’s truth for any n implies its truth for n+1, then the claim holds for all natural numbers. The mechanics are easy to memorize. The difficulty is choosing the right inductive hypothesis, which is often not the obvious one. Many inductive proofs fail because the statement being proved is too weak to support the inductive step — the hypothesis for n does not provide enough information to establish the claim for n+1 — and the fix is to strengthen the hypothesis to a more general statement, prove the stronger statement by induction, and observe that the original claim follows as a special case. The counterintuitive fact that a more general claim is sometimes easier to prove than a specific one is a recurring theme in mathematics, and encountering it through induction exercises is one of the ways discrete mathematics changes how people think.

Structural induction generalizes mathematical induction from natural numbers to recursively defined structures — lists, trees, programs, grammars, proofs. If a claim holds for the base cases of a recursive definition, and if the claim’s truth for the parts implies its truth for the whole, then it holds for all instances. This is the natural proof technique for recursive programs: prove the claim for the empty list and show that it holds for a nonempty list if it holds for the tail. The technique scales — it applies to binary trees, expression trees, program syntax trees, any structure defined by recursive cases — and it is essentially unavoidable in theoretical CS.

Proof by contradiction, by contrapositive, and by construction are the other major techniques. Contradiction establishes that a claim is true by assuming its negation and deriving an absurdity; it is the technique behind Euclid’s proof that there are infinitely many primes (assume finitely many, construct one that isn’t on the list, derive a contradiction) and behind most undecidability proofs in computability theory. Construction establishes existence by exhibiting a witness — the most computational of proof techniques, since an existence proof by construction is essentially a program. Contrapositive flips an implication, proving “if P then Q” by proving the logically equivalent “if not Q then not P,” which is sometimes easier to argue.

What these techniques share is that they are all ways of making a finite argument that ranges over an infinite domain. The argument has a definite end; the claim it establishes does not. This is what proof does, and it is why proof is irreplaceable for claims about software that must work on all inputs.

Logic as the Language of Precision

Propositional logic provides the grammar of precise mathematical statements: variables, connectives (and, or, not, implies, if and only if), and rules for deriving new statements from given ones. The grammar is simple, the semantics is a truth table, and the completeness theorem establishes that every tautology is provable — there are no missing inference rules. The practically important concept is implication and its relationship to the contrapositive, converse, and inverse; these relationships are a frequent source of error in informal mathematical reasoning, and internalizing them removes the source.

Predicate logic adds quantifiers — “for all” and “there exists” — making it possible to express claims that range over all elements of a set, or that assert the existence of an element with some property. The quantifiers interact in ways that are not obvious: “there exists an x such that for all y, P(x,y)” and “for all y, there exists an x such that P(x,y)” are logically distinct, and confusing them produces incorrect proofs. Most mathematical mistakes involving quantifiers are not about complex logical structure but about the order of quantifiers, and the cure is sustained practice recognizing and manipulating quantified statements.

Set theory provides the basic vocabulary of mathematical objects: sets, functions, relations, sequences, powersets, cartesian products. A function is a particular kind of relation; a relation is a set of ordered pairs; an ordered pair is a set of sets, by the Kuratowski encoding. The grounding is total, not just notational. What this means for CS is that the objects of computation — types, data structures, program states — can be defined with complete precision as set-theoretic objects, making it possible to state correctness conditions precisely and prove them rigorously.

Counting and Structure

Combinatorics is the art of counting without enumeration: answering “how many?” for collections too large to list. The basic toolkit — permutations, combinations, the binomial theorem, the pigeonhole principle — appears constantly in algorithm analysis, probability theory, and cryptography.

The pigeonhole principle is the simplest combinatorial argument and one of the most powerful: if n+1 items are placed in n containers, some container holds at least two items. This trivial observation proves non-trivial results: in a group of 23 people, two share a birthday with probability greater than 50% (the birthday paradox, essential for understanding hash collision probabilities); any hash function mapping from a large domain to a small codomain has collisions (essential for understanding hash tables and cryptographic hash functions); any lossless compression algorithm must make some files longer (essential for understanding the limits of compression).

Recurrence relations are combinatorics applied to algorithm analysis: express the cost of a problem of size n in terms of the cost of smaller problems. Mergesort divides the input in half, sorts both halves recursively, and merges in linear time: T(n) = 2T(n/2) + O(n). The recurrence has a closed-form solution by the Master Theorem: T(n) = O(n log n). The ability to set up and solve recurrences is the core analytical skill for recursive algorithms, and developing it requires substantial practice.

Graph theory provides the vocabulary for reasoning about networks, dependencies, and paths. Graphs are pairs (V, E) where V is a set of vertices and E is a set of edges; the structure is simple, the applications are vast. Trees are acyclic connected graphs; forests are acyclic graphs; DAGs (directed acyclic graphs) represent dependencies. The fundamental algorithmic results on graphs — reachability, shortest paths, spanning trees, flows — all depend on graph-theoretic properties, and understanding why the algorithms work requires understanding the underlying theorems.

Number theory appears primarily through modular arithmetic and divisibility. Bézout’s identity, the Chinese Remainder Theorem, Fermat’s Little Theorem, and the structure of integer remainders are the mathematical background for RSA and related cryptographic systems. The Extended Euclidean Algorithm, which computes multiplicative inverses modulo a prime, is both a number-theoretic result and an efficient algorithm; understanding why it works requires understanding the mathematics, not just the pseudocode.

What Studying This Changes

Discrete mathematics changes how practitioners reason, and the change is qualitative rather than quantitative — it is not that they become faster at the same kind of thinking but that they acquire access to a different kind.

The most immediate change is proof-reading and proof-writing ability. After serious engagement with this subject, a practitioner can read a mathematical argument and assess whether it actually establishes its conclusion — not just whether the steps look plausible, but whether the logical structure is valid and the claims follow from the hypotheses. This is a different skill from following an argument whose steps are explained; it is the active ability to verify correctness, and it is what makes it possible to read the CS theory literature rather than just be told its conclusions.

A second change is comfort with formal definitions. Mathematically trained practitioners instinctively ask, when encountering a new concept, what its definition is — not its intuitive gloss but its precise specification. This instinct transfers directly to interface design, type system design, and specification writing, where the cost of imprecision is bugs that appear only under conditions nobody thought to test. Programmers with mathematical training tend to write cleaner interfaces because they hold definitions to a higher standard.

A third change is combinatorial intuition: the habit of asking “how many?” and expecting a precise answer. This surfaces in algorithm analysis (how many operations does this loop perform?), in probability (what is the probability that two random elements collide?), in cryptography (how many possible keys does this scheme have?), and in data structure design (how much space does this structure require?). The answers are not always simple, but the habit of asking for precise answers — rather than accepting “it depends” or “a lot” — is itself the achievement.

A fourth change, perhaps the deepest, is a higher standard of rigor applied to all claims. The practitioner who has internalized proof technique distinguishes between “this seems to work on the cases I tried” and “this is correct for all cases, and I can prove it.” The distinction matters enormously in software, where the cases nobody tried are precisely the cases where serious bugs hide. The higher standard does not require formal proof of everything — most software is not formally proven — but it produces a disposition to identify the assumptions behind a claim, ask whether those assumptions hold, and seek evidence that covers the cases that intuition misses.

Resources

Books and Texts

The current canonical entry is Lehman, Leighton, and Meyer’s Mathematics for Computer Science (MIT 6.042J, free PDF), developed for MIT’s introductory mathematics course for CS students. The book’s distinguishing feature is that it treats proof technique as the central subject rather than as background: the first substantial chapter is about proofs, and the mathematical content (number theory, graph theory, probability, counting) is developed within a framework that constantly asks how claims are established, not just what the claims are. It is unusually well-written for the genre, with a direct voice that takes the philosophical content of mathematical reasoning seriously. The companion MIT 6.042J lectures (free on MIT OpenCourseWare and YouTube) are of high quality and are worth using alongside the text, especially for topics where a worked example helps more than additional reading.

For learners whose specific bottleneck is proof construction — who can follow definitions and theorems but cannot produce arguments themselves — Velleman’s How to Prove It (3rd ed., 2019) is the most focused intervention available. It isolates proof technique from mathematical content, working through the logical structure of mathematical statements (what does it mean to prove an implication? a universal statement? an existential?), the major proof strategies, and the process of translating informal arguments into rigorous ones. It is narrower than MCS — it does not cover combinatorics, graph theory, or probability — but for the learner who needs to build proof skill specifically, it addresses the bottleneck directly. Reading Velleman then MCS is a reliable path for learners who find MCS’s pace too fast.

Oscar Levin’s Discrete Mathematics: An Open Introduction (free online) is a gentler open textbook with many exercises, hints, and solutions. It is especially useful for readers who need more runway before MIT MCS or who want an accessible second explanation of counting, induction, relations, functions, and graph basics. It is less CS-oriented and less complete than MCS, so it should be used as auxiliary support rather than as the main spine.

Richard Hammack’s Book of Proof (free online) is a clean bridge into proof-writing. It does not replace Velleman, but its direct style, exercises, and low barrier to entry make it useful for learners who need repeated practice translating mathematical intuition into written proof.

Rosen’s Discrete Mathematics and Its Applications (8th ed.) is comprehensive and example-rich and is used in many undergraduate programs. Its weakness is that its writing is competent without being illuminating: it covers material without giving the reader a strong sense of why the material coheres or what kind of thinking it is building. It is better as a reference — to look up a specific topic or find additional worked examples — than as a primary text for self-directed learners.

For combinatorics at depth, Graham, Knuth, and Patashnik’s Concrete Mathematics is the canonical extension. Written specifically for algorithm analysis, it develops sums, recurrences, generating functions, and asymptotic notation with far more depth than introductory texts. The book is also genuinely entertaining — Knuth’s personality is visible throughout — and can be read after MCS by any learner planning serious algorithm analysis. It is one of the few mathematics books that is both technically demanding and a pleasure to read.

For graph theory at depth, Diestel’s Graph Theory (5th ed., free online) is the modern standard for rigorous treatment. Bondy and Murty’s Graph Theory is an alternative with broader problem coverage. Either is appropriate after introductory exposure for learners whose work involves networks, algorithms on graphs, or combinatorial optimization.

Pólya’s How to Solve It is the heterodox text that belongs alongside the technical curriculum. It is about the heuristics of mathematical problem-solving — what to do when the approach is not clear, how to specialize and generalize, when to work backward, how to use analogy — and these heuristics are applicable at every level of mathematical sophistication. It does not teach discrete mathematics, but it teaches the thinking style that makes discrete mathematics learnable and productive. Reading it alongside MCS improves performance on hard problems. Proofs from THE BOOK (Aigner and Ziegler) is a different kind of supplement: a collection of particularly elegant proofs with no pedagogical intent beyond demonstrating what mathematical beauty looks like. Exposure to elegant proofs develops mathematical taste, and mathematical taste influences which approaches a practitioner reaches for first.

Book Role Type
Lehman, Leighton, Meyer, Mathematics for Computer Science (free) Current canonical entry Entry
Velleman, How to Prove It (3rd ed.) Focused proof-technique entry Entry
Levin, Discrete Mathematics: An Open Introduction (free) Gentle open discrete math entry Auxiliary
Hammack, Book of Proof (free) Proof-writing bridge for beginners Auxiliary
Rosen, Discrete Mathematics and Its Applications Comprehensive reference Reference
Graham, Knuth, Patashnik, Concrete Mathematics Combinatorics and asymptotics at depth Depth
Diestel, Graph Theory (free online) Graph theory at depth Depth
Selected chapters from Mitzenmacher & Upfal, Probability and Computing Probabilistic methods for algorithms Depth
Pólya, How to Solve It Problem-solving heuristics Auxiliary
Aigner & Ziegler, Proofs from THE BOOK Mathematical taste and elegance Auxiliary
Hardy, A Mathematician’s Apology Philosophy of mathematical work Auxiliary

Courses and Lectures

MIT 6.042J is the course from which the MCS text emerged. Full lecture videos are freely available on MIT OpenCourseWare and YouTube, with problem sets and exams. For self-learners, the course provides pacing and worked examples that the text alone does not, and the combination of book and lectures is substantially more effective than either alone.

MIT 6.042J on OCW covers a slightly different syllabus from the most recent textbook edition; reading them together and reconciling the differences is itself a useful exercise in mathematical flexibility. The lectures emphasize different aspects than the text at various points, which means watching lectures on a topic after reading about it often resolves confusions that additional reading would not.

For combinatorics specifically, the lectures from MIT 18.212 (Algebraic Combinatorics) and Stanford’s CS 70 (Discrete Mathematics and Probability Theory) offer alternative expositions at comparable level. CS 70 is particularly strong on the probability material and on the connection between combinatorics and computation.

Course Platform Type
MIT 6.042J lectures + problem sets (free) MIT OCW / YouTube Entry
Stanford CS 70 Discrete Math and Probability (free) Stanford / YouTube Entry

Practice, Tools, and Code

3Blue1Brown (YouTube) has no single video on discrete mathematics as a subject, but videos on topics that appear in it — linear transformations, set theory intuitively, combinatorics problems — are unusually good at building geometric and visual intuition before the algebra. Watching the relevant videos before engaging with a textbook topic often makes the formalism feel like a description of something already partially understood rather than a definition of something entirely new.

Lean and Coq are proof assistants — software systems in which mathematical proofs are written as programs and mechanically verified. Working through even simple proofs in Lean changes how proof feels: the system forces complete precision, accepts no hand-waving, and provides immediate feedback when a step is invalid. The Lean community’s Mathematics in Lean (free, online tutorial) is the accessible entry point. Engaging with a proof assistant while learning discrete mathematics — even for a small fraction of the exercises — is one of the most effective ways to internalize what proof actually requires. It also connects forward to §3.6 (Formal Methods and Proof Assistants), where these tools are treated at depth.

The Xena Project (Kevin Buzzard, Imperial College) is formalizing undergraduate mathematics in Lean and making the results available online. Browsing formalized proofs of theorems one has just proved informally — seeing how the informal argument maps to the formal one — is illuminating in ways that no textbook description of proof assistants can replicate.

Resource Platform Type
3Blue1Brown (selected topics, free) YouTube Practice
Lean / Mathematics in Lean (free) leanprover-community.github.io Practice
Xena Project / Mathlib (free) leanprover-community.github.io/mathlib4 Practice

Traps

Trap Why it misleads Better response
Treating proof as computation Learners with a calculus or programming background approach problems expecting a procedure that produces an answer. Proof is a different activity — constructing an argument — and it produces a verdict (true or false, with justification), not a numerical value. The surface behavior of proof exercises looks like math homework; the cognitive demand is categorically different, and learners who do not make the shift produce work that has the form of proof without its substance. Identify early that a new kind of intellectual activity is being practiced, not just new material in a familiar mode. Seek problems that explicitly require argument, and read completed proofs asking “what kind of argument is this?” rather than “what did they compute?”
Memorizing proofs rather than techniques Reproducing a proof of the irrationality of √2 from memory demonstrates recall, not mathematical understanding. The point of the proof is the technique — proof by contradiction, using parity — which should become available for novel problems. A learner who cannot construct an analogous proof for √3 after memorizing the √2 proof has not learned the technique. After reading any proof, close the text and ask: what general method did this use? Then attempt a nearby problem using the same method before reading another proof. The technique is the learning, not the specific result.
Underestimating induction The mechanics of induction — base case, inductive hypothesis, inductive step — are taught in an hour and sound simple. The real difficulty is choosing the right inductive hypothesis, which is often not the obvious one, and making the move of strengthening the hypothesis when the obvious one is too weak. This phenomenon is counterintuitive and consistently surprises learners. Spend substantially more time on induction than the curriculum allocates. Practice specifically the move of strengthening: when an inductive proof fails, ask whether a stronger claim would actually be easier to prove. The exercise of finding a provable strengthening is one of the most productive in the subject.
Resisting formality Programmers and engineers often experience mathematical formalism as pedantry: “yes, but in practice everyone knows what this means.” In discrete mathematics, the ambiguities that formality eliminates are exactly the ambiguities that produce bugs in code. The discipline of constructing precise definitions is the same discipline as constructing correct interfaces. During the learning phase, treat every definition as a precision instrument and check its behavior on edge cases. The instinct to dismiss formality can be re-engaged in appropriate contexts later; it should not be operative while the subject is being learned.
Using AI assistance to complete proof exercises AI tools can produce passable proofs for most standard discrete mathematics exercises. Learners who use this to avoid the work are forfeiting the central benefit: the cognitive struggle of constructing a proof is itself what builds proof skill. The exercises cannot be separated from the learning they produce. Do proof exercises without AI assistance, attempting each problem independently before seeking any help. After a genuine attempt, AI can be useful for checking reasoning or explaining why an approach failed — but as a tutor after the attempt, not as a solution generator before it.
Moving on before induction is solid Induction underlies nearly every subsequent proof technique in the subject, and gaps in induction facility compound. A learner who moves through the combinatorics and graph theory material while remaining shaky on induction will find later arguments increasingly difficult to follow, because the later arguments use inductive reasoning as background rather than explaining it. Return to induction until it feels natural. The right test: given an unfamiliar claim about natural numbers or a recursive structure, can a proof sketch be produced from scratch? If not, the earlier material needs more time.

中文

多数计算机科学导论会把数学当作背景准备——仿佛它只是正式进入主题前需要先过一遍的东西。离散数学拒绝这种理解。它不是为了计算目的而从数学中借来的工具;它正是数学思维与计算思维重合的那个层次。离散数学提出的问题——这个命题能否被证明?这种对象一共有多少个?这个递归结构是什么样的?——恰恰也是计算机科学在算法、数据结构和程序中提出的问题。学习离散数学不是在为 CS 做准备;它本身就是开始以严格意义上的计算机科学家方式思考。

它的内容是一组看起来并不天然属于同一类的主题:命题逻辑和谓词逻辑、集合论、数学归纳法、组合数学与计数、图论、数论、递推关系。把它们统一起来的,是一种共同的底层活动——对有限结构或可数结构构造精确论证——以及一个共同目的:让关于计算的主张精确到可以被证明,而不只是看起来可信。一个程序员如果知道 mergesort 的运行时间是 O(n log n),只是因为别人这样告诉他,那么他拥有的是数学素养。一个程序员如果能从递推关系中推导出这一点,用归纳法证明这个递推关系是正确的,并解释为什么任何基于比较的排序都不可能做得更好,那么他拥有的是数学理解。离散数学培养的是第二种实践者。

前置知识:编程(§2.1)有助于提供具体感,但并非严格必需。除此之外,本节本身就是一个起点。

数学家如何学会数清看似不可计数之物,以及这为什么对代码重要

构成离散数学的这些主题,一开始并不是一个统一学科。它们在几个世纪中逐渐积累起来,每个主题最初都是为了回答自身面对的紧迫问题而独立发展。直到人们意识到:计算无论走到哪里,都会反复碰到同一批数学结构,它们才被组装进 CS 课程之中。

组合数学——计数的数学——是其中最古老的一支。它的现代形态可以追溯到 Blaise Pascal 和 Pierre de Fermat 在 1654 年的通信,而这场通信的起因是一个赌博问题:如果一场机会游戏中途被打断,赌注应该如何公平分配?这个问题要求人们数出游戏可能如何结束,也就是需要系统地枚举可能性,而不是把所有情况逐一列出。Pascal 三角形此前已经在中国和阿拉伯数学中以不同形式出现过,但 1654 年的通信第一次清楚建立了把组合、排列和概率当作具有确定性质的数学对象来思考的框架。这个主题在十八、十九世纪与概率论并行发展;到二十世纪,它已经成为任何需要在不逐一检查对象的情况下推理大量对象集合的领域的必备工具。算法分析几乎完全是组合数学:问题“这个算法会执行多少次操作?”本质上就是一个计数问题;要严谨回答它,就需要 Pascal 及其后继者建立的工具。

图论则是从一个谜题中进入的。1736 年,Leonhard Euler 被问到:能否在普鲁士城市 Königsberg 中散步,恰好经过七座桥中的每一座一次,并回到起点。Euler 通过抽象化这个问题证明了这是不可能的:把陆地替换为顶点,把桥替换为边,然后问所得图是否存在欧拉回路。答案只取决于图的结构——具体地说,是否每个顶点的度都是偶数——而与 Königsberg 的地理无关。这一动作——用一个抽象结构替换具体问题,对结构进行推理,再把答案翻译回原问题——是离散数学的定义性动作,而 Euler 在这篇论文中第一次完成了它。图论从这个起点发展为数学中应用最广泛的分支之一,而计算机科学可能是它当代最大的使用者。各种网络——互联网、社交图、软件中的依赖图、编译器中的状态机——都是图;作用于它们的算法,直接依赖可以追溯到 Euler 七桥问题的图论结果。

数理逻辑来自另一个方向,而且带有更强的紧迫感。十九世纪,数学家开始意识到,他们用来证明定理的非形式推理,不够严密,无法保证不会产生矛盾。两千年来作为严密数学基础的欧几里得几何,后来被发现依赖一些未明说的假设。Georg Cantor 在 1870 年代发展集合论,目的是严谨处理无限集合;但当同样的非形式推理被用于集合论时,它产生了悖论——Bertrand Russell 在 1901 年提出的悖论表明,“所有不包含自身的集合所组成的集合”在逻辑上是不一致的。对此的回应,是一场持续一个世纪的努力:为数学建立严密的逻辑基础,包括集合论公理系统(Zermelo-Fraenkel,1908 年和 1922 年)、逻辑形式系统(Frege 的 Begriffsschrift,1879 年;Russell 和 Whitehead 的 Principia Mathematica,1910–1913 年),以及对形式系统能够证明什么、不能证明什么的元理论研究。这个计划最终产生了 Gödel 在 1931 年的不完备性定理、Turing 在 1936 年对可计算性的分析,以及计算机科学本身的基础。

它与计算机科学的连接并非偶然。为了使数学变得严谨而发展出来的逻辑工具,恰好也正是推理程序所需要的工具。程序是形式对象——它有可以被精确定义的语法和语义,关于其行为的主张也可以在一个逻辑系统中被陈述和证明。Hoare 在 1969 年引入的前置条件—后置条件式推理,就是应用于程序的亚里士多德逻辑。类型系统是逻辑系统。编译器使用形式文法,而形式文法本身就是逻辑与基础传统的产物。每一种用于推理程序正确性的工具,都是十九世纪“让数学变得严密”这一计划的后裔。

数论从古代一路经由 Fermat、Euler 和 Gauss 发展而来,在其大部分历史中都是最“没有实际用途”的数学分支——Gauss 称它为“数学女王”,部分原因就在于它不服务于任何世俗目的。1976 年,这一切戏剧性地改变了:Diffie 和 Hellman 提出公钥密码学,表明通信安全可以建立在数论困难性假设之上,例如大整数分解的困难、计算离散对数的困难。第二年,Rivest、Shamir 和 Adleman 的 RSA 算法让这些思想变成具体系统。突然之间,Gauss 曾经只为美而研究的数论,成为互联网的承重基础设施。这个转变并不是历史偶然,而是这一学科的结构性特征:数论研究的是整数的性质,这些性质往往很容易陈述,却极难计算,而这正是密码学所需要的东西。

数学归纳法是一种证明技术:先证明某个基例,再证明命题可以向后传播,从而证明关于所有自然数的主张。它比上面多数主题都更古老,在 Blaise Pascal 的著作和十六世纪意大利数学家 Francesco Maurolico 的工作中已经出现了可识别的形式。它在计算机科学中的应用不只是有用,而是结构上必需:程序是递归结构,而推理递归程序在所有输入上的行为,最自然的方式就是沿递归结构做归纳。当 Hoare、Dijkstra 及其同代人在 1960 年代末发展程序正确性的系统研究时,他们立刻把归纳法作为主要工具。数学归纳法与程序正确性之间的联系如此紧密,以至于结构归纳法——把归纳法推广到递归定义对象上的技术——现在已经成为证明程序正确性的标准方法之一。这种契合是如此自然,以至于人们有时会惊讶地想起:归纳法早在第一个程序被写出之前几个世纪就已经发展出来了。

这些线索被正式整合进 CS 课程,主要发生在 1960 年代和 1970 年代,当时理论计算机科学正在建立为一门学科。Donald Knuth 的 The Art of Computer Programming(1968 年起)表明,算法分析需要复杂的组合数学,并为这个领域所期待的数学深度设定了标准。Turing、Church、Cook 和 Karp 关于计算理论的工作表明,复杂性理论需要逻辑、组合数学,以及关于计算结构的深层结果。编译器设计需要形式语言理论,而形式语言理论本身就是逻辑和可计算性传统的一支。到 1980 年,CS 实践者所需要的这组数学主题已经足够清楚,可以被作为一个统一学科来教授,离散数学教材也开始出现在大学 CS 项目中。

这段历史清楚表明,离散数学并不是为了教学便利而人为拼凑出来的一组主题。每一条线索都来自真实的数学问题,这些线索之间存在真正的概念关系,而计算机科学是最深入吸收它们的学科之一。这个主题的稳定性——几十年来内容变化很小,而 CS 其他部分已经反复转型——反映的是计算反复回到的那些数学结构本身的稳定性。

证明、结构与精确性的纪律

这门学科围绕三个问题组织自身,而每一个问题都会贯穿理论和实践计算机科学。

关于无限领域的有限论证

许多 CS 主张都覆盖无限集合:一个算法在所有输入上都是正确的,一个数据结构在每一次操作之后都保持不变量,一个协议无论时序如何都不会死锁。这些主张不能靠测试来验证——输入空间是无限的——但它们又必须被确定地建立,因为大规模部署的软件迟早会遇到任何测试都没有预料到的输入。

数学归纳法是主要工具。其逻辑很清楚:如果一个命题在最小情况(比如 n = 0)成立,并且如果它对任意 n 成立能够推出它对 n+1 也成立,那么它就对所有自然数成立。归纳法的机械步骤很容易记忆。困难在于选择正确的归纳假设,而正确假设往往不是最显然的那个。许多归纳证明失败,是因为要证明的陈述太弱,不足以支撑归纳步骤——n 情况下的假设提供的信息不够,无法建立 n+1 的结论——修复方式是把假设加强为一个更一般的陈述,对这个更强陈述做归纳证明,再观察原命题只是它的一个特例。一个反直觉的事实是:一个更一般的命题有时反而比一个更具体的命题更容易证明。这是数学中反复出现的主题,而通过归纳练习遇到它,是离散数学改变人们思考方式的途径之一。

结构归纳法把数学归纳法从自然数推广到递归定义的结构——列表、树、程序、文法、证明。如果一个命题对递归定义的基例成立,并且如果它对各个组成部分成立能够推出它对整体成立,那么它就对所有实例成立。这是递归程序最自然的证明技术:先证明空列表情形,再证明如果命题对尾部成立,那么它也对非空列表成立。这项技术可以扩展——它适用于二叉树、表达式树、程序语法树,以及任何由递归分支定义的结构——并且在理论 CS 中几乎无法回避。

反证法、逆否命题证明法和构造性证明,是另外几种主要技术。反证法通过假设命题的否定并推出荒谬结论,从而证明命题为真;Euclid 证明素数有无限多个时使用的就是这种技术(假设只有有限多个素数,构造一个不在列表中的新数,推出矛盾),可计算性理论中的多数不可判定性证明也依赖它。构造性证明通过展示一个见证对象来证明存在性——这是最具计算性的证明技术,因为构造性的存在性证明本质上就是一个程序。逆否命题证明法则会翻转一个蕴含,通过证明逻辑等价的“如果非 Q,则非 P”来证明“如果 P,则 Q”;在某些情况下,这样更容易论证。

这些技术的共同点在于:它们都是用一个有限论证来覆盖一个无限领域。论证本身有明确终点;它所建立的主张却没有。这就是证明所做的事,也是为什么对于那些必须在所有输入上工作的软件主张来说,证明是不可替代的。

逻辑是精确性的语言

命题逻辑提供了精确数学陈述的语法:变量、连接词(且、或、非、蕴含、当且仅当),以及从已知陈述推导新陈述的规则。它的语法简单,语义是真值表,而完备性定理则说明每一个永真式都是可证明的——不存在缺失的推理规则。实践上最重要的概念是蕴含,以及它与逆否命题、逆命题、否命题之间的关系;这些关系是非形式数学推理中常见错误的来源,一旦内化,就能消除这一类错误。

谓词逻辑加入了量词——“对所有”和“存在”——使我们能够表达覆盖一个集合中所有元素的主张,或者断言存在某个具有某种性质的元素。量词之间的互动并不显然:“存在一个 x,使得对所有 y,P(x,y)”和“对所有 y,存在一个 x,使得 P(x,y)”在逻辑上是不同的,混淆它们会产生错误证明。多数涉及量词的数学错误,并不是因为逻辑结构多复杂,而是因为量词顺序出错;解决办法是持续练习识别和操作带量词的陈述。

集合论提供了数学对象的基本词汇:集合、函数、关系、序列、幂集、笛卡尔积。函数是一种特殊的关系;关系是有序对的集合;按照 Kuratowski 编码,有序对本身又是集合的集合。这种基础是彻底的,而不只是记号上的。对 CS 来说,这意味着计算对象——类型、数据结构、程序状态——都可以被完全精确地定义为集合论对象,从而使我们能够精确陈述正确性条件,并严谨证明它们。

计数与结构

组合数学是不枚举而计数的艺术:为那些太大、无法逐一列出的集合回答“有多少个?”的问题。它的基本工具——排列、组合、二项式定理、鸽巢原理——会不断出现在算法分析、概率论和密码学中。

鸽巢原理是最简单的组合论证之一,也是最强大的论证之一:如果把 n+1 个物品放进 n 个容器中,那么至少有一个容器装了两个物品。这个平凡观察可以证明非平凡结果:在 23 个人组成的群体中,两个人生日相同的概率超过 50%(生日悖论,对理解哈希碰撞概率至关重要);任何把大定义域映射到小值域的哈希函数都会产生碰撞(对理解哈希表和密码学哈希函数至关重要);任何无损压缩算法都必然会让某些文件变长(对理解压缩的限制至关重要)。

递推关系是组合数学在算法分析中的应用:用较小问题的成本来表达规模为 n 的问题的成本。Mergesort 把输入一分为二,递归排序两半,并在线性时间内合并:T(n) = 2T(n/2) + O(n)。这个递推式可以通过主定理得到闭式解:T(n) = O(n log n)。建立并求解递推关系,是分析递归算法的核心能力,而发展这种能力需要大量练习。

图论提供了推理网络、依赖关系和路径的词汇。图是一个二元组 (V, E),其中 V 是顶点集合,E 是边集合;结构简单,应用却极其广泛。树是无环连通图;森林是无环图;DAG(有向无环图)表示依赖关系。图上的基本算法结果——可达性、最短路径、生成树、流——都依赖图论性质;理解这些算法为什么正确,需要理解底层定理。

数论主要通过模运算和整除性出现。Bézout 等式、中国剩余定理、Fermat 小定理,以及整数余数结构,构成 RSA 及相关密码系统的数学背景。扩展欧几里得算法可以计算模素数下的乘法逆元,它既是一个数论结果,也是一个高效算法;理解它为什么正确,需要理解数学,而不只是理解伪代码。

学习这一部分会改变什么

离散数学会改变实践者推理的方式,而且这种变化是质的,而非量的——不是让他们更快地做同一种思考,而是让他们获得进入另一种思考的能力。

最直接的变化,是读证明和写证明的能力。在认真学习这一主题之后,实践者可以阅读一个数学论证,并判断它是否真的建立了结论——不只是判断步骤看起来是否可信,而是判断逻辑结构是否有效,主张是否真的从假设中推出。这不同于跟着别人解释过的步骤走一遍;它是一种主动验证正确性的能力,也是让人能够阅读 CS 理论文献,而不是只被告知其结论的能力。

第二个变化,是对形式定义的适应。受过数学训练的实践者在遇到新概念时,会本能地追问它的定义是什么——不是直观解释,而是精确定义。这个习惯会直接迁移到接口设计、类型系统设计和规约写作中,因为在这些地方,模糊性的代价就是那些只会在没人想到要测试的条件下出现的 bug。受过数学训练的程序员往往写出更清晰的接口,因为他们对定义有更高标准。

第三个变化,是组合直觉:习惯性地问“有多少个?”,并期待一个精确答案。这会出现在算法分析中(这个循环会执行多少次操作?)、概率中(两个随机元素碰撞的概率是多少?)、密码学中(这个方案有多少个可能密钥?)、数据结构设计中(这个结构需要多少空间?)。答案并不总是简单,但要求精确答案的习惯——而不是接受“看情况”或“很多”——本身就是成就。

第四个变化,也许是最深的变化,是对所有主张施加更高的严谨标准。内化了证明技术的实践者,会区分“它在我试过的案例上似乎有效”和“它对所有情况都正确,而且我能证明”。这个区分在软件中极其重要,因为没人试过的案例,恰恰是严重 bug 藏身的地方。更高的标准并不要求对所有软件都做形式证明——多数软件并没有被形式证明——但它会形成一种倾向:识别一个主张背后的假设,追问这些假设是否成立,并寻找能够覆盖直觉遗漏情况的证据。

资源

书籍与文本

当前的经典入口,是 Lehman、Leighton 和 Meyer 的 Mathematics for Computer Science(MIT 6.042J,免费 PDF),这本书是为 MIT 面向 CS 学生的入门数学课程开发的。它的独特之处在于,它把证明技术当作中心主题,而不是背景知识:第一个实质性章节就是关于证明的;而数学内容(数论、图论、概率、计数)都是在一个不断追问“这些主张如何被建立”的框架中展开,而不只是说明“这些主张是什么”。对于这一类教材来说,它写得异常好,声音直接,并且认真对待数学推理中的哲学内容。配套的 MIT 6.042J 讲座(可在 MIT OpenCourseWare 和 YouTube 免费获取)质量很高,值得与教材一起使用;尤其在那些看一个完整例子比继续读更多文字更有帮助的主题上,讲座很有价值。

对于那些具体瓶颈在证明构造上的学习者——也就是能读懂定义和定理,却不能自己写出论证的人——Velleman 的 How to Prove It(第 3 版,2019)是目前最集中的干预工具。它把证明技术从数学内容中分离出来,逐步处理数学陈述的逻辑结构(证明一个蕴含意味着什么?证明一个全称命题意味着什么?证明一个存在命题意味着什么?)、主要证明策略,以及把非形式论证翻译成严密论证的过程。它比 MCS 更窄——不覆盖组合数学、图论或概率——但对于需要专门建立证明能力的学习者,它直接处理瓶颈。先读 Velleman,再读 MCS,是觉得 MCS 节奏太快的学习者的一条可靠路径。

Oscar Levin 的 Discrete Mathematics: An Open Introduction(免费在线)是一本更温和的开放教材,包含大量练习、提示和解答。它特别适合那些在进入 MIT MCS 前需要更多跑道的读者,也适合作为计数、归纳、关系、函数和图基础的第二种解释。它的 CS 导向不如 MCS,完整性也不如 MCS,因此更适合作为辅助支持,而不是主干教材。

Richard Hammack 的 Book of Proof(免费在线)是进入证明写作的清晰桥梁。它不能替代 Velleman,但它直接的风格、练习和较低进入门槛,使它非常适合那些需要反复练习把数学直觉翻译成书面证明的学习者。

Rosen 的 Discrete Mathematics and Its Applications(第 8 版)内容全面、例子丰富,被许多本科项目采用。它的弱点在于,写作合格但不够启发:它覆盖材料,却没有给读者很强的感觉,让人理解这些材料为什么组成一个整体,或者它正在训练哪种思维。它更适合作为参考书——查某个具体主题,或找额外例题——而不是自学者的主要文本。

对于深入学习组合数学,Graham、Knuth 和 Patashnik 的 Concrete Mathematics 是经典延伸。它专门为算法分析而写,以远超入门教材的深度发展求和、递推、生成函数和渐近记号。这本书也确实有趣——Knuth 的个性贯穿其中——任何计划认真学习算法分析的学习者,都可以在 MCS 之后阅读它。它是少数既技术要求高、又读起来有乐趣的数学书之一。

对于深入学习图论,Diestel 的 Graph Theory(第 5 版,免费在线)是严谨处理图论的现代标准。Bondy 和 Murty 的 Graph Theory 是另一个选择,题目覆盖更广。对于那些工作涉及网络、图算法或组合优化的学习者来说,在入门之后阅读其中任何一本都是合适的。

Pólya 的 How to Solve It 是一本文本取向不同、但应该与技术课程并列的异端书。它讨论的是数学解题的启发法——当方法不清楚时该做什么,如何特殊化和一般化,什么时候倒推,如何使用类比——这些启发法适用于各种数学成熟度层次。它不教授离散数学,但它教授一种思维风格,使离散数学更容易学习、更有生产力。把它与 MCS 一起阅读,会提高处理难题的能力。Proofs from THE BOOK(Aigner 和 Ziegler)是另一种补充:它收集特别优雅的证明,并不以教学为主要目的,而是展示数学之美。接触优雅证明会发展数学品味,而数学品味会影响实践者首先尝试哪些方法。

书籍 作用 类型
Lehman, Leighton, Meyer, Mathematics for Computer Science(免费) 当前经典入口 入门
Velleman, How to Prove It(第 3 版) 专门训练证明技术的入口 入门
Levin, Discrete Mathematics: An Open Introduction(免费) 温和的开放离散数学入口 辅助
Hammack, Book of Proof(免费) 面向初学者的证明写作桥梁 辅助
Rosen, Discrete Mathematics and Its Applications 综合参考 参考
Graham, Knuth, Patashnik, Concrete Mathematics 深入组合数学与渐近分析 深入
Diestel, Graph Theory(免费在线) 深入图论 深入
Mitzenmacher & Upfal, Probability and Computing 选章 面向算法的概率方法 深入
Pólya, How to Solve It 解题启发法 辅助
Aigner & Ziegler, Proofs from THE BOOK 数学品味与优雅证明 辅助
Hardy, A Mathematician’s Apology 数学工作的哲学 辅助

课程与讲座

MIT 6.042J 是 MCS 教材所来源的课程。完整讲座视频可在 MIT OpenCourseWare 和 YouTube 免费获取,并配有习题和考试。对自学者来说,这门课提供了教材本身不具备的节奏感和完整例题;教材与讲座结合起来,效果明显强于单独使用其中任何一个。

MIT 6.042J on OCW 的大纲与最新版教材略有不同;把二者一起阅读,并调和其中差异,本身就是一种有用的数学灵活性练习。讲座在不同主题上强调的侧面与教材不同,这意味着读完某个主题后再看讲座,往往能解决继续阅读无法解决的困惑。

对于组合数学本身,MIT 18.212(Algebraic Combinatorics)和 Stanford CS 70(Discrete Mathematics and Probability Theory)的讲座提供了相近层次的不同讲法。CS 70 在概率材料,以及组合数学与计算之间的连接上尤其强。

课程 平台 类型
MIT 6.042J lectures + problem sets(免费) MIT OCW / YouTube 入门
Stanford CS 70 Discrete Math and Probability(免费) Stanford / YouTube 入门

实践、工具与代码

3Blue1Brown(YouTube)并没有一个专门讲离散数学整体的单一视频,但它关于其中若干主题的视频——线性变换、直观集合论、组合数学问题——非常擅长在代数之前建立几何和视觉直觉。在接触教材主题之前观看相关视频,常常会让形式化内容感觉像是在描述某种已经部分理解的东西,而不是在从零定义一个完全陌生的东西。

LeanCoq 是证明助手——在其中,数学证明会像程序一样被书写,并由机器进行验证。哪怕只是在 Lean 中完成一些简单证明,也会改变证明给人的感觉:系统要求完全精确,不接受任何含糊跳步,并在步骤无效时立刻给出反馈。Lean 社区的 Mathematics in Lean(免费在线教程)是可访问的入口。在学习离散数学时接触证明助手——哪怕只完成一小部分练习——也是内化“证明到底要求什么”的最有效方式之一。它还会向前连接到 §3.6(形式化方法与证明助手),那里会深入处理这些工具。

Xena Project(Kevin Buzzard,Imperial College)正在用 Lean 形式化本科数学,并在线提供相关成果。浏览那些你刚刚用非形式方式证明过的定理的形式化证明——看到非形式论证如何映射到形式化论证——能带来任何关于证明助手的教材描述都无法复制的启发。

资源 平台 类型
3Blue1Brown(选定主题,免费) YouTube 实践
Lean / Mathematics in Lean(免费) leanprover-community.github.io 实践
Xena Project / Mathlib(免费) leanprover-community.github.io/mathlib4 实践

陷阱

陷阱 为什么会误导 更好的回应
把证明当成计算 有微积分或编程背景的学习者,容易期待一种能产生答案的程序。证明是另一种活动——构造论证——它产生的是判定(真或假,并带有理由),而不是一个数值。证明题的表面形式看起来像数学作业,但认知要求是另一类;如果学习者没有完成这种转变,就会写出只有证明外形、没有证明实质的东西。 早早识别:现在练习的是一种新的智力活动,而不是熟悉模式中的新材料。寻找明确要求论证的问题;阅读完成的证明时,问“这是什么类型的论证?”,而不是“他们算出了什么?”
记忆证明,而不是学习技术 背出 √2 是无理数的证明,展示的是记忆,而不是数学理解。这个证明的重点是技术——反证法,使用奇偶性——它应该能被用于新问题。如果学习者在背下 √2 的证明后,仍然无法为 √3 构造一个类似证明,就说明他没有学会技术。 读完任何证明后,合上文本,问:它使用了什么一般方法?然后先尝试用同一种方法解决一个相近问题,再去读下一个证明。学到的是技术,不是某个具体结果。
低估归纳法 归纳法的机械步骤——基例、归纳假设、归纳步骤——一小时就能讲完,也听起来很简单。真正困难的是选择正确的归纳假设,而这往往不是最显然的那个;当显然假设太弱时,需要加强假设。这个现象反直觉,并且持续让学习者感到意外。 在归纳法上花的时间要显著多于课程安排。专门练习“加强”这个动作:当一个归纳证明失败时,问是否有一个更强命题反而更容易证明。寻找可证明的加强命题,是这门学科中最有生产力的练习之一。
抗拒形式化 程序员和工程师常常把数学形式化感受为咬文嚼字:“是的,但实践中大家都知道这是什么意思。”在离散数学中,形式化所消除的那些歧义,恰恰就是代码中产生 bug 的歧义。构造精确定义的纪律,与构造正确接口的纪律是同一种纪律。 在学习阶段,把每一个定义都当作精密仪器,并检查它在边界情况下如何表现。以后在合适语境中,可以重新启用对形式化的适度怀疑;但在学习这门学科时,这种本能不应该主导。
用 AI 辅助完成证明练习 AI 工具可以为多数标准离散数学练习生成看起来过得去的证明。学习者如果用它来逃避工作,就放弃了核心收益:构造证明的认知挣扎本身,正是建立证明能力的过程。练习不能和它产生的学习分离。 在没有 AI 辅助的情况下完成证明练习,每道题都先独立尝试。真正尝试之后,AI 可以用于检查推理或解释某个方法为什么失败——但它应当是尝试之后的导师,而不是尝试之前的解答生成器。
归纳法不扎实就继续往后走 归纳法支撑这门学科中几乎所有后续证明技术,归纳能力上的缺口会不断累积。如果学习者在归纳法仍不稳定的情况下进入组合数学和图论,后面的论证会越来越难跟上,因为后续论证会把归纳推理当作背景,而不再重新解释它。 回到归纳法,直到它感觉自然。正确测试是:面对一个关于自然数或递归结构的陌生命题,能否从零写出一个证明草图?如果不能,前面的材料还需要更多时间。