Mygod 工作室™ - 象牙塔 https://zh.mygod.be/blog/ My brand new unmaintained blog. zh 如何将家中老电脑改造成私有云并丢掉 268GB 数据 https://zh.mygod.be/blog/how-i-repurposed-my-old-pc-as-nas-and-lost-268g-data/ <p>一台废旧的 PC 主机可以做什么?<!--more--></p> <h1>移动光驱</h1> <p>经比较淘宝上一个移动光驱大概 35,把一个废旧主机的光驱拆出来,擦一擦,上淘宝买个 30 元的 SATA 转 USB 3.0 的转换器(带电源,不过我的电源回收了我的老移动硬盘的电源适配器,刚好能用),就有了一个可用的 USB 3.0 移动光驱,省 5 元!</p> <p><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/TB2Z6gRwOlnpuFjSZFgXXbi7FXa_684326598.jpg~original" alt="USB 3.0 转 SATA 数据线" /></p> <p>缺点是很不便携,但是你可以轻松将它插到 SATA 硬盘上检查问题。</p> <h1>实验:热插拔 PS/2 会摧毁主板</h1> <p>实验目的:验证热插拔 PS/2 接口会将主板摧毁。</p> <p>实验过程:</p> <ol> <li>打开主机;</li> <li>插入 PS/2 键盘;</li> <li>发现键盘没有反应,回忆 PS/2 接口会将主板摧毁;</li> <li>忏悔;</li> <li>关机,重新插入 PS/2 键盘,开机;</li> <li>发现键盘仍然没有反应;</li> <li>怀疑键盘损坏,关机换了一块键盘,仍然没有反应;</li> <li>换了一块 USB 键盘,仍然没有反应;</li> <li>放弃并怀疑人生;</li> <li>几天后无法开机,反复尝试开机后终于成功,然而 BIOS 提示 over voltage error;</li> <li>手足无措,然而电脑仿佛正常开机了,忽略;</li> <li>再过几天彻底无法开机。</li> </ol> <p>实验结论:我脑子有些问题。</p> <h1>DIY 私有云</h1> <h2>搭建私有云</h2> <p>有用的硬盘当然可以用来做私有云啦。收集了一下家里破烂电脑里的主机,找到以下三块 320G 硬盘:</p> <ul> <li><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/81R4wqxQeAL.jpg~original" alt="HDP725032GLA380" /></li> <li><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/22-136-098-04.jpg~original" alt="WD3200AAJS" /></li> <li><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/22-148-455-04.jpg~original" alt="ST9320325AS" /></li> </ul> <p>很遗憾 ST9320325AS 这块 2.5" 硬盘不知道出于什么原因,死活最多只能读出型号,连容量都读不出来。据网上说 5V 的硬盘会忽略 12V 的电源,因此不应该有事故,不清楚为什么。至少另外两块好像用起来没什么问题。以下是 dmesg:</p> <p><code>sd 5:0:0:0: [sdg] Spinning up disk............................................................... not responding... sd 5:0:0:0: [sdg] Read Capacity(16) failed: Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE sd 5:0:0:0: [sdg] Sense Key : Not Ready [current] sd 5:0:0:0: [sdg] Add. Sense: Logical unit is in process of becoming ready sd 5:0:0:0: [sdg] Read Capacity(10) failed: Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE sd 5:0:0:0: [sdg] Sense Key : Not Ready [current] sd 5:0:0:0: [sdg] Add. Sense: Logical unit is in process of becoming ready sd 5:0:0:0: [sdg] 0 512-byte logical blocks: (0 B/0 B) sd 5:0:0:0: [sdg] 0-byte physical blocks sd 5:0:0:0: [sdg] Test WP failed, assume Write Enabled sd 5:0:0:0: [sdg] Asking for cache data failed sd 5:0:0:0: [sdg] Assuming drive cache: write through</code></p> <p>于是这下一共有 640G,并不够备份。</p> <p>非常凑巧,家里有一块不懂行的老年人买来的 1TB 的“移动硬盘”(其实是外置硬盘,WD My Book),S.M.A.R.T. 数据显示平均 spin up 时间需要 6 秒,需要外接电源。它的外壳有些难拆,经过职业人士(修电脑的)一凡精妙操作(暴力拆解)终于把它的塑料外壳去掉了,里面是一块非常平淡无奇的 WD10EARS-003BB1,然后一块 SATA 转 USB 3.0 的芯片,上面有一个特别闪瞎的 LED 灯,通过一根导光的塑料连到外壳,从而实现一闪一闪亮晶晶。芯片上还有一个电源插孔和 USB 3.0 母口直接连到外壳,一个红色圆形电源按钮被外壳的电源键塑料板覆盖。</p> <p><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/22-136-490-06.jpg~original" alt="WD10EARS-003BB1" /></p> <p>然而拿上文所说的通用 SATA 转 USB 3.0 发现什么都读不出来,拿出 wxMEdit 查看硬盘,发现是乱码。以为硬盘损坏了,有些吓人。复又将原厂芯片重新插上,发现移动硬盘正常读取。经比较原厂芯片读出来的硬盘少约 20G 容量。据此推测这块芯片还有加密的功能,目的不明。怪不得拆出来的时候发现芯片已经烧黑,职业人士(修电脑的)还下定论说“拆出来也没用,你看都烧焦了”云云。</p> <p>总而言之将三块硬盘都连到主机上,问题在于主机上只有两根 SATA 电源线。解决方案有两种:一种是在大街上 5 块钱买一根 IDE 电源转 SATA,当时买的时候我就后悔了,因为一看这玩意儿就是非常蹩脚,我可以轻易把线从口里抽出来,事实证明它的接触确实不怎么样,不推荐;另一种是淘宝上 10 块买一根 SATA 电源一分二线器,效果和质量都还不错。</p> <p><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/TB29ewGlY8kpuFjy0FcXXaUhpXa_1618937458.jpg~original" alt="SATA 电源线一分二线器" /></p> <p>本来还打算把淘汰的笔记本的 320G 硬盘也连上的,但是不知道出于什么原因它好像坏掉了,故将其放弃。</p> <p>在 BIOS 中确认全部出现(有意思的是在第二台主机上 1TB 硬盘只能在 SATA 1/3 口才读得出来,原因不明,大概和什么 master slave 有关系),首先给主机搭了一个 Linux + sshd,将所有三块硬盘用 LVM 连起来,错误地将所有空间用于根目录,然后从笔记本上 <code>ssh -x</code> 上去玩。一开始尝试了 NFS,但实在不是很好用,尤其是当和 Windows 一起使用时,微软的 NFS for Windows 真是不好用。最后还是决定用微软家的 SMB,在 Linux 上装了 Samba。用到现在,我觉得主要缺点是小文件处理地比较慢,尤其是删除一个文件很多的文件夹时不如直接 ssh 上去 <code>rm -rf</code>。</p> <p><img src="http://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/snipaste_20170715_235320.png~original" alt="SAMBA NFS" /></p> <p>然而正如上一节所说,不久之后我亲手不明觉厉地摧毁了第一台主机,之后我转而将魔掌伸向了另一台性能略差的主机上。迁移还是比较容易的,将三块硬盘插到另一台主机就行了。嗯?为什么主板蜂鸣器在不停地响?噢我忘把内存也搬过去了。</p> <p>然后这篇文章的剩余部分被我充满 BUG 的电脑吃掉了。未完不待续。</p> <h1>题外话:不同的备份方案比较</h1> <p>找到了这篇文章的一个备份,里面有这玩意儿,放出来供大家欣赏。</p> <p>| 参数 | 移动硬盘(随身) | 移动硬盘(放家里) | DIY 私有云 | 百度云网盘 | 靠谱的公有云 | | - | - | - | - | - | - | | 安全性 | 低 | 中 | 中高 | 低 | 高 | | 方便程度 | 高 | 高 | 低 | 低 | 低 | | 可用性 | 高 | 低 | 中 | 中高 | 中 | | 传输速率 | 高 | 高 | 高 | 中 | 低 | | 隐私 | 高 | 高 | 高 | 低 | 高 |</p> https://zh.mygod.be/blog/how-i-repurposed-my-old-pc-as-nas-and-lost-268g-data/ Fri, 28 Jul 2017 15:39:51 +0000 为什么你应该停止恐惧并卸载你的杀毒软件 https://zh.mygod.be/blog/why-you-should-stop-worrying-and-uninstall-your-antivirus-scanner/ <p>今年是 2016 年,然而不时还有人问我建议装什么杀毒软件。我的建议是裸奔。<!--more--></p> <h1>杀毒软件无用论</h1> <p>搜索一下杀毒软件无用论,我们大概可以看到以下观点。</p> <ul> <li><a href="http://tech.163.com/09/0916/02/5JA2L2JP000915BF.html">愚蠢的 360 董事长周鸿祎抛出“杀毒软件无用论”</a>,实际上只是在推广它自己家的产品,并且以为自己加个“云”字就很厉害似的,非常蠢。</li> <li><a href="https://www.zhihu.com/question/28361115">这篇知乎</a>认为没用只是因为误报率和漏杀率不够低。</li> </ul> <p>对于第一个蠢货的观点,他显然只是来恶搞的。分析一下,我估计这个所谓云查杀,只是将检测和病毒库放到了服务器端,用户的数据安全没有保障。专门找个记者来宣传这个功能,这家公司居心很可疑。</p> <p>至于第二个观点,其实还是有点道理的。然而……</p> <h1>所有杀毒软件都不可能从根本上防范病毒</h1> <p>根据<a href="https://www.emis.de/journals/IJOPCM/files/IJOPCM%28vol.1.2.3.S.08%29.pdf">这篇文章</a>,杀毒软件大概有以下检测病毒的方式:</p> <ul> <li><strong>基于签名的检测</strong>:杀毒软件有一个你可能不断更新的病毒库,这个病毒库是由一些二进制码的模式组成。这种检测方式就是将要扫描的文件在这个病毒库中匹配,如果匹配到则说明是一种已知的病毒。就这么简单;</li> <li><strong>启发式检测</strong>:动态地分析程序的代码,来看这个程序是否有恶意行为;</li> <li><strong>基于行为的检测</strong>:就是靠分析程序运行时的行为来判断是病毒的可能性;</li> <li><strong>沙盒检测</strong>:创建一个隔离程序的沙盒(类似于虚拟机),并在这个沙盒中运行程序来看它是否有恶意行为;</li> <li><strong>人工检测</strong>:一般是将可疑程序上传到服务器进行人工分析,这种方式速度最慢。</li> </ul> <p>大部分杀毒软件基本都是使用这些检测方式,大同小异。我们将设计一种病毒,它的目的仅仅是修改 <code>%windir%\System32\etc\hosts</code> 来使用户打开一个网站时实际上打开了另一个网站(当然真实的病毒肯定有更复杂的行为),并让我们所使用的杀毒软件漏杀它。</p> <p>我们分析一下我们要做的这个病毒,它仅仅只是修改了一个系统文件,除此以外与正常程序没有区别。就算杀毒软件为了安全起见,对于基于签名的检测选择使用了 <code>hosts</code> 作为签名,这样所有程序包含字符串 <code>hosts</code> 的程序都被识别为病毒(而且有一些正经使用这个文件的非恶意程序,如注册机)。这么做误杀率显然很高,而我们的病毒也可以轻易地通过使用如在运行时执行 <code>"ho" + "sts"</code> 将它拆成两个来绕过这种检测。由于病毒库大小是有限的,我们总可以找到一种方式使得它无法检测到自己。启发式检测如果足够聪明的话,也许能看出我们最后拼出了一个文件名 <code>hosts</code>。但是有一种很流行的手段叫做加壳。其原理大概只是将程序本身加密和/或压缩,然后在运行时解密并执行得到的代码。而且壳的种类有很多种,任何人都可以轻易设计出新的壳使得启发式十分困难。如果病毒是脚本文件,那么检测难度就更大。使用沙盒检测可以解决这些问题,但是病毒从原则上是有可能检测出自己运行在沙盒中的,那么要做的就是在沙盒中伪装成一个正常的程序即可了。最后对于基于行为的检测和人工检测往往属于事后诸葛亮。病毒完全可以先杀死甚至破坏杀毒软件来绕过它。</p> <p>总而言之,这些手段不可能从根本上防范病毒。而且我们断言,所有杀毒软件都不可能从根本上防范病毒。要说明这点,我们看看杀毒软件到底是什么。一句话说,它是一个检测其他恶意程序(病毒)的程序。由此可见它本身就是和病毒属于同一级别的,而两者又是相互排斥的关系,因此两者永远不可能决出胜负。</p> <h1>杀毒软件的积极效应</h1> <p>回忆一下这几年你的电脑上杀毒软件杀出了什么?我的 Windows 自带的 MSE 把我的 AutoKMS 误杀了,以及几年前某个国产杀毒软件还喜欢误杀我的<a href="https://zh.mygod.be/product/mygod-keyboard-player/">这个小程序</a>,因为它挂了一个键盘钩子,这是为了知道你什么时候按下什么键然后发出正确的声音,但它也完全可以监听你的密码输入——当然它并没有那么干,然而还是被误杀了。总而言之,病毒显然没有十年前那么猖獗。这就是杀毒软件的积极效应。</p> <p>为什么这么说?杀毒软件无法彻底防范病毒,因此在十年前发生的事情大概是这样的:</p> <ol> <li>一种新的杀毒软件无法应付的病毒出现;</li> <li>杀毒软件更新来应付这种病毒;</li> <li>这种病毒的作者制作了新的变种来绕过杀毒软件的更新;</li> <li>回到 1 反复,直到病毒的制作者停止制作更多变种。</li> </ol> <p>在这一过程中,无论是杀毒软件还是病毒都变得更加复杂。终于,制作一个可以防范杀毒软件的病毒的成本已经使得病毒制作者觉得太麻烦,以至于他们转向其他的更有利可图的攻击,例如 DoS 攻击、钓鱼等,因此现在病毒比以前少很多。</p> <h1>杀毒软件过时论</h1> <p>于是我说了半天只是为了证明杀毒软件还是有用的?恰恰相反。请注意,病毒变少,甚至几乎到了杀毒软件只能误杀的地步,并不意味着现在的杀毒软件已经绝对安全。事实上,它反而说明如今安装杀毒软件和不安装的区别已经不大了。</p> <p>而且正如我前面所分析的,经过进化,杀毒软件已经变得非常复杂。复杂的软件意味着消耗更多的资源,包括 CPU、内存、硬盘使用甚至网络,当然还有电(这对于笔记本续航更重要些)。所以为了让你的电脑更快,为什么不抛弃使用杀毒软件其微乎其微的安全性呢?</p> <h1>杀毒软件错误的设计逻辑</h1> <p>如果这篇文章到上面一段就结束了,那这个结论太弱了。事实上,杀毒软件的设计逻辑从出发点开始就是错的。换句话说,<strong>杀毒软件是用来解决一个本不存在的问题</strong>。</p> <p>我们分析可知,病毒的终极目标是为了在其他计算机上运行恶意代码。然而操作系统为什么当初会决定允许恶意代码执行?假如任意对系统或是用户的重要和/或隐私数据有潜在风险的操作(例如之前的修改 <code>hosts</code>,以及挂键盘钩子,或是更危险的系统更新),系统都提示用户可能的后果并要求确认,那么病毒根本无法存在,又何须杀毒软件呢?</p> <h1>安全与易用的平衡</h1> <p>用马克思的话讲,安全性和易用性是个辩证的关系;用正常的话讲,安全性(Security)和易用性(Convenience)存在一种平衡(Tradeoff)。这里的易用是指方便的使用,而不是易于上手的意思;平衡则表示两者可以此多彼少,或者相反,但不可兼得。</p> <p>Windows 之所以能够达到十年前如此高的市场份额,和它对易用性的极大化是不可分割的。以 Windows XP 这个老系统为例,默认配置情况下几乎所有程序一运行就是管理员权限,几乎可以修改系统的任意文件。再加上 U 盘的自动运行之类的功能,要想在这种系统上让用户一不小心运行什么简直轻而易举。从 Windows Vista 开始,我们看到加入了用户账户控制(UAC),就是那个运行什么程序还要确认下的窗口。有人觉得这个窗口神烦把它关掉了,这将不可避免造成风险。这也就是安全与易用的平衡。</p> <p>再看 Mac 和 Linux 则都需要用户输入密码提权来执行关键的操作。iOS 直接拒绝了很多操作,除非你越狱。Android 也拒绝很多操作,需要 root 启用,而且很多 root 的工具箱,如 SuperSU,也会在需要提权时提醒用户。即使没有 root,在 Android 6.0+ 以及很多自定义 ROM 上,对应用使用相机、位置、存储空间等都需要用户授权。Chrome 和 Chrome OS 也如此。</p> <h1>结论</h1> <p>由此可见,使用这些系统的保护功能可以使杀毒软件彻底成为一团占用系统资源的垃圾。只要你不是什么都授权,那么使用系统自带的安全措施的安全性比盲目相信杀毒软件的安全性更高,就算不考虑杀毒软件本身是否有恶意代码,例如那个很可疑的 360,从这点上讲,如果你实在为了保险起见,而且你的电脑也超级快,在 Windows 上你可以选择使用微软的 MSE 或自带的 Windows Defender 比较可靠,因为你没有引入第二家有嫌疑的公司。</p> <h1>附:其他安全措施</h1> <p>但是这样仅仅是完成了安全的一环。并不是不运行可疑的程序你的系统就安全了。只要有一环出差错,攻击者就有机可乘。</p> <p>因此下面介绍一些必要的安全措施。很多安全措施可以部分地由你之前使用的杀毒软件(从这个意义上来说它们不光是杀毒软件)来检测,不过你也可以不使用杀毒软件来(甚至更好地)完成这些工作。你也可以出于懒选择跳过下面部分,毕竟正如上面所说,你总是可以牺牲安全来换来方便(或懒)。</p> <h2>钓鱼</h2> <p>也许你从互联网上下载的文件,但是你并不知道你其实下载的是装作是你要找的程序的恶意软件。这被称作钓鱼(哦对了还有那种仿冒官方网站让你输密码的)。以下措施可以防范钓鱼:</p> <ol> <li>从官方网站下载;(然而识别官方网站这种事情纯粹靠经验)</li> <li>如果你下载的是盗版,请支持正版。(一般假如你下载到的是一个 4- MB 的下载器,那多半是下载到了奇怪的软件)</li> </ol> <h2>漏洞</h2> <p>漏洞是一个程序(你的操作系统、防火墙或是别的应用程序)写得出了问题,因此你作为用户没有什么可做的,除了尽可能及时地更新到最新版本,尤其是安装所有的安全补丁。</p> <p>漏洞很多时候是糟糕的设计造成的,例如 SQL 注入就是可以在你不知情的情况下在你的 SQL 服务器上执行任何代码。我不禁想吐槽 SQL 这个语言设计得多么失败(你从来没听说过 C++ 注入是吧)。</p> <p><a href="https://xkcd.com/327/"><img src="https://imgs.xkcd.com/comics/exploits_of_a_mom.png" alt="Exploits of a Mom - xkcd #327" /></a></p> <p>对于开发者来说:提供自动更新,而且保证你更新的版本不是各种崩溃,否则没有人会更新的。</p> <h2>运行不信任的程序</h2> <p>有时候你下载了一个程序但不知道它是否可靠?现在网上有成千上万的在线杀毒,例如 <a href="https://www.virustotal.com/">virustotal</a> 等。使用只需将可疑的文件上传上去即可。此外,你还可以选择使用虚拟机运行这个不信任的程序,根据它在虚拟机中的表现来估计它是否可信。</p> <h1>结语</h1> <p>总之这篇文章真是又臭又长,今天你裸奔了吗?</p> https://zh.mygod.be/blog/why-you-should-stop-worrying-and-uninstall-your-antivirus-scanner/ Mon, 01 Aug 2016 08:22:56 +0000 筷子游戏剩饭变种攻略 https://zh.mygod.be/blog/optimal-solution-for-chopsticks-leftovers-variant/ <p>写在最前面:这篇文章我要说 AlphaGo,我要说围棋。</p> <p>最近被人推荐了一款手游,意思是手的游戏(hand game)。这款手游叫筷子(Chopsticks),手指头法力无边(Magic Fingers)或者我很土地称之为碰手指。某不知名网站<a href="https://sakuraentorizasshi.wordpress.com/2008/10/16/japanese-games-chopsticks-hand-game/">&#91;来源请求&#93;</a>声称这款游戏来源于日本。<!--more--></p> <p>根据[维基](https://en.wikipedia.org/wiki/Chopsticks<em>(hand</em>game%29),其玩法大致如下:</p> <ul> <li>每一位玩家使用双手参加游戏,每只手伸出手指所代表的数字表示那只手的分数。所有人开始时每只手各有一分。</li> <li>玩家轮流使用一只活着的手触碰其他玩家的某只活着的手来将手上的分数加到那只手上,自己手上分数不变。</li> <li>在“剩饭”(leftovers)变种下,每只手的点数对 &#92;(M&#92;) 取模,一般情况下 &#92;(M = 5&#92;),当手上的分数为 0 则这只手被击毙。例如某玩家手上有 4 分,加到有 3 分的手上后,对“手”有 2 分,这是因为 &#92;&#91; 4 + 3 \equiv 2 \pmod 5. &#92;&#93; P.S. 其实这是很不科学的,由于 5 在这个状态下永远不会出现,所以应该让 &#92;( M = 6 &#92;)。</li> <li>当一个玩家双手都被击毙时这个玩家被击毙。击毙其对手的玩家获胜。</li> <li>在“劈叉”(splits)变种下,当一个玩家只剩一只手且另一只手的分数是偶数时在这回合可选择这些点数平均分给双手。</li> <li>在“交易”(transfer)变种下,玩家可在任意回合中自由安排双手上的点数,只要双手之和不变,被击毙的一只手可看作 0 点。不允许没有点数的交易,也不允许交换双手点数的交易。P.S. “交易”变种显然包含了“劈叉”变种。</li> <li>在“自杀”(suicidal)变种下,玩家获胜的条件是让对手将自己击毙。</li> </ul> <p>本篇文章主要介绍“剩饭”变种,一是因为我只玩过这类的,二是因为没有这个变种的话这个游戏将无聊很多,三是因为我在上面根本没有说基本的版本怎么玩。我见到两种玩法:</p> <ul> <li>第一种除了普通的“剩饭”变种以外加入了“劈叉”变种;</li> <li>第二种没什么特别的,除了 &#92;( M = 10 &#92;)。不过由于用一只手表示“7”和“9”的表示方式存在地区差异,这种玩法容易引起争端,须谨慎。</li> </ul> <p>本篇文章将主要尝试解决上面两种游戏,并顺便探索一些关于这个游戏及其“交易”、“自杀”变种的有趣的性质。</p> <h1>组合博弈论</h1> <p>要解决这个问题就不得不上组合博弈论。</p> <p>博弈,就是下围棋。说到围棋,就要说到 AlphaGo。这两天上个课基本每几节课就有哪个教授要谈一下 AlphaGo,有些教授还特别喜欢谈,虽然他上的内容跟 AlphaGo 一点关系都没有(和我完全不同),就是喜欢讲。有些学生啊上课专心致志地玩手机写作业,一听到教授开始说 AlphaGo 这几个字就马上抬起了高贵的头颅,饶有兴致的听起来。嗯,既然如此我也来谈一下 AlphaGo 怒刷一波存在感。也许你看到第一行才看到这里,或者是搜了一下 AlphaGo 看我第一行是不是在吹牛,嗯,那我多说几遍 AlphaGo 这个词。</p> <p>博弈论(Game Theory),就是关于玩游戏(Game)的理论(Theory)。组合博弈论(Combinatorial Game Theory)就是玩组合游戏(Combinatorial Game)的理论。</p> <p>组合游戏是指完美信息的确定的序列博弈。所谓序列是指玩家轮流行动,因此石头剪刀布不在我们讨论范围之内;所谓确定是指玩家采取行动后只有唯一的情况会发生,且一定会发生,因此投骰子猜大小不在我们讨论范围之内;完美信息则是指任意时刻所有玩家都知道当前状态和其他玩家的决策与未来可能的决策等,因此斗地主也不在我们讨论范围内。一般情况下,组合博弈论仅限于研究两个玩家的游戏。</p> <p>根据以上定义,组合游戏包括:</p> <ul> <li>围棋,这大概是你为什么读到现在的原因;</li> <li>中国象棋、国际象棋;</li> <li>五子棋;</li> <li>井字棋;</li> <li>跳棋;</li> <li>当然还有我其实真正主要要说的筷子游戏,所有之前介绍的变种都是;</li> <li>以及更多……</li> </ul> <p>组合游戏的特点是假设游戏双方都采用最佳策略,对于任意状态游戏的结果是定的,即一方必胜,或陷入僵局(将在之后看到)。即使是狂拽炫酷屌炸天的围棋也是如此,只是它的可能状态数过于大,我们暂时还不能全部试出接下来所有的下法,所以需要 AlphaGo 所提供的对局势判断的“直觉”等技术来指导下一步。</p> <h1>分析游戏</h1> <p>下面我们就利用解决组合游戏的一般方式来解决筷子游戏。</p> <p>首先我们找到状态。对于筷子游戏,我们用四元组 &#92;( \left(a<em>x, a</em>y, b<em>x, b</em>y\right) &#92;) 来表示状态,其中 &#92;( a<em>x, a</em>y &#92;) 是 A(或自己)手上的两个数,&#92;( b<em>x, b</em>y &#92;) 就是 B(或对手)手上的两个数,此时轮到 A 决策。由于交换 &#92;( a<em>x, a</em>y &#92;) 或 &#92;( b<em>x, b</em>y &#92;) 后的状态是等价的,因此为了方便计算与制定规则,我们不妨规定 &#92;( a<em>x \ge a</em>y &#92;) 且 &#92;( b<em>x \ge b</em>y &#92;)。</p> <p>对于每一个状态存在三种情况:</p> <ol> <li>第一是 A 必胜,我们用 &#92;(W&#92;) 表示;</li> <li>第二是 B 必胜,即 A 必败,我们用 &#92;(L&#92;) 表示;</li> <li>至于第三种情况稍后再介绍。哈哈哈哈。</li> </ol> <p>观察游戏过程,我们可以知道必胜或必败的条件:</p> <ol> <li>一个可以转移到 &#92;(L&#92;) 状态的状态是 &#92;(W&#92;) 状态,此时必胜策略是转移到这个 &#92;(L&#92;) 状态;</li> <li>一个只能转移到 &#92;(W&#92;) 状态的状态是 &#92;(L&#92;) 状态,此时由于无论怎么走对手都必胜,自己必败。</li> </ol> <p>根据游戏规则,状态有以下转移方式:</p> <ol> <li>&#92;( \left(a<em>x, a</em>y, b<em>x, b</em>y\right) \to \left(\left(a<em>x + b</em>x\right) \bmod M, b<em>y, a</em>x, a<em>y\right) \left(a</em>x, b_x \ne 0\right) &#92;);</li> <li>&#92;( \left(a<em>x, a</em>y, b<em>x, b</em>y\right) \to \left(b<em>x, \left(a</em>x + b<em>y\right) \bmod M, a</em>x, a<em>y\right) \left(a</em>x, b_y \ne 0\right) &#92;);</li> <li>&#92;( \left(a<em>x, a</em>y, b<em>x, b</em>y\right) \to \left(\left(a<em>y + b</em>x\right) \bmod M, b<em>y, a</em>x, a<em>y\right) \left(a</em>y, b_x \ne 0\right) &#92;);</li> <li>&#92;( \left(a<em>x, a</em>y, b<em>x, b</em>y\right) \to \left(b<em>x, \left(a</em>y + b<em>y\right) \bmod M, a</em>x, a<em>y\right) \left(a</em>y, b_y \ne 0\right) &#92;);</li> <li>在“劈叉”变种下,&#92;( \left(a<em>x, 0, b</em>x, b<em>y\right) \to \left(b</em>x, b<em>y, \frac{a</em>x}{2}, \frac{a<em>x}{2}\right) \left(a</em>x \equiv 0 \pmod 2\right) &#92;);</li> <li>在“交易”变种下,&#92;( \left(a<em>x, a</em>y, b<em>x, b</em>y\right) \to \left(b<em>x, b</em>y, x, a<em>x + a</em>y - x\right) \left(\frac{a<em>x + a</em>y}{2} \le x \le \min \left&#92;{M - 1, a<em>x + a</em>y\right&#92;}, x \ne a_x\right) &#92;)。</li> </ol> <p>注意这里前四个操作可能导致新的 &#92;( b<em>x &lt; b</em>y &#92;),违反了我们的规定,此时需要进行强行交换。</p> <p>我们知道 &#92;( \left(0, 0, b<em>x, b</em>y\right) &#92;) 是 &#92;(L&#92;) 状态,其中 &#92;( b_x > 0 &#92;),因为此时 A 两只手都已废。不过在“自杀”变种下这是 &#92;(W&#92;) 状态。</p> <p>有了这些状态和转移方式我们通过不断倒推就可以得到其他状态是必胜还是必败。对于大多数游戏,我们发现我们并不能倒推到初始状态 &#92;( \left(1, 1, 1, 1\right) &#92;)。所有剩下未知的状态都无法转移到 &#92;(L&#92;) 状态,且能转移到其它未知的状态。我们确定存在一种从初始状态转移到最终状态 &#92;( \left(0, 0, b<em>x, b</em>y\right) &#92;)。通过拓扑排序我们可以证明此时出现了环。由于任意玩家向 &#92;(L&#92;) 状态走都会导致其必败,因此双方都会选择在剩下的状态中走来走去。因此我们可将剩下所有未知且无法继续推的状态作为第三种状态,称为僵局(stalemate),记作 &#92;(S&#92;)。</p> <h1>确定状态</h1> <p>即使是第一种游戏,也存在着 &#92;&#91; \frac{5 \times \left(5 + 1\right)}{2} \times \left(\frac{5 \times \left(5 + 1\right)}{2} - 1\right) = 210 &#92;&#93; 种状态,而且对于其中很多状态都有可能需要验证五种转移方式中的几种,比较烦。因此我们选择让计算机完成这种机械的事情,就和 AlphaGo 一样。</p> <p>作为副产品,我们还可以确定在非 &#92;(S&#92;) 状态,且双方都采取最优策略的情况下,游戏将在几步之内结束,这个值与我们经过第几轮倒推确定它的状态的值相等。这是因为:</p> <ol> <li>如果是 &#92;(W&#92;) 状态,则其最先发现的 &#92;(L&#92;) 状态离游戏结束最接近,因此为了尽快结束游戏,最优策略在这一步会选择向这个状态转移;</li> <li>如果是 &#92;(L&#92;) 状态,则其最后确定的 &#92;(W&#92;) 状态离游戏结束最远,因此为了让对手有更大几率失误(不考虑是电脑),最优策略在这一步会选择这个状态转移。</li> </ol> <p>以下简单粗暴的 C++ 代码(有删减)确定了所有状态的胜负以及需要花的步数。</p> <p>```cpp enum Position { POSITION<em>UNKNOWN, POSITION</em>WINNING, POSITION<em>LOSING } status[RULE</em>MOD][RULE<em>MOD][RULE</em>MOD][RULE_MOD];</p> <p>constexpr int getMaxMoves(int ax, int ay);</p> <p>// Makes the move specified. Returns true if it's not allowed. bool makeMove(int &amp;ax, int &amp;ay, int &amp;bx, int &amp;by, int move);</p> <p>inline void updateStatus(Position &amp;flag, int ax, int ay, int bx, int by, int move, int &amp;nowSteps) { if (makeMove(ax, ay, bx, by, move)) return; switch (status[ax][ay][bx][by]) { case POSITION<em>UNKNOWN: flag = static</em>cast<Position>(flag &amp; ~POSITION<em>LOSING); break; case POSITION</em>WINNING: if (flag == POSITION<em>LOSING) nowSteps = max(nowSteps, steps[ax][ay][bx][by]); break; case POSITION</em>LOSING: // weakness found if (flag != POSITION<em>WINNING) { flag = POSITION</em>WINNING; nowSteps = steps[ax][ay][bx][by]; } else nowSteps = min(nowSteps, steps[ax][ay][bx][by]); break; default: assert(false); } }</p> <p>for (int i = 1; i &lt; RULE<em>MOD; ++i) for (int j = 0; j &lt;= i; ++j) status[0][0][i][j] = POSITION</em>FINAL; bool updated = true; while (updated) { updated = false; for (int ax = 1; ax &lt; RULE<em>MOD; ++ax) for (int ay = 0; ay &lt;= ax; ++ay) for (int bx = 1; bx &lt; RULE</em>MOD; ++bx) for (int by = 0; by &lt;= bx; ++by) if (!status[ax][ay][bx][by]) { auto flag = POSITION_LOSING; int nowSteps = -1; for (int move = 0, maxMoves = getMaxMoves(ax, ay); move &lt;= maxMoves; ++move) updateStatus(flag, ax, ay, bx, by, move, nowSteps); if (flag) { status[ax][ay][bx][by] = flag; steps[ax][ay][bx][by] = nowSteps + 1; updated = true; } } } ```</p> <h2>真正的攻略</h2> <p>写了这么多废话,我终于要说这个游戏的攻略了。为了便于使用,我们选择使用 &#92;( \left(b<em>x, b</em>y, a<em>x, a</em>y\right) &#92;) 输出,意思是我的回合结束后状态变成这样时对手必胜或必败,则我决策时要避免或转移到这个状态。同时我们可以省略输出 0 步或 1 步之内结束的状态,因为那些太明显了。</p> <p>为了好玩,我们顺便输出一下状态矩阵。</p> <p>```cpp // Print solution and debug information to cerr cerr &lt;&lt; "Positions to find:" &lt;&lt; endl; for (int bx = 1; bx &lt; RULE<em>MOD; ++bx) for (int by = 0; by &lt;= bx; ++by) for (int ax = 1; ax &lt; RULE</em>MOD; ++ax) for (int ay = 0; ay &lt;= ax; ++ay) if (status[ax][ay][bx][by] == 2 &amp;&amp; steps[ax][ay][bx][by] > 1) cerr &lt;&lt; bx &lt;&lt; by &lt;&lt; ' ' &lt;&lt; ax &lt;&lt; ay &lt;&lt; " (win within " &lt;&lt; steps[ax][ay][bx][by] &lt;&lt; " steps)" &lt;&lt; endl; cerr &lt;&lt; endl;</p> <p>cerr &lt;&lt; "Positions to avoid except obvious ones:" &lt;&lt; endl; for (int bx = 1; bx &lt; RULE<em>MOD; ++bx) for (int by = 0; by &lt;= bx; ++by) for (int ax = 1; ax &lt; RULE</em>MOD; ++ax) for (int ay = 0; ay &lt;= ax; ++ay) if (status[ax][ay][bx][by] == 1 &amp;&amp; steps[ax][ay][bx][by] > 1) cerr &lt;&lt; bx &lt;&lt; by &lt;&lt; ' ' &lt;&lt; ax &lt;&lt; ay &lt;&lt; " (lose within " &lt;&lt; steps[ax][ay][bx][by] &lt;&lt; " steps)" &lt;&lt; endl; cerr &lt;&lt; endl;</p> <p>for (int ax = 1; ax &lt; RULE<em>MOD; ++ax) for (int ay = 0; ay &lt;= ax; ++ay) { for (int bx = 1; bx &lt; RULE</em>MOD; ++bx) for (int by = 0; by &lt;= bx; ++by) cerr &lt;&lt; status[ax][ay][bx][by]; cerr &lt;&lt; endl; } ```</p> <p>下面来看输出。对于第一种游戏:</p> <p>``` Positions to find: 10 30 (win within 4 steps) 22 10 (win within 6 steps) 22 30 (win within 2 steps) 30 10 (win within 2 steps) 31 10 (win within 6 steps) 32 30 (win within 4 steps) 33 10 (win within 2 steps) 33 30 (win within 8 steps) 40 30 (win within 2 steps) 42 30 (win within 4 steps) 44 10 (win within 2 steps) 44 30 (win within 2 steps) 44 31 (win within 6 steps) 44 33 (win within 6 steps)</p> <p>Positions to avoid except obvious ones: 10 22 (lose within 3 steps) 10 32 (lose within 5 steps) 11 44 (lose within 3 steps) 20 10 (lose within 5 steps) 20 44 (lose within 3 steps) 21 30 (lose within 3 steps) 21 31 (lose within 7 steps) 21 33 (lose within 3 steps) 30 30 (lose within 3 steps) 30 31 (lose within 7 steps) 30 33 (lose within 3 steps) 30 40 (lose within 3 steps) 31 22 (lose within 7 steps) 31 40 (lose within 3 steps) 31 42 (lose within 5 steps) 31 44 (lose within 3 steps) 32 32 (lose within 5 steps) 32 33 (lose within 9 steps) 32 44 (lose within 7 steps) 33 22 (lose within 3 steps) 33 32 (lose within 5 steps) 33 42 (lose within 5 steps) 40 22 (lose within 7 steps) 40 40 (lose within 3 steps) 40 42 (lose within 5 steps) 40 44 (lose within 3 steps) 41 31 (lose within 7 steps) 41 44 (lose within 7 steps) 43 10 (lose within 5 steps) 43 44 (lose within 7 steps)</p> <p>00102220210012 00000000010000 00000100000000 00000100010000 10000110110000 20112102220202 00110100011002 10100101100000 00110101000002 10000110010000 10000000010000 10000110110000 10100000000000 11100011011010 ```</p> <p>对于第二种游戏:</p> <p>``` Positions to find: 10 20 (win within 12 steps) 10 40 (win within 6 steps) 10 50 (win within 10 steps) 10 77 (win within 14 steps) 11 60 (win within 10 steps) 11 80 (win within 16 steps) 11 90 (win within 2 steps) 20 40 (win within 2 steps) 20 53 (win within 12 steps) 20 70 (win within 8 steps) 20 90 (win within 2 steps) 22 20 (win within 8 steps) 22 30 (win within 8 steps) 22 40 (win within 2 steps) 22 60 (win within 6 steps) 22 80 (win within 2 steps) 22 90 (win within 2 steps) 30 11 (win within 14 steps) 30 20 (win within 6 steps) 30 50 (win within 10 steps) 30 60 (win within 12 steps) 31 60 (win within 16 steps) 31 80 (win within 10 steps) 31 90 (win within 8 steps) 32 70 (win within 4 steps) 33 40 (win within 16 steps) 33 70 (win within 2 steps) 33 80 (win within 10 steps) 40 30 (win within 2 steps) 40 51 (win within 12 steps) 40 80 (win within 2 steps) 40 90 (win within 8 steps) 41 90 (win within 4 steps) 42 40 (win within 6 steps) 43 80 (win within 6 steps) 44 10 (win within 8 steps) 44 20 (win within 6 steps) 44 30 (win within 2 steps) 44 40 (win within 8 steps) 44 60 (win within 2 steps) 44 80 (win within 2 steps) 50 10 (win within 4 steps) 50 30 (win within 4 steps) 50 70 (win within 4 steps) 50 90 (win within 4 steps) 52 50 (win within 10 steps) 52 80 (win within 6 steps) 54 50 (win within 10 steps) 54 60 (win within 6 steps) 55 50 (win within 2 steps) 60 10 (win within 8 steps) 60 20 (win within 2 steps) 60 70 (win within 2 steps) 60 95 (win within 12 steps) 61 40 (win within 12 steps) 62 20 (win within 6 steps) 64 20 (win within 8 steps) 64 40 (win within 4 steps) 64 50 (win within 6 steps) 64 60 (win within 4 steps) 64 80 (win within 8 steps) 65 40 (win within 6 steps) 65 50 (win within 10 steps) 66 20 (win within 2 steps) 66 40 (win within 2 steps) 66 60 (win within 8 steps) 66 70 (win within 2 steps) 66 80 (win within 6 steps) 66 90 (win within 8 steps) 70 40 (win within 12 steps) 70 50 (win within 10 steps) 70 80 (win within 6 steps) 70 99 (win within 14 steps) 71 20 (win within 16 steps) 71 30 (win within 8 steps) 71 60 (win within 10 steps) 72 80 (win within 12 steps) 73 30 (win within 14 steps) 73 50 (win within 6 steps) 73 70 (win within 14 steps) 76 20 (win within 6 steps) 77 20 (win within 10 steps) 77 30 (win within 2 steps) 77 60 (win within 16 steps) 80 10 (win within 2 steps) 80 30 (win within 8 steps) 80 60 (win within 2 steps) 80 75 (win within 12 steps) 81 60 (win within 6 steps) 82 20 (win within 4 steps) 82 40 (win within 8 steps) 82 50 (win within 6 steps) 82 60 (win within 8 steps) 82 80 (win within 4 steps) 83 20 (win within 12 steps) 84 80 (win within 6 steps) 85 20 (win within 6 steps) 85 50 (win within 10 steps) 86 60 (win within 6 steps) 87 30 (win within 4 steps) 88 10 (win within 2 steps) 88 20 (win within 2 steps) 88 40 (win within 6 steps) 88 60 (win within 2 steps) 88 70 (win within 8 steps) 88 80 (win within 8 steps) 90 33 (win within 14 steps) 90 50 (win within 10 steps) 90 60 (win within 6 steps) 90 80 (win within 12 steps) 91 10 (win within 14 steps) 91 50 (win within 6 steps) 91 90 (win within 14 steps) 92 40 (win within 6 steps) 93 40 (win within 10 steps) 93 70 (win within 8 steps) 93 80 (win within 16 steps) 94 60 (win within 12 steps) 96 10 (win within 4 steps) 97 10 (win within 8 steps) 97 20 (win within 10 steps) 97 40 (win within 16 steps) 99 10 (win within 2 steps) 99 20 (win within 16 steps) 99 40 (win within 10 steps)</p> <p>Positions to avoid except obvious ones: 10 10 (lose within 13 steps) 10 22 (lose within 9 steps) 10 33 (lose within 17 steps) 10 54 (lose within 7 steps) 10 60 (lose within 3 steps) 10 64 (lose within 7 steps) 10 66 (lose within 3 steps) 10 70 (lose within 7 steps) 10 71 (lose within 17 steps) 10 72 (lose within 13 steps) 11 40 (lose within 13 steps) 11 91 (lose within 15 steps) 11 96 (lose within 5 steps) 11 97 (lose within 9 steps) 11 99 (lose within 3 steps) 20 20 (lose within 3 steps) 20 22 (lose within 3 steps) 20 30 (lose within 11 steps) 20 42 (lose within 7 steps) 20 44 (lose within 3 steps) 20 50 (lose within 5 steps) 20 54 (lose within 7 steps) 20 64 (lose within 5 steps) 20 66 (lose within 7 steps) 20 71 (lose within 9 steps) 20 73 (lose within 7 steps) 20 91 (lose within 15 steps) 20 92 (lose within 7 steps) 20 94 (lose within 13 steps) 20 96 (lose within 5 steps) 20 97 (lose within 9 steps) 20 99 (lose within 3 steps) 21 80 (lose within 3 steps) 21 88 (lose within 3 steps) 21 97 (lose within 11 steps) 21 99 (lose within 17 steps) 22 82 (lose within 5 steps) 22 83 (lose within 13 steps) 22 85 (lose within 7 steps) 22 88 (lose within 3 steps) 30 10 (lose within 7 steps) 30 30 (lose within 13 steps) 30 31 (lose within 17 steps) 30 52 (lose within 7 steps) 30 61 (lose within 13 steps) 30 66 (lose within 9 steps) 30 80 (lose within 3 steps) 30 82 (lose within 7 steps) 30 88 (lose within 3 steps) 30 99 (lose within 17 steps) 31 97 (lose within 9 steps) 32 71 (lose within 17 steps) 32 76 (lose within 7 steps) 32 77 (lose within 11 steps) 32 80 (lose within 9 steps) 32 87 (lose within 5 steps) 32 97 (lose within 11 steps) 33 20 (lose within 13 steps) 33 71 (lose within 9 steps) 33 73 (lose within 15 steps) 33 77 (lose within 3 steps) 33 87 (lose within 5 steps) 40 10 (lose within 11 steps) 40 22 (lose within 7 steps) 40 32 (lose within 5 steps) 40 33 (lose within 3 steps) 40 40 (lose within 3 steps) 40 43 (lose within 7 steps) 40 44 (lose within 3 steps) 40 50 (lose within 5 steps) 40 73 (lose within 15 steps) 40 82 (lose within 5 steps) 40 83 (lose within 13 steps) 40 84 (lose within 7 steps) 40 85 (lose within 7 steps) 40 88 (lose within 3 steps) 40 91 (lose within 7 steps) 40 93 (lose within 9 steps) 40 97 (lose within 9 steps) 41 60 (lose within 9 steps) 41 92 (lose within 7 steps) 41 93 (lose within 11 steps) 41 96 (lose within 5 steps) 41 97 (lose within 17 steps) 41 99 (lose within 11 steps) 42 60 (lose within 3 steps) 42 62 (lose within 7 steps) 42 64 (lose within 9 steps) 42 66 (lose within 3 steps) 42 76 (lose within 7 steps) 42 82 (lose within 9 steps) 42 88 (lose within 7 steps) 43 70 (lose within 13 steps) 43 90 (lose within 15 steps) 43 97 (lose within 17 steps) 44 61 (lose within 13 steps) 44 64 (lose within 5 steps) 44 65 (lose within 7 steps) 44 66 (lose within 3 steps) 50 11 (lose within 11 steps) 50 20 (lose within 9 steps) 50 31 (lose within 11 steps) 50 32 (lose within 5 steps) 50 33 (lose within 11 steps) 50 40 (lose within 9 steps) 50 41 (lose within 5 steps) 50 43 (lose within 7 steps) 50 60 (lose within 9 steps) 50 71 (lose within 11 steps) 50 76 (lose within 7 steps) 50 77 (lose within 11 steps) 50 80 (lose within 9 steps) 50 81 (lose within 7 steps) 50 87 (lose within 5 steps) 50 92 (lose within 7 steps) 50 93 (lose within 11 steps) 50 96 (lose within 5 steps) 50 97 (lose within 11 steps) 50 99 (lose within 11 steps) 51 20 (lose within 13 steps) 51 50 (lose within 5 steps) 51 90 (lose within 11 steps) 51 91 (lose within 7 steps) 52 82 (lose within 7 steps) 52 85 (lose within 7 steps) 53 50 (lose within 5 steps) 53 60 (lose within 13 steps) 53 70 (lose within 11 steps) 53 73 (lose within 7 steps) 54 64 (lose within 7 steps) 54 65 (lose within 7 steps) 55 52 (lose within 11 steps) 55 54 (lose within 11 steps) 55 55 (lose within 3 steps) 55 65 (lose within 11 steps) 55 85 (lose within 11 steps) 60 22 (lose within 3 steps) 60 31 (lose within 9 steps) 60 50 (lose within 5 steps) 60 52 (lose within 7 steps) 60 60 (lose within 3 steps) 60 62 (lose within 7 steps) 60 66 (lose within 3 steps) 60 71 (lose within 9 steps) 60 72 (lose within 13 steps) 60 73 (lose within 15 steps) 60 76 (lose within 7 steps) 60 77 (lose within 3 steps) 60 82 (lose within 5 steps) 60 87 (lose within 5 steps) 60 88 (lose within 7 steps) 60 90 (lose within 11 steps) 60 91 (lose within 7 steps) 61 44 (lose within 9 steps) 61 90 (lose within 7 steps) 61 94 (lose within 13 steps) 62 44 (lose within 7 steps) 62 64 (lose within 9 steps) 62 80 (lose within 3 steps) 62 81 (lose within 7 steps) 62 82 (lose within 9 steps) 62 86 (lose within 7 steps) 62 88 (lose within 3 steps) 63 40 (lose within 3 steps) 63 44 (lose within 3 steps) 63 71 (lose within 11 steps) 63 77 (lose within 17 steps) 64 42 (lose within 7 steps) 64 44 (lose within 9 steps) 64 64 (lose within 5 steps) 64 66 (lose within 9 steps) 64 86 (lose within 7 steps) 65 54 (lose within 7 steps) 65 64 (lose within 7 steps) 66 44 (lose within 3 steps) 66 54 (lose within 7 steps) 66 64 (lose within 5 steps) 66 94 (lose within 13 steps) 70 11 (lose within 17 steps) 70 20 (lose within 3 steps) 70 22 (lose within 3 steps) 70 44 (lose within 9 steps) 70 70 (lose within 13 steps) 70 82 (lose within 7 steps) 70 85 (lose within 7 steps) 70 90 (lose within 7 steps) 70 94 (lose within 13 steps) 70 97 (lose within 17 steps) 71 93 (lose within 9 steps) 72 30 (lose within 7 steps) 72 83 (lose within 13 steps) 72 88 (lose within 9 steps) 73 73 (lose within 15 steps) 74 33 (lose within 17 steps) 74 60 (lose within 3 steps) 74 66 (lose within 3 steps) 74 93 (lose within 11 steps) 75 30 (lose within 11 steps) 75 40 (lose within 13 steps) 75 50 (lose within 5 steps) 75 73 (lose within 7 steps) 76 10 (lose within 15 steps) 76 30 (lose within 13 steps) 76 31 (lose within 17 steps) 77 32 (lose within 5 steps) 77 33 (lose within 3 steps) 77 73 (lose within 15 steps) 77 80 (lose within 13 steps) 77 93 (lose within 9 steps) 80 11 (lose within 3 steps) 80 31 (lose within 9 steps) 80 41 (lose within 5 steps) 80 44 (lose within 7 steps) 80 50 (lose within 5 steps) 80 61 (lose within 13 steps) 80 64 (lose within 5 steps) 80 65 (lose within 7 steps) 80 66 (lose within 3 steps) 80 70 (lose within 11 steps) 80 73 (lose within 7 steps) 80 80 (lose within 3 steps) 80 81 (lose within 7 steps) 80 86 (lose within 7 steps) 80 88 (lose within 3 steps) 80 91 (lose within 15 steps) 80 93 (lose within 9 steps) 81 30 (lose within 15 steps) 81 90 (lose within 13 steps) 81 93 (lose within 17 steps) 82 22 (lose within 9 steps) 82 62 (lose within 7 steps) 82 82 (lose within 5 steps) 82 84 (lose within 7 steps) 82 88 (lose within 9 steps) 83 22 (lose within 9 steps) 83 70 (lose within 7 steps) 83 72 (lose within 13 steps) 84 20 (lose within 3 steps) 84 22 (lose within 3 steps) 84 42 (lose within 7 steps) 84 64 (lose within 9 steps) 84 66 (lose within 7 steps) 84 82 (lose within 9 steps) 84 92 (lose within 7 steps) 85 52 (lose within 7 steps) 85 82 (lose within 7 steps) 86 22 (lose within 7 steps) 86 40 (lose within 3 steps) 86 43 (lose within 7 steps) 86 44 (lose within 3 steps) 86 64 (lose within 9 steps) 86 82 (lose within 9 steps) 86 84 (lose within 7 steps) 87 20 (lose within 9 steps) 87 31 (lose within 11 steps) 87 32 (lose within 5 steps) 87 33 (lose within 11 steps) 87 43 (lose within 7 steps) 87 93 (lose within 17 steps) 88 22 (lose within 3 steps) 88 52 (lose within 7 steps) 88 72 (lose within 13 steps) 88 82 (lose within 5 steps) 90 30 (lose within 7 steps) 90 40 (lose within 3 steps) 90 44 (lose within 3 steps) 90 64 (lose within 7 steps) 90 65 (lose within 7 steps) 90 77 (lose within 17 steps) 90 83 (lose within 13 steps) 90 88 (lose within 9 steps) 90 90 (lose within 13 steps) 90 93 (lose within 17 steps) 91 91 (lose within 15 steps) 92 10 (lose within 13 steps) 92 70 (lose within 15 steps) 92 71 (lose within 17 steps) 93 71 (lose within 9 steps) 94 10 (lose within 7 steps) 94 61 (lose within 13 steps) 94 66 (lose within 9 steps) 95 10 (lose within 11 steps) 95 50 (lose within 5 steps) 95 80 (lose within 13 steps) 95 91 (lose within 7 steps) 96 11 (lose within 11 steps) 96 31 (lose within 17 steps) 96 40 (lose within 9 steps) 96 41 (lose within 5 steps) 96 71 (lose within 11 steps) 96 81 (lose within 7 steps) 97 31 (lose within 9 steps) 98 11 (lose within 17 steps) 98 20 (lose within 3 steps) 98 22 (lose within 3 steps) 98 31 (lose within 11 steps) 99 11 (lose within 3 steps) 99 31 (lose within 9 steps) 99 41 (lose within 5 steps) 99 60 (lose within 13 steps) 99 91 (lose within 15 steps)</p> <p>100001000100022000002000000000000102000000021210112202 000002000000001000000000000100000001000000001000001011 201022001000021100002020202120000221022120120000000212 000000000000000000000000000000000001000000001000000000 101000000100000000001000000100000001011101010000000010 001021000200022000000000000121201122100000201000000000 000001000000001000001000000100000101000000101000001111 000000000100001000000000000100000011000000100000000000 100000000100001000000000000100010010000000102000000000 212020002102021000001201222200001000020001021022001202 000000000000001000001000000000000001000000001000001001 001000000000000000001000100000000001000100000000000000 000000000100001000001000000100000000000001100000000000 001000000100000000001111101100000001000001001000000000 201002000100001121221000220200201001020020002200010000 000000000200001000000000000000000000000000001000000000 000001000000001000011000000000000001000010010000000000 002000000000001000000000000100000000000000000000000000 101000000000001000011000011000000000000000000000000000 000000000000001000010000000000000000000000000000000000 120022200111021001201000202020010022220002022000200001 000001000100010000000000000000000001000000001000100000 000000000101000000001000000000000001010000000000000000 000000000100000000000000000100000000000000000000000000 101000000101010000101010111000000001000101001000000000 000000000100011000110000000000000001000000001000000000 101001000101010000001000100000010001000100000000100000 102001022000102001002000002100200001001000020012000000 101001011000001000001001000000000000000000001011001000 100001000000000000001000000000000001001000010000000000 001001001100000001001000000100101011000000000000000000 000001000000000000001000000000000000000000000000000000 000001000000001000000000000000000002000000000000000000 000001010101001000001000000000000000000000000000000000 200001011000001000001001000000000000000000001000000000 021121212200221020000010202202000011020200022002010000 001000000000001000000010000000000001000000001000001000 001011000101000010001010000100000001010111010000000000 001010000100000000000000000101000000000000001000000000 001000000100000000001000000000000000010001000000000000 001010000100001010010000000100000000000000000000000000 001000000100000000000010100000000001000000000000000000 001001011000001000001000000000000000000000000000000000 001111000101000000001010000001000001010000001000000000 122020200220102100001100002100000000100000001200000000 111000000100000100001000000000000001000000001100010001 101000000010001000000000000000000001000100000000000000 100000000110001000000000000110010011100000101000000000 101000000000000000001100001100000000000000000000000000 100000000000001000002000000000000000000000000000000000 111000000110001000000000000000000000000000000000000000 111101110110101000000000000100000000000000000000000000 101000000000000000000000000000000000000000000000000000 111101000010001000000000000200000000000000000000000000 ```</p> <p>通过观察并没有发现上面两种游戏必胜状态或必败状态有什么规律可言,而且以僵局状态居多。因此要保持不败,唯一的方式就是把上面两张表背下来。</p> <h1>击毙人脑</h1> <p>很不幸,人类还是没有计算机聪明。计算机可以轻松记下上面的表,因此我们可以友好地让计算机和人脑玩一局,而我们可以保证计算机不会输,因为它可以决定它先走还是让用户先走。</p> <p>为了加入额外的狡猾,当存在多种最优的走法时,我们让计算机随机选择一种走法,这样会防止使用同一种走法的无脑用户陷入死循环,同时也增加了正常玩家失误的概率。</p> <p>```cpp typedef uniform<em>int</em>distribution<mt19937::result_type> RandomRange; mt19937 range;</p> <p>inline bool isAcceptable(int ax, int ay, int bx, int by, int move, Position nowStatus, int nowSteps) { if (makeMove(ax, ay, bx, by, move)) return false; auto newStatus = status[ax][ay][bx][by]; return newStatus == POSITION_LOSING &amp;&amp; nowSteps == steps[ax][ay][bx][by] + 1 || !newStatus &amp;&amp; !nowStatus; }</p> <p>inline bool userMovesFirst(int status) { switch (status) { case POSITION<em>WINNING: return false; case POSITION</em>LOSING: return true; default: return static_cast<bool>(RandomRange(0, 1)(range)); // whatever } }</p> <p>// Kindly offer a game to the user range.seed(random<em>device()()); int ax = 1, ay = 1, bx = 1, by = 1, move = -1, maxMoves; if (userMovesFirst(status[ax][ay][bx][by])) while (makeMove(ax, ay, bx, by, move)) { cout &lt;&lt; ax &lt;&lt; ay &lt;&lt; ' ' &lt;&lt; bx &lt;&lt; by &lt;&lt; ' '; cin >> move; } vector<int> moves(static</em>cast<size_t>(getMaxMoves(RULE<em>MOD - 1, 0))); while (ax &amp;&amp; bx) { auto nowStatus = status[ax][ay][bx][by]; int nowSteps = steps[ax][ay][bx][by]; assert(nowStatus != POSITION</em>LOSING); // there's no way I'm losing the game! no! for (move = 0, maxMoves = getMaxMoves(ax, ay), moves.clear(); move &lt;= maxMoves; ++move) if (isAcceptable(ax, ay, bx, by, move, nowStatus, nowSteps)) moves.push_back(move); assert(!moves.empty()); // I have to have a move! // pick a random move in order to prevent getting into a loop cout &lt;&lt; bx &lt;&lt; by &lt;&lt; ' ' &lt;&lt; ax &lt;&lt; ay &lt;&lt; ' ' &lt;&lt; (move = moves[RandomRange(0, moves.size() - 1)(range)]) &lt;&lt; endl; makeMove(ax, ay, bx, by, move); if (!(ax &amp;&amp; bx)) { cout &lt;&lt; ax &lt;&lt; ay &lt;&lt; ' ' &lt;&lt; bx &lt;&lt; by &lt;&lt; " YOU LOSE" &lt;&lt; endl; return 0; } move = -1; while (makeMove(ax, ay, bx, by, move)) { cout &lt;&lt; ax &lt;&lt; ay &lt;&lt; ' ' &lt;&lt; bx &lt;&lt; by &lt;&lt; ' '; cin >> move; } } cout &lt;&lt; bx &lt;&lt; by &lt;&lt; ' ' &lt;&lt; ax &lt;&lt; ay &lt;&lt; " YOU LOSE" &lt;&lt; endl; // you don't have a single chance :/ return 0; ```</p> <p>最后附上<a href="https://gist.github.com/Mygod/e296b1047858bf4724a2508733eefefe">完整程序源码</a>。</p> <h1>一些其他的探索</h1> <p>以下是我使用以上源码乱玩玩发现的一些东西,你也可以下载上面的源码自己编译了乱玩。</p> <ul> <li>在 &#92;(M = 2&#92;) 时这个变种是平庸的,在这个游戏里先手必胜,在“自杀”变种中后手必胜,且任意回合的所有决策都是等价的;</li> <li>在 &#92;(M = 3&#92;) 时后手必胜,游戏在 6 步内结束,“自杀”变种中先手必胜,游戏在 7 步内结束;</li> <li>有趣的是此时加入“劈叉”变种时是僵局,再加入“自杀”变种后先手必胜,游戏在 11 步内结束;</li> <li>同样的加入“交易”变种时是僵局,再加入“自杀”变种后后手必胜,游戏在 4 步内结束;</li> <li>在 &#92;(M = 4&#92;) 时先手必胜(7 步),加入“劈叉”变种先手必胜(9 步),加入“交易”变种先手必胜(11 步),但无论哪种一旦加入“自杀”变种僵局;</li> <li>从 &#92;(M = 5&#92;) 开始僵局状态个数大量增加,基本所有初始状态 &#92;( \left(1, 1, 1, 1\right) &#92;)都是僵局;</li> <li>就尝试的这些情况来看都比较混沌,没发现什么规律。人类还是没有计算机聪明。</li> </ul> <h1>未来一些可能的探索</h1> <p>这个标题的意思是我懒得想。</p> <ul> <li>要是拥有 &#92;(H&#92;) 个手的火星人玩这个游戏会怎么样?</li> <li>要是 &#92;(N&#92;) 个人一起玩,情况如何?</li> <li>假如不允许转移到之前出现过的状态(此时不可能产生环,即不会产生僵局),当一个人无法继续移动时输(必产生胜负),情况如何?</li> <li>考虑这个游戏的一些其他变种?(其实吧维基给的剩下几个我觉得都挺无聊的)</li> </ul> https://zh.mygod.be/blog/optimal-solution-for-chopsticks-leftovers-variant/ Tue, 19 Apr 2016 15:15:56 +0000 论某疼讯红包的公平分配 https://zh.mygod.be/blog/fair-distribution-of-lucky-money/ <p>几个小时前刚打算睡觉,路过某知乎发现<a href="https://www.zhihu.com/question/22625187/answer/85530416">一篇回答</a>,里面有微信的红包钱是怎么分配的。简单说,就是生成从 0.01 元到剩余平均每人能拿到钱的两倍中的一个随机数,少废话,看他贴的代码。<!--more--></p> <p>```java public static double getRandomMoney(LeftMoneyPackage <em>leftMoneyPackage) { // remainSize 剩余的红包数量 // remainMoney 剩余的钱 if (</em>leftMoneyPackage.remainSize == 1) { <em>leftMoneyPackage.remainSize--; return (double) Math.round(</em>leftMoneyPackage.remainMoney * 100) / 100; } Random r = new Random(); double min = 0.01; // _leftMoneyPackage.remainSize--;</p> <pre><code>double max = _leftMoneyPackage.remainMoney / _leftMoneyPackage.remainSize * 2; double money = r.nextDouble() * max; money = money &lt;= min ? 0.01: money; money = Math.floor(money * 100) / 100; _leftMoneyPackage.remainSize--; _leftMoneyPackage.remainMoney -= money; return money; </code></pre> <p>} ```</p> <p>一看这个算法我就浑身不爽,这明显不对嘛!于是我放弃休息(其实是睡不着)开始分析这个算法有什么问题。</p> <h2>你看看你们搞的这个算法</h2> <p>鉴于不保证贴上来的就是原始代码,所以像 <code>money &lt;= min ? 0.01: money</code> 这种明显不对的细节就不抠了。假设疼讯员工把这些细节都搞对了,来看看这个大概的想法有没有问题。出乎我意料的是……</p> <p><strong>定理 1</strong> 疼讯的这个算法每个人获得收益的期望是一样的。</p> <p><strong>证明</strong> 假设总共有 &#92;(M&#92;) 分钱分给 &#92;(p&#92;) 个人吧,令 &#92;(E<em>i&#92;) 为第 &#92;(i \in \left&#91;0, p\right)&#92;) 个领的人的期望值,&#92;(M</em>i&#92;) 是这个人实际得到的钱。那么我们就是要证明对于任意 &#92;(i&#92;),</p> <p>&#92;&#91; E_i = \frac{M}{p}. &#92;&#93;</p> <p>为了证明这个结论,我们使用数学归纳法。</p> <ol> <li>&#92;(p = 0&#92;) 时没意义。是的,我是开玩笑的。(感觉我这道题要没分了)</li> <li>&#92;(p = 1&#92;) 时由于那个 <code>if</code> 语句导致红包全给了一个人,&#92;(M<em>0 = M = \frac{M}{1} = E</em>0&#92;) 成立。</li> <li>假设结论对 &#92;(\left&#40;0, p\right&#41;&#92;) 都成立。显然当第零个人拿掉 &#92;(M<em>0&#92;) 的钱以后的这个红包等价于小一号的红包。新的红包的特性是 &#92;&#91; \begin{cases} M' &amp;= M - M</em>0 &#92;&#92; p' &amp;= p - 1 \end{cases}. &#92;&#93; 由于 &#92;(p \in \mathbb{N}&#92;) 且 &#92;(\left&#40;0, p\right&#41;&#92;) 这个集合要有整数在里面(你们说是不是啊),所以 &#92;(p \ge 2&#92;) 必定成立,所以 &#92;(p' \ge 1&#92;),还是有意义的。此时我们可以大胆地使用数学归纳法,知道对于剩下的 &#92;(i > 0,&#92;) &#92;&#91; E'<em>i = \frac{M'}{p'} = \frac{M - M</em>0}{p - 1}. &#92;&#93; 现在我们忽略小数点后第三位开始,也就是小于 1 分钱的多少不管,也就是忽略掉那个 <code>Math.floor</code> 的调用对我们结果的影响(即使考虑对结果的影响也不大),这样我们把这玩意儿简化成个连续的算起来快点,于是就有 &#92;&#91; \begin{cases} E<em>0 &amp;= \int</em>0^{\frac{2M}{p}} M<em>0 \frac{dM</em>0}{\frac{2M}{p}} = \frac{M}{p} &#92;&#92; E<em>i &amp;= \int</em>0^{\frac{2M}{p}} E'<em>i \frac{dM</em>0}{\frac{2M}{p}} = \frac{p}{2M(p - 1)} \int<em>0^{\frac{2M}{p}} (M - M</em>0) dM<em>0 = \frac{M}{p} \end{cases}, &#92;&#93; 因此所有的 &#92;(E</em>i&#92;) 都是相同的。(刚刚验算算错了,差点以为我这个定理是错的要被打脸了,刚刚谁说连续的算起来快点的)因此结论对 &#92;(p&#92;) 也成立。</li> <li>由数学归纳大法好可知结论成立。&#92;(\square&#92;)</li> </ol> <p>总而言之这个定理告诉我们至少每个人拿钱是公平的。下面我将展示这个做法的不公平之处。</p> <p><strong>定理 2</strong> 疼讯的这个算法每个人获得收益的方差是不一样的,也就是说风险是不一样的。</p> <p><strong>证明</strong> 见前面说的知乎那篇文章上有人也做了实验确实方差很不一样。事实胜于雄辩。&#92;(\square&#92;)</p> <p>顺便举个例子,不妨设 &#92;(M = 7, p = 3&#92;),那么显然按照这个算法第一个人永远也不会拿到 5 分钱,因为他的上限是 &#92;(\frac{2 \times 7}{3} = \frac{14}{3}&#92;) 分钱,但是只要它拿了 1 分钱,后面人就有可能两人中有一人拿到 5 分钱。这显然不公平对不对!</p> <h2>最公平的算法</h2> <p>下面我将证明一个超级公平的算法的存在性,并证明它极其公平。</p> <p><strong>定理 3</strong> 存在一个比上面一种方案更公平的分配红包方案。</p> <p><strong>证明</strong> 大家好,我的名字叫构造性证明。意思是上面这条定理只是来凑数的。下面给出这个方案。</p> <ol> <li>把所有钱换成 1 分钱硬币排成一行,我们有一行 &#92;(M&#92;) 个 1 分钱硬币;</li> <li>拿走 &#92;(p&#92;) 个硬币,我们还剩一行 &#92;(M - p&#92;) 个 1 分钱硬币;</li> <li>我们观察到这些硬币之间和两端有 &#92;(M - p + 1&#92;) 个位置,我们在之间随机插 &#92;(p - 1&#92;) 块板子隔开;是的,正如您所见这个方案完全剽窃高中组合数学的隔板法。</li> <li>把之前取走的 &#92;(p&#92;) 个硬币插入每块板子之间,这样我们就保证每块板子之间至少有一分钱。</li> </ol> <p>好了这就是分配的方案,接下来你只要按顺序发给来领的人就好了。</p> <p>下面我们就要证明这个方案比上面一种方案更公平。由于上面隔板的放法是完全随机的,它们从左到右的顺序并无所谓,而且与最后分配钱的方案是一一对应的,先取的人和后取的人没有任何差别,因此无论是期望还是方差都是相等的。&#92;(\square&#92;)</p> <p>你是不是觉得这个证明很骗人?我也觉得,那么欢迎你去弄个严格一点的……</p> <p>我们来分析并比较一下两个算法公平性以外的指标。两者性能基本一样,只是后者需要一个初始化,实现得好的话时间复杂度应该是 &#92;(O(p \log p)&#92;),空间复杂度 &#92;(O(p)&#92;)。查了一下一个 VIP 群也就 1000 人,CPU 时间上来说应该对服务器基本没有什么负荷。至于空间,为了避免重复领取,疼讯肯定得记载所有人的领取记录的吧,所以我觉得应该不会占用太多额外空间,更何况红包 1 天后就退款了,到时候这些数据想删就删了。</p> <h2>你好,这部分是我闲得慌</h2> <p>纯粹出于好奇,我想知道有没有一种不用初始化,也就是只有点击领取时才生成会领到多少钱(这样一方面一定程度上防黑,另一方面比较好玩),但仍然可以和上面那种方案同样公平的方案。</p> <p>联想到定理 1 的证明,我们可以每次领取红包时都做一次初始化,但只保留第一个红包多大,根据那个证明的思路,这样得到的方案还是公平的。这样我们就消灭了那块没用的缓存。</p> <p>然而每次都做初始化太浪费时间了,我们只要知道第一个红包多大就行了,剩下的我们并管不着。而第一个红包的大小取决于最前面的那块隔板,也就是我们生成的所有随机数中最小的那个。</p> <p>我定义 &#92;(P(n, m, k)&#92;) 为 &#92;(m&#92;) 个 &#92;(&#92;left&#91;0, n\right)&#92;) 的随机数中最小值 &#92;(\ge k&#92;) 的概率。</p> <p>&#92;&#91; P(n, m, k) = \frac{(n - k)^m}{n^m} = \left&#40;1 - \frac{k}{n}\right)^m. &#92;&#93;</p> <p>接下来求这个函数关于 &#92;(k&#92;) 的反函数,我们就得到了按 &#92;(k&#92;) 出现的概率产生随机的 &#92;(k&#92;) 的函数。经考察那些点中间的点后,我们会发现对于那些部分应使用向下取整。(事后想了下取整方向不对将产生 1 分钱的误差)因此最后表达式是:</p> <p>&#92;&#91; k(n, m, P) = \left\lfloor(1 - \sqrt&#91;m&#93;{P}) n\right\rfloor. &#92;&#93;</p> <p>大功告成,接下来只需代入值就可以得到最终的表达式了:</p> <p>&#92;&#91; M_0 = 1 + k(M - p + 1, p - 1, 1 - random) = 1 + \left\lfloor(M - p + 1)(1 - \sqrt&#91;p - 1&#93;{1 - random})\right\rfloor, &#92;&#93;</p> <p>其中,&#92;(random&#92;) 是一个 &#92;(\left&#91;0, 1\right)&#92;) 的随机实数,但我们要的是 &#92;(\left&#40;0, 1\right]&#92;) 的随机实数,所以拿 &#92;(1 - random&#92;) 代入。</p> <p>什么都搞定了,接下来就可以改掉不知道哪个实习工人写的代码了。</p> <p><code>java public static double getRandomMoney(LeftMoneyPackage _leftMoneyPackage) { // remainSize 剩余的红包数量 // remainMoney 剩余的钱 if (--_leftMoneyPackage.remainSize == 0) return (double) Math.floor(_leftMoneyPackage.remainMoney * 100) / 100; double splitters = _leftMoneyPackage.remainSize - 1, money = 0.01 + Math.floor((_leftMoneyPackage.remainMoney * 100 - splitters) * (1 - Math.pow(1 - new Random().nextDouble(), 1.0 / splitters))) / 100; _leftMoneyPackage.remainMoney -= money; return money; }</code></p> <p>P.S. 这个代码还未经测试,但理论上来说应该没写错吧……吧……</p> <p>P.P.S. 顺便拿这篇文章研究了下怎么将 &#92;(\LaTeX&#92;) 和我现在的博客引擎集成。效果真不错,除了貌似 Firefox 好像不支持的样子,该死。</p> https://zh.mygod.be/blog/fair-distribution-of-lucky-money/ Sun, 14 Feb 2016 06:27:14 +0000 Windows 10 VS 512MB 内存 (RAM) https://zh.mygod.be/blog/windows-10-vs-512mb-ram/ <p><strong>太长不看:</strong>微软声称 Windows 10 不让任何人落下,但事实是 Windows 10 仍然在低端设备上是团垃圾。对于老古董你应该考虑安装 Windows XP 或 Linux。<!--more--></p> <h2>写在开头的废话</h2> <p><a href="http://www.gsmarena.com/leaked_windows_10_docs_detail_restrictions_for_512mb_devices-news-12413.php">某个和我一样无聊的网站说</a>……</p> <blockquote> <p>微软声称 Windows 10 不让任何人落下,但有人建议说 512MB 内存的设备可能会受到功能方面的限制。泄漏的文档中详细说明了一个传统设备可能的用户体验。</p> </blockquote> <p>然而我重新读了一遍文章,发现这篇文章其实是在说 Windows 10 Mobile。所以我还是不知道 Windows 10 能不能跑在 512MB RAM 上。</p> <p>又搜了下发现有人已经干过这件事情了……</p> <ul> <li><a href="https://youtu.be/JKClyvKgtBY">Windows 10 技术预览版 x64 在 512MB RAM 下花了 18 分零 4 秒启动</a>,你真棒;</li> <li><a href="http://www.techradar.com/news/software/operating-systems/you-don-t-need-a-crazy-powerful-pc-to-run-windows-10-here-s-the-proof-1288287">这人也测试了下</a> Windows 10 32 位,但是专门选了一个超烂的 CPU 跑的。他的测试结果是惊人的流畅,但是你想干什么都得卡半天。</li> </ul> <p>最近我一不小心搞到一台还跑着 XP 的垃圾古董电脑。不知道为什么它连不上 Wi-Fi。讲道理我应该重装 XP,但是因为 <a href="https://www.microsoft.com/windows/zh-cn/xp/end-of-xp-support.aspx">Windows XP 的支持已经结束了</a>,所以我决定还是装 Windows 10,看看微软到底能不能不让任何人落下。</p> <h2>安装 Windows 10</h2> <p>为了节约时间我果断去网上搜了下,果然用官方镜像安装的话会因为可用内存少于 1GB 而拒绝安装。(真挑三捡四!)这对我们实验很不利,我们需要干掉它。</p> <p><a href="http://www.techcular.com/install-windows-7-vista-server-2008-bypass-low-ram-limit/">网上不知道哪个谁发现了 Windows 7 可以通过给 <code>winsetup.dll</code> 打补丁来暴力安装</a>。很不幸,这个补丁对 Windows 8 开始就无效了。</p> <p>以下是我想到的超级二的做法。</p> <p>第一步:模仿实体机配置建个虚拟机,但要给它 1GB 内存。</p> <p>第二步:把那该死的操作系统装上虚拟机。</p> <p>第三步:当安装程序提醒可以重启时,暴力地关闭虚拟机。</p> <p>第四步:给虚拟机再加块硬盘(并格式化),把光驱切换到一键 GHOST。</p> <p>第五步:把整个 Windows 10 硬盘复制到那块新硬盘的一个镜像文件里。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/2016-02-10_132812.png~original" alt="复制到 gho 中……" /></p> <p>第六步:把这个镜像文件覆盖掉古董机的主分区。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_162046.jpg~original" alt="复制到分区中……" /></p> <p>第七步:重启,然后啦啦啦!</p> <h2>第一次启动</h2> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_163354.jpg~original" alt="一切都很好" /></p> <p>过了 16 分钟……</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_165010.jpg~original" alt="过了 16 分钟" /></p> <p>哈哈哈成功了!&#x1F44D;&#x1F44D;&#x1F44D;</p> <p>然而似乎点什么都没有什么卵用。微软你真棒!&#x1F602;</p> <p>一开始我以为由于内存小电脑还在忙着干什么事,然而过了一会儿(等于 6 分钟)我发现这好像是某种 BUG。于是我愤怒地将 ~~拳头~~ 手指砸向鼠标,暴力地尝试了 Esc, Tab, 回车,上上下下左右左右 B A 等键位组合……然后好像我按的某个东西把这个 BUG 修好了。总之能用了。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_165604.jpg~original" alt="Yay" /></p> <p>完成配置后继续等待……</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_165639.jpg~original" alt="配置完成" /></p> <p>靠系统重启了。但愿不是什么 BUG。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_170018.jpg~original" alt="显然是 BUG" /></p> <p>显然这就是个 BUG。过了 3 分钟我又回到了熟悉的界面。我希望是我一开始太蠢不知道怎么做浪费了太多时间才导致了它重启。因此这次我果断开始暴力尝试,然后果然就可以动了。这次仅花了 2 分钟就完成了配置!</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_170210.jpg~original" alt="进展顺利" /></p> <p>配置完成后继续无聊的等待……</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_170318.jpg~original" alt="这不需要很长的时间" /></p> <p>这不需要很长的时间。好翻译。</p> <p>过了五分钟后……</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_170708.jpg~original" alt="它花费的时间比通常的要长一点,但很快就会准备就绪" /></p> <p>好吧这下你改口了,而且翻译翻得更烂了。为什么微软中国不请我去翻译呢……</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_170914.jpg~original" alt="又过了两分钟" /></p> <p>又过了两分钟,系统终于启动啦!而且鼠标用起来很流畅。算下来大概第一次启动花了 13 分钟(去掉我比较笨的那一次),还不错。</p> <h2>食用 Windows 10</h2> <p>食用 Windows 10 是个噩梦!启动完成后我基本第一件事就是去打开任务管理器,第二件事就是等任务管理器打开。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_171110.jpg~original" alt="91% 内存占用" /></p> <p>内存用了 91%,真不错。这辣鸡果然神马都炒鸡慢。这老古董根本没法用。我尝试禁用了 Windows Defender,除了字体反锯齿(没有能看?)以外的所有可视化效果等,并安装了一切可用更新。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_174159.jpg~original" alt="禁用一切" /> <img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160211_014142.jpg~original" alt="还是用了一大堆内存" /></p> <p>顺便一提,Windows 10 还不自带这老古董的无线网卡驱动。因此我还得搞根网线插上去。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_171709.jpg~original" alt="没有 Wi-Fi" /> <img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_174406.jpg~original" alt="安装更新" /></p> <p>顺便一提这老古董电池也不太能被使用。在找网线的过程中这东西就没电关机了。&#x1F602;</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160210_173751.jpg~original" alt="辣鸡电池" /></p> <p>搞到最后仍然没法用,不得不放弃。鉴于我的祖父母肯定学不会 Linux,我网上随便找了个 Ghost XP SP3 刷了上去应付了事了。哈哈哈。</p> <p><img src="https://i1264.photobucket.com/albums/jj483/MygodStudio/Blog/IMG_20160211_165615.jpg~original" alt="大家都很高兴" /></p> <h2>结论</h2> <p>重装玩多了也没劲。不过给老古董装 Windows 10 还是挺好玩的,如果把什么都乱卡的那部分忽略不计的话。</p> <p>从这篇博客我们知道了什么?</p> <ul> <li>微软关心 512MB 内存的手机,但让 512MB 内存的古董电脑自生自灭。谢谢微软!</li> <li>微软中国需要一个更好的翻译,例如我。</li> <li>不幸的是看起来老古董只能装 Windows XP 或 Linux。</li> <li>……</li> <li>我也不知道,你学到了什么?</li> </ul> <p>为了感谢你看到了这篇博客的最后(或者你可能只是看了开头然后就跳到了最后看看评论啊什么的,一不小心瞄到了这里),你可以去玩玩<a href="https://win95.ajf.me">这个浏览器版 Windows 95</a>!另外这篇文章图像质量很差,因为我就是懒得截图。</p> https://zh.mygod.be/blog/windows-10-vs-512mb-ram/ Fri, 12 Feb 2016 22:46:23 +0000 推荐游戏 https://zh.mygod.be/blog/recommended-games/ <p>我在过去的 2.5 年曾在老博客上每个月推荐五个游戏,总共也就推荐了六七次吧。但我要承认就我自己来看,我觉得我推荐的游戏质量在逐渐下降。如今我等了这么久,那老博客终于死掉了,我们也该尝试一点有意思的新格式了。</p> <p>我<a href="https://gist.github.com/Mygod/7565dfb65c1dd3542203">在 GitHub Gist 上列了一张我个人比较喜欢的游戏清单</a>,并将时不时去更新。<!--more-->我的游戏清单里并不包含 FPS/动作类的游戏,因为这种高玩的游戏我玩不像。</p> <p>一开始的那些游戏是我玩过而且如果有时间和机会还会再去玩的游戏。如果你不希望花很多时间玩游戏,你也许应该试试这些。</p> <p>但如果你超级无聊,只是想找些好游戏玩玩打发时间,而且你也玩过了傻瓜难免的所有游戏,看看“More”里面的游戏。这些就个人看来可能没有傻瓜难免那些那么好,但我觉得还是值得一玩的。</p> <p>这个清单会不断更新,我可能会加进我发现的新游戏,也可能删掉我以后看来并没有那么好玩的游戏。总之欢迎使用。:-)</p> <p>另外我还给我的博客加了音乐,避免读者看着看着因为太无聊睡着了,音乐选择完全基于我个人喜好。不用谢!</p> https://zh.mygod.be/blog/recommended-games/ Fri, 12 Feb 2016 19:54:49 +0000 Mygod 象牙塔™ 移动到了主域名! https://zh.mygod.be/blog/moved-to-main-site/ <p>嗨!你可能眼睛过于大没看到标题:</p> <blockquote> <p>Mygod 象牙塔™ 现已搬迁到主站!<!--more--></p> </blockquote> <p>所以呢?由 <a href="http://vhostfull.com">V Host Full</a> 托管的<a href="http://blog.mygod.tk">老站</a>尚能饭,不过一饭三遗矢。等它死得差不多了也就完了。由于老站的内容要搬到这里过于麻烦,所以就不管了。因此如果你现在还想要从那里得到什么的话就抓紧吧。</p> <p>新站上的内容完全使用未来的潮流 Markdown 语言编写,不信自己查看页面源码。渲染采用了 <a href="https://github.com/chjj/marked">marked</a> 和 <a href="https://github.com/PrismJS/prism">Prism</a>。文章目录使用 JSON 维护。</p> <p><code>javascript { "current-entry": "moved-to-main-site" }</code></p> <p>欢迎您在新站上不知所措地乱点,或是无聊地去参观史前老站。</p> https://zh.mygod.be/blog/moved-to-main-site/ Mon, 27 Jul 2015 08:00:00 +0000