本文详解如何用 NumPy 正确实现超立方体图的递归邻接矩阵(如 $ Q_n $),重点解决因误用 np.array 拼接子矩阵导致的广播与形状不兼容错误,并推荐使用 np.block 进行语义清晰、维度安全的块矩阵构造。
本文详解如何用 numpy 正确实现超立方体图的递归邻接矩阵(如 $ q_n $),重点解决因误用 `np.array` 拼接子矩阵导致的广播与形状不兼容错误,并推荐使用 `np.block` 进行语义清晰、维度安全的块矩阵构造。
在图论与量子计算中,超立方体图($ n $-cube)的邻接矩阵 $ Q_n $ 常通过如下递归方式定义:
$$ Q_1 = \begin{bmatrix} 0 & 1 \ 1 & 0 \end{bmatrix}, \quad Qn = \begin{bmatrix} Q{n-1} & I{2^{n-1}} \ I{2^{n-1}} & Q_{n-1} \end{bmatrix}, \quad n \geq 2 $$
该结构天然适合分块构造,但直接用 np.array([[A, B], [C, D]]) 会导致失败——因为 np.array 试图将嵌套列表转为同质数值数组,而 Q_{n-1} 和 I 是二维 ndarray,其类型与尺寸无法被自动“拉平”为统一 dtype 的高维数组,从而触发 ValueError: setting an array element with a sequence。
✅ 正确解法是使用 np.block:它专为符号化分块矩阵拼接设计,能自动推断并校验子矩阵的维度兼容性,按块布局生成最终二维数组。
以下是完整、可运行的实现:
import numpy as np
from numpy import linalg as LA
Q1 = np.array([[0, 1],
[1, 0]])
def Qn(n):
if n <= 1:
return Q1
else:
Qnm1 = Qn(n - 1) # 递归获取 Q_{n-1}
I = np.eye(2**(n - 1)) # 2^{n-1} 阶单位阵(更简洁准确,替代 np.exp2 + int 转换)
return np.block([[Qnm1, I],
[I, Qnm1]]) # ✅ 语义明确、维度安全的块拼接
# 示例:构造 Q₃(8×8 矩阵)
Q3 = Qn(3)
print("Q3 shape:", Q3.shape) # 输出: (8, 8)
# 验证特征值(理论已知:Qₙ 有特征值 n−2k,重数 C(n,k),k=0,…,n)
eigvals, _ = LA.eig(Q3)
print("Eigenvalues (unsorted):", np.round(eigvals.real, 10))
# 输出示例: [ 3. -3. -1. -1. -1. 1. 1. 1.]⚠️ 关键注意事项:
- 勿用 np.array([...]) 拼接矩阵:它不是为块矩阵设计,会强制降维或报错;
- 优先使用 np.eye(k) 而非 np.identity(k):二者等价,但 eye 更常用且语义更清晰;
- 指数计算用 `2(n-1)更直观可靠**,比int(np.exp2(n-1))` 更少浮点误差风险;
- 递归深度限制:n ≥ 10 时矩阵达 $ 2^{10} \times 2^{10} = 1024^2 $,内存与计算开销显著上升,生产环境建议改用迭代或稀疏表示(如 scipy.sparse.bmat);
- 对称性保障:Qn(n) 恒为实对称矩阵,故 LA.eig 可替换为更高效的 LA.eigh(返回实特征值+正交特征向量)。
总结:np.block 是 NumPy 中处理分块矩阵构造的标准、健壮且可读性强的工具。掌握其用法不仅能解决本例中的递归超立方体矩阵问题,也为实现 Kronecker 积、分块对角阵、多层网络邻接矩阵等常见结构奠定坚实基础。










