问真八字 · 算法复刻

问真八字 · 排盘算法复刻

📊 完整进度报告:复刻总报告 —— 各模块复刻率、算法原理、未复刻项与原因

问真八字(App / 网页版)排盘能力的本地复刻实现。纯 Python 标准库,无第三方依赖。

复刻对象:https://bzapi4.iwzbz.com/getbasebz8.php 校验口径:7651 条去重真实样本(另含 2401 条非法时间样本,已剔除)


一、这个目录是什么

bazi/
├── engine.py                 # 主入口:paipan() 生成完整命盘
├── core/
│   ├── astro.py              # 天文历法:VSOP87、节气、儒略日、ΔT
│   ├── ganzhi.py             # 干支、四柱、六十甲子
│   ├── elements.py           # 五行、藏干、十神、纳音、十二长生、空亡
│   ├── extras.py             # 起运、交运、大运、小运、胎元、命宫、身宫
│   ├── lunar.py              # 农历(真朔望月天文算法,非查表)
│   ├── rysl.py               # 日课用事(人元司令)
│   ├── shensha_lib.py        # 神煞分发(44 种查表型 + 条件型)
│   ├── shensha_algebra.py    # 神煞公式层(代数规则 + 有限组合表)
│   └── tl_table.py           # 天罗地网 42×2 模式表
├── data/
│   └── jieqi_delta.json      # 节气时刻校准表(1931 个节气)
├── examples/
│   ├── demo.py               # 最小可运行示例(含 API 对拍)
│   ├── verify.py             # 批量对拍 samples.jsonl
│   └── rysl_2026_sample.json # 日课用事自测数据(2026 全年 366 天)

依赖链(单向,无环):

astro ← elements ← ganzhi ← extras ← engine
                              ↑
              lunar / rysl ────┤
        shensha_lib ← shensha_algebra ← tl_table

运行时只需要 data/jieqi_delta.json。其余文件均为自包含代码。


二、快速使用

cd bazi
python3 examples/demo.py                      # 单例,含 API 对拍
python3 examples/verify.py samples.jsonl      # 批量对拍(可选 --limit N)
python3 -m core.rysl                          # 日课用事自测(366 天)
from engine import paipan
from datetime import datetime

chart = paipan(
    datetime(1990, 5, 15, 10, 30),  # 出生时间(北京时间)
    sex=1,                          # 1=男  2=女
    late_zi_advance=True,           # True=早子时(23:00换日)  False=晚子时
    today='2026-11-11',             # 影响 tdbz(胎对八字)
)

chart['pillar_list']   # ['庚午', '辛巳', '庚辰', '辛巳']
chart['lunar']         # '1990年四月廿一 巳时'
chart['shensha'][2]    # 日柱神煞列表
chart['qiyun']['age']  # 起运年数

输出字段一览:

键 内容
pillar_list / pillars 四柱干支
ten_gods 十神(日柱为「日主」)
canggan / canggan_gods 藏干 / 藏干十神
nayin / xingyun / zizuo 纳音 / 星运 / 自坐
kongwang 空亡
shensha 四柱神煞(59 种)
taiyuan / taixi / minggong / shengong 胎元 / 胎息 / 命宫 / 身宫(含各自纳音)
qiyun 起运六位、交运日期与文案
dayun / xiaoyun / dyshensha 大运 / 小运 / 大运神煞
tdbz 胎对八字(today 当天子时四柱)
lunar / lunar_today 农历生日 / 今日农历
wuxing_count / wuxing_score 五行个数 / 加权分

三、核心算法

3.1 四柱

柱 规则
年柱 以立春为界(不是元旦)
月柱 以节为界(立春、惊蛰、清明…共 12 个节)
日柱 连续六十甲子;23:00 后是否进位由 late_zi_advance 决定
时柱 日干推时干,两小时一个地支

节气时刻由 astro.py 用 VSOP87 截断级数计算太阳视黄经,再用 data/jieqi_delta.json 校准到问真口径。

3.2 神煞:公式优先,枚举兜底

59 种神煞中,绝大多数是可以写成代数式的:

