仅根据BMI反推身高和体重?还真能做到!只需要简单的数论知识~

引言

本文视频版传送门

视频封面:

今天在castelu大佬评论区看到这样一条评论:

我们不禁要问:能否仅根据BMI反推身高和体重?答案是肯定的,但有没有更优雅的做法?

本文 52pojie: www.52pojie.cn/thread-2132...

本文 博客园: www.cnblogs.com/hans77/arti...

本文 juejin: juejin.cn/post/769633...

作者:hans774882968以及hans774882968以及hans774882968以及hans77

建模前的约定

我们知道BMI的计算公式:
B=wh2 B=\frac{w}{h^2} B=h2w

为了方便数学建模,不妨先结合现实生活进行一些约定:

  1. 体重的单位是kg,所以精确到1位小数,比如114.5kg
  2. BMI使用的身高的单位是米,所以身高精确到3位小数,比如1.919米
  3. 有了前两条约定,B显然是有理数,不妨设为 PQ \frac{P}{Q} QP

初步思路

进行上述约定后,凭Hans多年的算法竞赛经验(金(dǎ)牌(tiě)水平 ),一瞬间就想到,约定一个身高上限(因为很难见到3米以上的人类),便能直接枚举范围内所有可能的身高,用公式 w=Bh2w=Bh^2 w=Bh2 看何时能得到一位小数的体重即可。

虽然能解决问题,但局限性有点强啊......有没有更好的方法呢?有的。

Hans的想法:质因数分解

回顾BMI的计算公式,令 W=10w, H=1000hW=10w,\ H=1000h W=10w, H=1000h ,则W和H都为正整数,代入BMI公式:
PQ=wh2= W10 (H1000) 2 =105WH2  ⟹  W= PH2105Q (1) \frac{P}{Q}=\frac{w}{h^2}=\frac{\frac{W}{10}}{\left( \frac{H}{1000} \right)^2}=10^5 \frac{W}{H^2} \implies W=\frac{PH^2}{10^5Q} \tag{1} QP=h2w=(1000H)210W=105H2W⟹W=105QPH2(1)

这里同学们不妨思考一下:为什么不变形为 H2H^2 H2 等于xxx,而是这样变形呢?

也就是说,我们希望找到最小的正整数H,使得 PH2PH^2 PH2 是 105Q10^5Q 105Q 的倍数,这就是道数论题了。为了方便,不妨设BMI已经是最简分数,即P和Q互素,于是P对倍数关系没有影响,问题转化为:

求最小的正整数H,使得 H2H^2 H2 是 105Q10^5Q 105Q 的倍数

这时,等式的分子只剩H,足够纯净,我们便不难想到:考虑 D=105QD=10^5Q D=105Q 的质因数分解
D= p1α1 p2α2 ⋯ pkαk D = p_1^{\alpha_1} p_2^{\alpha_2} \cdots p_k^{\alpha_k} D=p1α1p2α2⋯pkαk

为了让 H2H^2 H2 包含 DD D 的所有质因数, HH H 至少要包含 ⌈αi/2⌉ \lceil \alpha_i / 2 \rceil ⌈αi/2⌉ 个 pi p_i pi ,因此,最小的
H= ∏i=1k pi ⌈αi/2⌉ H = \prod_{i=1}^{k} p_i^{\lceil \alpha_i / 2 \rceil} H=i=1∏kpi⌈αi/2⌉

对D质因数分解后立刻就能求出H,进而求出W。数据范围不太大的情况下,直接用试除法对D质因数分解即可,时间复杂度 O(D) O(\sqrt{ D }) O(D ) 。最后,我们把P和Q互素的假设去掉,1式只需稍作修改:
W= PgH2 105Qg , g=gcd(P,Q) W=\frac{\frac{P}{g}H^2}{10^5 \frac{Q}{g}},\ g=gcd(P,Q) W=105gQgPH2, g=gcd(P,Q)

因为 Pg \frac{P}{g} gP 和 Qg \frac{Q}{g} gQ 互素,所以只需要把上面假设的D改成 D=105Qg D=10^5 \frac{Q}{g} D=105gQ

完整代码:

python 复制代码
import math
from fractions import Fraction


def prime_factors(n):
    """对整数 n 进行质因数分解,返回字典 {素数: 幂次}"""
    factors = {}
    while n % 2 == 0:
        factors[2] = factors.get(2, 0) + 1
        n //= 2
    d = 3
    while d * d <= n:
        while (n % d) == 0:
            factors[d] = factors.get(d, 0) + 1
            n //= d
        d += 2
    if n > 1:
        factors[n] = factors.get(n, 0) + 1
    return factors


def find_exact_hw(bmi_value):
    """
    根据精确的有理数 BMI,构造出最小的一组
    身高精确到3位小数、体重精确到1位小数的 (h, w)
    """
    if isinstance(bmi_value, float):
        bmi_frac = Fraction(bmi_value).limit_denominator(10**9)
    else:
        bmi_frac = Fraction(bmi_value)

    P = bmi_frac.numerator
    Q = bmi_frac.denominator

    if P <= 0 or Q <= 0:
        raise ValueError("BMI 必须是正有理数")

    # 1. 计算有效分母 D = 100000 * Q / gcd(P, 100000 * Q)
    denom = 100000 * Q
    g = math.gcd(P, denom)
    D = denom // g

    # 2. 对 D 进行质因数分解
    factors = prime_factors(D)

    # 3. 构造最小的 H,使得 H^2 是 D 的倍数
    H = 1
    for p, alpha in factors.items():
        # 幂次向上取整:ceil(alpha / 2)
        power = (alpha + 1) // 2
        H *= (p ** power)

    # 4. 计算对应的 W = P * H^2 / (100000 * Q)
    # 因为 H 的构造保证了 H^2 是 D 的倍数,这里必定能整除
    W = (P * H * H) // (100000 * Q)

    # 还原为3位小数的身高和1位小数的体重
    h = H / 1000.0
    w = W / 10.0

    return h, w, H, W


