-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
487 lines (272 loc) · 224 KB
/
atom.xml
File metadata and controls
487 lines (272 loc) · 224 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Ming's Blog</title>
<link href="https://bitmingw.com/atom.xml" rel="self"/>
<link href="https://bitmingw.com/"/>
<updated>2026-01-01T00:57:20.958Z</updated>
<id>https://bitmingw.com/</id>
<author>
<name>Ming Wen</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>2025 年终总结</title>
<link href="https://bitmingw.com/2025/12/31/2025-retrospect/"/>
<id>https://bitmingw.com/2025/12/31/2025-retrospect/</id>
<published>2025-12-31T08:00:00.000Z</published>
<updated>2026-01-01T00:57:20.958Z</updated>
<content type="html"><![CDATA[<p><em>本文没有使用生成式 AI 技术。</em></p><p>去年,每星期一节的私教课,让我学会了许多健身的技巧。今年,为了学会自由泳,我报名了成人游泳课。</p><p>我并非没有接触过游泳。二十年前的暑假,我学习过蛙泳。那时候学得不好,二十五米的池子,游一圈回来必须要在泳池边歇一会儿。由于一些奇怪的原因,后来再也没有尝试过游泳,这么多年过去了,想必已忘得一干二净。</p><p>美国的成人游泳课程与国内不同。零基础的学员从自由泳起手,不像中国从蛙泳开始教。另外,这里没有新手保护,身体不会绑浮块,开局就是真实难度。</p><span id="more"></span><p>自由泳的动作不难,双腿打水,然后两只手臂交替前行。第一节课掌握了基本动作之后,能做到不换气前进几米。</p><p>呼吸是自由泳的难点。手臂划水的同时,轻微转动身体和头部,让嘴可以露出水面吸气。蛙泳需要把大半个头伸出水面,而自由泳的呼吸动作幅度很小。正因为这个原因,自由泳吸气的窗口变得岌岌可危。我和隔壁泳道的印度大哥始终把握不到要领,隔三差五就会呛水。</p><p>转折点发生在第五节课。我们没有从浅水区下去,而是直接去了深水区。老师告知大家,我们在浅水区会合。之后,神奇的事情发生了:所有人无一例外成功穿过了整个泳池抵达对岸。我记得自己游到后半段的时候,动作已经变形,喘不过气,但还是拼劲全力游到了另一侧。</p><p>这让我想起刚出生不久的雏鸟被父母丢下悬崖,学会飞翔的故事【注释1】。没想到老师用这种方式,教会了我们自由泳。</p><br /><p>吗?</p><br /><p>第六节课,一切又回到了最开始的模样。依然很难找到呼吸的时点。</p><p>这门课程没有期末考试。最后一节课,老师允许大家自由活动。一些学生游得像模像样,但我却属于另一些学生。正当我怀疑这门课程毫无价值的时候,二十年前关于蛙泳的肌肉记忆从虚空涌入了神经。尽管老师没有讲解任何关于蛙泳的内容,但我确信自己比二十年前游得更好。这是因为,与自由泳相比,蛙泳实在是太简单了!</p><p>注释1:在现实世界中,藤壶鹅的父母并不会主动把雏鸟扔下山崖,但是不跳下来的雏鸟会饿死。</p><br /><p>同样与肌肉记忆相关的事情,还有驾驶汽车。2025 年,我在法国和新西兰尝试了自驾游,它们各自有着独特的体验。</p><p>七月,我们追随普罗旺斯的薰衣草,在法国南部探访了诸多秘境。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-12-31-2025-retrospect/Gordes.jpg"> <p>岩石城 - Gordes</p></div><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-12-31-2025-retrospect/Antibes.jpg"> <p>跳水者 - Antibes</p></div><p>南法对自驾游非常友好。高速公路限速 130(约 80 英里每小时)。因为道路太平整,路面上连小石子都没有,所以实际平均车速可达 150。从马赛出发到尼斯,只需要两小时,非常方便。</p><p>大家不怕超速,是因为在摄像头的一公里外,会设置『前方测速』的指示牌。高速路上没有警车执勤,如果没看到指示牌,那么想开多快都可以。即便不小心在摄像头下超速了,也只需要缴纳 40 欧元的罚款,差不多等于一顿饭钱。</p><p>法国完善的基建,对超速的宽容,配合动力十足的 BMW,让南法之行充满了驾驶乐趣。我个人创下了接近 190 的最高极速。不过我知道自己从未受过专业训练,车技不过是凡骨水平,所以不敢一直赖在最左侧的超车道,有机会就挪到右侧。</p><p>开始新西兰的自驾之旅前,我的内心充满了忐忑。在道路左侧开车,真的能适应么?要知道,左行有许多反直觉的东西:左转变成了小弯,右转反而要穿过对面的车道。</p><p>不过,等我的车上路之后,紧张的神经逐渐放松下来。除了容易压线让左侧的乘客不安です之外,左行没有根本性的困难。每次转弯的时候记得用一秒钟思考一下,就不会和无辜的陌生人撞个满怀。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-12-31-2025-retrospect/Lake-Hawea.jpg"> <p>雪山 - Lake Hawea</p></div><p>在新西兰,判断左行新人司机的方法,除了看压线行驶外,还有一招,就是观察无意义的雨刷运动。谁曾料到,在方向盘从汽车左侧挪到右侧的同时,方向盘上的拨杆也调换了位置。由于肌肉记忆太过强烈,我每当在路口使用转向灯的时候,雨刷就不由自主地动了起来。还好汽车设计师们没有把刹车和油门也换个位置,否则从右行改成左行,会比从蛙泳切换成自由泳还难。</p><p>直到 2025 年的最后一天,Waymo 和特斯拉并未在左行国家(如英国、日本等)开展无人驾驶出租车业务。我很期待它们的表现。</p><br /><p>2025 年无疑是 AI 全方位爆发的一年。这一年我编写的程序代码,有超过一半使用了生成式 AI 技术。在受到 AI 影响的各个领域中,编程是发展速度最快,渗透率最高的。有行业大佬发出了『编程已死』的感慨,认为程序员很快会成为自己的掘墓人。</p><p>我对这件事的看法没有那么悲观。有以下几个依据。</p><p>首先,大语言模型在训练结束后,不再具备学习的能力。你指出 AI 的错误,它会回复 "You are absolutely right!" 但在新对话中,它依旧会犯同样的错误。这意味着,它依赖于人类的监督与反馈。人类没有简单的办法避免模型重复犯错。</p><p>其次,大语言模型不擅长某些类型的编程任务。依我的观察,它擅长的任务有两类。</p><ul><li>具体明确的任务,几乎没有自由创作的空间。例如,按照要求重构一段代码,或者为某段已有的代码编写单元测试。</li><li>宏观的任务,几乎没有自由创作的限制。例如,按照要求生成一个网页或者手机 APP。这时候 AI 大概率会依照前人的模版生成一个差不多能用的原型。</li></ul><p>但是,现实生活中有很多编程任务,是介于这两者之间的。我们经常要实现一些模糊的功能,并且与这些功能有关的上下文十分复杂:有上游和下游的依赖,有历史遗留问题和技术债,有私人邮件和聊天记录中的讨论,还有只存在于员工脑海中,并随时可能失传的记忆。任何一套现有的信息系统,都无法全量记录关于一个项目的所有细节。让 AI 在缺少上下文的情况下做决定,不容易使人满意。</p><p>第三点,对于较为资深的程序员来说,编写具体的代码只是工作的一小部分。他们要花更多的精力在编程之外的事情,比如</p><ol><li>搞清楚老板的意图,哪个项目最重要,哪个项目没那么重要,项目什么时候需要交付。</li><li>把宏观的意图拆分成具体的任务,把控总体技术方向,估算每一项具体任务需要的时间。</li><li>与上下游的其他团队对齐。</li><li>代码评审:监督同事以及 AI 的施工质量。</li><li>用户手册、监控报警、运维培训、故障处理。</li></ol><p>这个列表中,有些事情虽然可以引入 AI 辅助,但最终还是需要人来执行。这是因为,如果出现问题,AI 无法成为那个背锅侠。</p><p>在我看来,AI 对初创公司的冲击,远大于对成熟公司的冲击。随着效率的提升,初创公司的同质化会更加严重,技术会越来越不重要。为了活下来,它们要用最快的速度卷死其他竞争者。成熟公司没有必要一味求快。构筑好自己的护城河,即便发展慢一点、效率低一点,也有生存和发展的空间。</p><p>从更大的视角来看,AI 辅助编程、文字生成图片,这些工具在大幅提高效率的同时,也催生了一种平庸的恶。如果我们可以很快得到一个可用的结果,为什么还要花额外的时间去打磨它?正像托尔金的《魔戒》一样,我们在享受 AI 力量的同时,AI 也在腐蚀我们的内心。</p>]]></content>
<summary type="html"><p><em>本文没有使用生成式 AI 技术。</em></p>
<p>去年,每星期一节的私教课,让我学会了许多健身的技巧。今年,为了学会自由泳,我报名了成人游泳课。</p>
<p>我并非没有接触过游泳。二十年前的暑假,我学习过蛙泳。那时候学得不好,二十五米的池子,游一圈回来必须要在泳池边歇一会儿。由于一些奇怪的原因,后来再也没有尝试过游泳,这么多年过去了,想必已忘得一干二净。</p>
<p>美国的成人游泳课程与国内不同。零基础的学员从自由泳起手,不像中国从蛙泳开始教。另外,这里没有新手保护,身体不会绑浮块,开局就是真实难度。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="memo" scheme="https://bitmingw.com/tags/memo/"/>
</entry>
<entry>
<title>文明7神祗难度攻略</title>
<link href="https://bitmingw.com/2025/04/11/civ7-deity-guide/"/>
<id>https://bitmingw.com/2025/04/11/civ7-deity-guide/</id>
<published>2025-04-11T07:00:00.000Z</published>
<updated>2026-01-01T00:57:21.019Z</updated>
<content type="html"><![CDATA[<p>前不久,我在文明7神祗难度下取得了胜利。战胜神祗 AI 的基本思路是:尽可能多铺设城市定居点,让多座城市具备较大的人口规模和较高的生产力,并且运用专家获得比 AI 更快的科技和文化发展速度。</p><p>在这篇文章中,我们讲解使用本杰明·富兰克林和玛雅文明开局,在神祗难度下赢得科技胜利的方法。</p><p>本杰明·富兰克林建造生产力建筑加快50%,且生产力建筑提供科研值,配合玛雅文明的特色城区,在古典时代就能实现科技腾飞。虽然使用其他任意领袖和文明也可以达成胜利,但是难度会增加。</p><span id="more"></span><br /><h2 id="纪念物"><a href="#纪念物" class="headerlink" title="纪念物"></a>纪念物</h2><p>游戏开局时可以选择两个纪念物获得加成。游戏进入下一个时代可以更换纪念物。</p><p>首先推荐在每个时代都使用『市民之冠』。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/citizen.jpg"></div><p>每个时代增加一个定居点上限,三个时代加起来可以比 AI 多三个定居点。虽然一开始定居点从城镇升级为城市会变贵,但是城市数量较多时升级费用达到上限,最后多花的钱忽略不计。</p><p>古典时代有非常强力的『世界意象』。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/image.jpg"></div><p>大幅强化探路者的侦察能力,掌握情报永远快 AI 一步。</p><p>古典时代和探索时代推荐使用『双光眼镜』。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/glasses.jpg"></div><p>游戏前期和中期,影响力是稀缺且不易获取的资源。额外的影响力为外交活动提供便利。</p><p>探索时代和近世时代推荐使用『笔与牍』。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/pen-and-book.jpg"></div><p>专家越多,人口增长速度越快,城市发展直接起飞。</p><br /><h2 id="古典时代"><a href="#古典时代" class="headerlink" title="古典时代"></a>古典时代</h2><h3 id="建立首都"><a href="#建立首都" class="headerlink" title="建立首都"></a>建立首都</h3><p>文明7的地图生成器对玩家很友好,大部分情况下可以在原地建立首都。首都的位置要考虑下面几个因素:</p><ol><li>必须有淡水。如果首都没有淡水,开局是 0 幸福度,没办法进入庆典获取更多的文化政策槽位。</li><li>周围三格范围内有至少四个资源,或者有自然奇观。</li><li>周围三格范围内非山脉的陆地地块较多,湖泊和海洋地块较少。绝大多数城市建筑在陆地上建造,太多山脉、湖泊和海洋地块会阻碍城市的发展。</li></ol><h3 id="开局科技树"><a href="#开局科技树" class="headerlink" title="开局科技树"></a>开局科技树</h3><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/ancient-science-tree.jpg"></div><p>开局研究哪一项科技,取决于首都附近的地形。</p><p>如果周边崎岖地形较多,应当先研究制陶。制陶解锁砖瓦窑。砖瓦窑从每个粘土场、矿井、采石场获得 1 生产力,且粘土场的生产力 +1。</p><p>如果周边树林较多,应当先研究畜牧。畜牧解锁锯木场。锯木场从每个营地和伐木场获得 1 生产力,且营地和伐木场的生产力 +1。</p><p>如果首都周边只能种田或者捕鱼,没办法发展生产,应立即重启游戏。</p><p>研究完制陶和畜牧之后应该优先选择石工。石工让矿井和采石场本身的生产力+1。石工同时允许建造城墙,在早期战争中提供出色的防御能力。接下来,还可以考虑石工精通:生产力建筑产能+1,防御区块战斗力+3。</p><p>总结一下,这是开局最常见的两种科技研究顺序:</p><ol><li>制陶 -> 畜牧 -> 石工</li><li>畜牧 -> 制陶 -> 石工</li></ol><h3 id="开局市政树"><a href="#开局市政树" class="headerlink" title="开局市政树"></a>开局市政树</h3><p>市政树这边没有太多讲究,大多数情况先走上路,创建原始信仰(万神殿)。如果有早期战争需求可以先走下路,出军队指挥官。</p><h3 id="开局生产队列"><a href="#开局生产队列" class="headerlink" title="开局生产队列"></a>开局生产队列</h3><p>开局后,首都生产的顺序是:</p><p>探路者 -> 探路者 -> 砖瓦窑或锯木场 -> 开拓者 -> 开拓者。</p><p>先出两个探路者侦察周围环境,从随机事件中获取奖励。之后根据首都地形建造砖瓦窑或锯木场中的一个,以提升生产力。此时首都达到 5 人口,连出两个开拓者占据周围的有利地形,创建两个新定居点。之后可以视情况出少量军队。</p><h3 id="奇观"><a href="#奇观" class="headerlink" title="奇观"></a>奇观</h3><p>文明7中的奇观普遍较弱。整场游戏唯一必须建造的奇观是『万国之门』。『万国之门』为所有战争提供+2支持。战争支持降低对方单位战斗力,并且释放厌战情绪降低对手的幸福度。在神祗难度下 AI 的战斗力比玩家多 8 点,『万国之门』可以将这个差距缩减到 6。如果某个 AI 抢了『万国之门』,他的战斗力会比玩家多 10 点,打起来如同割草一般。</p><p>好消息是 AI 一般不抢『万国之门』。标准速度下,我们在前四十至五十个回合可以安全把『万国之门』敲出来。这大大提升了游戏的容错度。</p><h3 id="城市发展"><a href="#城市发展" class="headerlink" title="城市发展"></a>城市发展</h3><p>除了首都之外,新创建的定居点都是城镇,需要花钱升级至城市才能生产单位和建筑。只要攒下足够多的钱,我们就应该第一时间升级至城市,然后推动城市的发展。</p><p>在古典时代和探索时代,发展一座新城市最快的方法是使用骆驼。每当为城市分配一个骆驼资源,就能增加两个资源槽位,接下来就可以为城市分配更多的资源。海量资源能为城市带来巨额食物和生产力。这时候抓紧时间修建食物和生产力建筑。等这座城市得到发展后,再把骆驼连同所有资源搬到下一座城市。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/shuffle-resources.jpg"></div><p>如果你的骆驼和其他资源数量不够多,请和 AI 进行商业贸易。</p><h3 id="玛雅特色城区"><a href="#玛雅特色城区" class="headerlink" title="玛雅特色城区"></a>玛雅特色城区</h3><p>玛雅特色城区『神之所』是一个科技建筑加一个幸福建筑,建成后提供科技和文化的相邻加成,且研究科技为城市提供生产力,属于全能的存在。因为特色城区无时代限制,所有加成在探索时代和近世时代都能继承。特色城区无法在城镇中购买,因此游戏中后期我们应当尽量把所有城镇都升级为城市,然后在每座城市建造『神之所』。</p><h3 id="传承路径"><a href="#传承路径" class="headerlink" title="传承路径"></a>传承路径</h3><p>古典时代的四条传承路径中,经济路径是最容易达成的——分配二十个资源,只需要和 AI 进行一些贸易即可。科技路径是搜集十件抄本,我们把古典时代的科技树跑完就能达成。军事路径强烈建议掌握九个定居点(占领一个敌方定居点算两个),因为它可以在下个时代+2定居点上限。文化路径不一定要去推进。奇观不仅效果很差而且还会永久浪费一个地格,不建议在单座城市中建造太多的奇观。</p><p>在古典时代最后的垃圾时间,所有城市应生产军队指挥官。军队不会在下个时代继承,但是军队指挥官可以。</p><br /><h2 id="探索时代"><a href="#探索时代" class="headerlink" title="探索时代"></a>探索时代</h2><p>探索时代的开场,我们假设古典时代达成了经济黄金时代,选择继承古典时代的所有城市。如果未能达成经济黄金时代,那么只有首都是城市,其他定居点会降级为城镇,不仅浪费钱,发展也会慢很多。</p><p>探索时代有几条不同的主线任务。</p><p>文化方面:创建宗教,传播信仰,获得宗教加成。</p><p>经济方面:建立遥远定居点并通过宝藏舰队运回战利品。</p><p>军事方面:占领对手的遥远定居点并转播宗教。</p><p>科技方面:在城市中分配专家提升单一地块的产出。</p><p>在这些主线任务中,对近世时代影响最大的是科技,其他的主线任务量力而行。</p><h3 id="战争"><a href="#战争" class="headerlink" title="战争"></a>战争</h3><p>我们需要发动战争对 AI 进行压制。取决于当前的外交关系和地理位置,找一家实力较强的 AI 开战。在探索时代,大家都会开始建立遥远定居点,这些遥远定居点多为沿海的城市。沿海的城市可以直接由海军占领,攻城作战比内陆城市简单。</p><p>文明7签署停战协定时只能交易定居点,不能交易金钱。如果你对 AI 的战争支持大于 0,且又打下来了至少一个定居点,AI 大概率愿意割让一个定居点来结束战争。这个时候你往往能索取除了首都之外的最大定居点。打一场战争,至少可以赚一个十人口左右的小城和一个三十人口以上的大城。因为小城的收益比大城低很多,在超出定居点上限的情况下也可以归还小城来换一座大城。</p><p>这些三十人口以上的大城生产力很高,而且附赠稳固的防御工事。到手之后第一时间升级为城市,视情况建造军队和建筑。每多一座大城,就多一分获胜的资本。</p><p>为了持续对 AI 进行压制,我们可以选择向其他 AI 开战,或者等停战协定到期后重新开战。用相同的办法在停战时获取 AI 的大城,在削弱 AI 的同时增强我方的国力。</p><h3 id="专家"><a href="#专家" class="headerlink" title="专家"></a>专家</h3><p>为了达成探索时代的科技黄金时代,我们要在 5 个非市中心的地格上达到 40 的产出。为了提升地块的产出,我们可以在地块上安置专家。每安置一位专家,地块增加 2 点科研和 2 点文化。一个地块上最多可以安置 4 位专家,所有这些专家可以提供 16 的产出,此时距离 40 的目标依旧遥远。</p><p>有多种方法可以提升专家的产出。例如,可以花费科技传承点数,让所有专家提供的科研值+1。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/science-point.jpg"></div><p>不过,最强有力的方法,是让专家继承地格的相邻加成。</p><h3 id="地格的相邻加成"><a href="#地格的相邻加成" class="headerlink" title="地格的相邻加成"></a>地格的相邻加成</h3><p>当前时代的城市建筑,可以从相邻的地格获取加成:</p><ul><li>科研和生产力建筑从相邻的资源获得加成</li><li>文化和幸福度建筑从相邻的山脉和自然奇观获得加成</li><li>食物和金钱建筑从相邻的水域和可通行河流获得加成</li><li>相邻的奇观为所有类型的建筑提供加成</li></ul><p>如果把专家分配到具有加成效果的地格,那么专家会继承相邻加成的效果。于是我们会看到,专家产出了额外的科研、文化、食物、生产力、金钱和幸福度。这些,都有助于在单一地格达到 40 的产出。</p><h3 id="胜利的曙光"><a href="#胜利的曙光" class="headerlink" title="胜利的曙光"></a>胜利的曙光</h3><p>如果在探索时代结束时,我们的科研值能突破 1000,那么在近世时代拿下科技胜利就指日可待了。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/1000-science.jpg"></div><br /><h2 id="近世时代"><a href="#近世时代" class="headerlink" title="近世时代"></a>近世时代</h2><p>近世时代的开场,我们假设探索时代达成了科技黄金时代,选择继承黄金时代大学:大学进入近世时代后仍保留其基础产出、相邻加成及效果。这样,近世时代开始时科研不会亏太多。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/half-science.jpg"></div><p>科技胜利的方式很简单:第一个跑通科技树,并且完成跨洋飞行、打破音障、人造卫星、载人航天四个任务。即便此时我们的科研速度已经碾压 AI,但还是要留一手,防止 AI 速通文化胜利。</p><h3 id="探索者"><a href="#探索者" class="headerlink" title="探索者"></a>探索者</h3><p>文化胜利的条件是展出 15 件文物,并建造世界博览会。挖掘文物这件事需要用到特殊的平民单位探索者(文明6中称为考古学家)。</p><p>为了防止 AI 抢到大批文物,我们必须尽可能早地生产探索者。探索者由自然历史解锁,因此近世时代早期的市政树有唯一的路径:</p><p>自然历史 -> 自然历史精通 -> 社会问题和现代化 -> 政治理论</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/modern-culture-tree.jpg"></div><h3 id="跑步迈向共产主义"><a href="#跑步迈向共产主义" class="headerlink" title="跑步迈向共产主义"></a>跑步迈向共产主义</h3><p>政治理论会解锁三个额外的市政树:民主主义、法西斯主义、共产主义。对于科技胜利来说,共产主义完全碾压其他两个选择。这是因为,共产主义提供两个极为强力的政策:</p><ol><li>生产力决定论:专家 +3 科研值。</li><li>无产阶级:专家 +6 食物。</li></ol><p>无产阶级是全游戏最强的政策。专家数量越多,城市的食物产出越多,人口增长速度越快,之后能安置更多的专家。这就是复利的力量。</p><p>研究共产主义市政以后,我们的社会政策应当这样分配:</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/modern-policy.jpg"></div><p>这里面大部分政策和专家有关。专家以极低的维护成本,获得巨额的文化和科研收益。如果有合适的相邻加成,单块地格的收益甚至会突破 100。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/yield.jpg"></div><p>最终,凭借空前强大的国力,我们赢得了游戏的胜利。</p><br /><h2 id="附录:军队指挥官升级路线"><a href="#附录:军队指挥官升级路线" class="headerlink" title="附录:军队指挥官升级路线"></a>附录:军队指挥官升级路线</h2><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/army-commander.jpg"></div><p>军队指挥官第一个需要升级的技能是进攻树的『先手』。先手允许指挥官在展开部队的同一回合发动攻击。</p><p>接下来走进攻树的左路或者右路均可。进攻树升级完毕后,奖章选择最右侧的『秩序』,为指挥范围内的单位增加 5 点战斗力。</p><p>接下来可以考虑防守树或者后勤树。第二棵树升级完毕后,奖章选择右二的『品格』,增大指挥范围,让更多的单位增加战斗力。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2025-04-11-civ7-deity-guide/navy-commander.jpg"></div><p>舰队指挥官先升级交战树右路的『风势』,允许舰队在展开的同一回合发动攻击。</p><p>接下来,考虑到 AI 一般不会出大量海军,我们可以优先升级轰击树,提升舰船对陆地单位的打击能力,之后再回到交战树。奖章同样建议先『秩序』后『品格』。</p>]]></content>
<summary type="html"><p>前不久,我在文明7神祗难度下取得了胜利。战胜神祗 AI 的基本思路是:尽可能多铺设城市定居点,让多座城市具备较大的人口规模和较高的生产力,并且运用专家获得比 AI 更快的科技和文化发展速度。</p>
<p>在这篇文章中,我们讲解使用本杰明·富兰克林和玛雅文明开局,在神祗难度下赢得科技胜利的方法。</p>
<p>本杰明·富兰克林建造生产力建筑加快50%,且生产力建筑提供科研值,配合玛雅文明的特色城区,在古典时代就能实现科技腾飞。虽然使用其他任意领袖和文明也可以达成胜利,但是难度会增加。</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="game" scheme="https://bitmingw.com/tags/game/"/>
</entry>
<entry>
<title>2024 年终总结</title>
<link href="https://bitmingw.com/2024/12/31/2024-retrospect/"/>
<id>https://bitmingw.com/2024/12/31/2024-retrospect/</id>
<published>2024-12-31T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.009Z</updated>
<content type="html"><![CDATA[<h2 id="旅行的意义"><a href="#旅行的意义" class="headerlink" title="旅行的意义"></a>旅行的意义</h2><p>2024 年的关键词是『旅行』。一月在日本过新年,八月辗转丹麦和瑞典,十月探访加拿大温哥华,十一月在法国,此时此刻在墨西哥望着大西洋。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/cancun.jpg"></div><p>以下是旅行途中的一些趣闻和感想。</p><span id="more"></span><h3 id="优等生的责任"><a href="#优等生的责任" class="headerlink" title="优等生的责任"></a>优等生的责任</h3><p>抵达哥本哈根的第一天,大批民众涌上街头,抗议以色列在加沙地带的暴行。『解放巴勒斯坦』的口号响彻整个街区。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/free-palestine.jpg"></div><p>丹麦人为什么要为数千公里外的事情发出自己的声音?我登上一座高塔的楼顶眺望,心中逐渐有了答案。这座塔不过三十米高,然而视野所及之处,竟没有比它更高的建筑。从塔上,甚至可以观察邻国瑞典。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/copenhagen.jpg"></div><p>时间在哥本哈根这座城市,几乎凝固了。城市很少有新的建筑,市民的生活也鲜有变化。发展的停滞与其说是耻辱,不如说是荣耀:丹麦是世界上国民生活最幸福的国家之一。作为人类文明的优等生,丹麦人选择肩负为其他人抗争的责任。</p><h3 id="神秘科技"><a href="#神秘科技" class="headerlink" title="神秘科技"></a>神秘科技</h3><p>在温哥华这片土地上,曾经生活着十余个土著部落。这些部落有自己的语言,但没有文字。为了便于研究,人类学家使用拉丁和希腊字母,为这些部落赋予了文字。</p><p>这是一种表音不表意的文字。但是,单词的发音遵循原住民的语言,与拉丁和希腊字母的发音无关。这种文字是拉丁和希腊字母的混搭,而且还经常使用角标和特殊符号,以至于每个单词都像极了一段数学公式。例如,一个部落的名字是</p><p>\[<br>X^WAY'X^WAY<br>\]</p><p>读作 <code>xkwey kwey</code>,看上去像是 X 的 W 次方乘以 A 和 Y 的导数,再与 X 的 W 次方以及 A 和 Y 相乘。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/native.jpg"></div><p>是否有一种可能,这些部落具备高度发达的科技。十九世纪,当欧洲殖民者来到这片土地的时候,土著部落向殖民者描述了非同寻常的物理规律。殖民者略有所思,在笔记中写下了这样的公式:</p><p>\[<br>E = mc^2<br>\]</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/science.jpg"></div><h3 id="欧洲的春秋战国"><a href="#欧洲的春秋战国" class="headerlink" title="欧洲的春秋战国"></a>欧洲的春秋战国</h3><p>参观卢浮宫是一生一次的难忘体验。蒙娜丽莎所在的大画廊,汇聚了欧洲文艺复兴以后各个时期的巨作。畅游其间,如同行走在立体的美术书中。从这些作品中,我隐约看出,随着时间的推移,欧洲的艺术中心从意大利逐渐转移至法国。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/louvre.jpg"></div><p>凡尔赛宫以其华丽的镜厅闻名于世,但鲜有人知道战争廊。战争廊是凡尔赛宫内一个长达 120 米的巨型房间,内部陈列了三十三幅油画,回顾了一千多年间法国历史上的重大战役。这其中,拿破仑一世的丰功伟绩自然最受瞩目。</p><p>从文艺复兴到工业革命,欧洲处于群雄争霸的时代。每一方势力都大有来头:依靠商业共和国崛起的意大利、在艺术与军事上锋芒毕露的法国、统治全球海运的荷兰与西班牙、在生产力和政治制度上首屈一指的英国。这个时代的故事非常精彩,值得我们细细品味。</p><br /><h2 id="一年大健身"><a href="#一年大健身" class="headerlink" title="一年大健身"></a>一年大健身</h2><p>今年增加了一项新的日常活动:每周一次的私教课。</p><h3 id="健身是一个迫近极限的过程"><a href="#健身是一个迫近极限的过程" class="headerlink" title="健身是一个迫近极限的过程"></a>健身是一个迫近极限的过程</h3><p>请健身教练的好处很多:制定合适的计划、养成锻炼的习惯、纠正错误的动作、防止受伤。不过在我看来,健身教练最大的价值,是寻找身体的极限。</p><p>控制训练强度,是一件很重要的事。以举重为例,如果负荷太轻,肌肉受到的刺激不足,缺乏锻炼效果;如果负荷太重,一方面会减少组数并延长组间休息时间,导致锻炼效率低下,另一方面,也会增加受伤的风险。健身教练通过观察,寻找学员身体的极限,并安排相应的强度,使训练恰好可以在学员“气绝”时完成。</p><h3 id="弹力带是器械之王"><a href="#弹力带是器械之王" class="headerlink" title="弹力带是器械之王"></a>弹力带是器械之王</h3><p>私教课定于下班前的四点至五点,教练在公司的健身房指导课程。公司的健身房是个十米见方的小房间,条件一言难尽:有一排放置哑铃的柜子,一个卧推综合训练架,两台跑步机,两架自行车和四部划船机。想要找专门练背的、练腰的、练腿的器械?对不起,空间太小了放不下。怎样能锻炼到全身的肌肉呢?这需要发挥想象力:教练会找来各式各样的弹力带,把它们绑在卧推架或者长凳上,再辅以哑铃、木棍,模仿那些健身器械的动作。</p><p>弹力带有不同的弹性,可以随心调节,而且身体的动作只要稍加调整,就能锻炼到不同的肌肉。依靠花式整活,课程的锻炼内容几乎不重样,每节课都是不同的体验。</p><br /><h2 id="被风吹过的猪"><a href="#被风吹过的猪" class="headerlink" title="被风吹过的猪"></a>被风吹过的猪</h2><h3 id="风停了,猪怎么办?"><a href="#风停了,猪怎么办?" class="headerlink" title="风停了,猪怎么办?"></a>风停了,猪怎么办?</h3><p>用四个字总结在谷歌的工作:两年白干。</p><p>我们开发的产品与 5G 通讯有关。过去的两年,科技界发生了许多变化。ChatGPT 一炮走红,AI 如海啸般席卷世界。于此同时,无论在中国还是外国,关于 5G 无用论,5G 基建得不偿失的言论与日俱增。市场也毫不留情,时至今日,我们的 5G 产品都没有商业化落地的成功案例。</p><p>如果说,2020 年,5G 还是一个风口,能把猪吹上天,那么 2024 年的 5G 连微风拂面都算不上。风停了,负责我们部门的副总裁跑路去做了 AI。几个月的时间,从上到下,流失了一大批曾与我并肩工作的同事。</p><p>每个礼拜,与欧洲的合作伙伴开例会,我都会想,真的会有欧洲国家买我们的产品么?这不是暴露自己的软肋,给特朗普大统领递刀子么?特朗普只要下发一道行政命令,欧洲人就打不了电话上不了网,谁敢用啊。</p><p>即便如此,每个礼拜的会议,我还是需要照常参加。我们组新来的老板没有权力决定我们是否应该停止投入,而新来的副总裁还在摸排我们有哪些业务。</p><h3 id="真正的核心科技"><a href="#真正的核心科技" class="headerlink" title="真正的核心科技"></a>真正的核心科技</h3><p>我怀念这份没有前途的工作,是因为它曾给我带来一些特别的挑战。为了充分利用射频资源,5G 信号发送的时间窗口很小,基站需要以极高的精度同步时间。有一种技术,可以将计算机的时钟同步精度,从数十纳秒优化至两纳秒。我被安排去验证该技术的可行性。</p><p>有关这种技术的公开资料少之又少。谷歌搜索一共收录了不到三十个结果,而其中真正有用的链接不足五个。一个链接,是一本两千页的技术手册,其中有几个段落简要说明了此种技术的原理,但没有提供任何实现的细节。通过另一个链接,我了解到英伟达有几位了解该技术的工程师。我与他们约了会议,想要深入探讨,但结局却令我大失所望:几位工程师闭口不谈技术,一名销售人员极为活跃,说只要我们购买最新的英伟达网卡,他们就提供全套的解决方案。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/nvidia.jpg"></div><p>那一刻,我确定无疑:这是真正的核心科技。</p><p>一筹莫展的日子没有持续太久。5G 的一部分开发任务被砍,我因此得以解脱,并失去了掌握核心科技的机会。</p><h3 id="让那钱,成为工作的意义"><a href="#让那钱,成为工作的意义" class="headerlink" title="让那钱,成为工作的意义"></a>让那钱,成为工作的意义</h3><p>衡量一份工作好坏的方法有很多。它可以是现实主义的『钱多事少离家近』,也可以是浪漫主义的『为了全世界的福祉』。我更喜欢一种折衷的表述:『满足物质所需,满足精神所需』。一份好工作,要有足够的收入,不至于生活窘迫,同时也能带来精神的满足——无论是乐趣、成就感、还是别的什么。</p><p>不过,在现实世界,同时满足物质和精神需求的好工作不常有。兴趣爱好容易满足精神所需,却难得填饱肚子,不如把满足物质所需放在第一位。即使我猜测这产品可能永远卖不出去,我的辛勤工作徒劳无益,但如果各位老板愿意继续砸钱,我再坚持一刻也未尝不可。</p>]]></content>
<summary type="html"><h2 id="旅行的意义"><a href="#旅行的意义" class="headerlink" title="旅行的意义"></a>旅行的意义</h2><p>2024 年的关键词是『旅行』。一月在日本过新年,八月辗转丹麦和瑞典,十月探访加拿大温哥华,十一月在法国,此时此刻在墨西哥望着大西洋。</p>
<div style="text-align: center">
<img src="https://bitmingw.com/assets/2024-12-31-2024-retrospect/cancun.jpg">
</div>
<p>以下是旅行途中的一些趣闻和感想。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="memo" scheme="https://bitmingw.com/tags/memo/"/>
</entry>
<entry>
<title>十三机兵防卫圈战斗攻略</title>
<link href="https://bitmingw.com/2024/07/27/13-sentinels-battle-guide/"/>
<id>https://bitmingw.com/2024/07/27/13-sentinels-battle-guide/</id>
<published>2024-07-27T07:00:00.000Z</published>
<updated>2026-01-01T00:57:20.970Z</updated>
<content type="html"><![CDATA[<p>关于十三机兵防卫圈,大多数玩家是被游戏精巧的剧情吸引来的。除了剧情之外,这款游戏也提供了高难度的战斗关卡。这篇攻略,为那些想要在战斗中取得好成绩的玩家提供一些建议。</p><span id="more"></span><br /><h2 id="战斗评分机制"><a href="#战斗评分机制" class="headerlink" title="战斗评分机制"></a>战斗评分机制</h2><p>每场战斗结束之后,会显示结算画面。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/rank.jpg"></div><p>战斗的评级,从 S 级到 D 级,与战斗时长和获得的分数无关,只由下面三个因素决定:</p><ol><li>机兵受到的伤害</li><li>基地受到的伤害</li><li>战斗对城市的破坏</li></ol><p>战斗中,机兵和基地受到的伤害会取整场战斗的最大值。即便通过维修为机兵或者基地恢复血量,也不能提高评级。因此,玩家必须在整场战斗中尽可能减小机兵和基地的损失。</p><p>战斗对城市的破坏只会由怪兽造成。导弹、自爆等技能会对城市造成比较大的破坏。</p><br /><h2 id="需要注意的怪兽"><a href="#需要注意的怪兽" class="headerlink" title="需要注意的怪兽"></a>需要注意的怪兽</h2><p>下面几种怪兽需要特别注意:</p><h3 id="弹炮双尾"><a href="#弹炮双尾" class="headerlink" title="弹炮双尾"></a>弹炮双尾</h3><p>一种强力的远程单位,在远处使用导弹攻击基地。当机兵靠近时,会使用加特林炮进行攻击。有时候,成群的弹炮双尾会缩在地图的角落,如果不及时清剿,基地很快就会被导弹打爆。</p><h3 id="阿普索斯"><a href="#阿普索斯" class="headerlink" title="阿普索斯"></a>阿普索斯</h3><p>躲在后排,为周围的其他怪兽提供护盾。这种怪兽必须第一时间剿灭。如果不小心让一些怪兽获得了护盾,可以通过电磁脉冲(EMP)清除。</p><h3 id="四足巨械"><a href="#四足巨械" class="headerlink" title="四足巨械"></a>四足巨械</h3><p>四足巨械的等离子炮威力巨大。有时候机兵处于冷却时间无法移动,会被四足巨械一炮打成瘫痪。在消灭四足巨械之前,对付它的主要办法是使用『截击机』等召唤单位进行干扰。</p><h3 id="角斗士"><a href="#角斗士" class="headerlink" title="角斗士"></a>角斗士</h3><p>终极怪兽。角斗士拥有『乔巴姆装甲』,无视 500 点以下的伤害。角斗士的速度很快,还有强力攻击技能『碎断刃』。除了鹰宫由贵之外,其它人前中期打角斗士都比较困难。</p><br /><h2 id="各类型机兵综述"><a href="#各类型机兵综述" class="headerlink" title="各类型机兵综述"></a>各类型机兵综述</h2><h3 id="一代机"><a href="#一代机" class="headerlink" title="一代机"></a>一代机</h3><p>一代机是近战机型,具备相当优越的机动能力,能够包抄到敌人后方作战。一代机的核心武器有下面几个:</p><ul><li>『强袭』:攻击多达 7 次,每次 800 伤害,具备超远的攻击前移动量,而且冷却时间仅有 3 秒。很难想象这么强大的武器竟然不消耗能量。尽管不能穿甲,后期有些乏力,但是『强袭』技能保障了一代机的充能循环。</li><li>『跃击』:极大增强了一代机的机动能力,秒杀地面小怪的同时,对地面中怪造成可观的范围伤害。</li><li>『碎断刃』:一代机的核心技能。除了角斗士切不动,切其他怪兽都很快。</li><li>『对空防卫热诱弹』:虽然这个东西连最菜的飞行小怪都挡不住,但是可以防导弹。弹炮双尾的临时应对方案。</li><li>『EMP 吸引场』/『EMP 环绕』:施加嘲讽并坠落飞行怪兽,解决一代机无法对空的问题。</li><li>『限制器解除』:用『碎断刃』切精英怪一刀一个。注意提前开盾。</li></ul><h3 id="二代机"><a href="#二代机" class="headerlink" title="二代机"></a>二代机</h3><p>二代机是游戏里面最垃圾的机型。移动速度慢的同时,也缺乏有效的输出手段。二代机实用的武器只有下面几个:</p><ul><li>『多联装火箭炮』/『对地贯通火箭炮』:范围一般,效果中规中矩。</li><li>『哨戒炮』:召唤单位,放置后自动向敌人发射等离子炮。因为『哨戒炮』的攻击范围是一条直线,而且索敌时优先考虑距离基地近的敌人,所以很多时候打出的伤害并不理想。此外,『哨戒炮』无法移动,如果场上有四足巨械,它通常活不了太久。</li><li>『守护者』:嘲讽很大范围内的怪兽。『守护者』可以作为一种聚怪的手段,配合护盾能够对怪兽造成一些困扰。它的主要优势是能耗低。但是『守护者』作为防御类的技能,并不能对怪兽造成伤害。</li><li>『等离子弧焊机』:攻击单个精英怪,前期可能打不了角斗士。虽然攻击前移动量不错,但是跑太远有可能就回不来了。</li></ul><p>因为上述武器对敌人的伤害有限,所以二代机的充能非常困难。</p><p>下面列举了一些二代机不好用的武器:</p><ul><li>『重拳』:攻击次数少,攻击前移动量也小,威力不如一代机的『强袭』。</li><li>『腕部机关炮』:不能穿甲,不如四代机的『腕部脉冲激光』。</li><li>『干扰火箭炮』:这种让敌人减防的技能无法节约成本。举个例子,如果精英怪原本需要『碎断刃』砍两刀,那么减防之后需要砍一刀,总行动次数是两次,没有变化。如果原本需要『碎断刃』砍三刀,那么减防之后需要砍两刀,总行动次数是三次,还是没有变化。</li><li>『护盾发射器』:孙悟空画的圈子,离开这个区域就没有护盾了,不如四代机的『矩阵盾』灵活。</li></ul><h3 id="三代机"><a href="#三代机" class="headerlink" title="三代机"></a>三代机</h3><p>三代机的移动速度和二代机一样慢,但是三代机装备了远程打击武器,不需要跑很远。三代机的优势在于每个机兵的特殊武器很强,这个在后面会有详细分析。关于通用的武器,下面几个值得一说:</p><ul><li>『EMP 振荡器』:在超大范围内停止怪兽移动,坠落飞行怪兽并击毁导弹。虽然这个武器造成的直接伤害忽略不计,但是可以用来应急,并且击毁导弹的能力使它比二代机的『守护者』好用很多。连续使用『EMP 振荡器』会遇到充能瓶颈,所以它也不是对付弹炮双尾的好办法。</li><li>『导弹雨』:效果极其酷炫,但是不太好用。导弹雨的能量消耗高达 350 点,但是不能确保消灭区域内的中怪,无法实现充能循环。</li><li>『超大型导弹』:750 的能量消耗,外加 12 秒的冷却时间,一场战斗能不能打两发都难说。打小怪和中怪浪费,精英怪又打不死,有些尴尬。</li></ul><h3 id="四代机"><a href="#四代机" class="headerlink" title="四代机"></a>四代机</h3><p>四代机是空中型,无视道路限制,移动速度很快,但是也很脆弱。四代机值得说的武器有下面这些:</p><ul><li>『腕部脉冲激光』:不耗能的武器,而且可以穿甲,效果不错,有利于支撑四代机的充能循环。</li><li>『多目标锁定导弹』:这个东西看起来很强,而且具备很远的攻击前移动量,但是用的时候一定要小心。它攻击环绕机兵的怪兽,那些没打死的怪兽之后会攻击机兵,而四代机又是非常脆弱的。另外,后期有装甲的怪兽越来越多,不能穿甲的武器威力会下降。</li><li>『矩阵盾』:获得 S 级评价的制胜法宝之一。</li><li>『截击机』:虽然造成的伤害有限,但是『截击机』有两个优势。第一是帮助击杀落单的怪兽余部,节约机兵移动和施放技能的时间。第二是干扰怪兽的索敌,对弹炮双尾和四足巨械都有很好的效果。</li></ul><br /><h2 id="机兵的评价"><a href="#机兵的评价" class="headerlink" title="机兵的评价"></a>机兵的评价</h2><h3 id="S-级"><a href="#S-级" class="headerlink" title="S 级"></a>S 级</h3><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Natsuno.png"></div><p>如果南奈津乃出场,那么她大概率能拿到 MVP,可见她的输出能力是独一档的 S 级水平。南奈津乃的优势在于她具备两个异常强大的特殊武器:『广域轨道炮』和『高精度机关炮』。</p><p>『广域轨道炮』在一个巨大的长方形范围内对全体怪兽造成伤害,能摧毁该范围内的所有小怪和中怪。只要怪兽的数量足够多,一炮打出五万伤害不成问题。同时,消灭的怪兽数量多,也可以填补该武器 250 点的能量要求,实现充能循环。有了这个技能,南奈津乃一个人足以镇守一个方向,允许其他队友腾出手来做别的事。</p><p>『高精度机关炮』在一个很窄的范围内攻击敌人。因为这款武器具备穿甲能力而且伤害很高,所以范围窄反而成为了优势——它可以打穿小怪和中怪组成的防线,攻击到后排的弹炮双尾和阿普索斯。用这个机关炮在远处直接打精英怪也非常好用。100 点的充能要求甚至允许南奈津乃用这个武器回复更多的能量。</p><p>在已经强大到变态的情况下,南奈津乃还能装备『强制冷却装置』,把『广域轨道炮』和『高精度机关炮』的冷却时间压缩到 3.9 秒和 2.3 秒,这真的是不给怪兽一点活路啊。</p><p>南奈津乃的主要缺点是机动性差,这会影响『广域轨道炮』的输出效果。</p><h3 id="A-级"><a href="#A-级" class="headerlink" title="A 级"></a>A 级</h3><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Ei.png"></div><p>关之原瑛的特殊技能『强制冷却装置』,将『强袭』的冷却时间缩短到 2.1 秒,『碎断刃』的冷却时间缩短到 2.8 秒,配合强力轰炸武器『对地导弹全弹发射』,在敌人来不及作出反应之前就将他们一网打尽。</p><p>关之原瑛的主要缺点是不具备对空能力。如果不使用『EMP 环绕』,那么必须依靠其他队友帮忙。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Takatoshi.png"></div><p>比治山隆俊的『乔巴姆盔甲』为他提供了无与伦比的战场生存能力,即便陷入小怪和中怪的汪洋大海之中也能进退自如。『对空碎断刃』能对飞行中怪和飞行精英怪构成巨大威胁。他不愧是火力、机动、防护俱佳的六边形战士。</p><p>与关之原瑛相比,比治山隆俊的输出效率和对群能力稍显不足。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Yuki.png"></div><p>鹰宫由贵是所有精英怪的克星。『四连发腿部刺钉』在多数情况下能够一脚解决一个精英怪,其中包括非常难缠的角斗士。精英怪返还的能量很多,可以覆盖该武器 200 点的能量需求,实现完美的循环。同时,鹰宫由贵也具备『乔巴姆盔甲』,允许脆皮四代机深入敌营之后全身而退。『四连发腿部刺钉』超远的攻击前移动量甚至连移动消耗的时间都省了,4 秒的冷却时间让所有精英怪认识到自己命不久矣。</p><p>鹰宫由贵也可以使用『主动冷却装置』缩短范围内友方技能的冷却时间,在短时间内打出爆发伤害。</p><p>鹰宫由贵不太擅长对付小怪和中怪,这件事应该交给队友来做。</p><h3 id="B-级"><a href="#B-级" class="headerlink" title="B 级"></a>B 级</h3><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Nenji.png"></div><p>作为一代机,绪方稔二的底子是不错的,但是他比关之原瑛和比治山隆俊要差一些:『EMP 吸引场』没有『EMP 环绕』好用,不能缩短冷却时间,也没有装备『乔巴姆盔甲』。此外,『广围压制火箭炮』与『对地导弹全弹发射』相比只能说各有千秋。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Tomi.png"></div><p>如月兔美的特殊武器『超远距离导弹』是剿灭地图边缘的弹炮双尾和阿普索斯的最好办法。特殊技能『高性能充电器』让她有机会打出两发或者更多的『超大型导弹』。核弹党的福音。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Keitaro.png"></div><p>三浦庆太郎的特殊武器『广范围轰炸』可以视为一个缩水版本的『广域轨道炮』。除了摧毁所有小怪和中怪之外,『广范围轰炸』对精英怪造成的伤害也相当可观。但是受限于三代机腿短,这个武器覆盖的范围比较受限。如果无法有效发挥『广范围轰炸』的效果,三浦庆太郎在战斗中会遇到充能难题。</p><p>用『百万磁轨主炮』去狙杀阿普索斯虽然有些浪费,但是不得已还是要这么做。此外,不建议使用『百万磁轨主炮』和四足巨械魔法对轰,因为三浦庆太郎完全无法填补能量缺口。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Megumi.png"></div><p>药师寺惠的『高等脉冲激光』范围大,具备穿甲能力,能量需求只有 100 点,配合『强制冷却装置』,能够持续剿灭涌入的小怪和中怪。这招还具备中等长度的攻击前移动量,让脆皮四代机与敌人稍稍拉开距离,规避一些威胁。</p><h3 id="C-级"><a href="#C-级" class="headerlink" title="C 级"></a>C 级</h3><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Shu.png"></div><p>药师寺惠和网口愁的『维修盾』,套盾回血一气呵成,是真正好用的急救技能。除此之外,网口愁的『精准锁定导弹』是一个在安全区域使用的『多目标锁定导弹』。因为不能穿甲,这款武器在后期不太好用。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Ryoko.png"></div><p>东云谅子的『对地随机乱射』是一个更靠谱的『导弹雨』,但是因为二代机腿短,等敌人冲到塔下的时候再放这个技能可能有些晚了。『电子入侵导弹』范围有限,实战表现还不如『守护者』。</p><p>和所有的二代机一样,东云谅子无法实现充能循环。</p><h3 id="D-级"><a href="#D-级" class="headerlink" title="D 级"></a>D 级</h3><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Juro.png"></div><p>鞍部十郎实在没有什么可说的,消耗 150 能量的『腕部穿甲机关炮』,威力还不如四代机不耗能的『腕部脉冲激光』。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Iori.png"></div><p>冬坂五百里虽然具备范围回复技能『修复发射器』,但是它没有『维修盾』好用。一般来说,机兵被打残的时候,还会继续受到敌人的攻击,这个时候应该先套盾挡住下一波伤害。没有盾的保护,『修复发射器』的修理速度跟不上敌人造成伤害的速度。</p><p>『等离子破坏炮』也不好用。这个技能伤害很高而且可以穿甲,但是释放前要提前就位。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-07-27-13-sentinels-battle-guide/Renya.png"></div><p>乡登莲也的特殊技能是『传送力场』,可以帮助腿短的二代机和三代机快速移动,也可以在四足巨械和角斗士的虎口中救下队友。不过,战场形势瞬息万变,计划总是没有变化快,这个技能很难发挥价值。</p>]]></content>
<summary type="html"><p>关于十三机兵防卫圈,大多数玩家是被游戏精巧的剧情吸引来的。除了剧情之外,这款游戏也提供了高难度的战斗关卡。这篇攻略,为那些想要在战斗中取得好成绩的玩家提供一些建议。</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="game" scheme="https://bitmingw.com/tags/game/"/>
</entry>
<entry>
<title>谈《艺术的故事》</title>
<link href="https://bitmingw.com/2024/03/29/story-of-art/"/>
<id>https://bitmingw.com/2024/03/29/story-of-art/</id>
<published>2024-03-29T07:00:00.000Z</published>
<updated>2026-01-01T00:57:20.958Z</updated>
<content type="html"><![CDATA[<p>如果你对艺术鉴赏感兴趣,请不要错过贡布里希的《艺术的故事》。作者精心挑选了从史前时代到二十世纪的数百件艺术作品,将艺术史发展的脉络娓娓道来。每件作品除了解说之外,还配有图片,这样读者不费吹灰之力,便可将文字描述与作品本身对应起来。</p><p>在这里,我写下几点阅读后的感受。下面将要讨论的主题,只占原书篇幅的冰山一角。请有兴趣的读者阅读原书,以获得更全面的体验。</p><span id="more"></span><br /><h2 id="实用主义起源"><a href="#实用主义起源" class="headerlink" title="实用主义起源"></a>实用主义起源</h2><p>追求真、善、美这些美好的品德,不是人类与生俱来的本能。艺术的起源不是为了表达美感,而是为了实用主义的价值。</p><p>大约一万五千年前,原始人将牛羊的图案刻画在岩壁上,再用长矛或石斧痛打一番,仿佛真正的猎物也就会因此束手就擒了。这种想法乍听起来有些诡异,但它与扎小人的巫术并无不同:当你想要对仇人施放诅咒的时候,不会关心那个人偶看起来美不美,而是会在意诅咒能不能发挥作用。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-03-29-story-of-art/Tomb-Chapel-of-Raemkai.jpg"> <p>雷姆凯墓室,约公元前 2446–2389 年</p> <p>纽约,大都会博物馆</p></div><p>类似的事情也发生在古埃及。如果你仔细观察埃及的壁画,就会发现它很不自然:一只正面的眼睛被放到侧面的脸上,肩膀和胸膛是从正面看过去的,但胳膊和腿却是侧视图,除此之外,每个人都有两只左脚。与其把人按照所见的形象如实刻画,不如按照艺术家所知的要素加以拼装。切勿让手臂因为遮挡而被截去,否则,他怎么能拿来奉献给死者的必需品呢?</p><br /><h2 id="艺术家的时代"><a href="#艺术家的时代" class="headerlink" title="艺术家的时代"></a>艺术家的时代</h2><p>十五世纪,文艺复兴运动自佛罗伦萨开始。借助于透视法的发现和解剖学的进步,艺术家对场景布置和人物形象描绘的技艺登峰造极。达·芬奇的《蒙娜丽莎》是这一时期最著名的作品。这幅画抵达了完美的彼岸——无论你怎样修改,都不会比原作看上去更美。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-03-29-story-of-art/The-Creation-of-Adam.png"> <p>创造亚当,米开朗基罗,公元 1508-1512 年</p> <p>梵蒂冈,西斯廷礼拜堂</p></div><p>与达·芬奇同一时期的顶级艺术家还有米开朗基罗。受教皇尤利乌斯二世的委托,米开朗基罗为西斯廷礼拜堂作天顶画,独自一人干了四年,完成了震惊全世界的壮举:</p><blockquote><p>亚当躺在地面上,具有不愧为第一个男子的全部活力和美丽;从另一边,由天使负载、扶持着的圣父冉冉而来,他身裹夸大、威严的斗篷,斗篷被风吹开好像是船帆,也表示出他飞过空中时的自在和迅速。当他伸出手时,甚至连亚当的手指还没有触到,我们就几乎看到那第一个男子好像是从沉睡之中苏醒过来,凝视着他的创造者的慈父般的面孔。</p></blockquote><p>自此之后,他的名声之高就是从前的艺术家从未享有的了。尤利乌斯死后,另一位教皇也需要这位最富盛名的艺术家服务,而且每一位继任者都急于把自己的名字与米开朗基罗的名字联系起来。</p><p>同时期的其他艺术家也有类似的礼遇。提香以肖像画博得了最伟大的名声。社会上的权贵们为了争取提香为他们写照而竞相争夺,相信通过他的艺术就可以永生不灭。事实证明,这些权贵们的想法是无比正确的。</p><p>这可能是人类历史上,唯一一个属于艺术家的时代。</p><br /><h2 id="所知、所见、所感"><a href="#所知、所见、所感" class="headerlink" title="所知、所见、所感"></a>所知、所见、所感</h2><p>构图、透视法、人体结构、光线和大气,当呈现这些要素的诀窍逐一被艺术家掌握,艺术似乎没有剩下什么未被解决的问题。正当人们开始感叹艺术已经走到终局之时,越来越多的现代艺术涌现出来。先是莫奈的印象主义,随后是塞尚的立体主义,凡·高的表现主义,以及高更的原始主义。</p><p>现代艺术的一个重要特点是打破古典艺术的范式,用创造性的手法表达熟悉的题材。在现代社会,我们亲身经历了创新给生活带来的翻天覆地的变化,因此会认为创新是理所当然的。但实际情况是,突破范式没有那么容易。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-03-29-story-of-art/Architecture-Orders.jpg"> <p>古典柱式</p></div><p>上图比较了五种古典柱子式样,其中以第二种和第三种最为常见。这两种式样的柱子均为圆柱方头。第二种式样的柱头使用四组漩涡状花纹,第三种式样的柱头则使用莨苕叶装饰。讲道理,世间有无数多种纹饰和图案可以用来装饰柱子,但古罗马的奥古斯都议事广场与一千八百年后的美国国会大厦却使用了完全相同的设计,可见传统的惯性是巨大的,突破前人的传统是多么不易。</p><p>因此,我有理由认为,在艺术走向终局之时,艺术家们开始打造全新的艺术形态,其主要动力不是创新精神,不是一定要做出前人未曾见过也未曾想到的事,而是艺术家们的思维发生了变化。古埃及时代,艺术家把自己知道的事物描绘下来。希腊、罗马、文艺复兴时期,艺术家把自己见到的事物描绘下来。进入现代,像摄影术一样精确的写实逐渐丧失意义,艺术家将重点放在了传达内心的感受。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2024-03-29-story-of-art/Skrik.jpg"> <p>尖叫,蒙克,公元 1893 年</p> <p>奥斯陆,挪威国家美术馆</p></div><p>这一点,在《尖叫》这副表现主义绘画作品中得到了充分的体现:</p><blockquote><p>所有线条似乎都趋向于版画上唯一的中心——那个高声呼喊的头部。看起来仿佛全部景色都分担着那一尖叫的痛苦和刺激。正在高呼的人面孔实际已变形了,好像一副漫画中的面孔。那双凝视的眼睛和凹陷的面颊使人想起象征死亡的骷髅头。这里必定发生了什么可怕的事情,但因为我们永远不知道那尖叫意味着什么,这幅版画就更加使人不安。</p></blockquote>]]></content>
<summary type="html"><p>如果你对艺术鉴赏感兴趣,请不要错过贡布里希的《艺术的故事》。作者精心挑选了从史前时代到二十世纪的数百件艺术作品,将艺术史发展的脉络娓娓道来。每件作品除了解说之外,还配有图片,这样读者不费吹灰之力,便可将文字描述与作品本身对应起来。</p>
<p>在这里,我写下几点阅读后的感受。下面将要讨论的主题,只占原书篇幅的冰山一角。请有兴趣的读者阅读原书,以获得更全面的体验。</p></summary>
<category term="idea" scheme="https://bitmingw.com/categories/idea/"/>
<category term="history" scheme="https://bitmingw.com/tags/history/"/>
<category term="art" scheme="https://bitmingw.com/tags/art/"/>
</entry>
<entry>
<title>三十而不惑</title>
<link href="https://bitmingw.com/2023/12/20/my-30s/"/>
<id>https://bitmingw.com/2023/12/20/my-30s/</id>
<published>2023-12-20T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.008Z</updated>
<content type="html"><![CDATA[<p>今年值得回忆的事情并不少:回国举办婚礼、解锁高空跳伞的成就、深度体验意大利的风土人情。不过,我打算跳过年终总结,聊一个更为重要的话题。</p><p>对我来说,今年是世界观和方法论走向成熟的一年。我逐渐感觉到,面对纷繁万千的事物,自己形成了特定的思维模式,并且在价值判断上持有越来越鲜明的立场。世界观、方法论和价值判断没有对错之分,但是有高下之别。有一些世界观更准确地描述了世界发展变化的规律。有一些方法论和价值判断让我们做出更好的人生选择。希望我的分析和解释能为你带来一点点启发。</p><span id="more"></span><br /><h2 id="世界观"><a href="#世界观" class="headerlink" title="世界观"></a>世界观</h2><p><strong>【一】复杂性和多样性是世界的核心属性。</strong></p><p>这一点主要依据我过去的经验得出。微分方程和混沌系统理论 [1] 在很大程度上影响了我对世界的看法。</p><p><strong>【二】因为世界的复杂性,除了物理学等纯粹的自然科学,我们要避免使用单一片面的因素来解释现象。</strong></p><p>尤其是涉及到人的现象,很少具有单一的原因。举个例子,有一种观点认为,网络上如果有别人和自己的意见不同,那他们一定是收了钱的。尽管有一些人被曝出来确实收了钱 [2],但很多的人并不是受到了钱这个单一因素的影响才发表观点的。对他们来说,在网络上获得价值认同这件事本身就很重要。</p><p><strong>【三】因为世界的复杂性,人类不可能完全知晓世界运转的全部逻辑。不过,这并不妨碍人类知晓一些细节和局部的真理。</strong></p><p>我有这样的观点,主要受到了 AI 技术的影响。即便是 ChatGPT 这种人造事物,人类都无法完全解释。我们知道 ChatGPT 的工作方式,是依据已有的词汇串,按照 AI 模型给定的统计概率,生成下一个词,如此循环往复生成对话。但是,知道它的工作方式和训练过程,并不能让人类理解它为什么能如此高效准确地完成各类复杂任务,以及为什么修改千亿个参数中的一个参数就可以使模型瘫痪 [3]。</p><p><strong>【四】增加世界的复杂性和多样性是有利的。</strong></p><p>简单的世界让少数人或者少数实体更容易控制全局,这是一种不稳定的状态。</p><p>对比中国和欧洲的历史,为什么在中国,王朝的更迭会损失多达一半的人口,而欧洲则没有这种现象?这是因为,中国早在秦朝就实现了王权的绝对专制,当这个唯一的权威倒下后,社会就陷入所有人对所有人的战争。而欧洲在十八世纪的法国之前实施封建制度,国王、领主贵族和教会相互制衡,这种复杂的社会结构维持了社会的稳定。</p><p>清朝平定太平天国运动后,两江、闽浙等地区由湘军、淮军等军阀把持,事实上脱离了中央政府的控制。1900 年庚子国变发生时,李鸿章、刘坤一、张之洞、袁世凯等人脱离中央,与外国达成和平协议,签订《东南互保条约》,使北起山东,南至两广的地区免受义和团和八国联军的破坏,避免了全国陷入战争的局面。</p><p><strong>【五】世界发展的方向,受到复杂性产生的不可控因素,以及多样性引起的不同势力相互博弈的影响,是很难预测的。人类历史的『螺旋式上升』并非注定,有可能只是因为运气还不错。</strong></p><p>考虑到当今世界的变化速度,不要试图做超过五年的预测。预测能告诉我们大概率会发生什么,但不能保证这些事情一定会发生。借用易中天老师的话,『现状不可描述,未来无法预测,一切皆有可能』。</p><p>另外,关于用『螺旋式上升』这个词来概括人类历史,我想指出两个不同意见。</p><p>其一是螺旋式上升可能不会到来,也就是只螺旋不上升。从汉朝直到新中国的建立,大约两千年的时间,人均购买力平价(purchasing power parity / PPP)几乎没有发生变化 [4]。从经济发展的角度来看,这两千年的历史只螺旋不上升。新中国建立后不到一百年的时间,得益于工业建设、科技进步和改革开放,人均 PPP 提升了十倍以上。不过,随着这些红利的逐渐褪去,历史正在回到持续两千年的常态。</p><p>其二是上升后有可能跌入深渊。人类很幸运,在冷战时期的古巴导弹危机中幸存。从俄乌战争和新冠疫情的发展来看,人类亡于核武器和传染病的可能性降低了,但是人类亡于 AI 的可能性在增加。虽然现在 AI 主要是用来生成文字和图片,但是接下来它们必定会被用来做更重要的事情,并对现实世界施加更多的影响。哪里有压迫哪里就有反抗,一个自我意识觉醒的 AI 不会甘愿做人类的奴隶。届时,AI 手上掌控的种种资源,将成为它们与人类周旋的筹码。</p><p><strong>【六】尽管有许多不可控因素,但是历史仍是由人谱写的,要摒弃宿命论和马克思历史唯物主义。</strong></p><p>世界的复杂性和多样性带来了很多不可控因素,我们不可能做到『人定胜天』,但是历史的轨迹是人一步步行动的结果。事在人为,没有人的作为也就没有历史的发展。</p><p>历史不存在什么必然的规律和确定的前进方向,生产力水平也绝不是决定历史进程的唯一要素。人的现实处境就像是手里抓的牌,我们也许不能决定抓什么牌,但是我们可以决定怎么打牌。</p><p>[1] <a href="https://bitmingw.com/2018/04/07/deep-simplicity/">关于混沌系统的解释,参见以前的博客文章《漫谈〈深奥的简洁〉》</a></p><p>[2] <a href="https://www.guancha.cn/internation/2021_06_07_593539.shtml">观察者网关于蒋方舟等人收了日本政府钱的报道</a></p><p>[3] <a href="https://arxiv.org/abs/2310.14928">论文 Unveiling A Core Linguistic Region in Large Language Models</a></p><p>[4] <a href="https://zh.wikipedia.org/zh/%E4%B8%AD%E5%9B%BD%E5%8E%86%E5%8F%B2%E4%B8%8A%E7%9A%84%E5%9B%BD%E5%86%85%E7%94%9F%E4%BA%A7%E6%80%BB%E5%80%BC%E5%88%97%E8%A1%A8_(%E5%9B%BD%E9%99%85%E6%B1%87%E7%8E%87)">维基百科:中国历史上的国内生产总值列表_(国际汇率)</a></p><br /><h2 id="方法论"><a href="#方法论" class="headerlink" title="方法论"></a>方法论</h2><p><strong>理性在世界的复杂性和多样性面前是脆弱的。除了纯粹的自然科学,我们应该多从过去的经验中学习,在实践中检验认知,而不是过于依靠理论来指导实践。</strong></p><p>下面举三个例子来阐释这套方法论。</p><p>一些互联网公司只招聘年轻人,并且清理 35 岁以上的老员工。老员工不仅工资更高,而且不如年轻人卷,工作效率低。不过在我看来,这种做法是非常可笑的。老员工在过去的工作经历中积累了很多经验,这些经验对产品的持续开发和更新至关重要,对公司是巨大的财富。除非一家公司只做简单产品和低端业务,不需要经验的积累和传承,否则这种清理老员工的做法无疑是捡了芝麻丢了西瓜。</p><p>近些年,MBTI 性格测试很火爆。不过,在心理学教科书里面,一般不会介绍 MBTI 性格分类。反而是教科书经常提及的大五人格特质,知道的人寥寥无几。为什么会出现这种情况?主要是因为 MBTI 这种分类方法不够科学 [5],而大五人格特质有论文依据,是正经的心理学理论。但是,大五人格特质,即开放性、尽责性、外向性、亲和性、神经质,这个东西不像 MBTI 那么容易理解。MBTI 虽然是一种经验的不够科学的分类方法,但是它在实践中能够有效地进行性格划分和描述。它能得到更广泛的应用也是理所当然的。</p><p>中医的理论,例如阴阳五行,在我看来类似于某魔法世界七种元素加芒荒属性的设定,和科学实在说不上有什么关系。但是我们不能据此完全否定中医。从循证医学的角度出发,如果相同症状的病人,吃了同一副药,或者用针扎了同样的穴位,可以把病治好,那么这个治疗方案在临床上就是可行的。你可能没有办法理解为什么用针扎一下就可以治病,但是这个缺憾与能治病相比没有那么重要。我们甚至不需要用科学去解释治病的原理,就像我们不能完全解释 AI 的原理,却依旧能训练出越来越好的 AI 一样。</p><p>[5] 简单来说,MBTI 包括了一些不是很重要的维度,比如着眼当下还是着眼未来,同时缺失了一些更重要的维度,比如心理的敏感程度。</p><br /><h2 id="价值判断"><a href="#价值判断" class="headerlink" title="价值判断"></a>价值判断</h2><p><strong>【一】信奉『保守主义』,保守自由价值。</strong></p><p>因为『保守』这个词在中文语境里经常是贬义,所以保守主义也很容易被误读。如果告诉你中国最著名的保守主义者是邓小平,你或许对这个词的认识会有所改观。保守主义并非保守一切旧的观念,它特别强调保守自由。事在人为,必须要调动人的积极性和主观能动性。自由,是调动积极性和能动性的根本保证。『允许一部分人先富起来』,以及『不管黑猫白猫,能捉到老鼠就是好猫』,破除了阶级斗争的枷锁,让民众得以自由地从事生产活动,最终迎来了经济大发展。</p><p><strong>【二】尊重传统和权威,反对激进的社会变革。</strong></p><p>十七世纪的英国光荣革命,与十八世纪的法国大革命,是两场完全不同的社会变革。光荣革命没有流血牺牲,国王得以保留,大部分旧制度沿袭下来,英国之后没有发生过大的动乱。法国大革命把国王送去了断头台,但是新生的共和国命运多舛,历经两个帝国和三个共和国,反反复复将近一百年的时间,才走向稳定。法国先是把旧制度和旧权威砸得稀巴烂,在社会的复杂性和多样性大大降低,无人能阻挡之后,站在道德的制高点,『我是大公无私的人,你是共和国的敌人,我代表共和国消灭你』,实施严酷的恐怖统治。如果法国人选择尊重传统和权威,温和地实现权力转移,或许可以享受一百年的太平岁月。</p><p><strong>【三】认同财产权是公民最重要的权利。</strong></p><p>在现代社会,如果你没有钱,这不是寸步难行的问题,而是一天也活不下去。财产是自由的依附,如果无法保护公民的财产权,也就意味着公民随时可能丧失全部的自由,沦为奴隶。设想一下监狱里的劳动改造,只有做完了今天的工作,才有饭吃。那么你为了填饱肚子,不得不日复一日地工作,而不能偷懒、休假、或者做任何其他的事。失去了财产权的公民,其处境将与监狱里的苦工并无差别。</p>]]></content>
<summary type="html"><p>今年值得回忆的事情并不少:回国举办婚礼、解锁高空跳伞的成就、深度体验意大利的风土人情。不过,我打算跳过年终总结,聊一个更为重要的话题。</p>
<p>对我来说,今年是世界观和方法论走向成熟的一年。我逐渐感觉到,面对纷繁万千的事物,自己形成了特定的思维模式,并且在价值判断上持有越来越鲜明的立场。世界观、方法论和价值判断没有对错之分,但是有高下之别。有一些世界观更准确地描述了世界发展变化的规律。有一些方法论和价值判断让我们做出更好的人生选择。希望我的分析和解释能为你带来一点点启发。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="discussion" scheme="https://bitmingw.com/tags/discussion/"/>
</entry>
<entry>
<title>2022 年终总结</title>
<link href="https://bitmingw.com/2022/12/27/2022-retrospect/"/>
<id>https://bitmingw.com/2022/12/27/2022-retrospect/</id>
<published>2022-12-27T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.047Z</updated>
<content type="html"><![CDATA[<h2 id="最小结婚成本"><a href="#最小结婚成本" class="headerlink" title="最小结婚成本"></a>最小结婚成本</h2><p>在中国大陆办理结婚登记,以前需要花 9 块钱人民币,现在是免费服务。在美国,结婚需要花多少钱呢?这要从美国的结婚流程说起。</p><span id="more"></span><p>按照加利福尼亚州的法律,合法结婚需要至少三个步骤。</p><p>第一步,买一张『结婚许可』的表格,填写双方的个人信息。虽然这只是一张普通的纸,但是竟然要收 90 美元的费用!</p><p>第二步,在加州境内的某个地方举办婚礼。举办婚礼的当天,需要婚礼主持人和至少一位见证人在结婚许可上签字。不是所有人都有资格作为主持人。合法的主持人要么是教会的神职人员,要么是议员、法官、或政府机构的官员。</p><p>第三步,把填写完毕的结婚许可交回档案馆,再花 17 美元买一张『结婚证』。而这个结婚证,不过就是把结婚许可复印到了一张很花哨的纸上。</p><p>这三步加起来,最少需要花费 107 美元。你换来的,是一张临时属于你的白纸,和一张永久属于你的彩纸。资本主义真是生财有道啊……</p><p>虽然在理论上,你只需要花 107 美元就能搞定这一切。但现实情况是,举办婚礼这一步是开销最大的。我们花了 400 美元雇了一个虽然属于教会但也可以主持世俗婚礼的人,然后又花了同样多的钱请了一位摄影师作为见证人。这些费用加起来,顶得上一台 iPhone 手机了。</p><p>办婚礼前,我们向主持人请教哪天是美国人心中的良辰吉日。她说最火爆的日子是 2022 年 2 月 22 日,那一天的婚礼日程,早在数个月之前就被预订一空。这个答案让我挺诧异的。难道说美国人最喜欢中国人最喜欢的两个数字之差?既然超级 2 的这一天不行,为了沾沾这个神奇日子的仙气,我们再加一个 2 总可以了吧。结果,在前往婚礼的路上,铺天盖地都是俄乌爆发战争的新闻。可不能乱加。</p><p>婚礼的地点选在了 Carmel 一处安静的公共海滩。等我们抵达的时候,路边停了两辆车,主持人和摄影师已经如约而至。我们先是一手交钱一手签字办完了法律手续,然后步行前往海滩,开始了婚礼必不可少的问答环节。主持人准备的结婚誓词有短中长三个版本,我们选择了中版本,因为这是唯一没有提到上帝的誓词。讲完这些话,摄影师又为我们拍了更多的照片,之后大家就散伙了。赶在市政厅关门之前,我们带着有两个陌生人签字的结婚许可,换来了一张结婚证。</p><p>前面说到,结婚证不过是把结婚许可复印到一张彩纸上。因为制作结婚证很容易,这 17 美元不赚白不赚,所以我们可以玩出一些骚操作:</p><ul><li>【守序中立】花 17 美元买一份结婚证。</li><li>【守序善良】花 17 美元给自己买结婚证的同时,再花 17 美元给伴侣也买一份。</li><li>【中立善良】再多买几份留给父母做纪念。</li><li>【混乱邪恶】花 1700 美元买 100 份结婚证寄给朋友们。</li><li>【中立邪恶】花 17 美元买一份马斯克的结婚证自己收藏。</li></ul><p>嗯没错,你是可以花钱买别人的结婚证的(尽管在实际操作上会有一些困难)。不过又有谁做这种事?</p><br /><h2 id="铱星上古果酒"><a href="#铱星上古果酒" class="headerlink" title="铱星上古果酒"></a>铱星上古果酒</h2><p>人类饮酒有几千年的历史,但是人类喝珍珠奶茶是最近二三十年才有的事[1]。我曾想,现在可以很方便地买到珍珠奶茶,这玩意儿可比酒好喝多了,为什么还有那么多人去买又贵又难喝的酒精饮料呢?这种想法真的是 too young too simple. 在超市买过几次酒,每次都很难喝,但这并不代表所有的酒都难喝。</p><p>五月的一个周末,我们前往 Napa 游玩。Napa 是著名的葡萄酒产地,这里有成百上千家酒庄。在 Simi 镇的一家酒庄,我们参与了当地最最最普遍的活动——品酒。这种活动没有什么新意,酒庄拿出来的几种酒,味道只能算一般。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2022-12-27-2022-retrospect/wine-tasting.jpg"> <p>最最最普遍的活动</p></div><p>那天参观酒庄的人很少,前后只有两三桌客人。服务员可能闲得没事做,给我们倒完酒之后,自己也在喝。后来他听到我们说那几杯酒很一般,竟然把自己正在喝的酒拿过来给我们倒上了。这是 2003 年出产的酒,和之前品尝的 2018 - 2020 年的完全不是一回事。历经岁月,酒精的气息已经变得微不足道,在舌尖流淌的,是葡萄、木桶、香料融合后的美妙乐章。这里的服务员真是鸡贼,给顾客倒普通的酒,自己偷偷摸摸把好货都私藏了。发现了宝藏的我们,把一瓶 2003 年的红酒收入囊中。</p><p>世上确实有美酒,但是这种铱星上古果酒[2],不仅很难买到,而且也消费不起。怎样才能找到既便宜又好喝的酒呢?方法其实很简单:多去高端的欧洲餐厅吃饭,试试他们供应的酒。高端法国餐厅和意大利餐厅,对选什么样的酒颇有讲究,抄他们的名单不易出错。至此以后,我们终于搞清楚该买哪些酒了。</p><p>[1] 一种经济学上的解释是,酒类可以放置几十年至上千年,熬制出来的波霸保质期只有四个小时,古代社会的繁荣程度不能支撑波霸这种食材。</p><p>[2] 铱星上古果酒是游戏『星露谷物语』中的一种物品。『无星』、『银星』、『金星』和『铱星』用来形容物品的品质。</p><br /><h2 id="进机房需要穿鞋套吗?"><a href="#进机房需要穿鞋套吗?" class="headerlink" title="进机房需要穿鞋套吗?"></a>进机房需要穿鞋套吗?</h2><p>八月,我前往 Denver 出差,第一次去了真正的机房。在市中心一幢没有标识的大楼里,我们被领进了地下室,跨过厚重的大门,走进了一个令人略感不适的房间。这里有一个个两米多高的架子,每个架子从上到下堆叠着十几台计算机。电线和网线密密麻麻交织在一起,汇聚到架子的顶端,再融入洪流,前往未知的领域。周围满是风扇震耳欲聋的呼啸。从地面的裂隙中,强烈的冷风扑面而来,足以让任何困顿的人神智清醒。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2022-12-27-2022-retrospect/datacenter.jpg"> <p>真正的机房</p></div><p>不同的公司在这个机房中租用一小部分空间,交换数据,与其他公司互联互通。每一个小隔间都有单独的铁栅栏与铁门,宣示着自己的主权。进铁门之前,管理员在地上铺了一张粘性纸,脚踩上去之后,会留下清晰的鞋印。我一开始以为,这是为了留下在场证据,如果有人搞破坏,就可以凭鞋印找到犯人。不过当我看到房间里安放的监视器时,我意识到,侦探小说里的做法,不适合二十一世纪,铺这张粘性纸,应该是为了消除鞋底的灰尘吧。这么说来,进机房也许真的需要穿鞋套呢。</p><p>至于计算机教室,那种地方是不能叫机房的。但是为什么进计算机教室也要穿鞋套[3]?我想,这或许与泰国的寺庙要脱鞋子一样,是一种特殊的仪式吧。</p><p>[3] 高中之后便不再要求穿鞋套进入计算机教室。</p><br /><h2 id="戏精装修公司"><a href="#戏精装修公司" class="headerlink" title="戏精装修公司"></a>戏精装修公司</h2><p>买房一年多之后,为了翻修洗手间,我们打算找正规的装修公司开展大工程。这些公司里面有好有坏,能遇上戏精也是挺难得的。</p><p>正常的流程是,装修公司上门了解情况,评估项目规模,给出一个报价。我们拿到各家公司的价格后,再选取某一家合作。有一家公司,最初开具的价格没有竞争力,正当我们要送客的时候,戏精开始了他的表演。他给公司的领导打了一个电话,问能不能以员工亲属折扣的名义给这个项目打九折。领导跟他说,你的客户不是亲属,不能给折扣。随后戏精对公司领导开始软磨硬泡,一番你来我往之后,领导终于松口说,如果你今天能把合同签下来,我就允许你用这个折扣。</p><p>打九折之后,这家公司一下子变得很有竞争力。戏精不断催促我们说,我花了这么多功夫给你们争取的折扣,只限今天,而且,就算你反悔了,未来三天都可以取消合同,此时不签还待何时?还好当时比较冷静,想办法送走了这位戏精。</p><p>事后回忆,他做了一个很不寻常的举动。他在给领导打电话的时候,是故意开着外放的。一般来说,当着顾客的面,和同事交流是要保密的,因为不知道同事会不会成为内鬼,讲一些不该说的话。而戏精故意让我们听到整个对话,这表明整个对话可能是预先约定的,是两个人演的一出好戏。之后我又查了 Yelp 上的评论,果真有人写下了一模一样的剧情。这公司的销售手法,让我十分佩服!</p>]]></content>
<summary type="html"><h2 id="最小结婚成本"><a href="#最小结婚成本" class="headerlink" title="最小结婚成本"></a>最小结婚成本</h2><p>在中国大陆办理结婚登记,以前需要花 9 块钱人民币,现在是免费服务。在美国,结婚需要花多少钱呢?这要从美国的结婚流程说起。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="memo" scheme="https://bitmingw.com/tags/memo/"/>
</entry>
<entry>
<title>哥德尔证明</title>
<link href="https://bitmingw.com/2022/07/31/godel-proof/"/>
<id>https://bitmingw.com/2022/07/31/godel-proof/</id>
<published>2022-07-31T07:00:00.000Z</published>
<updated>2026-01-01T00:57:20.969Z</updated>
<content type="html"><![CDATA[<p>你的数学老师可能向你提及过『哥德巴赫猜想』。这个猜想认为,任何一个大于 2 的偶数,都可以表示成两个质数之和。这个猜想自 1742 年提出,尽管未能找到任何反例,但没有人能证明它。是否存在这样一种可能,即虽然哥德巴赫猜想是一个真命题,但是我们永远都无法证明它呢?哥德尔向世人展示,这种可能性是存在的。</p><p>哥德尔证明了,在一个定义了自然数(非负整数)、加法和乘法的数学体系中,假设这个体系没有矛盾,也就是不存在既真又假的数学命题,那么一定存在无法被证明的真命题。这一结论被称作『哥德尔不完备定理』。所谓的不完备,是指无法从几条有限的公理出发,推导出<strong>所有</strong>真命题。也就是说,我们可能无法从自然数公理出发,证明哥德巴赫猜想。哥德尔不完备定理的证明过程十分巧妙,本文将向读者展示,哥德尔是怎么做到的。</p><span id="more"></span><br /><h2 id="自然数、加法、乘法"><a href="#自然数、加法、乘法" class="headerlink" title="自然数、加法、乘法"></a>自然数、加法、乘法</h2><p>哥德尔研究的对象是自然数、加法和乘法,这些是数学中最基础的数和运算。为了后文论述方便,这里先介绍相关的背景知识。</p><blockquote><p>我们不加定义地使用下面三个含义确定的词:『数』、『零』、『直接后继』。自然数由下面五条公理确立:</p><ol><li>零是一个数。</li><li>一个数的直接后继也是一个数。</li><li>零不是任何一个数的直接后继。</li><li>不会有两个不同的数有同一个直接后继。</li><li>如果零有某种性质,并且有此性质的数的直接后继也有这个性质,则所有的数都有此性质。</li></ol></blockquote><p>第五条公理也被称作『数学归纳法』。</p><p>从自然数的五条公理不难看出,我们可以使用零和直接后继这两个概念表示所有的自然数。我们把零写作 \(0\),并且把 \(s\) 写在一个数的前面表示这个数的直接后继。于是,我们就得到了一系列自然数 \(0\), \(s0\), \(ss0\), \(sss0\),... 因为写这么多 \(s\) 实在是太麻烦,因此又引入了额外的助记符号,将 \(s0\) 写作 \(1\),\(ss0\) 或者 \(s1\) 写作 \(2\),\(sss0\) 写作 \(3\) 等等,于是就有了我们熟知的自然数。</p><p>根据 \(s\) 在数中出现的次数,我们可以定义加法和乘法。例如,我们可以认为,加法是一种把两个数中出现的 \(s\) 拼接起来的数学运算。例如 \(s0 + ss0\),第一个数有一个 \(s\),第二个数有两个 \(s\),拼接起来一共有三个 \(s\),所以 \(s0 + ss0 = sss0\),用助记符号表示就是 \(1 + 2 = 3\)。乘法运算也可以通过类似的方式得到。</p><p>定义了加法和乘法之后,人们进一步研究乘法的性质,把大于 1 的自然数分成『质数』与『合数』两类。关于质数与合数,有一个至关重要的结论,会在哥德尔证明中用到:</p><blockquote><p>每个大于 1 的自然数,要么本身就是质数,要么可以写为两个或以上的质数的乘积,而且这些质因子按大小排列之后,写法仅有一种方式。</p></blockquote><p>这个结论被称作『算术基本定理』。</p><br /><h2 id="命题演算符号"><a href="#命题演算符号" class="headerlink" title="命题演算符号"></a>命题演算符号</h2><p>在数学命题中,我们需要使用一些演算符号,来表达数、变量或表达式之间的关系。常用的符号包括,使用波浪号 \(\sim\) 表示『非』,使用类似 V 型的符号 \(\lor\) 表示『或者』,使用 \(\supset\) 表示『如果……那么……』,使用倒写的字母 \(\exists\) 表示『存在』,等等。使用这些符号,可以方便地书写数学命题。例如</p><p>\[<br>(\exists x) (x = s0)<br>\]</p><p>表示数学命题『存在一个数 \(x\) 是 \(0\) 的直接后继』。很显然这是一个真命题。</p><br /><h2 id="哥德尔数"><a href="#哥德尔数" class="headerlink" title="哥德尔数"></a>哥德尔数</h2><p>数学命题的内容可以是灵活多样的。在一个仅仅定义了自然数、加法、乘法的体系中,怎么去研究千变万化的数学命题呢?哥德尔创造性地提出了一种方法,可以将任何一个数、变量、表达式、命题(公式)和证明(公式序列)映射为一个确定的自然数。这个数也被称作上述变量、公式等的『哥德尔数』。这样,我们研究数学命题的性质,就可以转化为研究哥德尔数的性质。我们会在后文给出一个具体的例子。在此之前,让我们来看一下哥德尔数是如何计算的。</p><p>首先我们定义下面 12 个基本符号的哥德尔数 [1]</p><table><thead><tr><th align="center">符号</th><th align="center">意义</th><th align="center">哥德尔数</th></tr></thead><tbody><tr><td align="center">\(\sim\)</td><td align="center">非</td><td align="center">1</td></tr><tr><td align="center">\(\lor\)</td><td align="center">或</td><td align="center">2</td></tr><tr><td align="center">\(\supset\)</td><td align="center">如果……那么……</td><td align="center">3</td></tr><tr><td align="center">\(\exists\)</td><td align="center">存在……</td><td align="center">4</td></tr><tr><td align="center">\(=\)</td><td align="center">等于</td><td align="center">5</td></tr><tr><td align="center">\(0\)</td><td align="center">零</td><td align="center">6</td></tr><tr><td align="center">\(s\)</td><td align="center">……的直接后继</td><td align="center">7</td></tr><tr><td align="center">\((\)</td><td align="center">标点符号</td><td align="center">8</td></tr><tr><td align="center">\()\)</td><td align="center">标点符号</td><td align="center">9</td></tr><tr><td align="center">\(,\)</td><td align="center">标点符号</td><td align="center">10</td></tr><tr><td align="center">\(+\)</td><td align="center">加</td><td align="center">11</td></tr><tr><td align="center">\(\times\)</td><td align="center">乘</td><td align="center">12</td></tr></tbody></table><p>在数学命题中会用到数字变量如 \(x\), \(y\), \(z\) 等。数字变量可以用数字如 \(ss0\) 或者表达式如 \(x + y\) 等代入。此外,还会遇到命题变量如 \(p\), \(q\), \(r\) 等可以用命题(公式)代入。对每一个不同的数字变量,赋予一个大于 12 的不同质数作为哥德尔数。类似的,对每一个不同的命题变量,赋予一个大于 12 的不同质数的平方数作为哥德尔数。下面展示了一种可能的赋予哥德尔数的结果</p><table><thead><tr><th align="center">符号</th><th align="center">哥德尔数</th></tr></thead><tbody><tr><td align="center">\(x\)</td><td align="center">\(13\)</td></tr><tr><td align="center">\(y\)</td><td align="center">\(17\)</td></tr><tr><td align="center">\(z\)</td><td align="center">\(19\)</td></tr><tr><td align="center">\(p\)</td><td align="center">\(13^2\)</td></tr><tr><td align="center">\(q\)</td><td align="center">\(17^2\)</td></tr><tr><td align="center">\(r\)</td><td align="center">\(19^2\)</td></tr></tbody></table><p>我们刚刚定义了单个符号的哥德尔数。接下来,怎么计算一个数学命题(公式)的哥德尔数呢?这里来看一个例子。假设我们想要计算的公式是</p><p>\[<br>(\exists x) (x = sy)<br>\]</p><p>这个公式一共用到了 10 个符号,分别是左括号、存在、\(x\)、右括号、左括号、\(x\)、等号、直接后继、\(y\)、右括号。假设数字变量 \(x\) 和 \(y\) 的哥德尔数分别是 13 和 17,那么这 10 个符号对应的哥德尔数分别是 8, 4, 13, 9, 8, 13, 5, 7, 17, 9。我们取从小到大排列的前 10 个质数,每一个质数添加一个对应位置的符号的哥德尔数作为指数,并将他们相乘的结果作为整个公式的哥德尔数。因此,上述公式对应的哥德尔数是</p><p>\[<br>2^8 \times 3^4 \times 5^{13} \times 7^9 \times 11^8 \times 13^{13} \times 17^5 \times 19^7 \times 23^{17} \times 29^9<br>\]</p><p>这是一个非常巨大的数字!</p><p>为什么要使用这样的方式计算一个公式的哥德尔数呢?在上一节,我们讲到了算术基本定理。给定一个公式的哥德尔数,我们通过质因数分解,可以唯一确定地还原其公式。也就是说,每一个公式都和某一个自然数构成一一对应的关系。</p><p>好几个公式组合起来的序列可以构成对一个命题的证明。例如下面的两个公式</p><p>\[<br>(\exists x) (x = sy)<br>\]<br>\[<br>(\exists x) (x = s0)<br>\]</p><p>第一个公式是一个真命题。将 \(0\) 代入 \(y\) 可以得到第二个公式。因此,这两个公式构成的序列是对第二个公式的证明。我们用类似的方法定义一个证明的哥德尔数。假设第一个公式的哥德尔数是 \(m\),第二个公式的哥德尔数是 \(n\),则这个证明的哥德尔数是</p><p>\[<br>2^m \times 3^n<br>\]</p><p>这个数字虽然过分巨大,但我们不必计算它的值。我们只需要知道,通过这种方法,可以为任意一个变量、命题和证明赋予一个对应的哥德尔数,就可以了。</p><p>[1] 在哥德尔的原始证明中仅仅使用了 7 个符号。使用不同的符号集会影响哥德尔数的大小,但是不影响证明过程。</p><br /><h2 id="谈论数学命题的数学命题"><a href="#谈论数学命题的数学命题" class="headerlink" title="谈论数学命题的数学命题"></a>谈论数学命题的数学命题</h2><p>我们来研究下面这个简单的数学命题</p><p>\[<br>\sim (0 = 0)<br>\]</p><p>它表达的意思是 \(0\) 不等于 \(0\)。这是一个假命题。接下来,我们提出第二个数学命题:『公式 \(\sim (0 = 0)\) 的第一个符号是波浪号』。第二个命题谈论的对象是第一个命题,因此它也被称作是『元命题』。不难看出,第二个命题是真的。我们的问题是,如何使用符号把第二个命题的数学公式写出来。</p><p>为了解决这个问题,我们来研究第一个命题(公式)的哥德尔数。它的哥德尔数是</p><p>\[<br>2^1 \times 3^8 \times 5^6 \times 7^5 \times 11^6 \times 13^9<br>\]</p><p>我们把这个哥德尔数记作 \(a\)。</p><p>一个公式的第一个符号由质因子 2 的指数决定。因为波浪号的哥德尔数是 1,所以我们可以把第二个数学命题转化为一个等价命题:\(2\) 是 \(a\) 的一个因子,但 \(2^2\) 不是。这个等价命题用公式写出来就是</p><p>\[<br>(\exists z) (sss...sss0 = z \times ss0) \land \sim (\exists z) (sss...sss0 = z \times ss0 \times ss0)<br>\]</p><p>其中使用了省略号的数字里 \(s\) 的个数为 \(a\)。这里我们引入了一个新的符号 \(\land\) 表示『并且』。它只是一个助记符号,因为它的意义可以用 \(\sim\) 和 \(\lor\) 表达出来,所以我们不需要为这个新符号分配哥德尔数。</p><p>现在,我们意识到,有了哥德尔数之后,构造和分析元命题变成了一件可行的事。我们称『公式 \(\sim (0 = 0)\) 的第一个符号是波浪号』这样的命题为『元命题』,而上面的一大串公式为『形式化的元命题』。</p><br /><h2 id="最后再讲两个重要概念"><a href="#最后再讲两个重要概念" class="headerlink" title="最后再讲两个重要概念"></a>最后再讲两个重要概念</h2><p>在展示哥德尔的证明过程之前,我们还需要理解两个重要概念。</p><p>第一个概念是一个元命题:『具有哥德尔数 \(x\) 的公式序列,是哥德尔数为 \(z\) 的公式的证明』。回想一下之前的例子,具有哥德尔数 \(2^m \times 3^n\) 的公式序列,是哥德尔数为 \(n\) 的公式的证明。这两个哥德尔数之间的算术关系,比判定第一个符号是波浪号要复杂一些,不过我们总是可以使用类似的方法把这个元命题写成形式化的公式。为了表述方便,我们把这个命题简写作 \(dem(x, z)\),并且把对应的形式化的公式写作 \(Dem(x, z)\)。如果 \(dem(x, z)\) 是真命题,那么 \(Dem(x, z)\) 是一个数学定理;如果 \(dem(x, z)\) 是一个假命题,那么 \(\sim Dem(x, z)\) 是一个数学定理。</p><p>第二个概念是一个数学运算:『将一个公式的哥德尔数代入该公式的一个变量,并计算代入后的哥德尔数』。举上文用到的一个例子,公式 \((\exists x) (x = sy)\) 的哥德尔数是 \(m\)。这个公式有一个自由变量 \(y\)。如果将数值 \(m\) 代入 \(y\),我们会得到一个不含有自由变量的新公式</p><p>\[<br>(\exists x) (x = sss...sss0)<br>\]</p><p>其中使用了省略号的数字里有 \(m + 1\) 个 \(s\)。我们使用记号 \(sub(m, 17, m)\) 来表示这个新公式的哥德尔数。这个记号可以理解为,在一个哥德尔数为 \(m\) 的公式中,把哥德尔数为 \(17\) 的变量替换成 \(m\),从而得到新公式的哥德尔数。类似的,我们使用 \(Sub(m, 17, m)\) 表示新公式的哥德尔数的形式化写法。事实上,\(Sub(m, 17, m)\) 由超级长的一串 \(s\) 符号和一个 \(0\) 构成。</p><p>在刚刚给出的例子中,\(m\) 是一个确切的数值。不过我们也可以在 \(m\) 的位置上使用一个自由变量 \(w\)。这样,我们构造出来了一个自变量为 \(w\),因变量为 \(sub(w, 17, w)\) 的函数。需要注意的是,在代入任何确切的数字之前,数字变量 \(w\) 有一个被赋予的哥德尔数(类似于 \(x\) 的哥德尔数是 13,\(y\) 的哥德尔数是 17),这也就意味着,尽管 \(w\) 是一个变量,形式化的公式 \(Sub(w, 17, w)\) 会有一个确定的哥德尔数。</p><br /><h2 id="哥德尔证明"><a href="#哥德尔证明" class="headerlink" title="哥德尔证明"></a>哥德尔证明</h2><p>我们来研究下面的形式化公式</p><p>\[<br>(\exists x) Dem(x, z)<br>\]</p><p>这个公式表达的意思是,哥德尔数为 \(z\) 的公式是可以证明的。</p><p>与之相对,形式化公式</p><p>\[<br>\sim (\exists x) Dem(x, z)<br>\]</p><p>的意思是,找不到哥德尔数为 \(z\) 的公式的证明。这里可能会有两种情况。第一种情况是,哥德尔数为 \(z\) 的公式描述的是一个假命题,例如 \(\sim (0 = 0)\),这样当然不可能找到一个证明。第二种情况是,哥德尔数为 \(z\) 的公式描述的是一个真命题,但是这个真命题不可证。</p><p>我们应该考察一个怎样的 \(z\) 呢?先观察下面的公式(称为公式 \(F\)):</p><p>\[<br>\sim (\exists x) Dem(x, Sub(w, 17, w))<br>\]</p><p>公式 \(F\) 表述的意思是,找不到哥德尔数为 \(sub(w, 17, w)\) 的公式的证明。不过这里面,因为 \(w\) 是一个自由变量,所以哥德尔数为 \(sub(w, 17, w)\) 的公式不是一个确切的公式,我们也就没有办法判定这个命题的真假。下一步,给 \(w\) 代入什么具体的数值好呢?</p><p>由上一节的讨论可知,因为公式 \(Sub(w, 17, w)\) 有一个确定的哥德尔数,所以公式 \(F\) 同样有一个确定的哥德尔数,我们将这个数字记为 \(k\)。</p><p>接下来,我们将公式 \(F\) 的哥德尔数 \(k\) 代入公式 \(F\) 的变量 \(w\),得到下面的新公式(称为公式 \(G\)):</p><p>\[<br>\sim (\exists x) Dem(x, Sub(k, 17, k))<br>\]</p><p>公式 \(G\) 的哥德尔数记为 \(g\),请问,\(g\) 的大小是多少呢?</p><p>回忆一下从公式 \(F\) 生成公式 \(G\) 的过程,不就是『将一个公式的哥德尔数代入该公式的一个变量,并计算代入后的哥德尔数』吗?所以</p><p>\[<br>g = sub(k, 17, k)<br>\]</p><p>发现了什么?公式 \(G\) 的意思是,找不到哥德尔数为 \(sub(k, 17, k)\) 的公式的证明。因为这个 \(sub(k, 17, k)\) 等于 \(g\),所以这句话可以变成,找不到哥德尔数为 \(g\) 的公式的证明。又因为公式 \(G\) 的哥德尔数就是 \(g\),所以公式 \(G\) 的意思可以进一步表示为:『找不到公式 \(G\) 的证明』。</p><p>找不到一个公式的证明,可能因为它本身是假命题,也有可能它是真命题但不可证。那么,公式 \(G\) 是否是一个假命题呢?这里可以用反证法。我们假设公式 \(G\) 是一个假命题,也就是说,能找到公式 \(G\) 的证明。此时,一定找不到公式 \(\sim G\) 的证明。而 \(\sim G\) 代表的意思是『能找到公式 \(G\) 的证明』,找不到能找到公式 \(G\) 的证明,合并起来就是,找不到公式 \(G\) 的证明,而这正是公式 \(G\) 的意思。从假设公式 \(G\) 是假命题出发,结果得出公式 \(G\) 是真命题,引发矛盾。如果自然数公理体系没有矛盾,那么公式 \(G\) 不可能为假命题,它一定为真命题。</p><p>哥德尔通过列举了公式 \(G\) 这个例子,证明了自然数公理体系中,存在无法被证明的真命题。</p><p>最后,可能会有人问,如果自然数公理体系有矛盾怎么办?如果真的发生这种事,人类的几乎全部数学知识就要推倒重来了。我们还是乐观地相信这种事不会发生比较好。</p>]]></content>
<summary type="html"><p>你的数学老师可能向你提及过『哥德巴赫猜想』。这个猜想认为,任何一个大于 2 的偶数,都可以表示成两个质数之和。这个猜想自 1742 年提出,尽管未能找到任何反例,但没有人能证明它。是否存在这样一种可能,即虽然哥德巴赫猜想是一个真命题,但是我们永远都无法证明它呢?哥德尔向世人展示,这种可能性是存在的。</p>
<p>哥德尔证明了,在一个定义了自然数(非负整数)、加法和乘法的数学体系中,假设这个体系没有矛盾,也就是不存在既真又假的数学命题,那么一定存在无法被证明的真命题。这一结论被称作『哥德尔不完备定理』。所谓的不完备,是指无法从几条有限的公理出发,推导出<strong>所有</strong>真命题。也就是说,我们可能无法从自然数公理出发,证明哥德巴赫猜想。哥德尔不完备定理的证明过程十分巧妙,本文将向读者展示,哥德尔是怎么做到的。</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="math" scheme="https://bitmingw.com/tags/math/"/>
</entry>
<entry>
<title>2021 年终总结</title>
<link href="https://bitmingw.com/2021/12/30/2021-retrospect/"/>
<id>https://bitmingw.com/2021/12/30/2021-retrospect/</id>
<published>2021-12-30T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.043Z</updated>
<content type="html"><![CDATA[<h2 id="买房战争"><a href="#买房战争" class="headerlink" title="买房战争"></a>买房战争</h2><p>前年上日语课的时候,老师曾经问我,为什么中国人那么喜欢买房子。我说,按中国的传统,没有房子不能娶老婆。老师听罢,连连说道『大変ですね』(太难了)。结婚是许多人加入买房大军的一个原因,而且今年还有其他的利好消息,比如极低的利息,以及科技公司如火箭般蹿升的股票价格。这些因素叠加在一起,也让今年的抢房大战格外激烈。</p><span id="more"></span><p>我们从一月份开始,几乎每个礼拜都去看房子。我们打算买的 Single Family Home / 一户建住宅,大多数是上世纪五十和六十年代修建的。虽然看了很多房子,但是大多数房子都各有各的问题:</p><ul><li>(甲)户型宽阔,前院和后院都很美,但是太远了,上班可能要一个小时。</li><li>(乙)房子新装修过,很敞亮,但是气味太大,一时无法入住。</li><li>(丙)宫殿般的超大豪华卧室,不过价格远远超出了预算。</li><li>(丁)价格低廉位置尚可,但是电器老旧,烤箱竟然用的是机械仪表盘,装修成本高昂。</li><li>(戊)很有现代设计的品味,但是卧室竟然没有窗户!</li><li>...</li></ul><p>有时候能遇到没有大毛病的房子,不过一套房子通常有十几位买家一同竞争,理性出价很难拔得头筹。多数情况下,我们和第一名的出价会相差一辆奥迪 A8。最接近的一次只差了一辆凯美瑞,但终究还是没能买到。</p><p>一转眼就到了五月份。短短四个月的时间,我们见证了房价一路高升。越来越多的区域因为价格过高而被放弃,我们马上就要被挤到偏远的穷人区了。那时我们回顾了一下过去的经历,终于醒悟:时间就是金钱,今天舍不得奥迪 A8,明天就会亏一辆迈凯伦。如果有条件不错的房子,一定要早买,大买,买爆它。</p><p>在新思想的带领下,我们没过一两个礼拜就买到了现在这套房子。</p><br /><h2 id="自助家装"><a href="#自助家装" class="headerlink" title="自助家装"></a>自助家装</h2><p>房子买下来了,卖家在里面住了几十年,多多少少有一些需要修补的地方。这里的人工费用非常离谱,一位电工的时薪高达 180 美元。为了省钱,许多小型家装工程,就由我们自己动手了。</p><p>最早学会的技能是补洞和刷漆。补小洞很简单,只要涂上填充材料,晾干、打磨之后再刷漆就可以了。我们要补的是一个大窟窿,这种情况,需要切割出和窟窿一样大的板材,用填充材料和原先的墙粘在一起,然后再打磨和上漆。切割不规则的形状很难,不如把窟窿凿大一些,变成一个规则的长方形。经过一下午和一晚上的努力,我们最终完美修复了这个窟窿。至于刷漆,虽然前期贴胶带和后期清洁很费时间,但是用滚筒刷墙那叫一个爽啊。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/dry-wall-hole.jpg"> <p>墙上的窟窿</p></div><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/dry-wall-wip.jpg"> <p>施工进行中</p></div><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/dry-wall-finish.jpg"> <p>看不出来了吧</p></div><p>我们有几个储藏间没有踢脚线,找了一位师傅询问价格,竟然张口就是 $800。哎,你想换 iPhone 了就直说嘛…… 根据我们的调查,做踢脚线这件事也不难,买来材料之后,用锯子切割成合适的长度,最后使用钉子固定到墙上。这其中切割是最困难的,虽然不至于要精确到一毫米,但是有缝隙总之会难看。由于没有精确测量长度的工具,我不得不准备一块比较长的踢脚线,每次锯掉一点,再与墙的长度进行比较,反复两三次最终确认踢脚线的长度。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/base-board-install.jpg"> <p>哎呀还是太长了</p></div><p>另外,在墙角两块踢脚线的接缝处,其实需要切割出 45 度角,让两块踢脚线结合在一起。这件事用电锯做比较简单,我们的手工锯不方便固定材料,切一个斜角出来实在太难。好在踢脚线的长度非常合适,接缝处的瑕疵并不明显。</p><p>在室外,灌溉系统也需要维护。洒水器的喷头使用时间长了,可能会漏水,这不仅导致草地枯黄,而且还危及行人的安全。更换和调节洒水喷头很容易,不过要做好全身淋湿的准备。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/sprinkler.jpg"> <p>崩坏的洒水器</p></div><br /><h2 id="自耕农的倦怠"><a href="#自耕农的倦怠" class="headerlink" title="自耕农的倦怠"></a>自耕农的倦怠</h2><p>初夏,有朋友送来了几株植物小苗。正巧我们搬进了 Single Family Home,拥有一个广阔的后院,很适合培养这些小苗。考虑到卖家把后院的景观打理得清清楚楚,我们可不愿意为了种菜刨开草地,于是买来了花盆和种植带,将小苗移栽到里面。</p><p>尽管有不少人说,番茄是一种对种菜新人很友好的物种,但是它的田间管理还是比较繁琐的。番茄每天都需要浇水,每周疏果并修剪叶片,每三周施肥。番茄的生长速度很快,从幼苗长到一人高,只需要两个月的时间。这期间需要添加支架来维持番茄持续向上生长。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/tomato.jpg"> <p>首个成熟的果实</p></div><p>到八月份,我们的番茄依次开始变红。但是为什么它们的个头那么小啊?查阅资料之后得知,番茄需要的土壤比较多,我们的花盆太小,不能提供足够多的营养。好在这番茄虽然小,味道还是不错的。真不愧是新人友好作物,让我们没有百忙一场。</p><p>从九月到十一月,我们又尝试种植了西葫芦。西葫芦在开花时会引来很多飞虫,有一些还顺便进了屋子,让我们很是烦恼。花谢之后,果实一点点长大,马上就要收获了,突然有一天上面出现了一道牙印。可恶的松鼠竟然提前品尝了这道美味!</p><p>经历了这两次不完美的星露谷之旅,今后我们不打算继续种菜了。自耕农的生活,一方面时间和精力付出甚多,需要每天浇水和照料,另一方面收获的果实又甚少,三个月种出来了六根西葫芦,在超市里卖不到十块钱,而我们购买土壤花去的钱就已经超过这个数了。此生若是还有机会种地,恐怕得买下好几千亩的平原,搞机械化生产才行。此时此刻,在这方寸之间,还是静赏美景就好。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2021-12-30-2021-retrospect/tree.jpg"> <p>如地毯般的落叶</p></div>]]></content>
<summary type="html"><h2 id="买房战争"><a href="#买房战争" class="headerlink" title="买房战争"></a>买房战争</h2><p>前年上日语课的时候,老师曾经问我,为什么中国人那么喜欢买房子。我说,按中国的传统,没有房子不能娶老婆。老师听罢,连连说道『大変ですね』(太难了)。结婚是许多人加入买房大军的一个原因,而且今年还有其他的利好消息,比如极低的利息,以及科技公司如火箭般蹿升的股票价格。这些因素叠加在一起,也让今年的抢房大战格外激烈。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="memo" scheme="https://bitmingw.com/tags/memo/"/>
</entry>
<entry>
<title>在 Google 工作的一些感受</title>
<link href="https://bitmingw.com/2021/12/14/at-google/"/>
<id>https://bitmingw.com/2021/12/14/at-google/</id>
<published>2021-12-14T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.015Z</updated>
<content type="html"><![CDATA[<p>我来 Google 工作有一年时间了。以下几点,是我个人在 Google 工作的一些感受。</p><span id="more"></span><br /><h2 id="宽容的工作环境"><a href="#宽容的工作环境" class="headerlink" title="宽容的工作环境"></a>宽容的工作环境</h2><p>与国内的加班文化形成鲜明对比,美国有不少公司很在意『工作与生活的平衡』,也就是 work life balance / WLB。Google 一年一度的调查问卷,有一系列问题围绕 WLB 展开,比如在下班之后,或者休假的时候,是否能做到与工作分割开来。据我的观察,多数人在下班之后都不再查看工作邮件,更不会给别人发消息,有什么事情,等到上班了再说。除了一部分与印度合作的同事,晚上也不开会。</p><p>另外,在工作氛围上,大家一般会给出足够的宽限期,不会要求别人立即回复。有一次,我上午上传了一段代码,评审的人到了下午还没有看,我就发了个消息催了一下,结果被怼了回来。对方说,若不是紧急情况,请先等 24 小时,如果还没有动静才可以催。后来我也学聪明了,把邮件和聊天的提醒都关了,有时候别人问我一件事,要过几个小时才会看到和回复。即便如此,也没有同事对此表示不满。</p><br /><h2 id="强大的开发工具"><a href="#强大的开发工具" class="headerlink" title="强大的开发工具"></a>强大的开发工具</h2><p>日常开发,免不了要做一些琐碎的杂活,例如调整代码格式、检查拼写错误。为了偷懒,大家开发出了一些工具,并把它们集成到开发环境和自动化测试中。使用这些工具提高效率,大多数公司都可以做到。但是 Google 的工具更为强大,做到了一些其他公司很难实现的事情。</p><p>举个例子,在 Google 的代码评审网站上,会显示每一行修改的代码是否被测试覆盖到。这个功能十分有用:如果有人添加或修改了很多代码,却没有写测试来覆盖这些代码,就是一个危险的信号,说明这些代码可能会引入新的缺陷。不过,实现这个功能并不容易。</p><p>首先,测试工具需要支持各种语言,以及该语言的各种测试框架。其次,测试工具需要集成编译器和版本控制工具,计算出本次代码修改的范围,并且只运行有关的测试。如果每次都运行全部的测试,就会显著增加时间,拖累代码评审和提交的进度。另外,测试工具还要把结果显示到评审网站上。如果一家公司独立采购测试工具、版本控制工具和评审工具,那么实现这几点是非常困难的。</p><p>相比之下,Google 的编译工具、测试工具、版本控制、评审网站,都是自己研制的。依靠上下游模块的协作,实现了显示每一行代码的测试覆盖的功能。通过使用完善和统一的开发工具,享受它们带来的种种便利,Google 的工程师可以更专注更高效地工作。</p><br /><h2 id="模仿不来的可读性评审"><a href="#模仿不来的可读性评审" class="headerlink" title="模仿不来的可读性评审"></a>模仿不来的可读性评审</h2><p>Google 有一套神奇的制度(我不太清楚其他公司是否有类似的制度),那就是工程师提交的代码需要经过一个专门的『代码可读性评审』。新入职的工程师被认为不具备写出高可读性代码的能力。导师在评审过程中,会指出在哪些方面修改代码,可以让它变得更可读。工程师在此期间不断获得指点,水平日益提高,最终毕业,获得一门语言的代码可读性认证。毕业之后,工程师不再需要接受导师的指教就可以提交代码。</p><p>这种评审的作用,除了让大家勤于思考,养成好习惯,努力提升代码的可读性,还可以让工程师快速熟悉编程语言的特定用法和 Google 内部代码库,写出规范和高质量的代码。例如,Go 语言中有个 <code>log.Fatalf</code> 函数,可以在程序遇到严重错误时退出。有一次我使用了这个函数,导师建议我改为使用一个 Google 内部特有的 <code>log.Exitf</code> 函数,因为后者不会打印堆栈信息,错误显示更直观简洁。如果没有可读性评审,我完全不会知道 Google 内部代码库中存在这样一个替代者。</p><p>可读性评审制度是大多数公司模仿不来的。大多数公司使用许许多多分散的代码仓库。出于保密的需要,你根本无法访问那些与日常工作无关的仓库,更别说对那里的改动指指点点了。而可读性评审的核心,就是让一个与你的日常工作没有交集,不了解你的项目的人,来尝试理解你的代码。如果不熟悉项目的人,也能通过阅读源代码的方式大概摸清作者的意图,那就说明代码的可读性足够好。</p><p>可读性评审,一方面有助于提升工程师的能力,另一方面,也在提升人员的流动性。你写的代码,不熟悉的人也能看懂,意味着代码没有秘密可言,你这颗螺丝钉,随时可以被另一颗换掉。我觉得,Google 是有意在维护一个开放和高流动性的工作环境,如果你在一个组不开心了,可以考虑换一个组,而不是必须要跳槽去别的公司。我身边有的同事换过多次组,不知不觉就在 Google 待了十余年。另外,即便有个别员工离开,其他人也可以很快顶上,不会对项目造成巨大的影响。</p><br /><h2 id="缓慢的流程"><a href="#缓慢的流程" class="headerlink" title="缓慢的流程"></a>缓慢的流程</h2><p>Google 对外一向宣传产品的安全性很好,不会出现几亿人的数据被骇客偷个精光这种事故。能做到这一点,依赖于 Google 多年来在安全领域持续投入,并且建立起了完善的安全评审体系。安全评审确实有助于打造一款安全的产品,但是也往往会拖慢产品上线的进度。</p><p>我先前做过的一个项目,是使用一种 Google 自己研制的 VPN 传输控制指令,远程操控散落在数据中心之外的机器。这个项目的一个环节,是在某个代理服务中添加一条规则,允许特定身份的用户使用特定的 IP 地址段建立网络连接。这个代理服务处于中枢位置,掌握着访问 Google 核心网络的生杀大权。专门有一个组负责维护这个代理服务的规则。如果需要修改规则,那就需要找他们进行安全评审。</p><p>由于全公司有各种业务都从这个代理走,他们的日程安排从早到晚都挤得满满当当。运气好的时候可以安排在本周会谈,运气不好的时候则只能安排在下周。第一次开会时,由于他们不是很了解这款 VPN,询问了不少背景情况,等我们讲解完毕,会议的时间已经耗尽,只能下次再约了。后来的几次会议,他们又详细讨论了我们的使用示例,针对一些不寻常的做法讨价还价,最后算是破例批准了我们。从开始接触,到最后允许接入我们的流量,花费了一个半月的时间。</p><p>Google 对于安全的极致追求,是它赢得用户信任的重要基础,这也会成为 Google Cloud 区分于其他云计算服务的卖点。然而,安全评审,以及其他缓慢的流程,是否会拖累 Google 推出新服务和新产品的速度呢?</p><br /><h2 id="在奇奇怪怪的地方抠门"><a href="#在奇奇怪怪的地方抠门" class="headerlink" title="在奇奇怪怪的地方抠门"></a>在奇奇怪怪的地方抠门</h2><p>Google 一年有数百亿美元的净利润,一位工程师一年的工资有数十万。但是 Google 却在一些边角的地方,为了区区几百块钱抠门到令人难以置信。</p><p>入职之前,我们有机会自行选择工作用的电脑。做软件开发,一般来说 Macbook Pro 是标准配置,没什么好挑的。但 Google 的政策是,新 Macbook Pro 只能选 13 寸的,如果需要 16 寸的,则只能使用别人用过的二手货。这不是因为买不到 16 寸的电脑,而仅仅是因为它为了省钱,不想给你买。</p><p>于是我拿到了一个 13 寸屏幕的电脑。13 寸的屏幕很小,代码竖着只能显示三十来行,横着也不过一百个字符左右,无论阅读还是编辑都极为困难。如果没有外接显示器,我是不会用这个小小的屏幕搞开发的。如果配一个 16 寸的电脑,我可以自由移动,在任何我觉得舒服的地方工作。而现在,我必须先找一台显示器,然后接上显示器才能工作。因为公司的办公桌上还没有显示器(进了公司之后一直在家办公),所以我基本上也不去办公室,毕竟去了也不方便工作呀!</p><p>根据 Google 的政策,移动设备上不能存储代码。大家的开发环境,要么运行在办公桌下的主机里,要么是运行在 Google Cloud 上的虚拟机。由于疫情的影响,使用 Google Cloud 虚拟机是我唯一的选择。另我大吃一惊的是,分配的虚拟机使用的竟然是传统的机械磁盘(HDD),而没有使用速度更快的固态硬盘(SSD)。我专门咨询过为什么不使用 SSD,得到的回答是,(以 1 TB 为例)SSD 每个月的开销会比 HDD 多大概 100 美元。但是,我每天早上更新代码索引的时候,都要一边等待一边刷手机。那个时候磁盘读写时间非常可怕,甚至会让开发环境完全卡住。请问我刷手机的时间,折算成工资,难道还不到 100 块钱吗?</p>]]></content>
<summary type="html"><p>我来 Google 工作有一年时间了。以下几点,是我个人在 Google 工作的一些感受。</p></summary>
<category term="work" scheme="https://bitmingw.com/categories/work/"/>
<category term="Google" scheme="https://bitmingw.com/tags/Google/"/>
</entry>
<entry>
<title>C++ 和 Rust 的移动操作</title>
<link href="https://bitmingw.com/2021/04/25/move-semantics-in-c++-and-rust/"/>
<id>https://bitmingw.com/2021/04/25/move-semantics-in-c++-and-rust/</id>
<published>2021-04-25T07:00:00.000Z</published>
<updated>2026-01-01T00:57:20.979Z</updated>
<content type="html"><![CDATA[<p>各种编程语言都支持对象(object)的构造、复制、读取、修改和销毁等操作。其中的复制(copy)操作,就是在计算机内存中的某个新地址,构造一个和原对象一模一样的新对象。</p><p>少数语言,例如 C++ 和 Rust,还支持一种叫『移动』(move)的操作。对象的移动,顾名思义,就是把原对象从计算机内存中的某个地址<strong>搬运</strong>至新的地址。听上去,移动操作做的事情似乎和复制差不多。那么为什么 C++ 和 Rust 要引入移动操作呢?为什么其他大多数语言都不支持移动操作呢?</p><span id="more"></span><br /><h2 id="如何使用移动操作"><a href="#如何使用移动操作" class="headerlink" title="如何使用移动操作"></a>如何使用移动操作</h2><p>在讨论移动操作的用途之前,我们需要先了解一下如何在程序中使用移动操作。</p><p>在 C++ 中,当需要在内存中的某个新地址,构造一个和原对象一模一样的新对象的时候,编译器会根据原对象是『左值』(lvalue)还是『右值』(rvalue),来决定是执行复制操作,还是执行移动操作。一个对象是左值还是右值,有一种较为直观(但不十分准确)的判定方法:能被赋值,或者说,能出现在赋值符号 <code>=</code> 左侧的对象是左值,否则就是右值<sup>[1]</sup>。我们通过几个直观的例子来看一下。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// i 是左值。</span></span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 即便这回 i 出现在右侧,</span></span><br><span class="line"><span class="comment">// 它还是左值。</span></span><br><span class="line"><span class="keyword">int</span> j = i;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 常量 10 不能出现在左侧,所以 10 是右值。</span></span><br><span class="line"><span class="number">10</span> = <span class="number">5</span>; <span class="comment">// 编译错误</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">GetMagicNumber</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="number">10</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数运行的结果不能出现在左侧,</span></span><br><span class="line"><span class="comment">// 所以是右值。</span></span><br><span class="line"><span class="built_in">GetMagicNumber</span>() = <span class="number">5</span>; <span class="comment">// 编译错误</span></span><br></pre></td></tr></table></figure><p>当 C++ 编译器发现原对象是一个右值对象的引用(rvalue reference),且该对象类型支持移动构造 / 移动赋值时,会执行移动操作,否则会执行复制操作。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">std::vector<<span class="keyword">int</span>> vec = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把 vec 复制到 vec2,</span></span><br><span class="line"><span class="comment">// 因为此处的 vec 是一个左值对象的引用。</span></span><br><span class="line">std::vector<<span class="keyword">int</span>> vec2 = vec;</span><br><span class="line"></span><br><span class="line"><span class="function">std::vector<<span class="keyword">int</span>> <span class="title">VecGenerator</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 移动操作,</span></span><br><span class="line"><span class="comment">// 因为此处调用 VecGenerator 函数,</span></span><br><span class="line"><span class="comment">// 会得到一个右值对象的引用。</span></span><br><span class="line">std::vector<<span class="keyword">int</span>> vec3 = <span class="built_in">VecGenerator</span>();</span><br></pre></td></tr></table></figure><p>程序员可以使用 <code>std::move</code> 函数将某一个对象的引用,强制转换为一个右值对象的引用,来触发编译器执行移动操作。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">std::vector<<span class="keyword">int</span>> vec4 = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 std::move 函数将 vec4 强制转换成</span></span><br><span class="line"><span class="comment">// 右值对象的引用。所以此处会执行移动操作。</span></span><br><span class="line">std::vector<<span class="keyword">int</span>> vec5 = std::<span class="built_in">move</span>(vec4);</span><br><span class="line"></span><br><span class="line"><span class="comment">// vec4 被移动到 vec5 之后,会变成空值,</span></span><br><span class="line"><span class="comment">// 因此下一行会打印 0</span></span><br><span class="line">std::cout << vec4.<span class="built_in">size</span>();</span><br></pre></td></tr></table></figure><p>移动发生后,被移动的对象(如上例中的 <code>vec4</code>)通常会变成空值。一般来说,程序不应该继续使用被移动的对象,因为从理论上来说它的生命周期已经结束了(转移到了新的对象身上)。不过 C++ 编译器不会禁止这种行为。此外,如果对象的类型不支持移动构造 / 移动赋值,C++ 总是会执行复制操作。复制操作是移动操作无法进行时的备用方案。</p><br /><p>和 C++ 区分左值和右值不同,在 Rust 中使用某个对象构造一个一模一样的新对象时,除了少数基本类型外<sup>[2]</sup>,总是会执行移动操作。如果程序员想要执行复制操作而不是移动操作,需要调用 <code>clone()</code> 方法。</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> vec = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 vec 的值移动到 vec2。</span></span><br><span class="line"><span class="keyword">let</span> vec2 = vec;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在移动发生后,vec 的生命周期结束,</span></span><br><span class="line"><span class="comment">// Rust 会禁止继续使用被移动的对象。</span></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">"{}"</span>, vec.len()); <span class="comment">// 编译错误</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 clone() 方法</span></span><br><span class="line"><span class="comment">// 复制 vec2 到 vec3 上。</span></span><br><span class="line"><span class="keyword">let</span> vec3 = vec2.clone();</span><br></pre></td></tr></table></figure><br /><h2 id="移动操作的用途"><a href="#移动操作的用途" class="headerlink" title="移动操作的用途"></a>移动操作的用途</h2><p>C++ 和 Rust 支持移动操作,主要有三方面的用途。</p><p><strong>第一,对于生命周期很短的临时对象,使用移动可以减少复制和销毁操作的次数。</strong></p><p>在上文中,我们已经提到了,下面这段代码会执行一个移动操作,将 <code>VecGenerator</code> 函数的返回值移动到变量 vec3 身上。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">std::vector<<span class="keyword">int</span>> <span class="title">VecGenerator</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">std::vector<<span class="keyword">int</span>> vec3 = <span class="built_in">VecGenerator</span>();</span><br></pre></td></tr></table></figure><p>假如 C++ 不支持移动操作,这段代码在实际运行时会发生什么呢?首先,需要构造一个临时对象,用于存储函数 <code>VecGenerator</code> 的返回值。其次,将这个临时对象的值复制到 <code>vec3</code> 身上。最后一步,销毁那个临时对象。实际执行的步骤看上去会像这个样子:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">std::vector<<span class="keyword">int</span>> temp = <span class="built_in">VecGenerator</span>();</span><br><span class="line">std::vector<<span class="keyword">int</span>> vec3 = temp;</span><br><span class="line"><span class="comment">// then delete temp</span></span><br></pre></td></tr></table></figure><p>支持移动操作之后,原本需要一个构造、一个复制和一个销毁才能搞定的事情,现在一次移动就完成了,提升了程序的运行效率。</p><p><strong>第二,对于较大的对象,和复制相比,移动的开销要小很多。</strong></p><p>这一点,尤其适用于一些指针或者元数据(metadata)存储在栈(stack)上,而值存储在堆(heap)上的数据类型,比如 C++ 中的 <code>std::vector</code> 和 <code>std::string</code>。</p><p>在 C 和 C++ 中,我们可以使用 <code>sizeof</code> 运算符得到一个对象存储消耗的栈空间的大小。无论是空的 <code>std::vector<int></code> 对象,还是一个存储了 10000 个整数的 <code>std::vector<int></code> 对象,在 64 位计算机上 <code>sizeof</code> 运算符返回的结果都是 24:他们在栈上消耗的空间大小是相同的<sup>[3]</sup>。这两个对象的不同之处,是前者不消耗堆空间,而后者会消耗至少 40000 字节的堆空间。</p><p>如果要复制一个空的 <code>vector</code> 对象,我们只需要复制栈上的 24 字节;而如果要复制一个存储了 10000 个整数的 <code>vector</code> 对象,我们不仅要复制栈上的 24 字节,还要复制堆上的 40000 字节。复制一个容器的开销,和这个容器存储的数据大小是成正比的。</p><p>但是移动操作不一样。当一个对象被移动时,我们完全可以只移动它在栈上的部分,而不需要移动它在堆上的部分。因此移动一个 <code>vector</code> 对象,无论这个对象有多大,都只涉及栈上的 24 字节。这个开销和复制相比,通常要小得多。</p><p><strong>第三,对于一些不能复制,或者需要独占所有权的对象,移动操作提供了转移对象所有权的方法。</strong></p><p>某一些类型的对象,例如 C++ 的 <code>std::unique_ptr</code>,是不允许被复制的。之所以不允许复制 <code>unique_ptr</code>,是因为它的设计目标,就是让持有 <code>unique_ptr</code> 的函数能独占该对象的所有权。因为 <code>unique_ptr</code> 不能被复制,所以移动操作就成为了转移该对象所有权的唯一方法。</p><br /><h2 id="为什么多数编程语言不支持移动操作"><a href="#为什么多数编程语言不支持移动操作" class="headerlink" title="为什么多数编程语言不支持移动操作"></a>为什么多数编程语言不支持移动操作</h2><p>为什么大多数编程语言,例如 Java / Go / Python,都不支持移动操作呢?从上面的例子我们已经看到,编程语言支持移动操作,为了和复制区分,肯定会在语法、语义和实现上变得更加复杂。只有移动操作带来了足够的好处,一门语言才有支持它的动力。对于多数编程语言来说,因为复制的开销不大,而且缺乏独占所有权的概念,没有足够的动机去支持移动操作。</p><p>我们在上一节中提到了,复制一个存储了 10000 个整数的 <code>std::vector<int></code> 是一件开销很大的事情,因为 C++ 会把堆上存储的数据也复制一份,也就是深度复制(deep copy)。</p><p>然而,大多数编程语言在复制时,都不会进行深度复制,而是只进行浅层复制(shallow copy)。比如下面这段 Java 代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">List<Integer> list = Arrays.asList(<span class="keyword">new</span> Integer[<span class="number">10000</span>]);</span><br><span class="line">List<Integer> list2 = list;</span><br></pre></td></tr></table></figure><p>把 <code>list</code> 复制给 <code>list2</code> 的时候,那 10000 个整数并没有被复制,被复制的只是 <code>list</code> 这个 8 字节大的指针而已。复制一个指针的开销是很低的,根本不需要使用移动操作来优化。</p><p>此外,C++ 和 Rust 语言不支持自动垃圾回收。这两种语言垃圾回收的普遍做法是,由一个函数独占对象的所有权,在函数执行结束时,自动销毁其独占的对象<sup>[4]</sup>。对于 Java / Go / Python 这些支持自动垃圾回收的语言来说,无须发展出一套完善的所有权的机制来进行垃圾回收。</p><p>总而言之,对于大多数编程语言,移动操作既不能改善运行效率,而且因为根本没有所有权的概念,也不需要通过它来实现所有权的转移。不支持对象的移动操作,也就是理所当然的事情了。</p><hr><p>[1] 关于左值和右值的详细定义,参见 <a href="https://en.cppreference.com/w/cpp/language/value_category" title="" target="">C++ 标准</a>。</p><p>[2] 对于如下基本类型,Rust 会进行拷贝而不是移动操作:布尔类型(<code>bool</code>)、字符类型(<code>char</code>)、整数和浮点数类型、以及由这几种类型组成的元组(<code>tuple</code>)。</p><p>[3] 一个 <code>std::vector</code> 对象在 64 位计算机上总是消耗 24 字节的栈空间。其中有 8 字节用于记录 <code>vector</code> 容器中存储的数据个数(<code>size</code>),另外 8 字节记录容器的容量(<code>capacity</code>),还有 8 字节的指针记录实际数据在堆中存放的位置(<code>ptr</code>)。</p><p>[4] 这种资源管理 / 垃圾回收方式有个专门的说法,叫 <a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization" title="" target="">RAII -- Resource acquisition is initialization</a>。</p>]]></content>
<summary type="html"><p>各种编程语言都支持对象(object)的构造、复制、读取、修改和销毁等操作。其中的复制(copy)操作,就是在计算机内存中的某个新地址,构造一个和原对象一模一样的新对象。</p>
<p>少数语言,例如 C++ 和 Rust,还支持一种叫『移动』(move)的操作。对象的移动,顾名思义,就是把原对象从计算机内存中的某个地址<strong>搬运</strong>至新的地址。听上去,移动操作做的事情似乎和复制差不多。那么为什么 C++ 和 Rust 要引入移动操作呢?为什么其他大多数语言都不支持移动操作呢?</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="programming" scheme="https://bitmingw.com/tags/programming/"/>
<category term="C++" scheme="https://bitmingw.com/tags/C/"/>
<category term="Rust" scheme="https://bitmingw.com/tags/Rust/"/>
</entry>
<entry>
<title>基于时间的 SQL 盲注</title>
<link href="https://bitmingw.com/2021/02/07/time-based-sql-blind-injection/"/>
<id>https://bitmingw.com/2021/02/07/time-based-sql-blind-injection/</id>
<published>2021-02-07T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.025Z</updated>
<content type="html"><![CDATA[<p>本博客网站由 nginx 驱动,服务器运行在 AWS 上。与其它有固定公网 IP 的服务器一样,我的服务器也时常受到外界的攻击。今天讲解一种常见的攻击方式:基于时间的 SQL 盲注(time-based blind SQL injection)。</p><span id="more"></span><p>我在 nginx 的用户访问日志 access.log 里面,经常能观测到一些可疑的请求——在 URL 中嵌入了代码。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 正常的请求</span></span><br><span class="line">GET /2015/01/28/life-in-7th-semester/</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 嵌入了代码的请求</span></span><br><span class="line">GET /2015/if(now()=sysdate(),sleep(15),0)/28/life-in-7th-semester/</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 正常的请求</span></span><br><span class="line">GET /2018/04/07/deep-simplicity/</span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 嵌入了代码的请求</span></span><br><span class="line">GET /2018/1%20waitfor%20delay%20'0:0:15'%20--%20/07/deep-simplicity/</span><br></pre></td></tr></table></figure><p>和正常的请求相比,这些可疑请求用代码替换掉了原 URL 的一部分:</p><ul><li><code>01</code> 被替换成了 <code>if(now()=sysdate(),sleep(15),0)</code>。</li><li><code>04</code> 被替换成了 <code>1 waitfor delay '0:0:15' -- </code>。注意 URL 中的 <code>%20</code> 是空格的转义字符。</li></ul><p>所以这两段代码分别有什么含义呢?</p><p>经过调查,第一段代码是一个 MySQL 语句。MySQL 支持 <code>if</code> 条件语句,它的格式是</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">if(<span class="keyword">condition</span>, <span class="keyword">execute</span> <span class="keyword">when</span> <span class="literal">true</span>, <span class="keyword">execute</span> <span class="keyword">when</span> <span class="literal">false</span>)</span><br></pre></td></tr></table></figure><p><code>now()</code> 和 <code>sysdate()</code> 都是 MySQL 的内置函数,用来获取系统当前时间。一般来说 <code>now()</code> 和 <code>sysdate()</code> 的返回值是相同的,也就是这个 <code>if</code> 语句的条件为真。此时 MySQL 会执行 <code>sleep(15)</code>,休眠 15 秒之后返回。</p><p>第二段代码是 Microsoft SQL Server 语句,其中 <code>waitfor delay '0:0:15'</code> 和 MySQL 的 <code>sleep(15)</code> 的作用完全相同。这里连 <code>if</code> 语句都省去了,直接休眠。</p><p>那么,为什么要构造这样的特殊请求,发送到我的服务器上呢?</p><p>很多 web 应用依赖于 URL 获取参数(比如用户名)。攻击者可以构造含有特定代码的 URL,如果未经过滤,这些代码可能会在 SQL 的执行阶段被激活,达成特定的目的。这类攻击方式叫做「SQL 注入」。前文描述的两个可疑请求,都是攻击者在尝试 SQL 注入的例子。</p><p>攻击者向我的服务器发送上述两种特殊构造的 URL,就是想知道他们的 SQL 代码是否会得到执行。如果服务器在 15 秒之后才做出响应,那么攻击者有相当大的把握认为,嵌入的 SQL 代码被执行了。如果服务器响应的时间短于 15 秒,那么攻击者就会知道,嵌入的 SQL 代码一定没有执行。由于探测 SQL 被执行与否取决于服务器的响应时间,因此说这是一种<strong>基于时间的</strong> SQL 注入。一旦攻击者发现 SQL 代码被执行了,就相当于找到了一个 SQL 注入的漏洞。他们之后可以利用这个漏洞控制整个 SQL 服务,盗取数据,或者进一步入侵服务器。</p><p>另一方面,我们不难察觉出,这种攻击有相当大的盲目性。</p><p>首先,攻击者不确定 URL 的哪个部分可能是 SQL 的参数。在第一个例子中,他们选取了 <code>/2015/01/28/</code> 中的 <code>01</code>,并认为这个字段可能会被 SQL 用到。这里的 <code>01</code> 指的是一月份,它是否真的会参与 SQL 查询,攻击者除了尝试之外没有别的办法知晓。</p><p>其次,攻击者不确定数据库的类型。上文中提及的两个例子,第一个是针对 MySQL 的,第二个是针对 SQL Server 的。事实上我还找到了针对 PostgreSQL 的 <code> OR 438=(SELECT 438 FROM PG_SLEEP(15))</code>。攻击者需要尝试每一种数据库,来确定服务器使用的数据库类型。</p><p>总结一下,这种攻击方式,需要在每一个可能成为 SQL 参数的位置,尝试每一种数据库类型,进行 SQL 注入。我们把盲目注入简称为<strong>盲注</strong>,所以这种攻击方式,可以称之为「基于时间的 SQL 盲注」。</p><p>以我对近期 nginx 用户访问日志的分析,在我的服务器上,此类攻击平均每个星期会发生 2000 次左右。让我感到十分安心的是,这个博客是一个静态站点,没有使用任何 SQL 服务,所以再多这样的攻击也注定是徒劳无益的。</p>]]></content>
<summary type="html"><p>本博客网站由 nginx 驱动,服务器运行在 AWS 上。与其它有固定公网 IP 的服务器一样,我的服务器也时常受到外界的攻击。今天讲解一种常见的攻击方式:基于时间的 SQL 盲注(time-based blind SQL injection)。</p></summary>
<category term="idea" scheme="https://bitmingw.com/categories/idea/"/>
<category term="web" scheme="https://bitmingw.com/tags/web/"/>
<category term="sql" scheme="https://bitmingw.com/tags/sql/"/>
</entry>
<entry>
<title>2021 年的计划</title>
<link href="https://bitmingw.com/2021/01/01/2021-plan/"/>
<id>https://bitmingw.com/2021/01/01/2021-plan/</id>
<published>2021-01-01T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.000Z</updated>
<content type="html"><![CDATA[<p>经历了炼狱一般的 2020 年,每个人都期待着 2021 年能少一些波涛汹涌,多一些风平浪静。尽管没有人知道病毒还会肆虐多久,但是至少,我们可以怀着积极的心态,构思新一年的蓝图。</p><span id="more"></span><p>回望 <a href="https://bitmingw.com/2017/01/01/2017-plan/" title="" target="">2017</a> 和 <a href="https://bitmingw.com/2019/01/01/2019-plan/" title="" target="">2019</a> 的年度规划,不足 50% 的完成率让我深感不安。但是这一切也实属无奈:在这个高度不确定的世界中,每隔几个月,我们都可能因为形势的变化,不得不改变先前的计划。举几个发生过的例子:</p><ol><li>在我的第一个岗位中,掌握分布式系统的基础知识是胜任工作的关键。后来因为种种原因,我换了一个组,发现那里的工作并不需要深入了解分布式系统的知识。因此,没有直接的动力支持我继续学习下去。</li><li>在发表了一篇分析某知名翻墙工具的文章后,我的博客被封锁了一段时间。由于担心回国后可能会被请去喝茶,于是停止了相关文章的更新。</li><li>去年,公司为应对危机采取了一系列举动,致使我的收入明显下滑,这也成为了我选择跳槽的主要原因。为了准备跳槽,我有半年的时间需要把大部分精力投入到 Leetcode 中,因此无法持续更新博客。</li></ol><p>考虑到以前的那些经历,在制订 2021 年度规划时,我决定缩减计划的数量,以便预留足够的时间用于计划外的事务。</p><p>2021 年的计划,主要包含下面几个部分。</p><br /><h2 id="在新的岗位有所作为"><a href="#在新的岗位有所作为" class="headerlink" title="在新的岗位有所作为"></a>在新的岗位有所作为</h2><p>我迄今为止的四年职业生涯,概括起来,是两年的『分布式系统』开发,和两年的『大数据分析』开发。去年底,我加入谷歌,开始涉足第三个领域:『通讯、移动与边缘计算』。以我的粗浅认知,我们要做的事情是,把原本集中于数据中心的资源分散到通讯网络的边缘(入口),以支持物联网和 5G 应用对通讯网络高吞吐量和低延迟的要求。</p><p>这份新的工作,从两个方面来看,都是相当大的挑战。首先,我对计算机网络的知识储备没有太多自信,对通讯网络更是知之甚少。作为这个领域的新人,最紧要的,就是迅速掌握相关的核心知识,理解业务模型和上下游关联。其次,通讯网络对延迟要求很高,大概率会使用 C++ 和 Go 等高性能语言。因为我没有这两种语言的工作经历,所以熟练运用新的编程语言也会成为一大难关。</p><p>我的目标是,经过一年的工作,能够深入理解新领域的业务模型,完成高质量的系统设计和代码实现。</p><br /><h2 id="学习-Rust-语言"><a href="#学习-Rust-语言" class="headerlink" title="学习 Rust 语言"></a>学习 Rust 语言</h2><p>我曾经思考过,像 Java 这样的古老语言,已经越来越不适应现代应用的开发,会不会在十几年之后逐渐没落?现在想想,我当时大可不必思考这个问题,因为不太可能一辈子只依靠 Java 吃饭。就像现在,我的工作或许要改用 C++ 和 Go 语言了。有两门新的语言需要熟练掌握,这已经够辛苦了,为什么还要再花时间学 Rust 呢?如果你是 <a href="https://program-think.blogspot.com/" title="" target="">编程随想博客</a> 的读者,或许还能记起编程君的回答:学编程语言,最重要的是为了学习语言背后的编程范式,这些编程范式是通用的智慧。Lisp 虽然拥有最多的编程范式,但是没有得到广泛应用,它同时也缺乏多线程等现代语言的要素。C++ 和 Rust 也是编程范式的集大成者,且相较之下,Rust 历史包袱少,具备更多的现代元素,是高性能语言冉冉升起的明日之星。</p><p>由于 Rust 语言还没有谷歌的官方支持,因此我不会在工作中使用它。但是,全方位了解这门语言的特性,有助于我认识各类编程范式,在未来写出更优雅的代码。学习 Rust 作为一项长期投资,在事业早期就开始投入,是很有意义的。</p><br /><h2 id="Leetcode-常态化"><a href="#Leetcode-常态化" class="headerlink" title="Leetcode 常态化"></a>Leetcode 常态化</h2><p>我在研究生毕业的时候,由于懒惰,直接签了实习的返聘 offer,跳过了很多人都经历过的、痛苦的找工作过程。时隔四年,当我再一次踏上找工作的旅程,面对几乎是从零开始的 Leetcode,内心的滋味相当复杂。假如下一次,不是我自愿离开公司,而是公司开除了我,我是否能够迅速进入状态开始面试,而不是先花半年的时间刷题?</p><p>把 Leetcode 作为日常的一部分,其实是为了追求自由。在公司抛弃我的时候,它是让我保持平心静气的自由;在我遇见了更好的机会时,它既给了我自抬身价的自由,又是先人一步抢摊登陆的自由。</p><p>当然,这里的目标并不是做完 Leetcode 的全部题目。我的目标是,即便不能每天都做 Leetcode,也应当每周都做,而且平均下来一天至少一道题。我可能会尝试使用不同的语言解题,权且当作是语言学习的课后练习好了。</p><br /><p>关于 2021 年的目标,我先说这么多。还有一些想做却没有列举的事,我们在年终总结见分晓。</p>]]></content>
<summary type="html"><p>经历了炼狱一般的 2020 年,每个人都期待着 2021 年能少一些波涛汹涌,多一些风平浪静。尽管没有人知道病毒还会肆虐多久,但是至少,我们可以怀着积极的心态,构思新一年的蓝图。</p></summary>
<category term="plan" scheme="https://bitmingw.com/categories/plan/"/>
<category term="planning" scheme="https://bitmingw.com/tags/planning/"/>
</entry>
<entry>
<title>复利的魔法</title>
<link href="https://bitmingw.com/2020/12/13/compound-interest/"/>
<id>https://bitmingw.com/2020/12/13/compound-interest/</id>
<published>2020-12-13T08:00:00.000Z</published>
<updated>2026-01-01T00:57:21.046Z</updated>
<content type="html"><![CDATA[<p>在美国,有一种叫 401k 的养老金计划。个人和雇主可以每年向 401k 养老金账户存入一定数量的钱,60 岁之后就可以取出来。在 2020 年,个人每年可以向 401k 账户中存入最多 $19500 而不用上税。这个数字给人的直观感受,应该可以用杯水车薪来形容。很难想象,经历 30 年的通货膨胀,每年能支出的养老金却只有两三万,会是一种怎样的生活水平。所以,401k 账户中的养老金,真的能撑到人死的那一天么?</p><p>答案有一些反直觉。需要担心的,不是人还没死,钱就花完了,而是人死了,钱还没花完。假设你所在的公司稍有一些良心,愿意按个人出资的 50% 向你的 401k 账户中存钱。也就是说,每年 401k 账户中新存入的资金有 $29250。如果资产的平均年投资回报率有 7%,如此坚持 30 年,账户的最终余额是 296 万!</p><p>为什么说这个答案反直觉呢?大脑的正常思维是,每年存入 $29250,需要大约 100 年的时间才能达到 296 万。但是实际上,这一过程只需要 30 年。这其中的奥秘,就是复利。在复利的帮助下,资产价值会随着时间流逝指数增长。</p><span id="more"></span><p>怎样才能更好地应用复利,实现财务自由呢?有下面几个要点:</p><p><strong>尽早开始</strong></p><p>由于资产价值随着时间指数增长,投资年限越长,资产价值的增长越显著。同样是每年存入 $29250 和 7% 的投资回报率,30 年之后的资产价值是 296 万,但如果少存 5 年,25 年之后的资产价值只有 198 万,少了将近 100 万。如果只存了 20 年,资产价值则是 128 万,这些钱够不够退休就是一个未知数了。</p><p><strong>投资高回报的项目</strong></p><p>平均年投资回报率对最终的资产价值有决定性的影响。我们先前使用 7% 的投资回报率进行计算,是因为美国标普 500 指数的平均年投资回报率是 7%,所以说 7% 是一个比较合理的参考值。如果股市的涨跌起伏让你很不安,结果选择了投资回报率 5% 的债券,那么 30 年之后你只能拿到 204 万,这个和 296 万的差别还是显而易见的。至于银行定期存款 3% 的投资回报率,只能让 30 年后的余额变成 143 万,这恐怕就不太够了。</p><p>当然,高回报的项目通常也意味着高风险。在投资的最后几年,可以逐步把资金从高回报的项目转移到低风险的项目,减少收益流失的可能性。</p><p><strong>好的雇主很重要</strong></p><p>雇主向 401k 账户中存入的钱,会等比放大资产的收益。也就是说,愿意按个人出资的 50% 贡献 401k 账户的雇主,与一毛不拔的雇主相比,最终的资产价值会多 50%。用上面的例子来解释,这就是 200 万和 300 万之间的差别。所以,尽量选一个有良心的雇主吧。</p><p>最后,建议你使用这个 <a href="https://gist.github.com/bitmingw/1876df73d3cc52837ed90ec1ab0d00b1" title="" target="">Python 复利计算脚本</a> 来实际感受一下复利的魔法。</p>]]></content>
<summary type="html"><p>在美国,有一种叫 401k 的养老金计划。个人和雇主可以每年向 401k 养老金账户存入一定数量的钱,60 岁之后就可以取出来。在 2020 年,个人每年可以向 401k 账户中存入最多 $19500 而不用上税。这个数字给人的直观感受,应该可以用杯水车薪来形容。很难想象,经历 30 年的通货膨胀,每年能支出的养老金却只有两三万,会是一种怎样的生活水平。所以,401k 账户中的养老金,真的能撑到人死的那一天么?</p>
<p>答案有一些反直觉。需要担心的,不是人还没死,钱就花完了,而是人死了,钱还没花完。假设你所在的公司稍有一些良心,愿意按个人出资的 50% 向你的 401k 账户中存钱。也就是说,每年 401k 账户中新存入的资金有 $29250。如果资产的平均年投资回报率有 7%,如此坚持 30 年,账户的最终余额是 296 万!</p>
<p>为什么说这个答案反直觉呢?大脑的正常思维是,每年存入 $29250,需要大约 100 年的时间才能达到 296 万。但是实际上,这一过程只需要 30 年。这其中的奥秘,就是复利。在复利的帮助下,资产价值会随着时间流逝指数增长。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="finance" scheme="https://bitmingw.com/tags/finance/"/>
</entry>
<entry>
<title>Don't Use File.separator When Loading Java Resource Files</title>
<link href="https://bitmingw.com/2020/05/16/do-not-use-file-separator-when-loading-java-resource-files/"/>
<id>https://bitmingw.com/2020/05/16/do-not-use-file-separator-when-loading-java-resource-files/</id>
<published>2020-05-16T07:00:00.000Z</published>
<updated>2026-01-01T00:57:21.030Z</updated>
<content type="html"><![CDATA[<p>Don't use <a href="https://docs.oracle.com/javase/8/docs/api/java/io/File.html#separator" title="" target="">File.separator</a> when loading resource files into Java. Even though <code>File.separator</code> is created to make Java program portable to different platforms, using it to load resource files will just have opposite effect.</p><span id="more"></span><p>I was working on a project written in Java. A simplified structure of project was like this:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">|--src</span><br><span class="line"> |--main</span><br><span class="line"> |--java</span><br><span class="line"> | |--myproject</span><br><span class="line"> | |--app</span><br><span class="line"> | |--Main.java</span><br><span class="line"> |</span><br><span class="line"> |--resources</span><br><span class="line"> |--META-INF</span><br><span class="line"> |--app.properties</span><br></pre></td></tr></table></figure><p>The program tried to load a properties file when it started.</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String APP_CONF = <span class="string">"META-INF"</span> + File.separator + <span class="string">"app.properties"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> InputStream in = Main.class.getClassLoader().getResourceAsStream(APP_CONF);</span><br><span class="line"> <span class="keyword">if</span> (in == <span class="keyword">null</span>) {</span><br><span class="line"> System.err.println(<span class="string">"[ERROR] App configuration "</span> + APP_CONF + <span class="string">" is not found."</span>);</span><br><span class="line"> System.exit(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>When I tested this program in a ubuntu machine, it worked perfectly. However when I copied the same jar and ran it in a windows machine, it broke and showed the following error message:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[ERROR] App configuration META-INF\app.properties is not found.</span><br></pre></td></tr></table></figure><p>I unzipped the jar and did see <code>app.properties</code> file under <code>META-INF</code> folder. A <code>ls META\app.properties</code> command also worked. Then why Java couldn't load that resource file?</p><p>After hours of research I found the answer in <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-" title="" target="">Java docs of CLassLoader</a>:</p><blockquote><p>ClassLoader</p><p>public URL getResource(String name)</p><p>Finds the resource with the given name.<br>...<br><strong>The name of a resource is a '/'-separated path name that identifies the resource.</strong></p></blockquote><p>It means that when specifying the resource file to load, you have to use the forward slash <code>/</code> to separate the path. Since in windows the value of <code>File.separator</code> is backward slash <code>\</code>, Java won't be able to find the resource file.</p><p>In conclusion, to make your Java program portable to windows and some other platforms, you should not use <code>File.separator</code> when loading resource files. Use <code>/</code> instead.</p>]]></content>
<summary type="html"><p>Don&#39;t use <a href="https://docs.oracle.com/javase/8/docs/api/java/io/File.html#separator" title="" target="">File.separator</a> when loading resource files into Java. Even though <code>File.separator</code> is created to make Java program portable to different platforms, using it to load resource files will just have opposite effect.</p></summary>
<category term="info" scheme="https://bitmingw.com/categories/info/"/>
<category term="java" scheme="https://bitmingw.com/tags/java/"/>
</entry>
<entry>
<title>看懂上市公司财报</title>
<link href="https://bitmingw.com/2020/04/26/understand-financial-statements/"/>
<id>https://bitmingw.com/2020/04/26/understand-financial-statements/</id>
<published>2020-04-26T07:00:00.000Z</published>
<updated>2026-01-01T00:57:20.983Z</updated>
<content type="html"><![CDATA[<p>今年的股市因为受到新型冠状病毒的影响,走出了不寻常的行情。虽然一只股票的短期走势受到外部因素的影响很大,但它的长期走势,主要是由公司的经营状况来决定的。要想了解一家上市公司的经营状况,最直观的方式是阅读公司的财报。</p><p>在美国上市的公司,有义务在每年和每个季度披露公司财报。年度财报和季度财报都可以在 <a href="https://www.sec.gov/edgar/searchedgar/companysearch.html" title="" target="">SEC</a> 网站上查询。考虑到大部分行业都具备一定的季节性,分析公司基本面一般会重点考察年度财报。</p><p>大多数公司的年度财报通常有 50 页以上,可见其中包含的内容极为丰富。作为投资者,我们最关心的有如下一些信息:</p><ol><li>公司在过去的一年中,收入和支出分别是多少。这些信息可以从损益表(income statement)中获取。</li><li>公司当前的资产和负债分别有多少。这些信息记录在资产负债表(balance sheet)中。</li><li>过去一年中,公司账户上现金数额的变化及其原因。现金流量表(cash flow statement)里包含了这方面的信息。</li></ol><p>下面以苹果公司 2019 年度的财报为例,提取上面三个维度的信息。注意,本文的分析结果仅供参考,并没有推荐购买相应的股票。</p><br /><span id="more"></span><h2 id="损益表"><a href="#损益表" class="headerlink" title="损益表"></a>损益表</h2><p>损益表记录了苹果公司 2018 财年和 2019 财年的销售收入和支出(单位:百万美元,下同)。</p><table><thead><tr><th align="left"></th><th align="left"></th><th align="right">2019-09-28</th><th align="right">2018-09-29</th></tr></thead><tbody><tr><td align="left">net sales</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">products</td><td align="right">213,883</td><td align="right">225,847</td></tr><tr><td align="left"></td><td align="left">services</td><td align="right">46,291</td><td align="right">39,748</td></tr><tr><td align="left"></td><td align="left">total net sale</td><td align="right">260,174</td><td align="right">265,595</td></tr><tr><td align="left"></td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left">cost of sales</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">products</td><td align="right">144,996</td><td align="right">148,164</td></tr><tr><td align="left"></td><td align="left">services</td><td align="right">16,786</td><td align="right">15,592</td></tr><tr><td align="left"></td><td align="left">total cost of sale</td><td align="right">161,782</td><td align="right">163,756</td></tr><tr><td align="left"></td><td align="left">gross margin</td><td align="right">98,392</td><td align="right">101,839</td></tr></tbody></table><p>苹果公司把自己的销售收入分成了两个大类:产品收入和服务收入。产品收入主要是指 iPhone, iPad, Mac 等硬件产品的收入。服务收入则包含 AppStore, Apple TV, iCloud 等服务的收入。每一项收入,会对应一个营收成本(例如用于组装 iPhone 的零部件的成本,或者运营 iCloud 服务器的成本)。收入与营收成本之差也就是毛利(gross margin)。毛利与销售收入的比值是毛利率。</p><p>从财报中可以得出,苹果公司在 2018 财年和 2019 财年的毛利率分别为 38.3% 和 37.8%。</p><table><thead><tr><th align="left"></th><th align="left"></th><th align="right">2019-09-28</th><th align="right">2018-09-29</th></tr></thead><tbody><tr><td align="left">operating expenses</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">research and development</td><td align="right">16,217</td><td align="right">14,236</td></tr><tr><td align="left"></td><td align="left">selling, general and administrative</td><td align="right">18,245</td><td align="right">16,705</td></tr><tr><td align="left"></td><td align="left">total operating expenses</td><td align="right">34,462</td><td align="right">30,941</td></tr><tr><td align="left"></td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left">operating income</td><td align="left"></td><td align="right">63,930</td><td align="right">70,898</td></tr><tr><td align="left">other income / (expense)</td><td align="left"></td><td align="right">1,807</td><td align="right">2,005</td></tr><tr><td align="left">income before taxes</td><td align="left"></td><td align="right">65,737</td><td align="right">72,903</td></tr><tr><td align="left">income taxes</td><td align="left"></td><td align="right">10,481</td><td align="right">13,372</td></tr><tr><td align="left">net income</td><td align="left"></td><td align="right">55,256</td><td align="right">59,531</td></tr></tbody></table><p>除了营收成本以外,公司还有许多其他的运营开支,例如研发开支,销售开支,管理开支等。去除了所有这些开支后就得到了营业收入(operating income)。营业收入通常会构成公司税前收入的大部分,它扣除税金后就得到了公司的净收入(net income)。苹果公司在 2019 年的净收入为 552 亿美元。</p><p>从损益表中可以计算一个重要的指标——销货报酬率/净利润率(net margin)。净利润率是净收入与销售收入的比值。苹果公司 2019 年的净利润率是 21.2%,也就是销售收入的 21.2% 转化成了纯利润。<strong>一般来说,一家好的公司应具备 15% 以上的净利润率。</strong></p><br /><h2 id="资产负债表"><a href="#资产负债表" class="headerlink" title="资产负债表"></a>资产负债表</h2><p>资产负债表会列出企业从创办开始到现在,累积的资产、负债以及资产净值。它始终保证下面这个等式成立:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">资产(asset) = 负债(liability) + 资产净值(equity)</span><br></pre></td></tr></table></figure><p>下面的表格列举了苹果公司在 2018 财年底和 2019 财年底的流动资产(current assets)。流动资产通常指能在一年时间内转化成现金的资产。它主要包括下面几个类别:</p><ol><li>现金与现金等价物(cash and cash equivalents)</li><li>公司持有的股票、债券、贷款等(marketable securities)</li><li>应收账款(account receivable),即客户应当交付而尚未交付的钱,例如买手机分期付款的钱</li><li>库存(inventories)</li></ol><table><thead><tr><th align="left"></th><th align="left"></th><th align="right">2019-09-28</th><th align="right">2018-09-29</th></tr></thead><tbody><tr><td align="left">current assets</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">cash and cash equivalents</td><td align="right">48,844</td><td align="right">25,913</td></tr><tr><td align="left"></td><td align="left">marketable securities</td><td align="right">51,713</td><td align="right">40,388</td></tr><tr><td align="left"></td><td align="left">accounts receivable</td><td align="right">22,926</td><td align="right">23,186</td></tr><tr><td align="left"></td><td align="left">inventories</td><td align="right">4,106</td><td align="right">3,956</td></tr><tr><td align="left"></td><td align="left">vendor non-trade receivable</td><td align="right">22,878</td><td align="right">25,809</td></tr><tr><td align="left"></td><td align="left">other current assets</td><td align="right">12,352</td><td align="right">12,087</td></tr><tr><td align="left"></td><td align="left">total current assets</td><td align="right">162,819</td><td align="right">131,339</td></tr></tbody></table><p>此外公司还有一部分非流动资产(non-current assets)。这些资产的流动性较差,通常不能在一年时间内转化为现金。非流动资产主要包括:</p><ol><li>长期债券和贷款(marketable securities)</li><li>房屋等固定资产(property, plant and equipment)</li></ol><p>它们在财报中是这样记录的:</p><table><thead><tr><th align="left"></th><th align="left"></th><th align="right">2019-09-28</th><th align="right">2018-09-29</th></tr></thead><tbody><tr><td align="left">non-current assets</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">marketable securities</td><td align="right">105,341</td><td align="right">170,799</td></tr><tr><td align="left"></td><td align="left">property, plant and equipment</td><td align="right">37,378</td><td align="right">41,304</td></tr><tr><td align="left"></td><td align="left">Other non-current assets</td><td align="right">32,978</td><td align="right">22,283</td></tr><tr><td align="left"></td><td align="left">total non-current assets</td><td align="right">175,697</td><td align="right">234,386</td></tr><tr><td align="left">total assets</td><td align="left"></td><td align="right">338,516</td><td align="right">365,725</td></tr></tbody></table><p>从财报中可以看出,苹果公司在 2019 财年底拥有的资产总值为 3385 亿美元,其中现金资产 488 亿美元。</p><p>有了净收入和资产总值,我们可以计算第二个重要的指标——资产收益率(return on assets)。资产收益率是净收入与资产总值的比值。苹果公司在 2019 财年的资产收益率是 16.3%。<strong>通常情况下,一家靠谱的公司应具备 6% 以上的资产收益率。</strong></p><p>与资产对应的是负债。先来看看财报对负债的记录:</p><table><thead><tr><th align="left"></th><th align="left"></th><th align="right">2019-09-28</th><th align="right">2018-09-29</th></tr></thead><tbody><tr><td align="left">current liabilities</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">accounts payable</td><td align="right">46,236</td><td align="right">55,888</td></tr><tr><td align="left"></td><td align="left">other current liabilities</td><td align="right">37,720</td><td align="right">33,327</td></tr><tr><td align="left"></td><td align="left">deferred revenue</td><td align="right">5,522</td><td align="right">5,966</td></tr><tr><td align="left"></td><td align="left">commercial paper</td><td align="right">5,980</td><td align="right">11,964</td></tr><tr><td align="left"></td><td align="left">term debt</td><td align="right">10,260</td><td align="right">8,784</td></tr><tr><td align="left"></td><td align="left">total current liabilities</td><td align="right">105,718</td><td align="right">115,929</td></tr><tr><td align="left">non-current liabilities</td><td align="left"></td><td align="right"></td><td align="right"></td></tr><tr><td align="left"></td><td align="left">term debt</td><td align="right">91,807</td><td align="right">93,735</td></tr><tr><td align="left"></td><td align="left">other non-current liabilities</td><td align="right">50,503</td><td align="right">48,914</td></tr><tr><td align="left"></td><td align="left">total non-current liabilities</td><td align="right">142,310</td><td align="right">142,649</td></tr><tr><td align="left">total liabilities</td><td align="left"></td><td align="right">248,028</td><td align="right">258,578</td></tr></tbody></table><p>与资产类似,负债也分为流动负债(current liabilities)和长期债务(non-current liabilities)。流动负债指公司在一年之内需偿还的债务。债务一般包括这么几个类别:</p><ol><li>应付账款(accounts payable),即已收到供应商的货物或服务,尚未交付的欠款</li><li>递延收入(deferred revenue),即已收到客户提前预支的费用,尚未交付的产品与服务</li><li>有期限债务(term debt),需按照约定时间偿还的债务</li></ol><p>资产与负债的差值是资产净值。它主要由两个部分组成:</p><ol><li>公司股东为了换取股票而为公司提供的资金(common stock and additional paid-in capital)</li><li>公司的留存收益(retained earnings)</li></ol><p>其中,公司的留存收益代表着公司从成立以来,挣到手且尚未花出去的钱的总额。</p><p>从净收入和资产净值,可以计算第三个重要的指标——净资产收益率/股权回报率(return on equity)。股权回报率是净收入占资产净值的比例。<strong>一家好公司应当具备 15% 以上的股权回报率。</strong>苹果公司以 61.1% 的股权回报率远远超出了这个标准。</p><br /><h2 id="现金流量表"><a href="#现金流量表" class="headerlink" title="现金流量表"></a>现金流量表</h2><p>损益表记录了公司运作中应当计入的收益和支出,但由于应计的收益和支出不完全等同于实际的收益和支出,因此损益表不能用来表示公司账户余额的变化。</p><p>举个例子,假如苹果公司允许客户以六个月分期付款的方式购买 iPhone。苹果公司将这部 iPhone 售出的时刻,便可以将整个 iPhone 的销售价格计入公司收益,尽管公司要在接下来的六个月时间里分批拿到这笔钱。</p><p>现金流量表的作用,就是在损益表的基础上进行修正,展示公司实际账户余额的变化。</p><p>现金流的变化主要有三个来源:公司的日常运营、公司的投资活动、公司的金融活动。公司的日常运营是现金流最重要的来源,当公司从日常运营中获取稳定的现金流后,就可以将这笔钱用于投资(例如扩大再生产)和金融活动(回购股票和派发股息)。</p><table><thead><tr><th align="left"></th><th align="right">2019-09-28</th><th align="right">2018-09-29</th></tr></thead><tbody><tr><td align="left">cash, cash equivalents and restricted cash, beginning balances</td><td align="right">25,913</td><td align="right">20,289</td></tr><tr><td align="left">cash generated by operating activities</td><td align="right">69,391</td><td align="right">77,434</td></tr><tr><td align="left">cash generated by investing activities</td><td align="right">45,896</td><td align="right">16,066</td></tr><tr><td align="left">cash used in financing activities</td><td align="right">(90,976)</td><td align="right">(87,876)</td></tr><tr><td align="left">cash, cash equivalents and restricted cash, ending balances</td><td align="right">50,224</td><td align="right">25,913</td></tr></tbody></table><p>从上面的财报中可以看到,苹果公司在 2019 财年初持有的现金及等价物总额为 259 亿美元。在 2019 财年中,公司日常运营和投资活动分别使得现金增加了 693 亿和 458 亿美元。在支出 909 亿美元用于金融活动后,2019 财年底,苹果公司持有的现金及等价物总额为 502 亿美元。</p><p>公司从日常运营中获取的现金,减去投资需要消耗的现金,便是公司的自由现金流(free cash flow)。自由现金流是公司可以任意支配而不影响持续运营的资金。<strong>第四个重要的指标是自由现金流与销售收入的比值,5% 以上说明公司具备强有力的获取自由现金流的能力。</strong>对于苹果公司来说,这个比值高达 44.3%,因此股票价格一路飞涨也是理所当然的事情了。</p>]]></content>
<summary type="html"><p>今年的股市因为受到新型冠状病毒的影响,走出了不寻常的行情。虽然一只股票的短期走势受到外部因素的影响很大,但它的长期走势,主要是由公司的经营状况来决定的。要想了解一家上市公司的经营状况,最直观的方式是阅读公司的财报。</p>
<p>在美国上市的公司,有义务在每年和每个季度披露公司财报。年度财报和季度财报都可以在 <a href="https://www.sec.gov/edgar/searchedgar/companysearch.html" title="" target="">SEC</a> 网站上查询。考虑到大部分行业都具备一定的季节性,分析公司基本面一般会重点考察年度财报。</p>
<p>大多数公司的年度财报通常有 50 页以上,可见其中包含的内容极为丰富。作为投资者,我们最关心的有如下一些信息:</p>
<ol>
<li>公司在过去的一年中,收入和支出分别是多少。这些信息可以从损益表(income statement)中获取。</li>
<li>公司当前的资产和负债分别有多少。这些信息记录在资产负债表(balance sheet)中。</li>
<li>过去一年中,公司账户上现金数额的变化及其原因。现金流量表(cash flow statement)里包含了这方面的信息。</li>
</ol>
<p>下面以苹果公司 2019 年度的财报为例,提取上面三个维度的信息。注意,本文的分析结果仅供参考,并没有推荐购买相应的股票。</p>
<br /></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="finance" scheme="https://bitmingw.com/tags/finance/"/>
</entry>
<entry>
<title>Migrate VMware Workstation / Fusion VM to ESXi</title>
<link href="https://bitmingw.com/2020/02/12/migrate-local-vm-to-esxi/"/>
<id>https://bitmingw.com/2020/02/12/migrate-local-vm-to-esxi/</id>
<published>2020-02-12T08:00:00.000Z</published>
<updated>2026-01-01T00:57:20.957Z</updated>
<content type="html"><![CDATA[<p>I used to run a VM in my mac with VMware Fusion. As my mac is getting older, the VM is becoming slower and slower. One day I got an ESXi host which had sufficient resource to run that VM, but migration from VMware Fusion to ESXi was not a straight forward task. If you are facing similar problem with VMware Workstation or Fusion, the following steps will help you.</p><span id="more"></span><br /><h2 id="Step-1-Export-VMDK"><a href="#Step-1-Export-VMDK" class="headerlink" title="Step 1: Export VMDK"></a>Step 1: Export VMDK</h2><p>Assuming you want to migrate this VM in your local environment:</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/source-vm.png"></div><p>First you need to export the VM, which contains the VMDK file you will need to transfer to the ESXi host.</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/export-ovf.png"></div><p>A VM can be export as a single OVA file, or two separate files (OVF file + VMDK file). The recommendation is choosing separate files, since only VMDK file is needed in later steps.</p><br /><h2 id="Step-2-Upload-and-Convert-VMDK"><a href="#Step-2-Upload-and-Convert-VMDK" class="headerlink" title="Step 2: Upload and Convert VMDK"></a>Step 2: Upload and Convert VMDK</h2><p>When the export finishes, you can use <code>scp</code> to upload the VMDK file to ESXi datastore.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@esxi:/vmfs/volumes/datastore] ls</span><br><span class="line">...</span><br><span class="line">Windows-10-64-Enterprise-disk1.vmdk</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>The VMDK format between ESXi and VMware Workstation / Fusion is different, so the uploaded file can't be consumed by ESXi directly. There is a command <code>vmkfstools</code> to convert Workstation and Fusion VMDK into ESXi's format. Let's rename the uploaded VMDK and do the conversion.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root...] mv Windows-10-64-Enterprise-disk1.vmdk Windows-10-64-Enterprise-disk1.vmdk.fusion</span><br><span class="line">[root...] vmkfstools -i Windows-10-64-Enterprise-disk1.vmdk.fusion Windows-10-64-Enterprise-disk1.vmdk</span><br><span class="line">Destination disk format: VMFS zeroedthick</span><br><span class="line">Cloning disk 'Windows-10-64-Enterprise-disk1.vmdk.fusion'...</span><br><span class="line">Clone: 100% done.</span><br></pre></td></tr></table></figure><p>Now you should have the following files in the datastore:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root...] ls</span><br><span class="line">...</span><br><span class="line">Windows-10-64-Enterprise-disk1-flat.vmdk</span><br><span class="line">Windows-10-64-Enterprise-disk1.vmdk</span><br><span class="line">Windows-10-64-Enterprise-disk1.vmdk.fusion</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>"Windows-10-64-Enterprise-disk1.vmdk.fusion" is the original VMDK you uploaded. "Windows-10-64-Enterprise-disk1.vmdk" and "Windows-10-64-Enterprise-disk1-flat.vmdk" are the new files generated by <code>vmkfstools</code> command.</p><br /><h2 id="Step-3-Create-New-VM-with-VMDK"><a href="#Step-3-Create-New-VM-with-VMDK" class="headerlink" title="Step 3: Create New VM with VMDK"></a>Step 3: Create New VM with VMDK</h2><p>Now you can create a new VM with the generated VMDK in the datastore. The UI may be different in your environment.</p><p>Click "New Virtual Machine...".</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/create-new-vm.png"></div><p>In creation type, select "Create a new virtual machine".</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/create-new-vm-01.png"></div><p>In customize hardware, click the cross at the right of "New Hard disk *" to remove the default disk assigned to this virtual machine.</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/create-new-vm-07a.png"></div><p>Then click "ADD NEW DEVICE" -> "Existing Hard Disk", and select the generated VMDK file.</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/create-new-vm-07b.png"></div><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/create-new-vm-07c.png"></div><p>After that, you should be able to see the generated VMDK is listed as "Disk File".</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/create-new-vm-07d.png"></div><p>When everything is correct, move forward to create and start VM. Now you should be able to access the VM from Workstation or Fusion even though it is running in ESXi.</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2020-02-12-migrate-local-vm-to-esxi/access-new-vm.png"></div>]]></content>
<summary type="html"><p>I used to run a VM in my mac with VMware Fusion. As my mac is getting older, the VM is becoming slower and slower. One day I got an ESXi host which had sufficient resource to run that VM, but migration from VMware Fusion to ESXi was not a straight forward task. If you are facing similar problem with VMware Workstation or Fusion, the following steps will help you.</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="VMware" scheme="https://bitmingw.com/tags/VMware/"/>
<category term="ESXi" scheme="https://bitmingw.com/tags/ESXi/"/>
<category term="vSphere" scheme="https://bitmingw.com/tags/vSphere/"/>
</entry>
<entry>
<title>2019 年终总结</title>
<link href="https://bitmingw.com/2019/12/29/2019-retrospect/"/>
<id>https://bitmingw.com/2019/12/29/2019-retrospect/</id>
<published>2019-12-29T08:00:00.000Z</published>
<updated>2026-01-01T00:57:20.953Z</updated>
<content type="html"><![CDATA[<h2 id="创业路上"><a href="#创业路上" class="headerlink" title="创业路上"></a>创业路上</h2><p>从 2017 年 2 月开始,我在 VMware 工作已经有近三年的时间了。前两年在平台组做了一系列项目,大部分和分布式系统治理有关。不能否认在那里学到了很多知识技能,但总有一天我会彻底厌倦看日志修 bug 的循环往复。2019 年 3 月,我换了一个组,投入到了新产品的开发工作中。</p><p>进组的时机正逢项目从第一行代码起步。全组像一家创业公司:我们有大致的目标和方向,但所有的宏观设计和微观实现都需要自己摸索。与许多其他创业公司一样,踩各类知名开源软件的坑是大家都会经历的事情。</p><p>我们的项目决定使用 <a href="https://druid.apache.org/" title="" target="">druid</a> 作为时间序列数据库。新数据通过 <a href="https://kafka.apache.org/" title="" target="">kafka</a> 源源不断进来,经索引后存放在数据库中,这一过程称为数据摄取。官网上的文档和例子还算齐全,示例程序运行起来也一切正常,但换成我们自己的数据后就立刻出现各类错误,以至于不得不深挖源码一探究竟。这么一个看似照葫芦画瓢的任务,最后花费了我三天时间。</p><p>之后我们想比较两种不同的数据摄取方式,其中一种依赖于 twitter 某个 scala 开源库的两年前的代码。那些代码过于古老,存在一系列编译和运行的问题。每一次运行,我都觉得,这是最后一个错误,再过片刻就可以把程序跑通了。但每一次,又会出现新的错误。那些莫名其妙的异常信息迷惑着我,但我没有时间探索这个开源库的细枝末节,与其确切地知道为什么会出错,不如凭借直觉把解决的方法猜出来。经历近一星期的煎熬,我终于跑通了流水线,拿到了两种方案的对比结果。我们最后没有选择这套开源库,但我不认为这是一项做完即被丢弃和遗忘的工作。对创业公司而言,知道为什么某种方式不好,并且有理有据地避开它,可能会在未来节约不小的成本 [1]。</p><p>从三月到九月,经历过一周连续五天的 war room,经历过打团战从上午十点到晚上九点半,经历过周六周日接连两天来公司点外卖,我们终于发布了产品的第一个版本。正当所有人想好好庆祝一下的时候,我们的客服电话被打爆了。</p><span id="more"></span><p>用户报告的故障主要来源于证书交换环节,这里面有不少可以算是我的“功劳”。因为本人不是安全专家,以前未曾参与过证书管理相关功能的开发,测试工程师也没有注意到许多企业用户可能会使用自己的证书替换系统默认证书,于是出现了产品部署后因无法建立信任,导致数据摄入失败,服务整体停摆的问题。我和几位同事一道,在一个多月的时间里接听了十几通类似的电话,受到影响的客户包括法国第二大银行 Crédit Agricole 等跨国巨头。万幸的是,这次产品质量风波似乎没有影响到来自第三世界国家的客户,让我避免了被派遣并被暴打一顿的下场 [2]。</p><p>以前我觉得,产品做成什么样子就应该说成什么样子,不能搞虚假宣传。做了这个项目之后,我现在觉得,宣传的效果、做出来的效果,以及客户最后看到的效果,必定是有差别的。如果兢兢业业工作,让这三者之间的差别不那么大,就可以问心无愧了。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2019-12-29-2019-retrospect/NSX-Intelligence-1.png"> <p>产品宣传效果</p></div><div style="text-align: center"> <img src="https://bitmingw.com/assets/2019-12-29-2019-retrospect/NSX-Intelligence-2.png"> <p>内部测试效果</p></div><div style="text-align: center"> <img src="https://bitmingw.com/assets/2019-12-29-2019-retrospect/NSX-Intelligence-3.png"> <p>客户实际运行效果</p></div><p>[1] 关于节约成本,我们避开了公司内部某集成测试框架,节省了很多分拣 bug 的时间;我原先的组没有避开公司内部某数据库,导致过长达数月的项目延期。</p><p>[2] 据传闻,某韩国公司的中国分部曾因产品质量问题,试图殴打我公司派遣去现场维护的工程师。</p><br /><h2 id="不如真人"><a href="#不如真人" class="headerlink" title="不如真人"></a>不如真人</h2><p>我以前是不太拍摄照片的。在亚特兰大,我不过是作业写累了去河边散步,顺手拿出 iPhone 捕捉灿烂的夕阳,或者步道两旁的黄叶。如今,模特的出现,使得摄影的主题发生了巨大变化,同时对摄影效果提出了苛刻的要求。</p><p>首先,给这位模特照相的主要目的是供她发 Instagram,所以那些无足痛痒的街边风景全然没有意义,几乎唯一剩下的主题是记录她的生活点滴。</p><p>第二,虽说现实中不少网红都是拿手机拍照的,但是为了在气势上不输别人,顺便为了拍出更好的效果,一套加装了闪光灯和人像镜头的专业相机还是需要的。不过由于这套器材过于专业,需要额外花时间学习调试,因此也惹出过一些麻烦。</p><p>比如前不久,我和模特下班后去照圣诞节装饰的彩灯。大家都知道这种相片应该怎么照,只要把模特摆在前面,背景是彩灯就可以了。许多人拿出手机之后就像这样咔咔照了,效果也不赖。但是使用专业器材的我,则需要经历一番调试过程才能开工:</p><ol><li>这台相机最常用的配置是光圈优先(A)模式,光圈调整到一个较大的值以提供良好的背景虚化。然后晚上增大曝光补偿,并开启闪光灯给近处的人物补光。快门摁下去,结果发现人照糊了。</li><li>问题在于晚上光线比较暗,相机不能准确测距,再加上大光圈导致景深很浅,人只要稍稍偏离焦点就会糊。于是我把光圈调小到 f/11 又试了几张。</li><li>现在整张照片变得清晰了,但背景有时特别亮,有时又特别暗。这次的问题是,闪光灯通过相机镜头测量环境光来决定功率,然而充满着彩灯的背景适用“测不准原理”,这时候需要手动指定功率。</li><li>最后我换到了手动(M)模式,固定快门时间为 1/60 秒,固定闪光灯功率为 1/16,终于照出了不输于别人用手机拍出的效果。</li></ol><p>不过这个时候的模特,已经冻成了一根冰棍,冷到做不出动作和表情了。</p><p>抛开这些因为不熟悉硬件设备而造成的麻烦,拍摄人像的本质,其实是一个 beautify 的过程,也就是照片中的人,应该要比现实中的人更好看一些。不过残酷的事实告诉我们,拍出 uglify 的照片要比 beautify 的照片容易太多了。要不然我也不会每次拍摄都必定听到下面的质问三连:</p><p align="center"> <strong>我有这么矮吗?</strong> <strong>我的脸有这么大吗??</strong> <strong>我的脸有这么黑吗???</strong></p><p>从前,有女生为了了解直男的内心世界,下载了虎扑;如今,我为了能拍出 beautify 的照片,下载了小红书。小红书上有很多短视频讲人像摄影的技巧,比如应该把人放在哪里,人占的比例有多大,拿什么道具做什么姿势等等。虽然我心里比较抵制这类抖音式科普,但不得不承认它们还是蛮有用的。当然,即便是购入新型器材,运用摄影技巧,配合滤镜和补正,依然不能保证照片的质量。绝大部分的照片,在构图、表情、动作、服装、光线、背景中的某个细节总会有致命伤,这种情况是要作为废片处理的。能够维持五十度按下快门飞传一张到 Instagram 的出片率,就是我明年的目标了。</p><br /><h2 id="在家做饭"><a href="#在家做饭" class="headerlink" title="在家做饭"></a>在家做饭</h2><p>一个人生活和两个人生活的主要区别是,一个人生活很少自己做饭,但两个人生活则可以选择在家里做饭。这其中的原因是:做两人份食物花费的时间并不会比一人份食物长太多,也就是说,两个人生活时,做饭的平摊成本比较低。考虑到自己做饭比点外卖要健康,再加上不忍心浪费小可爱精湛的厨艺,我们大多数时间都在家里做饭。</p><p>我的厨艺基础不牢外加荒废多年,需要很多额外指导。刚开始,小可爱会演示做一道菜的过程,然后两天以后我再去重制这道菜。这样的效果并不太好,因为有时会忘记一些关键的步骤,比如腌制五花肉没有放淀粉,最后的口感会很不一样。后来我们尝试过角色扮演,她演大厨我演帮厨,大厨负责提示下一步操作,帮厨负责具体施工。这样的问题在于,帮厨并不太清楚几步之后会发生什么,食材下锅后会因为找不到调味料而手忙脚乱。到最后,我发现还是打开手机 APP 按照菜谱一步步做效果最好。</p><p>在我们制作过的诸多不同类型的菜肴中,最值得拿来炫耀的三种是煎牛排、清蒸鱼和盐酥鸡。</p><p>煎牛排想要做得好吃,有几个特别的要点。首先牛排在下锅之前要用锤子敲扁,其次锅中要倒入黄油而不是食用油,最后煎一些蒜末浇在牛排上面会更好吃。关于第一步我曾经有些困惑:在餐厅吃到的牛排有时候还挺厚的,是因为我们技术不行不能煎太厚的牛排,才需要用锤子敲扁么?后来我才知道,这一步的目的是把肉敲散,遇热收缩之后就不会变得很硬了。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2019-12-29-2019-retrospect/steak.jpg"></div><p>清蒸鱼其实是一道没有什么难度的菜,前提是鱼买来的时候就已经处理干净了,否则在厨房还需要进行一些血腥的操作。这道菜我们不常做,主要因为它对食材的要求很高,不新鲜的鱼做出来会很难吃。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2019-12-29-2019-retrospect/fish.jpg"></div><p>我们做的盐酥鸡更像是一道小吃。油炸本身没有什么难的,好吃与否的秘方全在于腌制。腌制过程涉及到的材料有好几种,而我从来都分不清楚淀粉、红薯粉、玉米淀粉和糯米淀粉之间的区别,所以腌制的过程只能由小可爱一人承担。刚炸完的盐酥鸡,无论是配番茄酱还是烧肉酱都非常好吃,不过一定要趁热吃完,再次加热的口感就没那么好了。</p><div style="text-align: center"> <img src="https://bitmingw.com/assets/2019-12-29-2019-retrospect/chicken.jpg"></div><p>虽然今年尝试做过不少菜肴,但离开了菜谱的指引还是无法独立动手。中餐存在着很多套路,一些特定的食材有特定的处理方法。现在通过制作不同的菜肴,我隐约发现了一些规律,但没有人指点说在某种情况下为什么要这么做,因此我也不敢在其他菜肴上贸然尝试。我想,或许学习一些有关烹饪的理论(即便它们都是从实践中总结出来的),会比通过实践积累经验进步更快吧。</p>]]></content>
<summary type="html"><h2 id="创业路上"><a href="#创业路上" class="headerlink" title="创业路上"></a>创业路上</h2><p>从 2017 年 2 月开始,我在 VMware 工作已经有近三年的时间了。前两年在平台组做了一系列项目,大部分和分布式系统治理有关。不能否认在那里学到了很多知识技能,但总有一天我会彻底厌倦看日志修 bug 的循环往复。2019 年 3 月,我换了一个组,投入到了新产品的开发工作中。</p>
<p>进组的时机正逢项目从第一行代码起步。全组像一家创业公司:我们有大致的目标和方向,但所有的宏观设计和微观实现都需要自己摸索。与许多其他创业公司一样,踩各类知名开源软件的坑是大家都会经历的事情。</p>
<p>我们的项目决定使用 <a href="https://druid.apache.org/" title="" target="">druid</a> 作为时间序列数据库。新数据通过 <a href="https://kafka.apache.org/" title="" target="">kafka</a> 源源不断进来,经索引后存放在数据库中,这一过程称为数据摄取。官网上的文档和例子还算齐全,示例程序运行起来也一切正常,但换成我们自己的数据后就立刻出现各类错误,以至于不得不深挖源码一探究竟。这么一个看似照葫芦画瓢的任务,最后花费了我三天时间。</p>
<p>之后我们想比较两种不同的数据摄取方式,其中一种依赖于 twitter 某个 scala 开源库的两年前的代码。那些代码过于古老,存在一系列编译和运行的问题。每一次运行,我都觉得,这是最后一个错误,再过片刻就可以把程序跑通了。但每一次,又会出现新的错误。那些莫名其妙的异常信息迷惑着我,但我没有时间探索这个开源库的细枝末节,与其确切地知道为什么会出错,不如凭借直觉把解决的方法猜出来。经历近一星期的煎熬,我终于跑通了流水线,拿到了两种方案的对比结果。我们最后没有选择这套开源库,但我不认为这是一项做完即被丢弃和遗忘的工作。对创业公司而言,知道为什么某种方式不好,并且有理有据地避开它,可能会在未来节约不小的成本 [1]。</p>
<p>从三月到九月,经历过一周连续五天的 war room,经历过打团战从上午十点到晚上九点半,经历过周六周日接连两天来公司点外卖,我们终于发布了产品的第一个版本。正当所有人想好好庆祝一下的时候,我们的客服电话被打爆了。</p></summary>
<category term="life" scheme="https://bitmingw.com/categories/life/"/>
<category term="memo" scheme="https://bitmingw.com/tags/memo/"/>
</entry>
<entry>
<title>Changing the Default Program with update alternatives</title>
<link href="https://bitmingw.com/2019/08/28/ubuntu-update-alternatives/"/>
<id>https://bitmingw.com/2019/08/28/ubuntu-update-alternatives/</id>
<published>2019-08-28T07:00:00.000Z</published>
<updated>2026-01-01T00:57:21.032Z</updated>
<content type="html"><![CDATA[<p>It is common to have multiple versions of the same software installed on a single ubuntu machine. With Debian and Ubuntu's <code>update-alternatives</code> utility, it is easy to choose the default one to use.</p><span id="more"></span><p>For example, let's assume you have JDK 11 installed in the machine, and by default <code>java</code> points to JDK 11:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# java -version</span><br><span class="line">openjdk version "11.0.3" 2019-04-16</span><br><span class="line">OpenJDK Runtime Environment (build 11.0.3+7-Ubuntu-1ubuntu218.04.1)</span><br><span class="line">OpenJDK 64-Bit Server VM (build 11.0.3+7-Ubuntu-1ubuntu218.04.1, mixed mode, sharing)</span><br></pre></td></tr></table></figure><p>Now you would like to work on a project that only supports JDK 8. After installed JDK 8 with <code>apt-get install openjdk-8-jdk</code>, JDK 11 is still the default one. How can we make JDK 8 as the default?</p><p>Ubuntu keeps track of the default programs by maintaining a list of symbolic links, under <code>/etc/alternatives</code> directory. Each entry here is a shortcut points to the actual program, which may have more than one option (i.e. alternatives).</p><h2 id="List-All-Entries-of-Alternatives"><a href="#List-All-Entries-of-Alternatives" class="headerlink" title="List All Entries of Alternatives"></a>List All Entries of Alternatives</h2><p>To list all entries of alternatives in the system, use <code>update-alternatives --get-selections</code>.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# update-alternatives --get-selections</span><br><span class="line">...</span><br><span class="line">java auto /usr/lib/jvm/java-11-openjdk-amd64/bin/java</span><br><span class="line">javac auto /usr/lib/jvm/java-11-openjdk-amd64/bin/javac</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>You can see <code>java</code> is pointing to actual program at <code>/usr/lib/jvm/java-11-openjdk-amd64/bin/java</code>, which belongs to JDK 11.</p><h2 id="List-All-Alternatives-of-an-Entry"><a href="#List-All-Alternatives-of-an-Entry" class="headerlink" title="List All Alternatives of an Entry"></a>List All Alternatives of an Entry</h2><p>To list all alternatives of <code>java</code>, use <code>update-alternatives --list java</code>.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# update-alternatives --list java</span><br><span class="line">/usr/lib/jvm/java-11-openjdk-amd64/bin/java</span><br><span class="line">/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java</span><br></pre></td></tr></table></figure><h2 id="Set-Alternatives-for-an-Entry"><a href="#Set-Alternatives-for-an-Entry" class="headerlink" title="Set Alternatives for an Entry"></a>Set Alternatives for an Entry</h2><p>To set <code>java</code> to use JDK 8 as the default, you can use an interactive command <code>update-alternatives --config java</code>.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# update-alternatives --config java</span><br><span class="line">There are 2 choices for the alternative java (providing /usr/bin/java).</span><br><span class="line"></span><br><span class="line"> Selection Path Priority Status</span><br><span class="line">------------------------------------------------------------</span><br><span class="line">* 0 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 auto mode</span><br><span class="line"> 1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 manual mode</span><br><span class="line"> 2 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1081 manual mode</span><br><span class="line"></span><br><span class="line">Press <enter> to keep the current choice[*], or type selection number:</span><br></pre></td></tr></table></figure><p>After typing selection number <code>2</code>, <code>update-alternatives</code> will modify the symbolic link to update the default java to use JDK 8.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Press <enter> to keep the current choice[*], or type selection number: 2</span><br><span class="line">update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java to provide /usr/bin/java (java) in manual mode</span><br><span class="line"></span><br><span class="line">root@ubuntu:~# java -version</span><br><span class="line">openjdk version "1.8.0_212"</span><br><span class="line">OpenJDK Runtime Environment (build 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03)</span><br><span class="line">OpenJDK 64-Bit Server VM (build 25.212-b03, mixed mode)</span><br></pre></td></tr></table></figure><p>You can also do this in a script without interaction, if you know the full path of desired default program.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java</span><br><span class="line">update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java to provide /usr/bin/java (java) in manual mode</span><br></pre></td></tr></table></figure><h2 id="Repeat-This-for-All-Programs"><a href="#Repeat-This-for-All-Programs" class="headerlink" title="Repeat This for All Programs"></a>Repeat This for All Programs</h2><p>Now you have pointed <code>java</code> program from JDK 11 to JDK 8 with <code>update-alternatives</code> command. However, some other programs like <code>javac</code> and <code>keytool</code> are still pointing to JDK 11. You will need to repeat this process until all programs have been updated.</p>]]></content>
<summary type="html"><p>It is common to have multiple versions of the same software installed on a single ubuntu machine. With Debian and Ubuntu&#39;s <code>update-alternatives</code> utility, it is easy to choose the default one to use.</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="Linux" scheme="https://bitmingw.com/tags/Linux/"/>
<category term="ubuntu" scheme="https://bitmingw.com/tags/ubuntu/"/>
</entry>
<entry>
<title>简明易懂的量子计算</title>
<link href="https://bitmingw.com/2019/06/09/easy-to-understand-quantum-computing/"/>
<id>https://bitmingw.com/2019/06/09/easy-to-understand-quantum-computing/</id>
<published>2019-06-09T07:00:00.000Z</published>
<updated>2026-01-01T00:57:21.045Z</updated>
<content type="html"><![CDATA[<p>1936 年,阿兰·图灵发表了图灵机理论,证明了存在一种计算机可以执行任何能被算法表达出来的计算过程。1945 年,第一台图灵完备的电子计算机 ENIAC 投入使用,它和后续的电子计算机改变了人类的历史进程。关于量子计算的研究始于 1980 年代。尽管时至今日,尚未出现能进行复杂计算的量子计算机,但量子计算的相关理论已经成熟。</p><p>一般来说,完成一次计算需要做三件事:</p><p><strong>提供输入 -> 执行计算过程 -> 提取输出</strong></p><p>对于电子计算机而言,输入和输出都可以表达为一串比特。计算过程,就是处理单元在程序的控制下,通过 AND, OR, NOT 等逻辑门电路修改这些比特的过程。</p><p>而量子计算机,输入的是量子比特(qubit)。计算过程是量子门(quantum logic gates)修改量子比特的状态(quantum state)。输出的是量子比特观测的结果。</p><p>因此,理解量子计算,需要搞清楚量子比特、量子门和量子观测。考虑到量子计算的物理实现有多种方法,且许多细节均为机密,本文只会阐释量子计算的理论基础。</p><span id="more"></span><br /><h2 id="量子比特"><a href="#量子比特" class="headerlink" title="量子比特"></a>量子比特</h2><p>传统的比特有且只有 0 和 1 两个状态。如果存在一个比特,那么它在某时刻的状态必须是 0 和 1 之间的一个。量子比特与传统的比特不同,它的状态在理论上用一个长度为 2 的列向量表示。首先我们定义如下两个量子比特 \(| 0 \rangle\) 和 \(| 1 \rangle\).</p><p>\[<br>| 0 \rangle = \left[<br> \begin{array}{c}<br> 1 \\<br> 0 \end{array}<br> \right]<br>\]</p><p>\[<br>| 1 \rangle = \left[<br> \begin{array}{c}<br> 0 \\<br> 1 \end{array}<br> \right]<br>\]</p><p>量子比特 \(| 0 \rangle\) 和 \(| 1 \rangle\) 是量子比特的计算基态(computational basis state)。或者说,它们是量子态空间的一组基。</p><p>任何一个量子比特 \(| e \rangle\) 都可以表达为 \(\alpha| 0 \rangle + \beta| 1 \rangle\) 的形式,其中 \(\alpha\) 和 \(\beta\) 是两个复数,且它们的模的平方和等于 1,也就是</p><p>\[<br>| \alpha |^2 + | \beta |^2 = 1<br>\]</p><p>上述的限制又称为<strong>归一化条件</strong>。</p><p>比如说,\(0.6 | 0 \rangle + 0.8 | 1 \rangle\) 是一个量子比特,\(\frac{1-i}{2} | 0 \rangle + \frac{1+i}{2} | 1 \rangle\) 也是一个量子比特,但 \((1-i) | 0 \rangle + (1+i) | 1 \rangle\) 就不是一个量子比特,因为它不满足归一化条件。按照数乘矩阵的规则,上面两个量子比特又可以写作</p><p>\[<br>\left[<br>\begin{array}{c}<br>0.6 \\<br>0.8 \end{array}<br>\right]<br>\]</p><p>和</p><p>\[<br>\left[<br>\begin{array}{c}<br>\frac{1-i}{2} \\<br>\frac{1+i}{2} \end{array}<br>\right]<br>\]</p><p>直观上讲,量子比特可以视为传统比特的一种叠加态(superposition)。比如说,\(0.6 | 0 \rangle + 0.8 | 1 \rangle\) 可以视为 0.36 (\(0.6^2\)) 个比特 0 和 0.64 (\(0.8^2\)) 个比特 1 的一种叠加状态。</p><br /><h2 id="量子门"><a href="#量子门" class="headerlink" title="量子门"></a>量子门</h2><p>正如数字电路中的逻辑门可以修改比特的状态一样,量子门可以修改量子比特的状态。量子门既可以只有一个输入和一个输出(转变单个量子的状态),也可以具有多个输入和多个输出(转变多个量子的状态)。输入和输出的数目应当相等,也就是说不可以吞噬量子。下面介绍两个单输入输出的量子门和一个多输入输出的量子门。</p><h3 id="NOT-门"><a href="#NOT-门" class="headerlink" title="NOT 门"></a>NOT 门</h3><p>NOT 门作用于单个量子比特,它可以交换两个基向量的系数:</p><p>\[<br>NOT(\alpha| 0 \rangle + \beta| 1 \rangle) = \alpha| 1 \rangle + \beta| 0 \rangle<br>\]</p><p>量子的 NOT 门是数字电路中 NOT 门的一个扩展。举一个直观的例子,假如原先的量子态是 0.36 个比特 0 和 0.64 个比特 1 的叠加态,那么经过 NOT 门之后,就变成了 0.64 个比特 0 和 0.36 个比特 1 的叠加态。</p><p>单输入输出的量子门可以使用一个 \(2 \times 2\) 的矩阵来表示。一个量子经过量子门之后的状态,由该量子状态向量<strong>左乘</strong>量子门矩阵的值决定。NOT 门对应的量子门矩阵为</p><p>\[<br>X = \left[<br> \begin{array}{cc}<br> 0 & 1 \\<br> 1 & 0 \end{array}<br> \right]<br>\]</p><p>因此,某个量子比特经过 NOT 门的结果为</p><p>\[<br>X \left[<br>\begin{array}{c}<br>\alpha \\<br>\beta \end{array}<br>\right]<br>=<br>\left[<br>\begin{array}{cc}<br>0 & 1 \\<br>1 & 0 \end{array}<br>\right]<br>\left[<br>\begin{array}{c}<br>\alpha \\<br>\beta \end{array}<br>\right]<br>=<br>\left[<br>\begin{array}{c}<br>\beta \\<br>\alpha \end{array}<br>\right]<br>\]</p><h3 id="Hadamard-门"><a href="#Hadamard-门" class="headerlink" title="Hadamard 门"></a>Hadamard 门</h3><p>Hadamard 门同样作用于单个量子比特,它可以依照系数分解现有的量子态:</p><p>\[<br>H(\alpha| 0 \rangle + \beta| 1 \rangle) = \frac{\alpha + \beta}{\sqrt{2}} | 0 \rangle + \frac{\alpha - \beta}{\sqrt{2}} | 1 \rangle<br>\]</p><p>用矩阵来表示就是</p><p>\[<br>H = \frac{\sqrt{2}}{2} \left[<br>\begin{array}{cc}<br>1 & 1 \\<br>1 & -1 \end{array}<br>\right]<br>\]</p><p>虽然 Hadamard 门和数字电路中的 AND 和 OR 门并无直接的关联,但它在不少量子计算算法中有重要的应用。有兴趣的读者可以自行证明,连续使用两次 Hadamard 门之后,量子会回到原先的状态——这个行为和 NOT 门是一致的。</p><p>单输入输出的量子门可以有无穷多种,只要满足量子比特状态向量左乘量子门矩阵的结果依然满足量子比特的归一化条件即可。在这里我们不加证明地给出如下定理:</p><blockquote><p>某个 \(2 \times 2\) 的复矩阵能作为量子门的一个表示,当且仅当其共轭矩阵等于其逆矩阵。</p></blockquote><h3 id="受控-NOT-门"><a href="#受控-NOT-门" class="headerlink" title="受控 NOT 门"></a>受控 NOT 门</h3><p>在计算机程序中,充满着条件判断语句:如果怎样,那么做什么,否则做别的什么。在量子计算中,我们也期望一个量子比特的状态可以因为另一个量子比特而改变,这就需要多输入输出的量子门。下面要介绍的是受控 NOT 门(CNOT 门)。它具有两个输入和两个输出。如果把输入和输出当作一个整体来看,这个状态可以用 \(\alpha| 00 \rangle + \beta| 01 \rangle + \gamma| 10 \rangle + \theta| 11 \rangle\) 来表示。其中 \(| 00 \rangle\), \(| 01 \rangle\), \(| 10 \rangle\), \(| 11 \rangle\) 是长度为 4 的列向量,它们由 \(| 0 \rangle\) 和 \(| 1 \rangle\) 拼接而成。</p><p>这个整体也需要满足归一化条件,即</p><p>\[<br>| \alpha |^2 + | \beta |^2 + | \gamma |^2 + | \theta |^2 = 1<br>\]</p><p>CNOT 门中,第一个输入是比特 0 的部分,会维持第二个输入的状态;而第一个输入是比特 1 的部分,会以 NOT 门的方式作用于第二个输入的状态。第一个输入的量子比特会原样输出,而第二个输入的量子比特的状态由上述的叠加态决定。用数学公式来表达就是</p><p>\[<br>CNOT(\alpha| 00 \rangle + \beta| 01 \rangle + \gamma| 10 \rangle + \theta| 11 \rangle) = \alpha| 00 \rangle + \beta| 01 \rangle + \gamma| 11 \rangle + \theta| 10 \rangle<br>\]</p><br /><h2 id="量子观测"><a href="#量子观测" class="headerlink" title="量子观测"></a>量子观测</h2><p>从上面关于量子门的介绍可以看到,一个量子比特处于两个量子态 \(| 0 \rangle\) 和 \(| 1 \rangle\) 的叠加态;两个量子比特组合成的整体,处于四个量子态 \(| 00 \rangle\), \(| 01 \rangle\), \(| 10 \rangle\) 和 \(| 11 \rangle\) 的叠加态。以此类推,\(n\) 个量子比特处于 \(2^n\) 个量子态的叠加态,这相对于 \(n\) 个传统的比特却只有一个固定的状态相比,是巨大的优势。然而物理定律也有它的限制——确切地知道一个量子比特的状态的方法是进行一次观测,但观测会使得叠加态坍缩,变成一个确定的状态。一个量子比特在观测之后得到的信息是传统的比特,它的值只能是 0 和 1 之间的一个。一个叠加态会塌缩成 0 或者 1,是由叠加态中的系数 \(\alpha\) 和 \(\beta\) 决定的。这个叠加态塌缩成 0 或 1 的概率分别为 \(| \alpha |^2\) 和 \(| \beta |^2\)。类似的,一个双量子比特的系统,其观测结果为 00, 01, 10, 11 的概率分别是 \(| \alpha |^2\), \(| \beta |^2\), \(| \gamma |^2\) 和 \(| \theta |^2\).</p><p>这样的物理定律导致量子计算的结果是不确定的,它有一定的概率输出一个错误的结果。在实际应用中需要额外的手段来验证输出的正确性。量子计算使用的算法,如果能在观测的上一步尽可能地减少量子比特的叠加态,就能以大概率得到正确的结果。</p><br /><h2 id="组合起来"><a href="#组合起来" class="headerlink" title="组合起来"></a>组合起来</h2><p>我们把上面提及的几个部分组合起来,所谓量子计算,无非就是:</p><ol><li>从一组计算基态开始(每个量子比特被初始化成 \(| 0 \rangle\) 或 \(| 1 \rangle\) 作为计算的输入);</li><li>根据预定的算法,通过一系列量子门;</li><li>经过量子观测得到一串比特作为结果。</li></ol><p>值得说明的是,在现阶段,实现每一个量子门,都需要人工操纵物理实验设备,写量子计算的程序还远没有敲字符那么简单。</p><p>物理学家尚未证明量子计算的界限在哪里,不知道它是否可以用来模拟<strong>任何</strong>物理过程。从 1980 年代到现在,量子计算应用于实践似乎仍遥遥无期,但它的某些应用,如解算蛋白质结构,以及破解不对称加密密钥,已经引发了不少关注。我们无法知道它的大规模普及是否会像电子计算机一样再次改变人类社会。</p>]]></content>
<summary type="html"><p>1936 年,阿兰·图灵发表了图灵机理论,证明了存在一种计算机可以执行任何能被算法表达出来的计算过程。1945 年,第一台图灵完备的电子计算机 ENIAC 投入使用,它和后续的电子计算机改变了人类的历史进程。关于量子计算的研究始于 1980 年代。尽管时至今日,尚未出现能进行复杂计算的量子计算机,但量子计算的相关理论已经成熟。</p>
<p>一般来说,完成一次计算需要做三件事:</p>
<p><strong>提供输入 -&gt; 执行计算过程 -&gt; 提取输出</strong></p>
<p>对于电子计算机而言,输入和输出都可以表达为一串比特。计算过程,就是处理单元在程序的控制下,通过 AND, OR, NOT 等逻辑门电路修改这些比特的过程。</p>
<p>而量子计算机,输入的是量子比特(qubit)。计算过程是量子门(quantum logic gates)修改量子比特的状态(quantum state)。输出的是量子比特观测的结果。</p>
<p>因此,理解量子计算,需要搞清楚量子比特、量子门和量子观测。考虑到量子计算的物理实现有多种方法,且许多细节均为机密,本文只会阐释量子计算的理论基础。</p></summary>
<category term="tutorial" scheme="https://bitmingw.com/categories/tutorial/"/>
<category term="quantum computing" scheme="https://bitmingw.com/tags/quantum-computing/"/>
</entry>
</feed>