禄神 = 五行长生 + 3
羊刃 = 禄 ± 1(阳干 +1,阴干 −1)
飞刃 = 羊刃 + 6
文昌 = 禄 + 3      国印 = 禄 + 8      金舆 = 禄 + 2
将星 = (−3 × (支 mod 4)) mod 12
驿马 = 将星 + 2    桃花 = 将星 − 3    亡神 = 将星 − 1
劫煞 = 将星 + 5    灾煞 = 将星 + 6    华盖 = 将星 + 4
红鸾 = 3 − 支      天喜 = 红鸾 + 6
披麻 = 支 − 3      丧门 = 支 + 2      吊客 = 支 − 2
勾绞 = 支 + 3      天医 = 月支 − 1
孤辰 = 2 + 3 × ((支+1)//3 mod 4)      寡宿 = 孤辰 − 4

天乙:甲戊庚→丑未  乙己→子申  丙丁→亥酉  辛→午寅  壬癸→卯巳
金神 = {乙丑, 己巳, 癸酉}

只有在数学上无法归约时,才退回有限组合表——且必须证明表内零歧义:

神煞 表结构 依据
学堂 / 正学堂 年纳音 → 干支(5 键) 25 组合零混合
词馆 / 正词馆 年纳音 → 干支(5 键) 25 组合零混合
天罗 / 地网 日支 + 年纳音(2 条) 999/999、2056/2056
天罗地网 辰巳/戌亥 各 42 种模式 42×2 全穷尽
童子煞 年柱 × 时支 5 个年柱下 96–97%

3.3 天罗地网:三个名字,两套规则

这是整个复刻里最难的一项,前 10 轮全部失败。

根因:API 实际返回三个不同神煞名,此前只认合并名。

天罗     日支 ∈ {戌,亥} 且 年纳音 = 火        → 只标日柱   999/999
地网     日支 ∈ {辰,巳} 且 年纳音 ∈ {土,水}    → 只标日柱   2056/2056
天罗地网  辰巳 / 戌亥「异字配对」              → 按模式表标多柱

「天罗地网」的规则是两组配对各自独立判定后取并集:

把四柱地支映射成 4 字符模式串
  本组两字(辰/巳 或 戌/亥)原样保留,其他记 'x'
  例:年辰、月巳、日辰、时午 → '辰巳辰x'
查表得到应标柱集合,两组结果取并集

42+42 种模式全部穷尽,5 折交叉验证 99.96%,全量 100%。

3.4 起运

问真用的是名义时间体系——1 天折 4 个月,1 年 = 360 天:

名义秒 = round(距节真实秒 × 120) − bias
bias   = 30 if (年干为阳 == 顺行) else −30
        (数学上等价于:男 −30,女 +30)

年 = 名义秒 // 31104000      (360×86400)
月 = 余 // 2592000           (30×86400)
日 = 余 // 86400
时 = 余 // 3600
分 = 余 // 60
秒 = 余 % 60

交运:交运日 = 出生日 + 起运年/月/日;首干取交运日年柱天干(立春为界),次干取合化干(甲↔己、乙↔庚、丙↔辛、丁↔壬、戊↔癸);节气取交运日前最近的「节」,输出「逢X、Y年 <节气>后N天 交大运」。

3.5 农历

不是查表,是真天文计算:

环节 方法
朔日 Meeus《Astronomical Algorithms》第 49 章完整公式(主项 + 14 项行星摄动)
中气 黄经 30° 整倍,按北京日期判定归属(非精确时刻)
闰月 含冬至之月恒为十一月;十三月区间中首个无中气之月为闰月
日序 朔在当日即为初一(不问时刻)
换日 23:00 起算次日(与问真早子时口径一致)

3.6 日课用事(人元司令)

每月支按藏干顺序分段当值,实测「节气日 offset 上界」表:

寅:戊≤8   丙≤15  甲≤29       卯:甲≤11  乙≤30
辰:乙≤10  癸≤13  戊≤30       巳:戊≤6   庚≤15  丙≤31
午:丙≤11  己≤20  丁≤31       未:丁≤10  乙≤13  己≤31
申:戊己≤11 壬≤14 庚≤31       酉:庚≤11  辛≤31
戌:辛≤10  丁≤13  戊≤30       亥:戊≤8   甲≤13  壬≤29
子:壬≤11  癸≤29              丑:癸≤10  辛≤13  己≤30

两个不在任何命理书上的细节:


四、复刻精度

项 精度 样本
四柱 / 十神 / 藏干 / 藏干十神 100% 7651
纳音 / 星运 / 自坐 / 空亡 100% 7651
胎元 / 胎息 / 命宫 / 身宫(含纳音) 100% 7651
大运 / 小运 / tdbz 100% 7651
神煞(59 种,整柱完全一致) 100%(30604/30604) 7651
szshensha 100% 7651
农历 bz[8] 99.97%(7649/7651) 7651
农历 tdbz[8] 100% 7651
日课用事 100%(366 天逐日 + 跨 5 年抽样) 486
dyshensha 内容 99.6%,顺序不同 2428
起运六位全对 41.11% 7651

已知边界

1. 起运秒位 —— δ 表精度,不是公式问题

误差分布(API 名义秒 − 本地名义秒):

统计量 值 换算
中位 +0.0083s 恰好 1 名义秒
P25 / P75 0 / 2 名义秒 —
P95 +0.5000s 60 名义秒
标准差 1.53s —

三条证据排除舍入假设:

  1. 按 x 的小数部分分桶,各桶均值在 −11.76 ~ +11.01 名义秒之间摆动,无单调性。若只是舍入差异,均值应局限在 −0.5 ~ +0.5。
  2. 分层测试中 round 与 ceil 方向完全相反:

    数据集 round ceil
    samples (103) 84.5% 20.4%
    full (2271) 46.5% 53.6%
    balanced (2397) 39.0% 29.8%
    jieqi_dense (2880) 37.0% 49.5%

    合计 44.18% > 41.11% 属样本组成效应,不是真实改进。

  3. P95 达 500ms,远大于舍入量级(8.33ms)。

结论:误差来自节气时刻标定(δ 表),量级 500ms。此前四种修法(整体 +1 秒、性别残差、δ 表加密采样、δ 表按年拆分)全部失败,因为它们都在调 8ms 量级的舍入,而真实误差是 500ms 量级。

唯一可行修法:每节气补采 ≥100 条密集样本重建 δ 表(约 15 万请求)。此前 dense_fill 2638 条仍不够,反而降到 44%。

2. dyshensha 顺序无法复刻

内容一致 99.6%,逐字一致 0%——只差列表顺序。尝试拓扑排序反推全局顺序失败:49 对神煞存在 49–50% 的顺序矛盾(如「亡神 vs 劫煞」前后各 2595 次,完全对称)。说明 API 按判定代码执行路径动态排序,不存在固定表。

3. 独家五柱算法

App Store 描述提及,但公开接口 27 个字段中无任何痕迹,无从下手。

三条路径均已查证:

结论:属营销话术或未公开付费接口。


三项边界的完整排除性证据见 NOT-DONE.md,含可复现探针脚本清单。


五、复刻过程中的关键教训

教训 实例
审计脚本可能和被测代码一样错 差分脚本比对 5 vs 4 元素,导致「神煞 0%」误报;审计归一化丢弃 bz[8],导致农历长期以为已对齐
小样本结论在扩样后失效 起运 65.32% 来自 400 条子集,全量 7651 条实际 41.11%
中间值被舍入后不能用于高精度差分 用 round(x,4) 的 total_days 反推,分位 86.7% 直接崩到 5.5%
自然样本里各维度高度相关,无法分离 天罗地网前 9 轮都在挖自然样本,改用 API「对照实验」(只变一个变量)才破
百分比必须写清口径 「58.3%」可能是样本占比也可能是纯格子数,曾因此误判
同字重复是常见假阳性源 天罗地网 巳巳、亥亥 被误判为配对,P 从 57.7% 修正到 97.8%

六、许可

衍生自对问真八字公开接口的黑盒拟合,仅供学习研究。原始算法版权归问真所有。