if __name__ == "__main__":
    test_cases = [
        25,             # 整数
        18.5,           # 常见 BMI
        "3.14159",      # 字符串输入,避免浮点误差
        Fraction(1, 3),  # 分数 1/3
        Fraction(22, 7),  # 分数 22/7
        Fraction(560000, 29241),  # 56kg 1.71m
        Fraction(565000, 29241),  # 56.5kg 1.71m
        Fraction(65000, 3249),  # 58.5kg 1.71m
        Fraction(30625, 1681),  # 49kg 1.64m
        Fraction(5952000, 104329),  # 595.2kg 3.23m
        Fraction(28625000, 1846881),  # 114.5kg 2.718m
        114.514,
        810.1919,
    ]

    for bmi in test_cases:
        h, w, H, W = find_exact_hw(bmi)
        print(f"目标 BMI: {bmi} | 最小解: h={h:<5}米, w={w:<5}千克 | 验证: {w} / {h}^2 = {w / (h**2):.5f}")

输出:

ini 复制代码
目标 BMI: 25 | 最小解: h=0.2  米, w=1.0  千克 | 验证: 1.0 / 0.2^2 = 25.00000
目标 BMI: 18.5 | 最小解: h=1.0  米, w=18.5 千克 | 验证: 18.5 / 1.0^2 = 18.50000
目标 BMI: 3.14159 | 最小解: h=100.0米, w=31415.9千克 | 验证: 31415.9 / 100.0^2 = 3.14159
目标 BMI: 1/3 | 最小解: h=3.0  米, w=3.0  千克 | 验证: 3.0 / 3.0^2 = 0.33333
目标 BMI: 22/7 | 最小解: h=3.5  米, w=38.5 千克 | 验证: 38.5 / 3.5^2 = 3.14286
目标 BMI: 560000/29241 | 最小解: h=0.855米, w=14.0 千克 | 验证: 14.0 / 0.855^2 = 19.15119
目标 BMI: 565000/29241 | 最小解: h=1.71 米, w=56.5 千克 | 验证: 56.5 / 1.71^2 = 19.32218
目标 BMI: 65000/3249 | 最小解: h=0.57 米, w=6.5  千克 | 验证: 6.5 / 0.57^2 = 20.00616
目标 BMI: 30625/1681 | 最小解: h=1.64 米, w=49.0 千克 | 验证: 49.0 / 1.64^2 = 18.21832
目标 BMI: 5952000/104329 | 最小解: h=1.615米, w=148.8千克 | 验证: 148.8 / 1.615^2 = 57.05029
目标 BMI: 28625000/1846881 | 最小解: h=2.718米, w=114.5千克 | 验证: 114.5 / 2.718^2 = 15.49910
目标 BMI: 114.514 | 最小解: h=10.0 米, w=11451.4千克 | 验证: 11451.4 / 10.0^2 = 114.51400
目标 BMI: 810.1919 | 最小解: h=100.0米, w=8101919.0千克 | 验证: 8101919.0 / 100.0^2 = 810.19190

最后公布前面思考题的答案:

  1. 为什么不变形为 H2H^2 H2 等于xxx,而是这样变形呢?因为验证一个分式是完全平方数 虽然可行(同学们不妨动手试试),但相比于验证一个分式是正整数,更为困难
相关推荐
第九序言T8 小时前
数据分析岗 Pandas 面试题 20 道(上):概念与读代码
python·数据分析·pandas·数据处理
Dmz丶8 小时前
前端转 AI 100 天 Day 37:Few-shot 与 CoT——让模型学会“推理”
python
孟健8 小时前
我用Grok Bot本地剪口播视频,3段提示词搞定气口切除、字幕与封面
人工智能·python·aigc
2601_966949658 小时前
如何封装 qd.klines.batch(),让批量 K 线查询函数在多个策略中复用?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
神仙别闹10 小时前
基于Python实现英词语自动对齐系统
开发语言·python
老歌老听老掉牙10 小时前
圆柱端面四平面交线及其在yoz面投影的几何解析与 Python 实现
开发语言·python·平面
我要见SA姐111 小时前
Chrome 扩展安全攻防实录:从权限滥用到供应链投毒
运维·人工智能·python·机器学习·自动化
2601_9628857211 小时前
给 backtrader 接 A 股数据:用 AlphaFeed 喂PandasData
python
YYYing.11 小时前
【Agent系列 (十一) 】上下文工程
人工智能·python·ai·agent·上下文工程
计算机毕业编程指导师11 小时前
基于深度学习的光伏组件红外图像缺陷识别系统设计与实现
人工智能·python·深度学习·目标检测·毕业设计·课程设计·光伏