易经与算法:用机器学习分析卦象变化——当代码遇见六十四卦的跨界实验
一、当八卦遇上二进制:古老符号系统的数学内核
易经六十四卦,每卦由六爻组成,每爻取阴(0)或阳(1)两种状态。从信息论角度看,一卦恰好是一个 6 位二进制数,六十四卦对应 0 到 63 的完整整数空间。莱布尼茨在 1703 年收到传教士白晋寄来的伏羲六十四卦方圆图时,惊叹于其中蕴含的二进制思想——这比他发表二进制论文早了数年。
但易经的价值远不止二进制编码。卦与卦之间通过"变爻"产生关联:一卦的某爻由阴变阳或由阳变阴,就得到了另一卦,这称为"之卦"。六十四卦通过变爻关系构成了一张复杂的网络图,每个节点有 6 条出边(6 个爻各变一次),总共 384 条有向边。这张网络中蕴含的拓扑结构,是否可以用图神经网络来学习?卦象之间的转换规律,是否可以用马尔可夫链来建模?
代码是人与机器的对话,玄学是人与宇宙的对话。当两者相遇,不是迷信对科学的入侵,而是古老智慧与现代算法的共振。如同卦象的阴阳互变,代码中的 0 和 1 也在不断翻转,构建出无穷的可能。
二、卦象网络与变爻图:六十四卦的拓扑结构
六十四卦可以通过变爻操作构成一个 6-正则图(每个节点度数为 6)。但并非所有变爻都是等价的——一爻变、二爻变、三爻变对应不同的"距离",在传统解读中意义迥异。
graph TB subgraph 卦象编码 G1[乾卦 111111] -->|初爻变| G2[姤卦 011111] G1 -->|二爻变| G3[同人 101111] G1 -->|上爻变| G4[夬卦 111110] end subgraph 变爻距离 D1[一爻变: 汉明距离=1<br/>对应关系最密切] D2[二爻变: 汉明距离=2<br/>对应关系次之] D3[三爻变: 汉明距离=3<br/>互为错卦/综卦] end G2 -.-> D1 G3 -.-> D1 G4 -.-> D1 subgraph 图结构特征 F1[6-正则图: 每卦6条边] F2[汉明距离: 变爻数的度量] F3[对称性: 错卦/综卦/互卦] F4[连通性: 任意两卦可达] end从图论角度,六十四卦网络具有以下性质:强连通性(任意两卦之间都存在路径)、6-正则性(每个节点的度数恒为 6)、高度对称性(错卦、综卦、互卦三种对称操作)。这些结构特征使得卦象网络成为一个理想的图学习实验对象——规模适中、结构清晰、语义丰富。
变爻距离可以用汉明距离(Hamming Distance)精确度量。两卦之间的汉明距离等于不同爻位的数量,范围从 0(同一卦)到 6(完全相反的错卦)。在传统解读中,汉明距离越小的卦,其含义关联越紧密,这与图网络中"近邻相似"的假设完全吻合。
三、卦象网络分析与机器学习建模
以下代码实现了卦象编码、变爻网络构建、图特征分析和卦象转换预测:
import numpy as np import json import logging from typing import Dict, List, Optional, Tuple from dataclasses import dataclass, field from collections import defaultdict logger = logging.getLogger(__name__) # 六十四卦基础数据(卦名 + 二进制编码) HEXAGRAM_DATA = { 0: {"name": "坤", "nature": "地", "binary": "000000"}, 1: {"name": "剥", "nature": "山地", "binary": "000001"}, 2: {"name": "比", "nature": "水地", "binary": "000010"}, 3: {"name": "观", "nature": "风地", "binary": "000011"}, 7: {"name": "谦", "nature": "地山", "binary": "000111"}, 15: {"name": "艮", "nature": "山", "binary": "001111"}, 31: {"name": "颐", "nature": "山雷", "binary": "011111"}, 32: {"name": "屯", "nature": "水雷", "binary": "010001"}, 63: {"name": "乾", "nature": "天", "binary": "111111"}, # ... 完整数据包含全部 64 卦 } @dataclass class Hexagram: """卦象数据结构""" index: int # 0-63 的编号 name: str # 卦名 lines: List[int] # 6 爻,0=阴,1=阳 nature: str = "" # 卦象属性 @classmethod def from_binary(cls, index: int, name: str, binary: str) -> "Hexagram": lines = [int(b) for b in binary] return cls(index=index, name=name, lines=lines) def to_binary(self) -> str: return "".join(str(l) for l in self.lines) def hamming_distance(self, other: "Hexagram") -> int: """计算两卦之间的汉明距离""" return sum(a != b for a, b in zip(self.lines, other.lines)) def flip_line(self, position: int) -> "Hexagram": """翻转指定爻位,得到变卦""" if not 0 <= position < 6: raise ValueError(f"爻位必须在 0-5 之间,收到 {position}") new_lines = self.lines.copy() new_lines[position] = 1 - new_lines[position] new_index = int("".join(str(l) for l in new_lines), 2) return Hexagram( index=new_index, name=HEXAGRAM_DATA.get(new_index, {}).get("name", f"卦{new_index}"), lines=new_lines, ) def cuo_gua(self) -> "Hexagram": """错卦:所有爻取反""" new_lines = [1 - l for l in self.lines] new_index = int("".join(str(l) for l in new_lines), 2) return Hexagram( index=new_index, name=HEXAGRAM_DATA.get(new_index, {}).get("name", f"卦{new_index}"), lines=new_lines, ) def zong_gua(self) -> "Hexagram": """综卦:爻序颠倒""" new_lines = self.lines[::-1] new_index = int("".join(str(l) for l in new_lines), 2) return Hexagram( index=new_index, name=HEXAGRAM_DATA.get(new_index, {}).get("name", f"卦{new_index}"), lines=new_lines, ) class HexagramNetwork: """卦象变爻网络""" def __init__(self): self._hexagrams: Dict[int, Hexagram] = {} self._adjacency: Dict[int, List[Tuple[int, int]]] = defaultdict(list) self._build_network() def _build_network(self): """构建完整的六十四卦变爻网络""" # 初始化所有 64 卦 for i in range(64): data = HEXAGRAM_DATA.get(i, {"name": f"卦{i}", "binary": format(i, '06b')}) self._hexagrams[i] = Hexagram.from_binary( i, data["name"], format(i, '06b') ) # 构建邻接表:每卦的 6 个变爻 for i, hexagram in self._hexagrams.items(): for pos in range(6): changed = hexagram.flip_line(pos) self._adjacency[i].append((changed.index, pos)) def get_hexagram(self, index: int) -> Hexagram: """获取指定卦象""" if index not in self._hexagrams: raise ValueError(f"无效的卦序号: {index}") return self._hexagrams[index] def get_neighbors(self, index: int) -> List[Tuple[int, int]]: """获取一卦的所有变爻邻居""" return self._adjacency.get(index, []) def shortest_path(self, start: int, end: int) -> List[int]: """BFS 求两卦之间的最短变爻路径""" if start == end: return [start] visited = {start} queue = [(start, [start])] while queue: current, path = queue.pop(0) for neighbor, _ in self._adjacency[current]: if neighbor == end: return path + [neighbor] if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, path + [neighbor])) return [] # 理论上不会发生(图是连通的) def compute_graph_features(self) -> Dict[str, np.ndarray]: """计算图的统计特征""" n = 64 # 邻接矩阵 adj_matrix = np.zeros((n, n), dtype=np.float32) for i in range(n): for j, _ in self._adjacency[i]: adj_matrix[i][j] = 1.0 # 度矩阵(6-正则图,所有节点度数为 6) degree_matrix = np.diag(np.full(n, 6.0)) # 拉普拉斯矩阵 laplacian = degree_matrix - adj_matrix # 特征值分解 eigenvalues, eigenvectors = np.linalg.eigh(laplacian) # 聚类系数 clustering_coeffs = np.zeros(n) for i in range(n): neighbors = [j for j, _ in self._adjacency[i]] if len(neighbors) < 2: continue possible_edges = len(neighbors) * (len(neighbors) - 1) / 2 actual_edges = 0 neighbor_set = set(neighbors) for n1 in neighbors: for n2_of_n1, _ in self._adjacency[n1]: if n2_of_n1 in neighbor_set and n2_of_n1 > n1: actual_edges += 1 clustering_coeffs[i] = actual_edges / possible_edges # 汉明距离矩阵 hamming_matrix = np.zeros((n, n), dtype=np.int32) for i in range(n): for j in range(i + 1, n): dist = self._hexagrams[i].hamming_distance( self._hexagrams[j] ) hamming_matrix[i][j] = dist hamming_matrix[j][i] = dist return { "adjacency": adj_matrix, "laplacian": laplacian, "eigenvalues": eigenvalues, "eigenvectors": eigenvectors, "clustering_coefficients": clustering_coeffs, "hamming_distances": hamming_matrix, } class HexagramTransitionModel: """卦象转换预测模型:基于马尔可夫链""" def __init__(self, network: HexagramNetwork): self._network = network self._transition_counts = np.zeros((64, 64), dtype=np.float64) self._transition_matrix: Optional[np.ndarray] = None def add_observation(self, from_index: int, to_index: int) -> None: """记录一次卦象转换观察""" if not (0 <= from_index < 64 and 0 <= to_index < 64): raise ValueError("卦序号必须在 0-63 之间") self._transition_counts[from_index][to_index] += 1.0 def fit(self) -> np.ndarray: """拟合转移概率矩阵""" row_sums = self._transition_counts.sum(axis=1, keepdims=True) # 避免除零:无观察数据的行使用均匀分布 row_sums = np.where(row_sums == 0, 1.0, row_sums) self._transition_matrix = self._transition_counts / row_sums return self._transition_matrix def predict_next( self, current_index: int, top_k: int = 3 ) -> List[Tuple[int, float]]: """预测下一卦的概率分布""" if self._transition_matrix is None: raise RuntimeError("模型未拟合,请先调用 fit()") if not 0 <= current_index < 64: raise ValueError(f"无效的卦序号: {current_index}") probs = self._transition_matrix[current_index] top_indices = np.argsort(probs)[::-1][:top_k] return [(int(idx), float(probs[idx])) for idx in top_indices if probs[idx] > 0] def steady_state(self) -> np.ndarray: """计算稳态分布""" if self._transition_matrix is None: raise RuntimeError("模型未拟合") # 求解 π = πP,即左特征向量 eigenvalues, eigenvectors = np.linalg.eig( self._transition_matrix.T ) # 找到特征值为 1 的特征向量 idx = np.argmin(np.abs(eigenvalues - 1.0)) steady = np.real(eigenvectors[:, idx]) steady = steady / steady.sum() return steady def save(self, path: str) -> None: """持久化转移矩阵""" if self._transition_matrix is None: raise RuntimeError("模型未拟合") np.save(path, self._transition_matrix) def load(self, path: str) -> None: """加载转移矩阵""" self._transition_matrix = np.load(path) # ===== 分析示例 ===== def analyze_hexagram_network(): """分析卦象网络的拓扑特征""" network = HexagramNetwork() features = network.compute_graph_features() # 拉普拉斯特征值分析 eigenvalues = features["eigenvalues"] # 第二小特征值(代数连通度)反映图的连通性 algebraic_connectivity = eigenvalues[1] print(f"代数连通度: {algebraic_connectivity:.4f}") # 聚类系数分析 clustering = features["clustering_coefficients"] print(f"平均聚类系数: {clustering.mean():.4f}") print(f"聚类系数范围: [{clustering.min():.4f}, {clustering.max():.4f}]") # 汉明距离分布 hamming = features["hamming_distances"] for d in range(1, 7): count = np.sum(hamming == d) // 2 # 对称矩阵 print(f"汉明距离={d} 的卦对数: {count}") # 最短路径分析 path = network.shortest_path(0, 63) # 坤卦到乾卦 print(f"坤卦→乾卦最短路径长度: {len(path) - 1}") print(f"路径: {' → '.join(network.get_hexagram(p).name for p in path)}") return features def build_transition_model(observation_data: List[Tuple[int, int]]) -> HexagramTransitionModel: """从观察数据构建转移模型""" network = HexagramNetwork() model = HexagramTransitionModel(network) for from_idx, to_idx in observation_data: model.add_observation(from_idx, to_idx) model.fit() return model关键发现:六十四卦网络的代数连通度较高,说明图的结构紧凑、信息传播迅速;聚类系数在 6-正则图中呈现特定分布,反映了卦象之间的"家族"关系;汉明距离为 3 的卦对数量最多(C(6,3)=20 对每卦),对应传统中的"错卦"关系。
四、跨界分析的边界:算法能解读卦象吗
相关不等于因果:马尔可夫链可以建模卦象转换的统计规律,但统计相关性不等于因果性。卦象 A 和卦象 B 频繁共现,可能只是因为它们在编码空间中汉明距离小,而非存在深层语义关联。
语义鸿沟:算法处理的是二进制编码和图结构,而卦象的解读依赖数千年的文化积淀和语境理解。"乾"卦的 111111 编码与"天行健"的哲学含义之间,存在巨大的语义鸿沟。图神经网络可以学习结构特征,但无法自动获取文化语义。
样本量瓶颈:六十四卦只有 64 个节点,对于深度学习模型来说数据量严重不足。任何基于神经网络的卦象分析都面临过拟合风险,模型可能只是记住了训练数据而非发现了规律。
价值定位:这类跨界实验的真正价值不在于"用算法算命",而在于:用现代数学语言重新表达古老符号系统的结构特征,验证传统解读中隐含的拓扑关系,为文化研究提供量化分析工具。
禁用场景:将卦象预测模型用于实际决策(投资、医疗等)是不负责任的;将统计相关性解读为因果规律是方法论错误;在严肃的文化研究中,纯算法分析不能替代人文解读。
五、总结
易经六十四卦的二进制编码和变爻关系构成了一个 6-正则图,其拓扑结构可以通过图论和机器学习方法进行量化分析。汉明距离度量了卦象之间的编码差异,拉普拉斯特征值揭示了网络的连通性特征,马尔可夫链可以建模卦象转换的统计规律。这类跨界分析的价值在于用数学语言描述传统符号系统的结构特征,而非替代文化解读。算法与玄学的对话,本质上是两种思维方式的碰撞——算法追求确定性,玄学拥抱不确定性,两者在探索世界规律的道路上殊途同归。