简介:《量子计算和量子信息》是量子信息领域公认的经典教材,这份由GitHub用户goropikari整理发布的配套课后答案属非官方解题手册,旨在帮助读者逐题核对解题思路、检验对核心概念的理解,适合高校量子计算课程学生、自学者及备考研究者。资源打包为单个PDF文档,大小仅466KB,内容按原书章节顺序编排,覆盖第2章量子力学导论、第8章量子噪声与量子操作、第9章距离度量、第11章熵与信息等主要内容,每章均给出详细推导与解答。除习题解答外,手册特别附有勘误列表,针对原书方程编号(如2.150、9.49、9.73等)中的ρ应写为ρ′、迹距离计算公式系数、矩阵乘式等错误给出修正提示,提醒读者自行验证,有助于避免被原书印刷问题误导。目前已有1359人学习下载,配合原书使用可提升对量子态、量子操作、保真度及信息熵等难点的掌握,尤其适合需要自查解题过程或梳理证明细节的进阶学习者。
1. 《量子计算和量子信息》习题答案:为什么“对答案”比“抄答案”更需要方法论
翻开《量子计算和量子信息》这本教材的课后题,很多人第一反应是找答案,第二反应是发现自己找到的答案手稿跟书上对不上。这本书的题不是用来“做完”的,是用来“想清楚”的——线性代数题可以硬算,但涉及量子电路、纠缠判据和信息论的题,算完不等于算对。尤其尼尔森与赵千川这版的中译本,习题编号与英文原版一致,网上流传的答案手稿又常常只写结论不写过程,于是“对答案”变成了一场对约定、对相位、对位序的排查。这篇笔记就是讲这套排查方法:习题分哪几类、每类的答案应该长什么样、怎么用十几行代码验证一个答案是不是真的对。适合正在啃这本书、手里有半份答案却不敢信的读者。
2. 拆解课后习题的五种类型:先知道答案应该长什么样
2.1 线性代数与单量子比特:答案是一个矩阵或一个数,用特征值说话
这本书前几章的习题里,相当一部分是给一个算子,让你求本征值、本征态、迹或者验证恒等式。这类题的答案形式很明确:要么是一个矩阵,要么是一个数。核对这类答案最有效的办法不是重算一遍,而是抓住“量子力学里算子必须满足的性质”来检验。常见做法是先检查算符是否满足幺正性(U†U = I),再检查它作用在基态上得到的概率幅模平方是否恒为 1。
我一般会建议读者在推导时保留每一步的中间矩阵,尤其是转置共轭这一步——很多人算 U† 时把取共轭漏了,导致后面所有特征值全变成共轭复数。核对答案时,先看题里给的算符是 Hermitian 还是 unitary,这决定了答案应该满足什么约束。比如 Pauli 矩阵的特征值必须是 ±1,如果算出 0.5 或者复数特征值,不用看过程也知道前面的矩阵写错了。这类题没有太多捷径,但有一个非常管用的回归测试:把答案里的矩阵代回原式,看等号是否成立。
import numpy as np # 验证一个算符是否是幺正的:U†U 应等于单位阵 U = np.array([[0, 1], [1, 0]]) # 以 Pauli-X 为例 U_dagger = U.conj().T prod = U_dagger @ U print(np.allclose(prod, np.eye(2))) # 输出 True 说明满足幺正性np.allclose比较的是浮点近似,不是精确相等,量子计算里的大量验证都要靠它。这里的核心参数是两种:一个是rtol(相对误差),另一个是atol(绝对误差),默认值分别是 1e-05 和 1e-08。实际做习题验证时,如果遇到算符维度大、矩阵元素是根号 2 分之一这种数,建议把atol调到 1e-10,避免浮点误差把本来正确的答案误判成错误。这个习惯能从第 2 章一直用到第 5 章。
2.2 复合系统与纠缠:答案不是矩阵本身,是“部分迹”和“约化密度矩阵”
从第 2 章后半段开始,习题进入了多比特系统。这类题的答案经常是一个复合系统的密度矩阵,或者是一个子系统对应的约化密度矩阵。核对这类题最容易犯的错是搞混比特的顺序:题目里说“第一个比特处于状态 X,第二个比特处于状态 Y”,不同教材对“第一个比特”的定义可能正好相反。中译本的约定通常跟随英文原版,也就是第一个比特写在张量积的最左边,但在做计算时很多手稿会把顺序反过来,最后得到的矩阵差了一个转置。
这类答案的核对有一个特别实用的技巧:先检查整个复合系统的密度矩阵的迹是否为 1,再检查它是否半正定。两个条件都满足,才说明这个密度矩阵物理上合法;然后再对其中一个子系统求部分迹,看得到的约化密度矩阵是否符合题目描述的状态。约化密度矩阵如果出现负特征值,那答案一定错了,因为合法密度矩阵的特征值必须非负。
import numpy as np # 构造一个两比特纠缠态 |00> + |11> 的密度矩阵 psi = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2) rho = np.outer(psi.conj(), psi) # 对第二个比特求部分迹:把 rho 看成 2x2 分块矩阵 rho_A = rho[0:2, 0:2] + rho[2:4, 2:4] print(np.trace(rho_A)) # 应为 1 print(np.linalg.eigvalsh(rho_A)) # 两个特征值都应为 0.5这里的部分迹做法是教科书里的标准分块求和。注意np.outer的第一个参数取的是psi.conj(),因为外积的定义是 |ψ⟩⟨ψ|,左边取共轭。如果你把共轭放错位置,整个密度矩阵会多一个全局相位,虽然对单比特观测没影响,但部分迹的结果会变得很奇怪。核对答案时,先看迹,再看特征值,这两个指标能过滤掉绝大多数错误。
2.3 量子电路与逻辑门:答案是一组电路或一个么正矩阵,注意控制位顺序
从第 4 章开始,习题大量涉及电路分解和门操作。题目让你用基本门实现某个幺正变换,答案的形态可能是电路图,也可能是一个矩阵。核对电路类答案最可靠的方法是把它翻译成矩阵:从输入态 |0⟩ 开始,把每个门对应的矩阵按顺序作用上去,看最终输出矩阵是否等于题目要求。任何一个门放错位置,最终矩阵都会对不上。
这里有一个约定问题值得反复强调:CNOT 门的矩阵表示在不同材料里有两种写法。一种约定是 control 比特在前(书里的标准用法),另一种是 target 在前。两种写法本质是同一个门,但矩阵元位置不同。如果你用某个模拟器验证答案,必须确认模拟器的 qubit 顺序与题目一致。很多翻车现场不是答案错了,而是答案手稿用的约定和你的验证代码不一样。后面第 4 章会专门讲这个坑,这里先记住一个原则:验证之前,先统一约定。
import numpy as np # CNOT 的两种矩阵表示:control=0, target=1 CNOT_q0_q1 = np.array([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0] ]) # 验证是否幺正,并检查作用在 |10> 上是否得到 |11> input_state = np.array([0, 0, 1, 0], dtype=complex) # |10> output_state = CNOT_q0_q1 @ input_state print(np.allclose(output_state, np.array([0, 0, 0, 1], dtype=complex)))这个例子里,输入态 |10⟩ 对应的向量是[0, 0, 1, 0],因为按 q1q0 的位序约定,最高位是第一个比特。如果你按相反的位序写输入态,同样的矩阵作用上去结果会完全不对。做这种验证题时,我习惯把位序约定写在注释里,免得过两天回来自己都看不懂。矩阵维度和基向量顺序这两件事,占电路类习题错误的一半以上。
2.4 量子算法:答案不是一个数字,是一组概率分布,别只看高概率那一路
等到 Deutsch-Jozsa、Grover、Shor 这类算法题出现,答案的形式变成“测量结果的概率分布”或者“某种预言机的查询次数”。这类题不能只看最终哪个态被测出来了,还要看失败概率和复杂度边界。Grover 算法的题经常会问“迭代几次成功率最高”,答案里一定有一个概率表达式,核对时要特别留意题里给的是“一次查询的成功率”还是“k 次迭代后的成功率”。
量子算法题的答案核对,最高效的办法是用模拟器把电路跑一遍:构造对应的量子电路,把输入态设成题目给定的初始态,然后看输出态的概率分布。概率分布能和手稿对得上,且总概率归一,基本可以确认答案框架没问题。如果概率对不上,则优先查两个地方:一是门的顺序是否与算法步骤一致,二是多比特门是否被错误拆成了单比特门。
from qiskit import QuantumCircuit, Aer, execute # 构造一个两比特 Deutsch-Jozsa 电路的核验版本 qc = QuantumCircuit(2, 1) qc.h(0) qc.h(1) qc.cx(0, 1) # 当作常函数电路,最终第一位应为 0 qc.h(0) qc.measure(0, 0) backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shots=1000) counts = job.result().get_counts(qc) print(counts) # 应接近 {'0': 1000}注意这里shots=1000是模拟次数,模拟器不是精确计算而是按概率抽样,所以结果不会严格是 1000,会有几十次的统计波动。核对习题答案时,我建议把 shots 调到至少 10000,或者直接用statevector_simulator拿到精确概率。这个差异是新手最容易忽视的参数:用带抽样的模拟器验证算法题,等于用一个有误差的工具去查一个需要精确值的答案。熟练之后你会知道什么时候该用哪种模拟器。
2.5 信息论与信道容量:答案是数字和边界,用不等式去卡
最后一类习题来自量子信息论章节,涉及 Holevo 界、信道容量、保真度等。这类题的答案往往是一个数值范围或一个不等式的取等条件。核对它们的方法和前面完全不同:不能靠矩阵验证,得靠“物理直觉”和“边界条件”双重检查。比如 Holevo 界题,答案不能超过 log d(d 是 Hilbert 空间维度);信道容量题,答案不能超过对应经典信道的容量上界。如果某个答案超出了这些天然边界,它一定是错的,不管推导过程多么漂亮。
我常见的做法是先把题里所有出现的维度、概率、特征值列成一张小表,然后检查答案是否在每一个物理边界之内。这道工序看起来简单,但能拦截大量答案。比如保真度题,F 必须在 0 和 1 之间,如果得到 1.2,说明前面归一化出了问题;信道容量的答案不会大于 log2(d),因为这是 Holevo 界的直接推论。这些边界不是额外的知识,而是题目本身给出的信息,只是很多人忙着算过程,忘了在最后一步用边界条件做回归。
| 题型类别 | 答案形态 | 首要核对指标 | 常见错误来源 |
|---|---|---|---|
| 线性代数与单比特 | 矩阵 / 数值 | 幺正性、特征值范围 | 转置共轭漏算 |
| 复合系统与纠缠 | 密度矩阵 | 迹为 1、半正定 | 张量积顺序颠倒 |
| 量子电路与逻辑门 | 电路 / 矩阵 | 作用结果与基向量 | 控制位 / 目标位约定 |
| 量子算法 | 概率分布 | 概率归一、成功概率 | 门顺序、抽样误差 |
| 量子信息论 | 数值 / 不等式 | 物理边界 | 归一化、维度混淆 |
3. 用 Python 验证答案:从张量积到量子电路的三个可复现例子
3.1 张量积顺序:为什么答案矩阵跟你算的不一样
这一节要解决一个让很多人头疼的问题:同一个题目,自己推出来的矩阵和答案手稿里的矩阵长得完全不同。绝大多数情况下不是推错了,而是张量积的顺序不一致。设第一个比特的算符是 A,第二个比特的算符是 B,按教材约定复合算符是 A⊗B,但有些手稿和图谱会写成 B⊗A。这两种写法得到的矩阵互为转置加置换,物理上等价,但形式上对不上。
import numpy as np H = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) X = np.array([[0, 1], [1, 0]], dtype=complex) A_then_B = np.kron(H, X) # 第1比特 H,第2比特 X B_then_A = np.kron(X, H) # 第2比特 X,第1比特 H print(A_then_B) print(B_then_A) print(np.allclose(A_then_B, B_then_A)) # 几乎总是 Falsenp.kron的行为没有任何歧义:第一个参数是左边的子系统,第二个是右边的。你在验证时只需要确定“题目里的比特 1 到底是左边还是右边”。中译本书里的图表和符号顺序通常不明确标注这一点,所以建议用注释在代码里锁定自己的约定。一旦在代码里约定好,整份答案的核对就统一了;如果每道题都临时换顺序,验证代码本身的 bug 会掩盖习题答案的错误。
3.2 用状态向量模拟器核对电路答案:比手推矩阵快一个数量级
电路类习题的答案验证,手推矩阵在维度超过 4 之后就非常容易出错,尤其是含 3 个比特的电路,矩阵是 8×8,手工做矩阵乘法极易抄错。这时候用模拟器推状态向量就非常省事:把题目描述的门依次加到电路上,模拟器会替你完成所有矩阵乘法,你只需要看最终状态。
from qiskit import QuantumCircuit, Aer, execute qc = QuantumCircuit(3) qc.h(0) # 第 0 比特加 Hadamard qc.cx(0, 1) # CNOT:0 控制 1 qc.x(2) # 第 2 比特翻转 backend = Aer.get_backend('statevector_simulator') result = execute(qc, backend).result() sv = result.get_statevector() print(sv)这段代码的核心要点是statevector_simulator返回的是精确振幅,而不是抽样统计。它给出的每个复数元素的模平方,就是这个基态被测量到的概率。验证习题答案时,把模拟器的输出振幅和手稿里的答案做对比,只要每个元素的模平方相等(允许一个全局相位差),就说明电路实现对了。全局相位不影响任何可观测物理量,所以对比时不要苛求复数完全相等,而是比较概率幅度的模。
这个工作流最关键的是一个参数:模拟器的选择。qasm_simulator模拟的是测量过程,输出的是统计计数;statevector_simulator输出的是精确态矢量。核对理论答案永远用 statevector,只有需要模拟“跑很多次测出来的概率”时才用 qasm。命令本身几乎一样,只差一个后端名字,但很多人在这一步栽过跟头——用抽样结果去比精确答案,怎么看都对不上。
3.3 用幺正性验证门分解答案:一种不依赖电路形状的快速判断
有些题不要求画电路,只问“这个门能不能由这几个基本门构成”。答案是一个分解方案或一句“不能”。核对这类题,不需要逐门验证,直接检查最终组合是否等于目标门即可。更进一步,可以先检查目标门本身是否满足某些性质,比如 Fredkin 门是幺正的且是自逆的,如果题目给的分解方案最终结果不是幺正,那方案一定错了。
import numpy as np # 构造 Fredkin 门(CSWAP):交换|101>与|110>的振幅 F = np.eye(8) F[[5, 6], :] = F[[6, 5], :] F[:, [5, 6]] = F[:, [6, 5]] # 用法一:验证幺正性 method_1 = np.allclose(F.conj().T @ F, np.eye(8)) print(f"Fredkin gate unitary: {method_1}") # 用法二:验证自逆性 method_2 = np.allclose(F @ F, np.eye(8)) print(f"Fredkin gate self-inverse: {method_2}")这里用到了两次行交换和两次列交换。行索引 [5, 6] 和 [6, 5] 的互换,作用是把矩阵中对应 |101⟩ 和 |110⟩ 的两行交换;再做一次列交换,是把对应的两个列也交换。两次交换合在一起,才构成完整的 CSWAP。如果你只做行交换不做列交换,得到的矩阵虽然不是 Fredkin 门,但仍然是合法幺正矩阵,这时候用“是否幺正”这个判据就检不出错误,必须用“是否等于目标门”这个判据。所以验证门分解方案时,我通常同时检查两点:最终结果是不是合法的量子门,以及它和题目目标是否一致。前一个检查能拦截笔误,后一个检查能拦截方案错误,两个都过才算通过。
4. 核对课后答案的五个翻车现场:相位、顺序、约定与手稿质量
4.1 翻车现场一:全局相位不一致,误以为答案算错
现象:手稿答案的复振幅和书上答案差了一个负号或者差了 i 倍,自己重新算一遍还是差,于是怀疑推导有问题。原因:量子态在全局相位上等价,|ψ⟩ 和 e^{iθ}|ψ⟩ 代表同一个物理状态,但答案手稿里可能把相位因子提到最前面,也可能隐含在某个振幅里没展开。解决:对比答案时只比较每个基态的模平方,或者把两个态矢量的内积模取绝对值后再和 1 比较。这个规则对线性代数题和电路题都适用,我列在下面:
import numpy as np # 两个“不同”的态矢量只差一个全局相位 psi = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2) psi_with_phase = np.exp(1j * np.pi / 3) * psi # 正确对比方式:看内积的模 overlap = np.abs(np.vdot(psi_with_phase, psi)) print(overlap) # 应为 1.0 print(np.allclose(psi_with_phase, psi)) # 应为 False,忽略相位则不应这样对比np.vdot会自动取第一个参数的共轭,这是比较两个态矢量时最不容易出错的函数。如果你用np.dot而不是np.vdot,结果会差一个全局共轭,模虽然不受影响,但后续若拿来做部分迹计算就会出错。这个细节在验证密度矩阵答案时尤其致命,共轭位置不对会把整个矩阵的转置方向搞反。养成只用np.vdot的习惯,能省掉后面一大半的排错时间。
4.2 翻车现场二:张量积顺序混乱,两个答案都对但就是长得不像
现象:自己算的 |01⟩ 态对应的向量是[0, 1, 0, 0],答案手稿写的是[0, 0, 1, 0],于是以为某个门算错了。原因:两个人都没算错,只是一个人把第一个比特放在高位,另一个人放在低位。解决:在验证脚本开头用一行注释写明你的位序约定,并且在所有代码里贯彻同一约定;遇到多位向量下标的题,先手动验证一个最基础的态(比如 |00⟩ 对应向量[1, 0, 0, 0]),确认你的索引序没问题再继续。
这个问题在图文答案里几乎无法避免,因为文字不会自动标注位序。我见过最典型的例子是:一道题要求将两个量子比特分别初始化为 |+⟩ 和 |0⟩,手稿把 |+⟩ 写在第一位,代码里却把 |0⟩ 放进高位索引,结果输出态和答案长得全不一样。验证时先看输入态的构造,再看出门,比直接对比最终矩阵更容易定位是哪一侧出了问题。
4.3 翻车现场三:CNOT 控制位和目标位写反,矩阵差一个部分转置
现象:用模拟器验证一道 CNOT 电路题,结果总是不对,而且错的模式很规则——凡是输入态里控制位为 1 的情况,输出都和预期相反。原因:某一些常见量子编程框架里cx(0, 1)的定义是第 0 比特控制第 1 比特,但手稿里那个答案可能默认是第 1 比特控制第 0 比特。解决:不要凭感觉猜,先做一个基线实验:用 CNOT 作用在 |10⟩ 上,看输出是 |11⟩ 还是 |10⟩,以此判断当前环境下的控制约定,再对比习题答案。
from qiskit import QuantumCircuit, Aer, execute # 基线测试:|10> 经过 CNOT(0,1) 后应得到 |11> qc = QuantumCircuit(2) qc.x(0) # 第 0 比特置 1 qc.cx(0, 1) # 第 0 比特控制第 1 比特 backend = Aer.get_backend('statevector_simulator') sv = execute(qc, backend).result().get_statevector() print(sv)这个基线测试只需要两行,却能解决一大半“为啥我的 CNOT 答案不对”的困惑。写代码验证别人手稿里的电路时,我永远先跑一次基线实验,再做正式验证。这个习惯我用到现在,几乎所有诡异的对不上都是约定问题,不是数学问题。做完基线后再看答案,如果 CNOT 的控制方向确实不同,把代码里的参数顺序换一下就行,不要直接改答案。
4.4 翻车现场四:答案手稿本身的错——流传版本质量参差
现象:用三种方式验证同一道题,三个结果相互矛盾,最后发现是答案手稿的公式抄错了。原因:这本书的流传答案手稿来自多个来源,质量参差不齐,有的漏了共轭,有的是把上一题的图复制过来忘了改。解决:对任何手稿答案都持“怀疑但可参考”的态度,用性质检验优先于数值对比。也就是说,先检查答案本身是否满足幺正性、迹是否为 1、特征值是否在物理范围内,然后再拿它和你的推导对比。
这条经验我自己的血泪教训是:有一道 3 比特电路题,手稿答案的矩阵元素非零位置和我的推导完全一致,只有一个元素的符号相反,我当时以为是自己算错了,改了自己的推导去迁就手稿,后来用基态逐一验证,才发现手稿的符号是错的。所以要记住,答案手稿不是标准答案,它只是另一个人的推导记录。凡是涉及最终输出的判断题,一律以物理性质为准。
4.5 翻车现场五:模拟器 shots 太次,概率验证怎么都对不齐
现象:用 qasm_simulator 跑量子算法题,答案手稿说成功概率 0.8,自己跑出来 0.75、0.82、0.79 来回跳,就是稳定不到 0.8。原因:抽样模拟本身就是随机过程,shots 太小,统计涨落会让结果偏离理论值好几个百分点。解决:验证理论概率时索性不用 qasm_simulator,改用 statevector_simulator 直接取精确振幅;非要看抽样效果,把 shots 调到 100000 以上,误差能降到个位数百分比以内。
from qiskit import QuantumCircuit, Aer, execute qc = QuantumCircuit(1) qc.h(0) qc.measure_all() backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shots=1000) counts = job.result().get_counts(qc) p0 = counts.get('0', 0) / 1000 print(f"P(0)={p0:.3f}, theoretical=0.5")shoot=1000 时,P(0) 通常落在 0.48~0.52 之间,看着“差不多”;但习题如果有精确定量要求,这个误差就不可接受。理论验证和模拟验证是两件不同的事:前者要精确,用 statevector;后者要近似真实实验,用 qasm 并把 shots 调大。把这两件事分开做,能避免“明明概率看着对,手稿的公式却怎么都对不上”的困惑。记住,模拟器的重要参数不是 API 表面上的 shots,而是你选择它时要验证的目标——精确值还是统计值。
5. 把“查答案”升级成“跑答案”:自建一套习题验证工作流
到这个阶段,你已经知道习题答案的核对不是“看一遍结论”,而是“跑一遍性质”。真正的进阶用法是给自己搭一个最小可复用的验证工作流:一个 Python 文件里写清楚位序约定、基线测试、性质检验三个函数,以后每做一道题就往里加一段测试代码。这套工作流花半小时搭好,能用完这一整本书,之后看别的量子计算材料也一样适用。
我自己的习惯是每个习题文件都保持三块结构:第一块定义所有用到的矩阵和常量,第二块写基线测试(比如 CNOT 的控制方向),第三块写针对这道题的性质断言。这样但凡遇到答案与预期不符,先跑基线,再跑性质,最后才动手查推导,定位速度会快很多。这里有一个关键参数建议:所有用到浮点比较的地方统一使用np.allclose并显式设置atol=1e-10,不要依赖默认值,因为量子力学里的很多答案包含 1/sqrt(2) 这类无理数,浮点表示误差在矩阵维度大时会累积得比默认容差更快。
进阶技巧里最值得一试的是把“验证答案”换成“探索题目”:把教材习题里的参数变成变量,例如 Grover 迭代次数从 1 改到 2、3,看成功率曲线怎么变化。这样一道题的价值就翻倍了——你不再只是确认“是不是 0.8”,而是理解了“为什么这个次数是最优的”。这个方法对算法章节特别有用,因为算法题的答案往往是一个具体数值,而数值背后的曲线才是真正的知识点。我当年就是这么啃完量子搜索那一章的:先跑通答案,再把迭代次数参数化,跑出一张成功率与迭代次数的关系表,章节学完的同时,对算法的理解比只看答案的同伴深了一大截。
另一个实用习惯是把每道题的验证脚本按章节存档,命名里带题号和自己的位序约定。比如Ex4_15_q0ctrl_q1tgt.py。这个小习惯听起来不值一提,但当你三个月后回过头来复习时,文件名本身就是最好的笔记——它提醒你这道题用的是什么约定、验证时踩过哪个坑。这套工作流最大的价值不是帮你省时,而是让你不再依赖“某个人的手稿”来判断对错,你自己就是最终裁判。希望帮到你。
本文还有配套的精品资源,点击获取