5D Library › 酮体代谢、β-羟丁酸信号转导与间歇性禁食的生物能量学 · English
第八章 实践与预注册试验
注
再提醒一次。 下面的核查表只适用于没有本书开头所列禁忌情况、并已与医生确认可以调整进食时间的成年人。它是一份记录与观察的工具,不是治疗方案,不设体重目标,进食间隔不超过 16 小时,也不涉及任何补充剂或酮体产品。
第一部分 个人代谢灵活性的四步日常核查表
- 夜间禁食时长。 记录前一天最后一次进食到第二天第一次进食之间的小时数。在本书的范围内,它不超过 16 小时。(第一章)
- 晨起血糖与血酮,以及两者的比值。 如果你已经出于其他原因(例如在医生建议下)使用指尖血糖仪与血酮仪,可以记录晨起的读数,并计算葡萄糖–酮体指数(GKI,血糖 mmol/L 除以 BHB mmol/L)。不需要为了这份核查表专门购买仪器。(第一章、第三章)
- 电解质与水分的信号。 记录是否有站起来头晕、心悸、头痛或肌肉痉挛,以及大致的饮水量。这些信号出现时应当停止尝试并咨询医生,而不是自行补充。(第六章)
- 运动恢复指标。 记录晨起静息心率、主观疲劳程度(例如 0–10 分),以及常规运动的完成感受。(第五章、第六章)
推导
把第二步用于一周的虚构记录,并看看 GKI 在血酮较低时有多不稳定。
# Step 2 of the daily checklist: the glucose-ketone index, GKI = glucose (mmol/L) / BHB (mmol/L)
# (Meidenbauer et al. 2015). Glucose from a meter in mg/dL is divided by 18.016 to give mmol/L.
# A week of (fictional) fingertip readings on waking, after the previous evening's last meal:
log = [("Mon", 92, 0.2, 12.0), ("Tue", 88, 0.2, 13.0), ("Wed", 95, 0.2, 11.0), ("Thu", 86, 0.4, 15.5),
("Fri", 90, 0.5, 14.0), ("Sat", 98, 0.1, 10.5), ("Sun", 84, 0.3, 16.0)]
print("day glucose mg/dL mmol/L BHB mmol/L GKI overnight fast h")
for day, g, b, fast in log:
gm = g / 18.016
print(f"{day} {g:13d} {gm:6.2f} {b:10.1f} {gm / b:5.1f} {fast:5.1f}")
# The index is dominated by the denominator when BHB is low. Typical capillary BHB meters read in
# steps of 0.1 mmol/L, so a single step changes the GKI a lot at the low end:
g = 90 / 18.016
for b in (0.1, 0.2, 0.3, 1.0, 1.1):
print(f"BHB {b:.1f} mmol/L -> GKI {g / b:5.1f}")
pairs = sorted((fast, b) for _, _, b, fast in log)
n = len(pairs); mx = sum(p[0] for p in pairs) / n; my = sum(p[1] for p in pairs) / n
r = (sum((x - mx) * (y - my) for x, y in pairs) /
(sum((x - mx) ** 2 for x, _ in pairs) * sum((y - my) ** 2 for _, y in pairs)) ** 0.5)
print(f"correlation of overnight fast length with morning BHB this week: r = {r:.2f} (n = 7, descriptive only)")
# day glucose mg/dL mmol/L BHB mmol/L GKI overnight fast h
# Mon 92 5.11 0.2 25.5 12.0
# Tue 88 4.88 0.2 24.4 13.0
# Wed 95 5.27 0.2 26.4 11.0
# Thu 86 4.77 0.4 11.9 15.5
# Fri 90 5.00 0.5 10.0 14.0
# Sat 98 5.44 0.1 54.4 10.5
# Sun 84 4.66 0.3 15.5 16.0
# BHB 0.1 mmol/L -> GKI 50.0
# BHB 0.2 mmol/L -> GKI 25.0
# BHB 0.3 mmol/L -> GKI 16.7
# BHB 1.0 mmol/L -> GKI 5.0
# BHB 1.1 mmol/L -> GKI 4.5
# correlation of overnight fast length with morning BHB this week: r = 0.72 (n = 7, descriptive only)这份虚构记录说明了两点。第一,在一般人的晨起状态下,BHB 只有 0.1–0.5 mmol/L,GKI 在 10 到 50 多之间大幅波动。程序中间几行显示了原因:常见的指尖血酮仪以 0.1 mmol/L 为一档,BHB 从 0.1 变为 0.2,GKI 就从 50 降到 25;而 BHB 从 1.0 变为 1.1,GKI 只从 5.0 变为 4.5。在血酮较低时,GKI 主要反映的是血酮仪的分辨率,而不是代谢状态。 第二,这一周中,夜间禁食时间较长的早上血酮也较高(相关系数 0.72),但只有 7 个数据点,只能作描述,不能作结论。
注
GKI 不是健康目标。 GKI 最初是为癌症代谢治疗研究提出的一个计算工具 (Meidenbauer et al. 2015),并没有在一般人群中被验证为健康指标,也不存在适合一般人的「目标值」。本书只把它当作一个描述性的数字,提醒读者注意它在低血酮时的不稳定。
第二部分 预注册随机交叉试验
问题
在超重成年人中,与把同样的食物分成三餐、在一天中较长的时间内吃完相比,热量与蛋白质相同的 16:8 早期限时进食(所有进食在 8:00–16:00 之间完成),能否减小连续血糖监测测得的日间血糖波动幅度?它对毛细血管血 BHB 的日间曲线有什么影响?
为什么用交叉设计与提供餐食
第五章说明,限时进食的许多效果在热量不匹配时无法与「少吃」区分。本试验通过提供全部餐食使两种安排的热量与营养组成完全相同,只改变进食的时间;用交叉设计让每位参与者都经历两种安排,以个人自身为对照,减少所需的样本量。
设计
| 要素 | 规定 |
|---|---|
| 参与者 | 25–60 岁、体重指数 25.0–29.9 的成年人;排除糖尿病(包括糖化血红蛋白达到糖尿病诊断标准者)、使用任何降糖药或 SGLT2 抑制剂、妊娠或哺乳、进食障碍病史或筛查阳性、肝肾疾病、心律失常、轮班工作者,以及本书开头列出的其他禁忌情况;不从作者网站的读者或客户中招募 |
| 饮食 | 由研究厨房提供全部餐食,每日热量按个人估算的维持需要量配给(不以减重为目的),蛋白质约占能量的 25%;两种安排的食物完全相同,只是分配时间不同 |
| 早期限时进食 | 早餐 8:00、午餐 11:30、最后一餐在 15:30 开始、16:00 前吃完;16:00 至次日 8:00 只喝水或无热量饮料 |
| 三餐对照 | 同样的食物在 8:00、13:00、19:00 三餐吃完,夜间禁食约 13 小时 |
| 周期 | 两个各 3 周的周期,顺序随机;中间 2 周洗脱期恢复平常饮食 |
| 主要结局 | 每个周期第 8–21 天的平均 MAGE(连续血糖监测,日间血糖波动的平均幅度),按预先写定的算法计算 (Service et al. 1970) |
| 次要结局 | 毛细血管血 BHB 在 7、10、13、16、19、22 时测量的日间曲线下面积(每周期两天);连续血糖监测的平均血糖、变异系数与目标范围内时间 (Battelino et al. 2019);饥饿评分;睡眠时长;不良事件 |
| 盲法 | 参与者和厨房人员无法设盲;连续血糖数据由不知道周期分配的分析人员处理,分析代码在揭盲前写定 |
| 分析 | 线性混合模型,固定效应为安排、周期与顺序,参与者为随机效应;主要结局双侧 ;检验残留效应 |
推导
从原始数据计算结局:一条合成的连续血糖日曲线上的 MAGE,以及毛细血管血 BHB 的曲线下面积。数据都是虚构的,只用来说明计算方法。
# Computing the trial's outcomes from raw data: MAGE from a continuous glucose monitor (CGM) trace
# and the area under the capillary BHB curve. The CGM day below is SYNTHETIC (5-minute readings).
# MAGE (Service et al. 1970): the mean height of glucose excursions (here nadir-to-peak rises; one
# direction is counted), counting only
# excursions larger than one standard deviation of that day's readings. Software implementations
# differ in detail (smoothing, which direction is counted), so the trial will fix one in advance.
import math, statistics
def cgm_day(meals, base=5.0, rise=2.6, seed=3):
"""Synthetic glucose (mmol/L): each meal adds a gamma-shaped rise peaking ~45 min later."""
import random
rng = random.Random(seed); out = []
for i in range(288):
t = i * 5 / 60
g = base + 0.25 * math.sin(2 * math.pi * (t - 4) / 24)
for m in meals:
x = t - m
if x > 0: g += rise * (x / 0.75) * math.exp(1 - x / 0.75)
out.append(g + rng.gauss(0, 0.12))
return out
def mage(g):
sm = [statistics.mean(g[max(0, i - 2):i + 3]) for i in range(len(g))] # light smoothing
sd = statistics.pstdev(g)
turns = [sm[0]]
for i in range(1, len(sm) - 1):
if (sm[i] - sm[i - 1]) * (sm[i + 1] - sm[i]) < 0: turns.append(sm[i])
turns.append(sm[-1])
def compress(t): # keep only alternating peaks and nadirs
out = [t[0]]
for x in t[1:]:
if len(out) >= 2 and (out[-1] - out[-2]) * (x - out[-1]) >= 0: out[-1] = x
else: out.append(x)
return out
turns = compress(turns)
while len(turns) > 2: # drop swings smaller than 1 SD, then re-compress
small = [i for i in range(len(turns) - 1) if abs(turns[i + 1] - turns[i]) < sd]
if not small: break
i = small[0]
del turns[max(1, min(i, len(turns) - 3)):max(1, min(i, len(turns) - 3)) + 2]
turns = compress(turns)
swings = [turns[i + 1] - turns[i] for i in range(len(turns) - 1)]
ups = [s for s in swings if s > sd]
return (statistics.mean(ups) if ups else 0.0), sd
def auc(times_h, values):
return sum((times_h[i + 1] - times_h[i]) * (values[i] + values[i + 1]) / 2 for i in range(len(values) - 1))
for name, meals, rise in (("three meals 08, 13, 19 h", (8, 13, 19), 2.6), ("early TRE 08, 11.5, 15.5 h", (8, 11.5, 15.5), 2.6)):
m, sd = mage(cgm_day(meals, rise=rise))
print(f"{name:27s}: day SD {sd:.2f} mmol/L, MAGE {m:.2f} mmol/L")
# Capillary BHB measured at 07, 10, 13, 16, 19, 22 h (fictional values, mmol/L)
times = [7, 10, 13, 16, 19, 22]
for name, bhb in (("three meals", [0.2, 0.1, 0.1, 0.1, 0.1, 0.1]), ("early TRE", [0.3, 0.1, 0.1, 0.1, 0.2, 0.3])):
print(f"BHB area under the curve 07-22 h, {name:11s}: {auc(times, bhb):.2f} mmol/L x h")
# three meals 08, 13, 19 h : day SD 0.92 mmol/L, MAGE 2.99 mmol/L
# early TRE 08, 11.5, 15.5 h : day SD 1.00 mmol/L, MAGE 2.67 mmol/L
# BHB area under the curve 07-22 h, three meals: 1.65 mmol/L x h
# BHB area under the curve 07-22 h, early TRE : 2.40 mmol/L x hMAGE 的计算细节(是否平滑、计算上升还是下降的波动)在不同软件之间并不一致,同一组数据用不同的实现可能得到不同的数值,所以试验方案必须在开始前写定一种算法并公开代码。
样本量
推导
# Sample size for the pre-registered randomised crossover trial: early time-restricted eating
# (eTRE, all food 08:00-16:00) versus the same food spread over three meals (08:00, 13:00, 19:00),
# energy-matched with provided meals, two 3-week periods in random order, 2-week washout.
# Primary outcome: MAGE from continuous glucose monitoring, days 8-21 of each period.
# In a crossover the precision comes from the within-person difference: SD of the paired difference
# sigma_d. Effect expressed as d_z = delta / sigma_d. Normal approximation, two-sided alpha 0.05.
import math
from statistics import NormalDist
N = NormalDist()
def n_crossover(dz, alpha=0.05, power=0.90):
return math.ceil((N.inv_cdf(1 - alpha / 2) + N.inv_cdf(power)) ** 2 / dz ** 2) + 2 # +2: t correction
for delta, sd_diff in ((0.3, 0.8), (0.4, 0.8), (0.5, 0.8), (0.4, 1.0)):
dz = delta / sd_diff
print(f"difference {delta} mmol/L, SD of within-person difference {sd_diff}: d_z = {dz:.2f}"
f" -> {n_crossover(dz, power=0.8)} for 80 % power, {n_crossover(dz)} for 90 %")
n = n_crossover(0.4 / 0.8)
recruit = math.ceil(n / 0.80)
print(f"chosen: 0.4 mmol/L, SD 0.8, 90 % power -> {n} completers; allowing 20 % loss, recruit {recruit}")
print(f"(a parallel-group trial with between-person SD 1.0 would need about"
f" {math.ceil(2 * (N.inv_cdf(0.975) + N.inv_cdf(0.9)) ** 2 / 0.4 ** 2)} per arm)")
# difference 0.3 mmol/L, SD of within-person difference 0.8: d_z = 0.37 -> 58 for 80 % power, 77 for 90 %
# difference 0.4 mmol/L, SD of within-person difference 0.8: d_z = 0.50 -> 34 for 80 % power, 45 for 90 %
# difference 0.5 mmol/L, SD of within-person difference 0.8: d_z = 0.62 -> 23 for 80 % power, 29 for 90 %
# difference 0.4 mmol/L, SD of within-person difference 1.0: d_z = 0.40 -> 52 for 80 % power, 68 for 90 %
# chosen: 0.4 mmol/L, SD 0.8, 90 % power -> 45 completers; allowing 20 % loss, recruit 57
# (a parallel-group trial with between-person SD 1.0 would need about 132 per arm)按最小关注差 0.4 mmol/L、个体内差值的标准差 0.8 mmol/L、90% 把握度计算,需要 45 人完成两个周期;考虑 20% 的脱落,招募 57 人。个体内差值的标准差是假设值,将在 10 人的预试验中核对;如果它明显大于 0.8,正式试验开始前重新计算样本量。作为对照,同样的问题如果用平行组设计,每组需要约 132 人。
判定规则
推导
# Decision rule for the crossover trial, fixed before any data exist.
# d = eTRE minus three-meal MAGE (mmol/L), from a mixed model with period and sequence terms,
# with its 95 % CI. Negative = smaller glucose excursions with eTRE.
SESOI = 0.4
def verdict(d, lo, hi, carryover_p):
if hi <= -SESOI: v = "eTRE lowers MAGE by at least the smallest worthwhile amount"
elif hi < 0: v = "eTRE lowers MAGE; size not shown to reach 0.4 mmol/L"
elif lo > 0: v = "eTRE RAISES MAGE"
elif lo >= -SESOI and hi <= SESOI: v = "no difference worth acting on"
else: v = "unclear"
flag = "; carry-over test p < 0.10: report first-period data as a sensitivity analysis" if carryover_p < 0.10 else ""
return f"MAGE difference {d:+.2f} ({lo:+.2f} to {hi:+.2f}) mmol/L: {v}{flag}"
# Invented results, to show in advance how each pattern will be reported:
for r in ((-0.62, -0.88, -0.41, 0.50), (-0.20, -0.45, 0.05, 0.30), (0.05, -0.18, 0.28, 0.04)):
print(verdict(*r))
print("Secondary outcomes (capillary BHB area under the curve, mean glucose, time in range, hunger,")
print("sleep) are reported with intervals; none of them can make the trial positive.")
# MAGE difference -0.62 (-0.88 to -0.41) mmol/L: eTRE lowers MAGE by at least the smallest worthwhile amount
# MAGE difference -0.20 (-0.45 to +0.05) mmol/L: unclear
# MAGE difference +0.05 (-0.18 to +0.28) mmol/L: no difference worth acting on; carry-over test p < 0.10: report first-period data as a sensitivity analysis
# Secondary outcomes (capillary BHB area under the curve, mean glucose, time in range, hunger,
# sleep) are reported with intervals; none of them can make the trial positive.示例结果是编造的。只有当 MAGE 差值的置信区间上限低于 −0.4 mmol/L 时,报告才会写「早期限时进食把 MAGE 降低了至少最小关注的幅度」;如果残留效应检验提示前一周期的影响延续到后一周期,报告还会给出只用第一周期数据的敏感性分析。
这项试验不能说明什么
第一,参与者都是超重但没有糖尿病的成年人,结果不能推广到糖尿病患者、使用降糖药的人或体重正常的人。第二,餐食由研究提供,热量按维持需要量配给,这保证了内部效度,却与自由生活中的情况不同;即使结果为阳性,也不能说明人们自己安排的限时进食会有同样的效果。第三,MAGE 是一个血糖波动的指标,它的减小本身不等于健康结局的改善。第四,每个周期只有 3 周,不能说明长期效果。第五,血酮曲线下面积反映的是进食时间对酮体生成的影响,不说明任何「自噬」或「抗衰老」的作用。
伦理与利益冲突
试验须经独立伦理委员会批准,取得书面知情同意,并在第一位参与者入组之前登记方案和分析计划 (Nosek et al. 2018)。研究医生每周审查不良事件;出现低血糖症状、晕厥、心悸或其他不适时,参与者可以随时退出,并由研究医生评估。
利益披露
作者运营 ketohackers.com 与 nadplusnadplus.com 两个以生酮代谢和 NAD+ 为主题的网站,因此与本书主题有直接的商业利益关系。本书不链接这两个网站,不提及也不推荐任何产品、补充剂、检测仪器或课程;第八章的试验设计为由独立于作者企业的研究人员实施和分析;书中所有代码与研究方案均公开,供任何人检验。为限制利益的影响:参与者不从作者网站的读者或客户中招募;餐食方案和分析代码在入组前公开;无论结果如何,结果、数据和代码都将公开。
参考文献
Fan, Y. (2026). Ketone Metabolism, β-Hydroxybutyrate Signalling and the Bioenergetics of Intermittent Fasting: From the Randle Cycle and Epigenetic Inhibition to Autophagy Kinetics, Read Beside Classical Chinese Fasting Texts [酮体代谢、β-羟丁酸信号转导与间歇性禁食的生物能量学] (Version 1.0.0). 5D Library. https://doi.org/10.5281/zenodo.23753263 · Licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0).