“租房是替房东还贷,买房是替银行打工”——这句流行语两边都对,也都没说到点子上。买房与租房之争的实质是两条复利曲线的赛跑:一条是房屋净值(市值 − 剩余贷款),一条是把首付款和每月省下的差额拿去投资的账户。买房 vs 租房计算器把这场赛跑做成了一个表单:9 个输入、一次算出 N 年后两条曲线各值多少。这篇文章拆它的模型——尤其是哪部分用了闭式解、哪部分必须逐月循环,以及哪些账刻意没算。
1. 模型的骨架:两条曲线的对称定义
- 买房曲线 = 房屋市值 − 剩余贷款;
- 租房曲线 = 首付账户终值 + 每月差额定投终值。
输入九个参数:房价、首付比例、房贷利率与年限、房价年涨幅、当前月租金、租金年涨幅、理财年化收益率、对比年限。四个“汇率”参数(房价涨幅、投资收益、贷款利率、租金涨幅)的拔河决定胜负——没有先验答案,这正是需要算而不是吵的原因。
2. 买房路径:闭式解一行算清
买房曲线的两个部件都有闭式解。房屋市值是几何增长:
const homeValueAtHorizon = price * Math.pow(1 + homeApprec, horizon);
剩余贷款是等额本息的残值公式——复利前推 n 期再减去年金的终值:
const powN = (1 + monthlyRate) ** monthsEvaluated;
remainingLoan = Math.max(0, loanAmount * powN - origPayment * ((powN - 1) / monthlyRate));
这两行直接复用了站内 房贷计算器已验证过的公式(推导过程另有一篇)。实现里 monthlyRate > 0 的分支之外还有一个零利率直减的 else——利率为 0 时 (powN - 1) / monthlyRate 是 0/0,闭式解在那里退化,必须换直线的算法。每个除法都要问自己分母能不能是零,金融公式里这不是防御性编程,是必要分支。
Math.max(0, ...) 同理:对比年限超过贷款年限时,残值公式会给负数,物理含义是“已结清”,钳到 0。
3. 租房路径:为什么必须逐月循环
租房曲线没有可用的闭式解,因为月供是常数、房租按年阶梯涨——差额(月供 − 房租)不是年金,是分段函数。所以这里老实上循环:
let rentInvestPool = downPayment;
for (let m = 1; m <= horizon * 12; m++) {
const yearIndex = Math.floor((m - 1) / 12);
const currentMonthlyRent = initRent * Math.pow(1 + rentInfl, yearIndex);
const monthSavings = origPayment - currentMonthlyRent;
rentInvestPool = rentInvestPool * (1 + monthlyInvestRate) + monthSavings;
}
四行循环体,每一行都是一个建模决策:
- 租金按年涨、按月扣:
yearIndex让同一历年内的 12 个月共用一个租金,避免“每月涨一点点”的复利幻觉——租约本来就是一年一签的; - 差额可以为负:前期房租高于月供时(低房价高租金市场),
monthSavings是负数,账户被抽水——这正是“租房者在替买房者付机会成本”的反向情形,循环不用任何特判就把它算进去了; - 月复利:
rInvest / 12的月度化是最简假设,年化收益本来就是月复利口径的约定。
50 年上限 × 12 = 600 次迭代,性能上免费。能在循环里说清楚的事,不要硬凑解析式——这条循环的存在本身就是一个取舍记录:两条曲线共用一个时间轴,买房那条走了公式、租房这条走了模拟,因为它们的时间结构不同。
4. 输出设计与“刻意没算”的账
结果行的第一行不是数字,是结论:
const winner = diff >= 0 ? 'Buying a Home' : 'Renting & Investing';
之后才是领先优势、两条曲线各自的终值、预估房价、剩余贷款。把 winner 放最前且加 emphasis,是给“只想知道答案”的用户的第一眼;把两条曲线的明细放后面,是给“想知道差多少、差在哪”的用户的第二眼。
模型刻意没算的账,在工具说明里明说:
- 交易摩擦(税费、中介 2%–5%):一次性成本,取决于持有期内的换手次数,表单没有“中间卖一次”的输入,硬编只会更不准;
- 持有成本(物业、维护、房产税):与房价线性相关但各地税率差异巨大,参数化会从 9 个涨到 12 个;
- 弹性不对称(通勤半径锁定 vs 换城市自由):无法定价。
这不是偷懒,是参数化边界的决策:9 个输入以内,每个参数用户都能从自己的合同/报价单上找到真实数值;再加三个“估一个吧”的参数,输出就从测算退化为装饰。同一张表上,赢家优势不到 10% 时,答案本来就该按生活弹性选而不是按表格选——模型只负责把“差得很少”和“一眼可见”分开。
5. 工程收获
- 闭式与模拟的分界:市值/残值用公式(时间结构均匀),差额定投用循环(月供常数、房租阶梯,时间结构不均匀);
- 除法必查零分母:零利率分支不是边角,是公式退化的地方;
- 负差额自然处理:账户被抽水的情形不需要特判,循环语义自己覆盖了它;
- 参数化边界:只收用户拿得到真实数值的参数,估值的账交给文字说明。
工具在此:买房 vs 租房计算器,配合房贷计算器核对月供与摊销、复利计算器核对定投终值。示例数据均为演示假设,不构成投资建议;全部本地运算,数据不出浏览器。