#921 电动车相关知识

2023-11-03

这里的电动车是指骑行的那种,不是小汽车。
又因为武汉限摩,所以这里的电动车是指电动自行车,而非什么轻便型电动车或者电摩。

新国标

电动自行车安全技术规范》(GB 17761-2018)是由国家市场监督管理总局、中国国家标准化管理委员会于 2018 年 5 月 15 日批准发布的,自 2019 年 4 月 15 日正式实施。该标准代替了 1999 年发布的《电动自行车安全技术规范》(GB 17761-1999),标准全部技术内容均为强制性。

  1. 车速:<= 25 km/h(旧国标为 20
  2. 重量:<= 55 kg(旧国标为 40
  3. 电压:<= 48 Vkeep...
  4. 功率:<= 400 W(旧国标为 240
  5. 支持人力(脚踏)骑行(keep...

比旧国标稍微强了那么一丢丢,依旧令人失望。

我认为就现状而言,禁摩令十足的恶法。为禁摩服务的国标也完全没有必要。

上牌步骤

常见品牌

  • 绿源
  • 爱玛
  • 小牛
  • 小刀
  • 九号
  • 台铃
  • 新日
  • 哈啰
  • 雅迪

价格

以淘宝上爱玛官方电动车直销店排名第一的“爱玛新国标电瓶车可带人小型女士代步电动自行车成人可带人长跑王”为例,现在的价格:

  • 爆款 MVP 战神-源彩灰【防爆真空胎/350W 高速电机/前后舒适减震】 48v12ah 1739.00
  • 爆款 MVP 战神-源彩绿【防爆真空胎/350W 高速电机/前后舒适减震】
  • 爆款 MVP 战神-源彩粉【防爆真空胎/350W 高速电机/前后舒适减震】
  • 爆款 MVP 战神 2-源彩莓红【防爆真空胎/350W 高速电机/前后舒适减震】
  • 2023 新品 AT102-星空白【防爆真空胎/350W 高速电机/前后舒适减震】 48v12ah 1509.00
  • 2023 新品 AT102-哑黑【防爆真空胎/350W 高速电机/前后舒适减震】
  • 乐行版小电驴 2.0-素颜绿【小 U 弯把/透视大灯/STT 自动启停】 48v12ah 1919.00
  • 乐行版小电驴 2.0-源彩粉【小 U 弯把/透视大灯/STT 自动启停】
  • 乐行版小电驴 2.0-奶酪白【小 U 弯把/透视大灯/STT 自动启停】
  • 尖货泡泡-源彩咖【时尚车筐/中置减震/基因高亮后尾灯】 48v12ah 2099.00
  • 尖货泡泡-源彩灰【时尚车筐/中置减震/基因高亮后尾灯】
  • 尖货泡泡-雾桃粉【时尚车筐/中置减震/基因高亮后尾灯】
  • 尖货泡泡-气泡绿【时尚车筐/中置减震/基因高亮后尾灯】
  • 尖货泡泡-谧淡蓝【时尚车筐/中置减震/基因高亮后尾灯】
  • 战将-源彩黄【48V20AH 电池/350W 高速电机/前后舒适减震】 48v20ah 1969.00
  • 战将-源彩咖啡【48V20AH 电池/350W 高速电机/前后舒适减震】
  • 战将-豆蔻绿【48V20AH 电池/350W 高速电机/前后舒适减震】
  • 新品元宇宙-甜美粉【可提锂电池/萌感透镜大灯/NFC 智能刷卡】 48v24ah 3739.00
  • 新品元宇宙-爱尔兰【可提锂电池/萌感透镜大灯/NFC 智能刷卡】
  • 新品元宇宙-茶白【可提锂电池/萌感透镜大灯/NFC 智能刷卡】
  • 新品元宇宙-奶糖绿【可提锂电池/萌感透镜大灯/NFC 智能刷卡】

赠送头盔,雨衣,U 型锁。

电池

  • 铅酸
  • 便宜,安全(不容易着火),循环利用(以旧换新)
  • 使用寿命短(一到两年,充电还有记忆效应),亏电,能量密度低(大块头,死重),怕冷
  • 石墨烯:一种很好的材料,但是现阶段还在实验室里。市面上的石墨烯电池都是炒作。
  • 锂电池
  • 贵很多,质量问题或者不正确的使用方式可能导致电池爆燃
  • 使用寿命长,不怎么亏电,储电量高很多

#920 毁于所爱

2023-11-01

进击的巨人中,凯尼 ∙ 阿克曼死之前,对侄子利威尔说的:“我所见过的人全都一样,要么是酒,要么是女人,要么是神、家族、王、梦想、子女、力量...人如果不沉醉于某些东西,估计都撑不下去的吧!所有人都是某些东西的奴隶...”

对应后面艾伦的命运。艾伦从被圈养的艾族人,变成追求自由的少年。然后不停地被迫作出选择,放弃一些重要的东西。再然后,为了这些牺牲不被白白浪费,只能沿着划好的线路继续走下去。到最后,才明白,自己其实成为了自由的奴隶。

追求自由,变成了自由的奴隶。
这个故事其实是在告诉我们,人们很容易会被自己的热爱所伤害,甚至毁灭。


恰巧,今天看了一个抖音短视频(疯狂 123),非常有意思,感觉可以放在一起说说。

2022:手机!把我的孩子都毁了!
2012:电脑!把我的孩子都毁了!
2008:电视机!把我的孩子都毁了!
2000:网络小说...
1999:流行音乐...
1995:小霸王游戏机...
1985:彩色电视...
民国:看戏剧...
明清:斗蛐蛐...
宋朝:蹴鞠...
唐朝:遛鸟...
秦朝:击剑狩猎...
夏商周:不种地...
公元前 5000 年:不狩猎...
...
元古宙:那个多细胞生物把我的孩子都毁了!

有一个很有名的小品,之前经常在电视上播放,1993 年,赵丽蓉、黄宏、蔡明主演的《追星族》,给我的感觉是,好像在那个时候追星是多么可怕的事情。我读小学的时候,就有同学迷各种我不知道的明星。抄歌词,抄明星的各种信息,贴纸,看杂志,把图片剪下来贴本子上,等等。
后来,有各种电视节目(言情剧、动漫),还有光碟,这是洪水猛兽。
再后来,游戏厅、游戏机是洪水猛兽。
再后来,网吧,电脑游戏,又变成了新的洪水猛兽,似乎沾上就没有前途可言了。

现在,倒回来看,“追星族”、“网瘾”,这些词怎么好像都没有听人提到了?我这一代人被毁了么?好像也没那么可怕嘛!
有点喜欢的东西,有点爱好都是正常的事情。前人说过:人无痴者,无可与之交,其无深情也;人无癖者,无可与之交,因其无深情也。
能毁掉一个人的不是上面所有的这些东西,而是放纵,是没有自制力,是没有更坚定的志向和为此付出努力的觉悟。
光靠躲,是躲不开的,就算现在避开了这个坑,早晚还是要掉到一个更大的坑。

退一步说,公正的去看,这些事物难道就都百分百是坏的么?

更重要的是,等到子女十几岁之后,以我的水平,我是自认完全没有什么能力去“引导”他们做什么了,主要靠他们自己努力了。我能做的就是陪伴,还有真诚的沟通。


我又想起来,从小到大听了不少:
谁谁谁喜欢某事之后,就不爱学习,成绩差,一辈子毁掉了;
谁谁谁什么杂事都不做,就关屋子里做题,最后成功考上什么大学之类的故事。
考上大学,进入某个单位,是我们人生的目标么?
这顶多只能算一个阶段性目标。为这个阶段性目标付出多少是一个比较平衡的点?

其实大多数家长都是只看结果的,A 考得好,B 考的不好,那 B 的付出一定没有 A 多。
思考问题的方式也比较简单化,A 这样做收获了好的结果,那么 B 应该照着这个方法肯定没差。
这样应该不合理吧!

#919 作息安排

2023-10-31

理想中的作息时间:

  • 08:00 ~ 09:30 洗漱-运动-早餐
  • 09:30 ~ 11:30 工作(2H)
  • 11:30 ~ 13:00 午餐-午间休息
  • 13:00 ~ 16:00 工作(3H)
  • 16:00 ~ 17:30 休息
  • 17:30 ~ 18:30 晚餐
  • 18:30 ~ 21:30 工作(3H)
  • 21:30 ~ 23:00 夜宵-洗漱
  • 23:00 ~ 01:00 自由安排
  • 01:00 ~ 08:00 休息

根据实际情况调整:

  • 07:00 ~ 09:30 洗漱-运动-早餐-通勤
  • 09:30 ~ 12:00 上午工作(2.5H)
  • 12:00 ~ 13:00 午餐-午间休息
  • 13:30 ~ 19:00 下午工作(5.5H)
  • 19:00 ~ 22:00 通勤-晚餐-家务-洗漱
  • 22:00 ~ 23:00 自由安排
  • 23:00 ~ 07:00 休息

#918 生活感悟

2023-10-30

有时候真的是秀才遇到兵,有理说不清。

可能有些人就是坚持为自己的利益斗争的思想,一言不合就要掀桌子。
可能有些人就是喜欢信口开河,胡搅蛮缠,歇斯底里。

我总是有一种感觉,就好像我正在吃饭,有人一边往我的碗里面丢沙子,我却只能继续默默吃下去。

#917 PromQL Cheat Sheet

2023-10-29

Selecting series

Select latest sample for series with a given metric name:

node_cpu_seconds_total

Select 5-minute range of samples for series with a given metric name:

node_cpu_seconds_total[5m]

Only series with given label values:

node_cpu_seconds_total{cpu="0",mode="idle"}

Complex label matchers (=: equality, !=: non-equality, =~: regex match, !~: negative regex match):

node_cpu_seconds_total{cpu!="0",mode=~"user|system"}

Select data from one day ago and shift it to the current time:

process_resident_memory_bytes offset 1d

Rates of increase for counters

Per-second rate of increase, averaged over last 5 minutes:

rate(demo_api_request_duration_seconds_count[5m])

Per-second rate of increase, calculated over last two samples in a 1-minute time window:

irate(demo_api_request_duration_seconds_count[1m])

Absolute increase over last hour:

increase(demo_api_request_duration_seconds_count[1h])

Aggregating over multiple series

Sum over all series:

sum(node_filesystem_size_bytes)

Preserve the instance and job label dimensions:

sum by(job, instance) (node_filesystem_size_bytes)

Aggregate away the instance and job label dimensions:

sum without(instance, job) (node_filesystem_size_bytes)

Available aggregation operators: sum()min()max()avg()stddev()stdvar()count()count_values()group()bottomk()topk()quantile()

Math between series

Add all equally-labelled series from both sides:

node_memory_MemFree_bytes + node_memory_Cached_bytes

Add series, matching only on the instance and job labels:

node_memory_MemFree_bytes + on(instance, job) node_memory_Cached_bytes

Add series, ignoring the instance and job labels for matching:

node_memory_MemFree_bytes + ignoring(instance, job) node_memory_Cached_bytes

Explicitly allow many-to-one matching:

rate(demo_cpu_usage_seconds_total[1m]) / on(instance, job) group_left demo_num_cpus

Include the version label from "one" (right) side in the result:

node_filesystem_avail_bytes * on(instance, job) group_left(version) node_exporter_build_info

Available arithmetic operators: +, -, \*, /, %, ^

Filtering series by value

Only keep series with a sample value greater than a given number:

node_filesystem_avail_bytes > 10*1024*1024

Only keep series from the left-hand side whose sample values are larger than their right-hand-side matches:

go_goroutines > go_threads

Instead of filtering, return 0 or 1 for each compared series:

go_goroutines > bool go_threads

Match only on specific labels:

go_goroutines > bool on(job, instance) go_threads

Available comparison operators: ==, !=, >, <, >=, <=

Set operations

Include any label sets that are either on the left or right side:

up{job="prometheus"} or up{job="node"}

Include any label sets that are present both on the left and right side:

node_network_mtu_bytes and (node_network_address_assign_type == 0)

Include any label sets from the left side that are not present in the right side:

node_network_mtu_bytes unless (node_network_address_assign_type == 1)

Match only on specific labels:

node_network_mtu_bytes and on(device) (node_network_address_assign_type == 0)

Quantiles from histograms

90th percentile request latency over last 5 minutes, for every label dimension:

histogram_quantile(0.9, rate(demo_api_request_duration_seconds_bucket[5m]))

...for only the path and method dimensions:

histogram_quantile(
  0.9,
  sum by(le, path, method) (
    rate(demo_api_request_duration_seconds_bucket[5m])
  )
)

Changes in gauges

Per-second derivative using linear regression:

deriv(demo_disk_usage_bytes[1h])

Absolute change in value over last hour:

delta(demo_disk_usage_bytes[1h])

Predict value in 1 hour, based on last 4 hours:

predict_linear(demo_disk_usage_bytes[4h], 3600)

Aggregating over time

Average within each series over a 5-minute period:

avg_over_time(go_goroutines[5m])

Get the maximum for each series over a one-day period:

max_over_time(process_resident_memory_bytes[1d])

Count the number of samples for each series over a 5-minute period:

count_over_time(process_resident_memory_bytes[5m])

See all available xxx_over_time() aggregation functions.

Time

Get the Unix time in seconds at each resolution step:

time()

Get the age of the last successful batch job run:

time() - demo_batch_last_success_timestamp_seconds

Find batch jobs which haven't succeeded in an hour:

time() - demo_batch_last_success_timestamp_seconds > 3600

Dealing with missing data

Create one output series when the input vector is empty:

absent(up{job="some-job"})

Create one output series when the input range vector is empty for 5 minutes:

absent_over_time(up{job="some-job"}[5m])

Manipulating labels

Join the values of two labels with a - separator into a new endpoint label:

label_join(rate(demo_api_request_duration_seconds_count[5m]), "endpoint", " ", "method", "path")

Extract part of a label and store it in a new label:

label_replace(up, "hostname", "$1", "instance", "(.+):(\\d+)")

Subqueries

Calculate the 5-minute-averaged rate over a 1-hour period, at the default subquery resolution (= global rule evaluation interval):

rate(demo_api_request_duration_seconds_count[5m])[1h:]

Calculate the 5-minute-averaged rate over a 1-hour period, at a 15-second subquery resolution:

rate(demo_api_request_duration_seconds_count[5m])[1h:15s]

Using the subquery result to get the maximum rate over a 1-hour period:

max_over_time(
  rate(
    demo_api_request_duration_seconds_count[5m]
  )[1h:]
)

#915 转载:Linux 命令行:获取上一个命令的参数

2023-10-24

readline 快捷键

readline 是 GNU 的库,实现命令行编辑功能,bash、ftp、python、zsh、mysql 等程序的命令行界面都是使用 readline 实现的,具体实现有 ctrl-r(查找历史命令)、ctrl-p(上一历史命令)、ctrl-a(跳到行首)等。

最重要的还有本次我们所需的 alt+.(dot)esc+.meta+. 得到上一命令的最后一个参数。还有更多快捷键可以参考 readline shortcuts

mac 上没有 alt 和 meta 键,所以我一般使用 esc+. 来获取上一条最后的参数。

shell/bash 历史展开 (history expand)

!$ 获取上一命令的最后一个参数。

历史展开由命令、参数、操作符三部分构成,分别表示展开哪一条命令、该命令的哪个参数、对命令的操作。命令展开完成之后才会进入 .bash_history 中,即执行 history 命令不会看到用于历史展开的参数。

本节的所有命令都假设当前 bash 已经有如下的历史:

$ history
1 echo 1 2 3 4 5
2 ls 6 7 8 9 10
3 echo 11 12 13 14 15
4 cat 16 17 18 19 20
5 echo 21 22 23 24 25
  • a. 命令(Event Designators),用 ! 开始一个历史展开。
$ !n                # 表示第n条命令,如!2表示执行ls 6 7 8 9 10
$ !-n               # 表示倒数第n条命令,如!-3表示执行echo 11 12 13 14 15
$ !!                # 表示上一条命令,是!-1的快捷方式
$ !string           # 表示以string开始的最近的一条命令,如!echo表示echo 21 22 23 24 25
$ !?string?         # 表示含有string的最近的一条命令,如!?6?表示cat 16 17 18 19 20
$ ^string1^string2^ # 表示执行上一条命令,并将其中的第一个string1替换为string2,如果string1不存在则替换失败,不会执行命令。
$ !#                # 表示当前命令现在已经输入的部分,如echo 1 2 !#会执行echo 1 2 echo 1 2
  • b. 参数(Word Designators),命令中选取指定的参数,: 用于分割命令部分与参数部分。
$ !!:0              # 表示上一命令的第0个参数,即命令本身,得到的是echo
$ !2:n              # 表示第2个命令的第n个参数,如!2:2得到的是7
$ !!:^              # 表示上一命令第1个参数,可进一步简写为!^,与!!:1同义,得到的是21
$ !!:$              # 表示上一命令的最后一个参数,可进一步简写为!$,得到的是25
$ !!:x-y            # 表示第x到第y个参数,-y意为0-y,如!-2:3-4得到的是18 19
$ !!:*              # 表示上一命令的参数部分,可进一步简写为!*,如!!:*得到的是21 22 23 24 25
$ !!:n*             # 跟!!:n-$同义
$ !!:n-             # 意为!!:n-$-1,从第n个参数到倒数第二个参数,如!-2:3-得到的是18 19

通过 bash 历史展开实现创建并 cd 到目录的方式为:

$ mkdir somewhere/dir && cd !#:1          # 其中!#表示本行所有命令"mkdir somewhere/dir && cd”,:1取第一个参数就是目录名
  • ​c. 操作符(Modifiers),在可选的参数部分之后,用一个或多个 : 操作符加特定字符。

  • h 去除最后的一个文件路径部分,

    • 假设上一条命令 echo /tmp/123/456/,则 cd !:1:h:h 意为 cd /tmp/123
    • 假设上一条命令 echo /tmp/123/456,则 cd !:1:h:h 意为 cd /tmp
  • t 去除所有的开始的文件路径部分,
    • 假设上一条命令为 echo /tmp/123/456/,则 cd !:1:t 意为 cd
    • 假设上一条命令为 echo /tmp/123/456,则 cd !:1:t 意为 cd 456
  • r 去除后缀,
    • 假设上一条命令为 echo /tmp/bbs.c,则 echo !:1:r 意为 echo /tmp/bbs
  • e 得到后缀,
    • 假设上一条命令为 echo /tmp/bbs.c,则 echo !:1:e 意为 echo .c
  • p print 命令而不执行
  • [g]s/string1/sting2/ 将命令的 string1 替换为 string2,g 意为全局替换,
    • 假设上一条命令为 echo 1 2 1,则 !:gs/1/3/ 意为 echo 3 2 3
    • 上面的 ^string1^string2^ 相当于 !!:s/string1/string2/
  • [g]& 意为执行上次替换,g 意为全局替换。
    • 接上例,假设上一条命令为 echo 123451,则 !:& 意为 echo 323451

使用符号 $$_

在 shell/bash 里 $ 符号表示当前是普通用户(# 是 root),在 sh 脚本或命令行里,$ 开头的表示变量。

# root 用户
[root@localhost iotl] # echo root
# 普通用户
[root@localhost iotl] $ echo hello

以下是一些特殊变量:

  • $_ 代表上一个命令的最后一个参数
  • $# 参数个数。
  • $0 当前 shell 名称(zsh or bash)或脚本的名字。
  • $1 传递给该 shell 脚本的第一个参数。
  • $2 传递给该 shell 脚本的第二个参数。
  • $@ 表示所有的独立的参数,将各个参数分别加双引号返回。
  • $* 以一对双引号给出参数列表。
  • $$ 脚本运行的当前进程 ID 号或所在命令的 PID。
  • $? 显示最后命令的退出状态,0 表示执行成功,其他表示失败。
  • $! 代表最后执行的后台命令的 PID

总结

  1. 快捷键使用 esc + .
  2. 执行最近的以 xxx 开头的命令 !xxx
  3. 快捷修改上一条命令用三个 ^^^ (例如 ^/^/etc^ls / 改为 ls /etc)
  4. 重复当前行前面的命令 !#
  5. 灵活的找到前面的参数: 冒号 !!:3^ 开始、& 结束(可以缩写为 !^!&,注意不是 !#
  6. 重复前面的命令的参数 !#:1
  7. 实际中更多使用快捷键,比如交换前后字母有 ctrl + tesc + t ,比如交换前后单词(word)有 alt + t (别和 alt + dot 搞混了)

参考链接:

  • [1] http://www.gnu.org/software/bash/manual/bashref.html#History-Interaction
  • [2] http://stackoverflow.com/questions/4009412/bash-first-argument-of-the-previous-command
  • [3] https://www.cnblogs.com/tianyapiaozi/archive/2012/09/21/bash_history_arguments.html

#914 奔 35 程序员的职业生涯思考

2023-10-22

周一被问到这个问题,我真的是一身汗,因为我发现自己真的很长一段时间没有思考过这个问题了。
每天按照固定的步骤处理各种问题,时间久了,工作就成为了一种惯性。
或许很多人都是这样,但我认为这不是一种积极的,上进的人生态度。

生活中充满了变化,我们应该能够适应不断变化的生活,而不是在稳定中消磨掉自己的斗志。
运气好的话,一直稳定下去,运气不好,今后要摔大跟头的。

我刚开始工作时,经理告诉我们,大家在这里相聚,应该要有一点基础的认知,大家都不是杰出人才,只有资历深浅而已,没有谁高人一等。
我认为说的非常实际,现实就是这样,大多数人都是普通人。我也是一个一般水平的开发者。
这么说并不是要打击个人的积极性,这完全不耽误我们大家在工作学习中力争上游。只是说,我们要清楚认知到我们力争的这个“上游”在社会上的一个真实位置。
如果我们没有这个认知,可能对职业生涯的规划产生错误的影响。

好,现在开始说说职业生涯规划的事情。

TODO: 未完待续...

#913 PSF & JetBrain:2022 Python 开发者调查

2023-10-21

https://lp.jetbrains.com/zh-cn/python-developers-survey-2022/

主要用途

  • 数据分析
  • Web
  • 机器学习
  • 运维
  • 爬虫

59% 是全职员工,其中 65% 是程序员,年龄主要是 20~40 岁,大部分人在 10 人以下团队工作。

操作系统

  • Linux 59%
  • Windows 58%
  • macOS 26%
  • BSD 3%

PS:Linux 相较去年下降了 4%

IDE

  • VSCode
  • PyCharm

Python 版本

Python 3 的比例达到 95% 左右,其中:

  • Python 3.10 45%
  • Python 3.9 23%
  • Python 3.8 17%

虚拟环境

Name Percent Remark
venv 43%
virtualenv 37%
conda 21% environment.yml
poetry 16% poetry
pipenv 14% pipfile
virtualenvwrapper 6%
hatch 3%

Web 框架

  • Flask 39%
  • Django 39%
  • FlaskAPI 25%

单元测试框架

  • pytest 51%
  • unittest 24%
  • mock 10%

ORM 框架

  • SQLAlchemy 35%
  • Django ORM 28%
  • Raw SQL 16%

数据库

  • PG 42
  • MySQL 37
  • SQLite 36
  • Mongo 19
  • Redis 16

大数据

  • Apache Spark
  • Apache Kafka
  • Apache Hadoop/MapReduce
  • Dask
  • Apache Hive
  • Apache Beam
  • ClickHouse
  • Apache Flink
  • Apache Samza
  • Apache Tez

#912 贷款计算公式的推导

2023-10-17

等额本息

设:

  • 总金额 A
  • 月利率 R
  • 贷款期限 N
  • 每月还款 X

那么:

第 1 期剩余金额:$Q_1 = A(1+R)-X$
第 2 期剩余金额:$Q_2 = Q_1(1+R)-X$
。。。
第 n 期剩余金额:$Q_n = Q_{n-1}(1+R)-X$

推导一下:

$$
\begin{aligned}
Q_2 &= Q_1(1+R)-X \
&= (A(1+R)-X)(1+R)-X \
&= A(1+R)^2-X(1+R)-X \
&= A(1+R)^2-X(1+(1+R))
\end{aligned}
$$

$$
\begin{aligned}
Q_3 &= Q_2(1+R)-X \
&= (A(1+R)^2-X(1+(1+R)))(1+R)-X \
&= A(1+R)^3-X(1+(1+R))(1+R)-X \
&= A(1+R)^3-X(1+R)^2-X(1+R)-X \
&= A(1+R)^3-X(1+(1+R)+(1+R)^2)
\end{aligned}
$$

$$
\begin{aligned}
Q_k &= A(1+R)^k-X(1+(1+R)+ \cdots +(1+R)^{k-1})
\end{aligned}
$$

根据等比数列公式 $ S_n=\frac {a_1(1-q^n)} {1-q} $(q 不等于 1):

$$
\begin{aligned}
1+(1+R)+ \cdots +(1+R)^{k-1} &= \frac {1 \times (1-(1+R)^k)} {1-(1+R)} \
&= \frac {1-(1+R)^k} {1-(1+R)}
\end{aligned}
$$

代入上面的推导方程中:

$$
\begin{aligned}
Q_k &= A(1+R)^k-X \cdot (1+(1+R)+ \cdots +(1+R)^{k-1}) \
&= A(1+R)^k - X \cdot \frac {1-(1+R)^k} {1-(1+R)}
\end{aligned}
$$

k = N 的时候:

$$
\begin{aligned}
Q_n &= A(1+R)^N - X \cdot \frac {1-(1+R)^N} {1-(1+R)} = 0 \
A(1+R)^N &= X \cdot \frac {1-(1+R)^N} {1-(1+R)} \
X &= \frac {A(1+R)^N} {\frac {1-(1+R)^N} {1-(1+R)}} \
&= \frac {A(1+R)^N} {\frac {(1+R)^N-1} {R}} \
&= \frac {AR(1+R)^N} {(1+R)^N-1}
\end{aligned}
$$

也就是:

$$
\begin{aligned}
每期还款金额 = 贷款金额 \times 月利率 \times \frac {(1+月利率)^{还款期数}} {(1+月利率)^{还款期数}-1}
\end{aligned}
$$

等额本金

设:

  • 总金额 A
  • 月利率 R
  • 贷款期限 N

每月还款计算:

$$
\begin{aligned}
X_1 &= {\frac A N} + A \cdot R \
X_2 &= {\frac A N} + (A - ({\frac A N})) \cdot R \
X_3 &= {\frac A N} + (A - ({\frac A N}) \cdot 2) \cdot R \
X_N &= {\frac A N} + (A - ({\frac A N}) \cdot (N - 1)) \cdot R
\end{aligned}
$$

也可以视作:

$$
\begin{aligned}
X_1 &= {\frac A N} + {\frac N N} \cdot A \cdot R \
X_2 &= {\frac A N} + {\frac {N - 1} N} \cdot A \cdot R \
X_3 &= {\frac A N} + {\frac {N - 2} N} \cdot A \cdot R \
X_{N-1} &= {\frac A N} + {\frac 2 N} \cdot A \cdot R \
X_N &= {\frac A N} + {\frac 1 N} \cdot A \cdot R
\end{aligned}
$$

总还款金额:

$$
\begin{aligned}
T &= A + A \cdot R \cdot {\frac {(1 + 2 + \cdots + N)} N} & 总还款金额\
&= A + A \cdot R \cdot ({\frac {N + 1} {2}}) \
I &= A \cdot R \cdot ({\frac {N + 1} {2}}) & 总利息 \
\end{aligned}
$$