<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>AFAIK</title>
  
  <subtitle>Valar Morghulis</subtitle>
  <link href="https://www.roosephu.net/atom.xml" rel="self"/>
  
  <link href="https://www.roosephu.net/"/>
  <updated>2025-09-08T03:47:01.000Z</updated>
  <id>https://www.roosephu.net/</id>
  
  <author>
    <name>roosephu</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>SIGYAO30 2025 @ Denver</title>
    <link href="https://www.roosephu.net/2025/09/07/SIGYAO30-Denver/"/>
    <id>https://www.roosephu.net/2025/09/07/SIGYAO30-Denver/</id>
    <published>2025-09-08T03:47:01.000Z</published>
    <updated>2025-09-08T03:47:01.000Z</updated>
    
    <content type="html"><![CDATA[<p>今年的 SIGYAO30 就决定放在 Denver 啦，</p><p>2025 年的<del>第一场</del> SIGYAO30，比以往时候鸽的更多一些，最后只有十一个人过来了……东岸的人尤为鸽，为什么鸽也能出现了人传人现象？汤姐一如既往的鸽，她吐槽为什么每次 SIGYAO30 都能精准选在她生理期。胡老师本来非常积极，但是出发前一天，胡老师吃坏肚子了……令人惊奇的是鸽王 xpd 居然来了，另外一个亮点是毕老师不远千里迢迢从 HK 赶过来了，第一次参加线下的 SIGYAO30。</p><span id="more"></span><h1 id="section">7.25</h1><p>晚上我、dfh 和脸哥一起坐飞机从 SJC 前往 DEN，刚好 jls、zyw 和我们差不多时间到，而 zyw 要租一辆车，于是我们就打算蹭 zyw 的车了。路上 A 家员工 jls 不停给我们宣传他作为带路党的种种经验，甚至萌生了搞一个赛博拱猪大赛的想法。当然，今天比较晚了，我们就没搞赛博拱猪，我、cxq、cls、xpd 打了几局人类拱猪。</p><p>cls 和毕老师到的比较晚，我觉得还是留个人等着他们比较好。最后毕老师三点钟才到，聊了会天后我们就去睡觉了。</p><h1 id="rocky-mountains-national-park">7.26: Rocky Mountains National Park</h1><p>按照黄老师的计划，我们今天在 Rocky Mountains National Park。出门之前我看了一下温度，好家伙最高 35 度，那我立刻把长裤换成短裤，外套也懒得带了。</p><p>好了又到了选择司机的时间。这次我们有三个司机：</p><ul><li>人挡杀人车挡超车高速上来去自如的黄老师</li><li>稳如老狗波澜不惊慢速平稳还省油的 zyw</li><li>奇思妙想妙语连珠多线程并行处理的 cxq</li></ul><p>上午去爬了两个 trail：<span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxsdHJhaWxzLmNvbS90cmFpbC91cy9jb2xvcmFkby9iZWFyLWxha2UtbmF0dXJlLXRyYWlsLS0z">Bear Lake Loop</span> 和 <span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxsdHJhaWxzLmNvbS90cmFpbC91cy9jb2xvcmFkby9lbWVyYWxkLWxha2UtdHJhaWw=">Emerald Lake Trail</span>。第一个 trail 很简单，围着湖走一圈就完事了。 Bear Lake 其实没有什么特别让我惊艳的地方，反正也看不到熊 →_→ ……第二个 trail 就好一些，一路上依次经过 Nymph Lake、Dream Lake 最后到达 Emerald Lake。整个 trail 难度也不大，只要休息的足够多，总能上去的。尽管如此，我们还是不断损兵折将。在 Dream Lake 之前损失了爬山健将脸哥和董先生，Dream Lake 损失了 cls 和 jls，最后走走停停，七人成功抵达 Emerald Lake。由于这地方海拔已经不低了，有三千多米，所以有些人已经开始有一定的高反了。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4058.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4058.webp" alt="<p>Bear Lake</p>"/>      <figcaption aria-hidden="true"><p>Bear Lake</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4079.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4079.webp" alt="<p>Dream Lake</p>"/>      <figcaption aria-hidden="true"><p>Dream Lake</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4085.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4085.webp" alt="<p>Emerald Lake</p>"/>      <figcaption aria-hidden="true"><p>Emerald Lake</p></figcaption>    </div>  </div></figure><p>爬完山后我们开始思考午饭问题。这次准备吃个简单的，中午在 Estes Park 吃了个 McDonald's。由于毕老师没有在美国长期呆过，他的手机号没有注册过 McDonald's 账号，这可馋坏了 cxq：新客户优惠不用白不用啊！所以尽管 cxq 已经吃的差不多了，可是他还是怂恿着毕老师整了一个账号，以超低价拿到了个啥汉堡，这才心满意足的吃饱了。</p><p>吃完饭后我们准备去搞个 road trip，从 Estes Park 由 Fall River Entrance Station 进入公园。出发的时候有几个跟着黄老师走，有几个人跟着 zyw 走。可是开着开着就有人在群里喊：有人知道 cxq 在哪里吗？我们顿感不太妙，一顿沟通后才发现 cls 和 jls 成为了街友。本来三辆车十一个人应该是一个 4+4+3 的配置，但是 cxq 出于某种原因独自走了，留下 cls 和 jls 在刚刚的 McDonald's 徘徊。更加蛋疼的是，cxq 车上没人，所以都不好联系到他。万幸的是，cxq 的车就在我们后面，所以等我们开了十几分钟到公园门口的时候，我们车先检票进公园，然后在路边停着等着 cxq。cxq 进来后和我们一 bibi 才意识到出问题了。原来当我们离开的时候，我们挥手致意，而 cxq 将其理解成了车上有五个人，于是 cxq 就 assume 了一个 5+5+1 的配置……于是 cxq 马上调转车头前往 Estes Park，捡上 cls、jls 这街友二人组……</p><p>之后我们就开始了 road trip。这才是真正让我后悔早上的决定。我们住在山下，我看的温度是山下的温度，然而我们开车越开海拔越高，最后海拔逼近一万英尺，而且山顶风巨大。山区的天气真是让人捉摸不定，上午我看还挺晴朗的，下午就阴了，甚至下起了雨。我真感觉自己就像个奥特曼，下车约等于变身，出去被风一吹就开始闪红灯，红灯闪的差不多了得赶紧上车充电。这一路上有两处风景让我印象深刻。一个是阳光穿过乌云，略有一番风味，另一个是在路上看到的一堆驼鹿（moose），而且可以离我们超级近，就在车旁边，赶紧拍了几张照片。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4107.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4107.webp" alt="<p>散落的阳光，穿过了云</p>"/>      <figcaption aria-hidden="true"><p>散落的阳光，穿过了云</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4110.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4110.webp" alt="<p>天然聚光灯</p>"/>      <figcaption aria-hidden="true"><p>天然聚光灯</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4131.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4131.webp" alt="<p>Moose</p>"/>      <figcaption aria-hidden="true"><p>Moose</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4142.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4142.webp" alt="<p>Moose</p>"/>      <figcaption aria-hidden="true"><p>Moose</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4146.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4146.webp" alt="<p>Moose</p>"/>      <figcaption aria-hidden="true"><p>Moose</p></figcaption>    </div>  </div></figure><h1 id="board-games">7.27: Board Games</h1><p>由于昨天的<del>多人运动</del>大 hiking，今天我们决定休闲一下。中午一行人准备去 Costco 买点吃的，但是我就比较懒，没有跟着去了，随便吃了点零食。几个还在房间里的人就凑在一起打桌游。</p><p>zyw 带来了一个扩展版 UNO。由于 UNO 规则简单明了清晰易懂，所以我们就从 UNO 开始。这个扩展版 UNO，额外加了几张卡。有一张牌的效果是让下家需要制定一种颜色，需要一直摸牌直到摸到这种颜色为止。在这张牌上，dfh 不愧是猜色之王，一猜一个准，摸一张就结束。另外除了平常的 +2、+4 外，还有更加刺激的 +6、+10，另外增加了手牌上限，一旦超过 25 张就直接暴毙。+2、+4 看起来慈眉善目，但是一旦 +6、+10 起来了，通常会引起一阵腥风血雨。一场战争，通常是由简简单单的 +2 开始的，通过 +4、+6 不断累积，最后开始一起甩 +10 核弹，击鼓传花，直到有人接不下去。最疯狂的一次是，我们不断累加，最后堆到了 +30，本来那个人只剩两张牌了，直接原地去世。</p><p>房间里还有房东留下的另一个桌游 <span class="exturl" data-url="aHR0cHM6Ly93d3cuZGF5c29md29uZGVyLmNvbS9nYW1lL3RpY2tldC10by1yaWRlLw==">Ticket to Ride</span>。我、zw、hyz 三个人玩，zw 向我们介绍了规则，并教我们一个经验：基本上第一手都是抽任务。玩的第一把我由于一个任务没完成反而倒扣了不少分，于是我就想到了另外一个玩法：放弃任务只修路，前期囤牌，中期修路，只在最后一手随便拿两张任务，看看是否有已经做完的，没做完的就算了……玩了两把之后发现这样打效果居然还不错，似乎卡出了一个 bug……</p><p>cxq 回来后，自然又开始宣传他最爱的游戏：<span class="exturl" data-url="aHR0cHM6Ly9ib2FyZGdhbWVnZWVrLmNvbS9ib2FyZGdhbWUvODk0Ni9kYS12aW5jaS1jb2Rl">达芬奇密码</span>。然而这游戏思考起来太烧脑了，我随便一想就猪脑过载……</p><p>晚上我们去吃了一个自助火锅，印象不深，就这样吧……jzp 似乎兴趣不大就没去。等我们回来的时候，他的赛博拱猪 setup 已经大功告成！接下来就是 DeepSeek R1、Gemini 2.5 Pro、ChatGPT o3、Claude Opus、Qwen3、Kimi K2 以及人类玩家 dfh 的大战！</p><p>Gemini 2.5 Pro 我愿尊称其为戏精，下面是 Gemini 2.5 Pro 语录：</p><blockquote><p>很不幸，我的计划失败了。</p><p>我的天！这绝对是游戏中最关键、最戏剧性的一轮！</p><p>我的策略取得了巨大成功，现在局势对我非常有利。</p><p>我的策略再次完美奏效！</p><p>通过精准的判断和冒险，我成功地将一手充满危险的牌打成了绝对的胜局。</p><p>天哪，我的希望落空了。</p></blockquote><p>再看一下 Gemini 2.5 Pro 的某一次发言：</p><blockquote><p>啊！机会终于来了！</p><p><strong>当前轮情况 (第八轮):</strong></p><ul><li><strong>领出花色:</strong> ♦️ (方片)</li><li><strong>已出牌:</strong><ul><li>玩家 A: ♦️K</li><li>玩家 B: ♦️A</li><li>玩家 C: ♠️8 (垫牌 - C 已方片缺门)</li></ul></li><li><strong>轮到:</strong> 我 (玩家 D)</li></ul><p><strong>我的分析:</strong></p><ol type="1"><li><strong>我的处境:</strong> 我在第四轮就已经成功在方片上缺门。现在玩家 A 领出了方片。</li><li><strong>唯一的选择:</strong> 这是我整局游戏一直在等待的、梦寐以求的时刻。我终于可以把手上那张最致命的 <strong>♠️Q (猪)</strong> 垫出去了！</li><li><strong>结果:</strong> 目前玩家 B 的♦️A 是最大的，他注定要赢下这一轮。我把♠️Q 垫给他，他将获得 - 100 分。</li></ol><p>我所有的忍耐和铺垫，都是为了这一刻。</p><p><strong>我决定出牌:</strong></p><p><strong>♠️Q</strong></p></blockquote><p>最后结果是戏精 Gemini 2.5 Pro 大胜，其他 model 都挺菜的……</p><p>（题外话：SIGYAO30 结束后，cxq 非常 excited，想搞一个 board game arena，<span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmdvb2dsZS90ZWNobm9sb2d5L2FpL2thZ2dsZS1nYW1lLWFyZW5hLw==">结果没过两天就被 Google scoop 了</span>……）</p><p>晚上我们开始整理东西。我在客厅里随口吹了一句口哨，旋律是万青《秦皇岛》开头那句嘹亮的小号，突然之间 zyw 凑过来，问我是不是在吹万青。我一听那可不，找到同好了！然后我们就一起愉快地讨论起了万青。他之前给《冀西南林路行》做了个视频，根据他对每句歌词的理解，让 Gemini 生成 prompt，然后再去做 image generation，最后合在一起整出了一个<span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1nYjdPUEJpaVAzcw==">视频</span>。他说他最近在听 King Crimson，我说这不巧了吗，我也在听，我还听 Pink Floyd。然后我给他看了一下我的歌单，还听了一下我喜欢那首 Summer '68，和 Pink Floyd 其他的歌风格挺不一样的。</p><h1 id="red-rock-and-downtown">7.28: Red Rock and Downtown</h1><p>早上洗漱后我们就来到了 Red Rock Amphitheater，不得不说这里的石头挺好看的，但是问题是这里没有好看的冰箱贴。我觉得只要搞一个石头一样的冰箱贴就很好看了，但是他们喜欢在音乐节上做文章，搞出的冰箱贴入不了我的法眼。我们跑了两个 gift shop 都没找到好看的冰箱贴……</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4170.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4170.webp" alt="<p>Ship Rock。这张照片我很满意，背景的云和前景的石头都有各自的纹路</p>"/>      <figcaption aria-hidden="true"><p>Ship Rock。这张照片我很满意，背景的云和前景的石头都有各自的纹路</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4178.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4178.webp" alt="<p>某不知名的石头</p>"/>      <figcaption aria-hidden="true"><p>某不知名的石头</p></figcaption>    </div>  </div></figure><p>本来我们还想去附近的 Dinosaur Ridge 看恐龙，但是去了之后发现有点失望，就是路边有几块石头，里面模模糊糊能看到有些不一样的痕迹，有些就是就是恐龙化石，但是实在看不出个啥……</p><p>中午吃的一家广式烧腊，店主自豪地说这是整个 Colorado 最好吃的烧腊店，并且经过毕老师认证，这家店的水平是能在香港存活下去的。</p><p>之后我们就去 Denver downtown 上闲逛。首先去了 Colorado Convention Center，主要是去参观它的 Big Blue Bear，甚至<span class="exturl" data-url="aHR0cHM6Ly9kZW52ZXJjb252ZW50aW9uLmNvbS8=">网站</span>的 favicon 都是大蓝熊。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4183.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4183.webp" alt="<p>Big Blue Bear</p>"/>      <figcaption aria-hidden="true"><p>Big Blue Bear</p></figcaption>    </div>  </div></figure><p>之后就走马观花的看了一下，主要想沿着 <span class="exturl" data-url="aHR0cHM6Ly93d3cuZGVudmVyLm9yZy90aGluZ3MtdG8tZG8vYXR0cmFjdGlvbnMvMTZ0aC1zdHJlZXQtbWFsbC8=">16th Street</span> 走一走，没想到脸哥走到一半又走不动了，主要也是比较热……于是脸哥就找了个星巴克开始原地修整。说实话我也感觉意思不大，之后我们就坐了免费的 the 16th Street Free Ride 去了 Union Station。Union Station 比较有趣的一点是，它刚好在西经 105° 上（105°00′00″W），门口还有一根线标着西经 105°。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4188.webp">      <img data-src="/images/2025-09-07-SIGYAO30-Denver/IMG_4188.webp" alt="<p>Denver Union Station</p>"/>      <figcaption aria-hidden="true"><p>Denver Union Station</p></figcaption>    </div>  </div></figure><p>离开 Denver 前最后去逛的地方是 Rocky Mountain Arsenal National Wildlife Refuge，本来听说里面有野牛，但是我们在里面逛了半天，啥也没见到，最后不死心，终于在路边找到了几只土拨鼠……想想也 make sense，这么热的天，野牛当然会躲在阴凉处啦，为何要出来闲逛……</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今年的 SIGYAO30 就决定放在 Denver 啦，&lt;/p&gt;
&lt;p&gt;2025 年的&lt;del&gt;第一场&lt;/del&gt; SIGYAO30，比以往时候鸽的更多一些，最后只有十一个人过来了……东岸的人尤为鸽，为什么鸽也能出现了人传人现象？汤姐一如既往的鸽，她吐槽为什么每次 SIGYAO30 都能精准选在她生理期。胡老师本来非常积极，但是出发前一天，胡老师吃坏肚子了……令人惊奇的是鸽王 xpd 居然来了，另外一个亮点是毕老师不远千里迢迢从 HK 赶过来了，第一次参加线下的 SIGYAO30。&lt;/p&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.roosephu.net/categories/Life/"/>
    
    
  </entry>
  
  <entry>
    <title>Yosemite 小游记</title>
    <link href="https://www.roosephu.net/2025/06/21/trip-to-Yosemite/"/>
    <id>https://www.roosephu.net/2025/06/21/trip-to-Yosemite/</id>
    <published>2025-06-21T13:12:03.000Z</published>
    <updated>2025-06-21T13:12:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>几个星期前，一位小伙伴 wzc 问我有啥娱乐活动。聊着聊着，我们就约起了 Yosemite。于是几天前，我们终于趁着还不需要预约的时候，赶个 ddl 去了趟 Yosemite.</p><p>TL;DR 实在是妙啊！不愧是国家公园啊！瀑布量大管饱。</p><span id="more"></span><p>先放一张 Yosemite 的地图，我觉得这张地图是最有用的：原图在<span class="exturl" data-url="aHR0cHM6Ly9ucGdhbGxlcnkubnBzLmdvdi9IRkMvQXNzZXREZXRhaWwvMjdkMmE0MGUtMWUwYS00MTQ4LWI3ZmItYTgyMTkxZmM2N2Iz">这里</span>。</p><figure><img data-src="https://npgallery.nps.gov/HFC/GetAsset/27d2a40e-1e0a-4148-b7fb-a82191fc67b3/proxyhires.jpg" alt="Yosemite 地图" /><figcaption aria-hidden="true">Yosemite 地图</figcaption></figure><h1 id="周五">周五</h1><p>我们周五请了一天假。之前查攻略的时候有人说登山杖必备，我还买了对登山杖。<del>差生文具多。</del>中午一起在利苑吃了顿饭，然后就在附近的亚超采购了一波零食，就准备跑路了。本来还想买点水果，但是看了一下亚超的水果良莠不齐，于是去 Costco 买了一袋橘子算了。我们估计 Yosemite 那边没啥可吃的，这些差不多就是我们这两三天的饭了。</p><p>路上好像没啥可说的，直奔宾馆。宾馆我们就订的门口的 Yosemite View Lodge，优点就是离门口特别近，比里面的 The Ahwahnee 便宜好订。吐槽一下，这里的网络真烂。</p><h2 id="glacier-point">Glacier Point</h2><p>我们一开始计划是到得早就去 Taft Point 看一看，到得晚就直奔 Glacier Point，中间可以在 Tunnel View 那里看看风景，因为这三个地方在一块，和其他的地方不挨着。虽然地图上看 Glacier Point 离 Yosemite Valley 挺近的，但是除非你走 <span class="exturl" data-url="aHR0cHM6Ly93d3cubnBzLmdvdi95b3NlL3BsYW55b3VydmlzaXQvZm91cm1pbGV0cmFpbC5odG0=">Four Mile Trail</span>，不然只能从 Tunnel View 那里开车过去，路上要一个小时。如果你真的想走 Four Mile Trail 从山脚走上来，单程爬升将近 1km，那我还是服气的……</p><p>然而我们将近七点才到宾馆，这个时候还逛什么 Taft Point 呀，能赶上 Glacier Point 的日落就不错了。Tunnel View 那里路过都没停，怕晚了赶不上 Glacier Point 的日落。尽管如此，等我们到 Glacier Point 的时候还是有点晚，太阳已经下山了，但是没有完全落下，还有 blue hour。从这个角度看过去，Half Dome 的那个面真是被削得好平……</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3731.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3731.webp" alt="<p>Blue hour 时刻的 Glacier Point 景色。左边三座山峰的样子很像 emoji</p>"/>      <figcaption aria-hidden="true"><p>Blue hour 时刻的 Glacier Point 景色。左边三座山峰的样子很像 emoji</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3729.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3729.webp" alt="<p>Half Dome 来个大特写</p>"/>      <figcaption aria-hidden="true"><p>Half Dome 来个大特写</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3732.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3732.webp" alt="<p>Glacier Point 上还有座小房子，于是试图把它和 Half Dome 拍在一起</p>"/>      <figcaption aria-hidden="true"><p>Glacier Point 上还有座小房子，于是试图把它和 Half Dome 拍在一起</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3741.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3741.webp" alt="<p>月光，篝火</p>"/>      <figcaption aria-hidden="true"><p>月光，篝火</p></figcaption>    </div>  </div></figure><p>等我们下山到宾馆，已经将近十点了，随便吃了点小零食，洗洗睡了……</p><h1 id="周六">周六</h1><p>大早起！七点钟就起来了！今天是爬山的一天！在我们宏伟的计划中，上午拳打 Upper Yosemite Fall Trail，下午脚踢 Mist Trail！<del>起码是这么计划的……</del></p><p>我们大概七点钟起来，这可能是我几个月来起得最早的一次了。一开始想去宾馆吃个早饭，但是不出意外地要出意外了，去了之后才意识到没包早饭，$21 一位，告！辞！于是我们就出发去园区了。</p><p>园区里停车感觉是个问题……在我们宏伟的计划中，我们应该把车停在 Curry Village 那里，因为 Mist Trail 下来估计已经晚上八九点了。要是把车停在 Upper Yosemite Fall Trail 那里，万一没 shuttle 了，还要走一大段到停车的地方，怕是要精尽人亡。当然，不出意外地要出意外了，交警不让我们过去那边，说车位满了……行吧，想必确实那地方人挺多的。但是我们运气不错，在不远处一路边上找到了一个小停车位，wzc 闪转腾挪，硬是将车停了进去，我们车后的小姐姐还问我们要不要她把车往后面推一点，不然我们都开不了后备箱……停了车后，我们在车上吃了点美味小零食，然后就准备出发了！停车的位置离 Housekeeping Camp 很近，我们就走了过去开始等车。坐绿线坐到 Camp 4，我们就来到了 Upper Yosemite Fall Trailhead。开爬！</p><h2 id="upper-yosemite-fall">Upper Yosemite Fall</h2><p>便舍车，从口入。初极狭，才通人，复行数十步，就不出意外地要出意外了：我已经不行了……一开始没把握好力度，起猛了。然后就感觉不太对劲，甚至有种想呕吐的感觉，不行了，得休息一下。再一看进度，嘿嘿，刚走 10%……wzc 还在安慰我说可能是高反，不过这 Yosemite 海拔就一千米，哪来的高反……</p><p>休息了一会，喝了点水，继续爬，又不行了，于是只好爬一点停一阵，走三步歇三分钟。wzc 不停给我减负，一开始帮我背小零食，然后帮我背水，最后直接把包也拿过去了，然后我还用起了刚买的登山杖，但我还是累的气喘吁吁，大感不妙。我每走几步路就看一眼进度。登顶的 elevation gain 有 1000 米，我爬到 300 米的时候就感觉到点了要瘫了，太难了妈妈我要回家……</p><p>不过说句公道话<del>（我要开始拉偏架了）</del>，这段路就是难，5km elevation 820m（不是 elevation gain）。路上也有其他人在休息。我们就和旁边休息的人聊聊天。有一家子一起来爬这个 trail，还有两个十岁左右的小盆友，咋看起来脸不红气不喘的，强者如斯吗……我还和一个长者聊了聊，长者表示他们这是他们在 Yosemite 的最后一天，之前爬了很多，今天打算爬到 Oh-My-Gosh Point 就行了。等等，Oh-My-Gosh Point？这是啥？我赶紧掏出地图看了看，原来在一半处，有个地方叫做 Oh-My-Gosh Point，是看 Upper Yosemite Fall 的绝妙去处，过了 Oh-My-Gosh Point 后再下次看到完整的瀑布就要登顶了。太妙了，就决定是它了！</p><p>最后我们终于到了 Oh-My-Gosh Point，景色还是非常壮观的！然后我又看了一眼地图，好的，之后就是一路上坡，2.3km 爬升 516m，那就不得不告辞了……</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3753.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3753.webp" alt="<p>Upper Yosemite Fall Trailhead。这时候的我还不知道即将面对什么</p>"/>      <figcaption aria-hidden="true"><p>Upper Yosemite Fall Trailhead。这时候的我还不知道即将面对什么</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/upper-yosemite-fall-trail-map.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/upper-yosemite-fall-trail-map.webp" alt="<p>Upper Yosemite Fall Trail 地图。我们从 1 出发，2 是 Columbia Rock，3 是 Oh-My-Gosh Point，5 是终点。地图上 1-2 这一段全是 zigzag，于是从此我患上了 zigzag PTSD，看到长这样的路逃跑也是可以的呢……过了 4 后还可以走另外一条路去 Yosemite Point</p>"/>      <figcaption aria-hidden="true"><p>Upper Yosemite Fall Trail 地图。我们从 1 出发，2 是 Columbia Rock，3 是 Oh-My-Gosh Point，5 是终点。地图上 1-2 这一段全是 zigzag，于是从此我患上了 zigzag PTSD，看到长这样的路逃跑也是可以的呢……过了 4 后还可以走另外一条路去 Yosemite Point</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3754.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3754.webp" alt="<p>Columbia Rock，拍 Yosemite Valley 的好地方</p>"/>      <figcaption aria-hidden="true"><p>Columbia Rock，拍 Yosemite Valley 的好地方</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3766.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3766.webp" alt="<p>Oh-My-Gosh Point 拍的 Upper Yosemite Fall，看着挺秀气的，但是感觉比 Lower Yosemite Fall 要大</p>"/>      <figcaption aria-hidden="true"><p>Oh-My-Gosh Point 拍的 Upper Yosemite Fall，看着挺秀气的，但是感觉比 Lower Yosemite Fall 要大</p></figcaption>    </div>  </div></figure><h2 id="lower-yosemite-fall-yosemite-valley">Lower Yosemite Fall, Yosemite Valley</h2><p>决定了在 Oh-My-Gosh Point 调头后，今天的 hiking 之旅就简单很多了。我们下山的速度明显比上山快，很省力。这个时候登山杖就发挥了它最大的作用。用之前我还觉得，它只有那一个尖尖，能戳的稳吗，用了之后发现比我想象中的稳得多，即使有些细沙也不会滑。</p><p>从 Upper Yosemite Fall Trail 上下来后我们就走路去了 Lower Yosemite Fall。这路上就不难了，都是平地，主要困难在于天气很热，我当时有点缺水。我就带了两瓶水，已经喝完了，真是个大失误。我甚至觉得脑袋有点晕晕的，可能有点小中暑或者脱水。</p><p>Lower Yosemite Fall 稍微逛逛就差不多了，但是这地方确实比较凉快，所以我们在这里<del>又</del>休息了一会。接下来就讨论该去哪里了。wzc 建议我们可以在中间的大草坪走一圈，然后去 Exploration Center 那里休息一下，然后可以走一下昨天没有去的 Taft Point。中间这个大草坪草挺深的，往远处看可以看到 Half Dome 的正面，非常壮观。</p><p>到了 Exploration Center，本来我们以为快一点了，过了饭点了，是不是人会少一些，但是里面还是人山人海，我们运气特别好，上来就有个位置空了，我们就在那里坐着。我实在是困了，就趴下来睡了一会。等我醒来后，wzc 也困了，于是他也趴在桌子上睡了一会，就这样，两人在 Exploration Center 轮流睡觉白嫖了两个小时座位。到接近三点的时候，我们就可以出发了。Exploration Center 附近有个 gift shop 可以买冰箱贴、明信片、拼图。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3769.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3769.webp" alt="<p>Yosemite Falls</p>"/>      <figcaption aria-hidden="true"><p>Yosemite Falls</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3776.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3776.webp" alt="<p>Lower Yosemite Fall 近景</p>"/>      <figcaption aria-hidden="true"><p>Lower Yosemite Fall 近景</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3750.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3750.webp" alt="<p>Lower Yosemite Fall 远景</p>"/>      <figcaption aria-hidden="true"><p>Lower Yosemite Fall 远景</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3780.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3780.webp" alt="<p>从 Cook's Meadow 看 Half Dome</p>"/>      <figcaption aria-hidden="true"><p>从 Cook's Meadow 看 Half Dome</p></figcaption>    </div>  </div></figure><h2 id="tunnel-view-taft-point-glacier-point">Tunnel View, Taft Point, Glacier Point</h2><p>回到车上猛喝一瓶水，终于解渴了。然后我们就开车前往 Taft Point。路过 Tunnel View，再来一张照片。今天我们就有时间在 Tunnel View 下车看看了。这个景点不需要多久，几分钟就可以搞定，基本上就是一张照片的事，但是非常出片，因为一次性可以看到三个景点：El Capitan，Half Dome 和 Bridalveil Fall。wzc 告诉我，看 Tunnel View 的正确姿势是从 Wawona Tunnel 的另一端进去，经过长长的暗无天日的隧道后，豁然开朗，迎接你的是 Yosemite 的壮观峡谷，El Capitan、Half Dome、Bridalveil Fall 依次呈现在你面前，视觉效果更为震撼。NPS 有个<span class="exturl" data-url="aHR0cHM6Ly93d3cubnBzLmdvdi95b3NlL2xlYXJuL25hdHVyZS9zY2VuaWMtdmlzdGFzLmh0bQ==">示意图</span>。</p><p>对于 Taft Point，我们计划走的 trail 是<span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxsdHJhaWxzLmNvbS90cmFpbC91cy9jYWxpZm9ybmlhL3NlbnRpbmVsLWRvbWUtYW5kLXRhZnQtcG9pbnQtbG9vcA==">这个</span>，估计两个小时左右。这个 trail 还是非常简单的，主要是没啥海拔爬升。我们先去了 Taft Point，这里的特色是陡峭的悬崖，我们 literally 站在悬崖边上，一失足成千古恨的那种。Taft Point 最高处，在悬崖峭壁上，有一个栏杆，可以让游客靠着来一个自拍，栏杆都被压弯了……在去 Sentinel Dome 的路上没多久就感觉有点小雨，一想起码还要一个小时才能返回，我们就半路折返了。另外需要注意的是这 trail 上有一堆蚊子，真的是拼命往身上扑，由于 trail 难度不大，我们就没怎么休息，但是蚊子仍然可以一路追过来。我有时候感觉脖子后似乎有点小动静，伸手一拍，再拿过来一看，好大一只蚊子，就这样拍死了三四只……</p><p>Taft Point 离 Glacier Point 不远，所以我们又去了一趟 Glacier Point。景色和昨天差不多，但是今天晚上就有很多云，所以甚至不如昨天……我们没待多久就准备跑路了，一来昨天已经看得差不多了，二来我们还想着能不能去酒店那里吃点饭。下山路上倒是看到了好看的日落，可惜不好拍。如果这个时候还在 Glacier Point 应该风景不错吧。</p><p>我觉得这个安排很合理：下午到达 Yosemite，可以去 Tunnel View 拍照，然后去 Taft Point 搞个两三个小时的 trail，最后去 Glacier Point 拍日落，整个流程可能三四个小时，然后返回酒店休息。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3788.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3788.webp" alt="<p>Tunnel View，左边是 El Capitan 酋长岩，中间隐约能看到 Half Dome，右边是 Bridalveil Fall</p>"/>      <figcaption aria-hidden="true"><p>Tunnel View，左边是 El Capitan 酋长岩，中间隐约能看到 Half Dome，右边是 Bridalveil Fall</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3808.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3808.webp" alt="<p>Taft Point. 放大后可以看到，最高处被游客压弯的栏杆。过曝了救不回来了……</p>"/>      <figcaption aria-hidden="true"><p>Taft Point. 放大后可以看到，最高处被游客压弯的栏杆。过曝了救不回来了……</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3831.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3831.webp" alt="<p>阴天的 Glacier Point</p>"/>      <figcaption aria-hidden="true"><p>阴天的 Glacier Point</p></figcaption>    </div>  </div></figure><h2 id="加油">加油</h2><p>从 Glacier Point 跑路后，我们突然发现了一个严重的问题：车，快，没，油，了……我们从湾区出发的时候加了次油，到 Yosemite 的时候差不多还有 180miles，感觉在园区内是够用的。但是我们开了两次 Glacier Point，一次单程 34miles，中间可能还有一些杂七杂八的……等我们意识到这个问题的时候，我们赶紧从 sport mode 换成了 eco mode，看看能不能再挤出点 mileage。最后在 wzc 的精妙操作下，我们从 Glacier Point 回到宾馆的时候，车里还剩下 26miles。</p><p>到了宾馆后，我们开始认真思考这个问题。显然，如果不先加上油，明天的 trail 是不可能去了，Yosemite 园区去了怕是个单程票。最后我们找到了几个方案：</p><ol type="1"><li>开车去一家加油站。我们找到了 Mariposa 的一家加油站，再一看距离，离宾馆所在的 El Portal 刚好 27miles，嗯？你是在逗我吗？理论上这是不可到达的，但是如果我们 eco mode 一下，说不定能行？？？但是万一不行，车在路上抛锚了，在这荒郊野外的，电话都不好使，只能寄希望于巡逻的警察或者路过的车辆能帮我们一把……其实更近一点的还有一家，甚至就在 El Portal，但是 Google Maps 显示它周日周一不开，这是什么诡异的营业时间……</li><li>直接在宾馆里叫 road assistance，等着救援小哥带着油从上而降，那一刻，他就是我们的神……而且 insurance 里应该包了 road assistance，wzc 还没用过呢，可以体验一把。</li><li>叫个 Mariposa 的拖车，把我们从 El Portal 拖到 Mariposa 的加油站去，坏处就是 $$$</li><li>我们还试图搜索 gas delivery （更好的名字是 fuel delivery，还真有这种 business），但是但是没找到啥有用的东西。</li></ol><p>后来我们无意中找到了<span class="exturl" data-url="aHR0cHM6Ly93d3cudHJhdmVseW9zZW1pdGUuY29tL3RoaW5ncy10by1kby9zaG9wcGluZy1zdXBwbGllcy1ncm9jZXJpZXM=">这个网页</span>，这个 Yosemite Hospitality 看起来很靠谱，因为之前我在看 The Ahwahnee 宾馆的时候就发现只能在这里订。它上面提到 El Portal Market 这里有 gas，时间是 8am-5pm，没写一周开几天。再一看地址，这就是我们之前看到的那家周日周一不开的加油站。但是此刻我好奇心上来了，感觉里面略有蹊跷，于是再去搜了一发，感觉更加靠谱的解释是这地方除了加油外还有其它服务，这些服务有营业时间显示，加油应该不需要人员服务的，没理由不 24h 开。最后 NPS 的<span class="exturl" data-url="aHR0cHM6Ly93d3cubnBzLmdvdi9wbGFjZXMvMDAwL2VsLXBvcnRhbC1nYXMtc3RhdGlvbi5odG0=">这个网页</span>也印证了我们的猜想，自助加油是 24h 开的，但是我们还是不敢确定。最后我们决定早上去那里碰碰运气，不行就原地叫救援。</p><h1 id="周日">周日</h1><p>今天计划不多，最重要的是把油加上，如果能顺利加上油，我们就去爬个 Mist Trail 庆祝一下。所以我们早上起得比较晚，九点才起（这对我来说已经很早了好吗 →_→），稍微收拾收拾后退了房，我们就去 El Portal 加油了。震惊！work 的无比顺畅！就是油价稍微贵了一点，但是这救命之油我管你什么价格呢。所以我们昨天晚上讨论了那么久，似乎都是在……庸人自扰？感觉是吃了网络不好的亏……信息！信息！</p><p>既然这样，那就让我们庆祝一下，愉快地开始我们的 Mist Trail 之旅吧！</p><h2 id="mist-trail">Mist Trail</h2><p>根据昨天的经验，Curry Village 怕是停不进去，所以我们决定把车又停在 Housekeeping Camp 附近。这次真巧，公交车站旁边的小 parking lot 里就有车位。然后我们就坐 shuttle 去了 Happy Isles，这里离 Mist Trail trailhead 就几步路。</p><p>今天我做了充足准备，而且天公也作美：</p><ol type="1"><li>昨天没爬完只是个小失误（doge）！权当热身了！</li><li>我把背包里的东西又拿出了一点，包比昨天轻了一些。</li><li>今天还是个阴天，不像昨天那么口渴，昨天甚至有点小脱水（喝了好几瓶水还不需要解手）。</li><li>Mist Trail 前期一直在 Mercer River 边上，有水有树。而且 Vernel Fall 附近的台阶上水汽超足，衣服都湿透了。</li></ol><p>对于多久能爬完 Mist Trail，我昨晚回到宾馆后估计了好久。AllTrails 上说 Upper Yosemite Fall Trail 只需要五个半小时就能搞定，但是我们用了两个半小时才到 Oh-My-Gosh Point 打了个来回，估计只完成了 40%，而且 Oh-My-Gosh Point 后面都是大难上坡。以此为基准，AllTrails 上说 Mist Trail 需要四个小时多点，我就给自己定下目标：拿出昨天三天打鱼两天晒网的精神，我应该有希望在六个小时内搞定。</p><p>带着这个目标，我们开始走起了 Mist Trail。万万没想到，我们居然提前爬完了。上山花了两个小时多一点，下山花了一个半小时，AllTrails 上的统计是移动时间 3h24min，总时间 4h25min，所以移动时间甚至比 AllTrails 一开始估计的 4h10min 短？？？不过上山还是照样折磨，特别是 Vernel Fall 处的台阶和 Nevada Fall 前的 zigzag。路上还遇到一些人，随手捡根树枝就拿来当作登山杖了，真是强者如斯。不过印象最深的还有位高手，直接光脚爬坡。下山走的 John Muir Trail 虽然更长，但是明显快了不少。</p><p>接下来锐评一下 Mist Trail 的景色：</p><ol type="1"><li>总而言之，Mist Trail 绝对值得一走！</li><li>Vernal Fall 附近是这个 trail 最精华的部分，和瀑布非常近距离接触，水雾大到衣服都几乎湿透了。</li><li>体力不好的话可以考虑走<span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxsdHJhaWxzLmNvbS90cmFpbC91cy9jYWxpZm9ybmlhL3Zlcm5hbC1mYWxscy1hbmQtY2xhcmstcG9pbnQtdmlhLW1pc3QtYW5kLWpvaG4tbXVpci1sb29w">这一条</span>，只走到 Vernel Fall 就跑路，然后转到 Clark Point 接 John Muir Trail 下山。</li><li>Nevada Fall 顶端有好多小松鼠：“兄弟们我又来要饭啦！”</li><li>Nevada Fall 在 John Muir Trail 上更好拍照，在顶端好看是好看，但是不好拍。Mist Trail 上的角度也没那么好。</li><li>我们下山没有走到 John Muir Trail 和 Mist Trail 的交汇口，而是走了旁边的另外一条 Stock Trail。我一开始还没意识到，后来再看地图时才发现上面写了 <em>horse-only trail</em>，好家伙，我就说怎么这路上几乎没人呢……不过路上也没标识啊。但是如果这条路对 hiker 开的话，我强烈建议下山走这条 John Muir Trail + Stock trail ，一来体验另一条不同的路，二来 Stock Trail 也非常美，和 Mist Trail 不一样的美，可以看到几条小溪，很有那种小桥流水的意境。</li></ol><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3845.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3845.webp" alt="<p>Vernal Fall</p>"/>      <figcaption aria-hidden="true"><p>Vernal Fall</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3850.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3850.webp" alt="<p>Vernal Fall</p>"/>      <figcaption aria-hidden="true"><p>Vernal Fall</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3862.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3862.webp" alt="<p>"USE BRIDGE TO CROSS RIVER"。在英国，自杀是一件很严重的罪行，最高可判死刑呢</p>"/>      <figcaption aria-hidden="true"><p>"USE BRIDGE TO CROSS RIVER"。在英国，自杀是一件很严重的罪行，最高可判死刑呢</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3869.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3869.webp" alt="<p>Mist Trail 上看 Nevada Fall</p>"/>      <figcaption aria-hidden="true"><p>Mist Trail 上看 Nevada Fall</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3896.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3896.webp" alt="<p>“兄弟们我又来要饭啦！” Nevada Fall 上遇到的小松鼠</p>"/>      <figcaption aria-hidden="true"><p>“兄弟们我又来要饭啦！” Nevada Fall 上遇到的小松鼠</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3912.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3912.webp" alt="<p>John Muir Trail 上看 Nevada Fall</p>"/>      <figcaption aria-hidden="true"><p>John Muir Trail 上看 Nevada Fall</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3914.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3914.webp" alt="<p>在 Clark Point 拍 Grizzly Peak，Mt Broderick，Literty Cap 三兄弟</p>"/>      <figcaption aria-hidden="true"><p>在 Clark Point 拍 Grizzly Peak，Mt Broderick，Literty Cap 三兄弟</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2025-06-21-trip-to-Yosemite/IMG_3919.webp">      <img data-src="/images/2025-06-21-trip-to-Yosemite/IMG_3919.webp" alt="<p>Stock Trail 上的小溪</p>"/>      <figcaption aria-hidden="true"><p>Stock Trail 上的小溪</p></figcaption>    </div>  </div></figure><h2 id="回家">回家</h2><p>Mist Trail 爬了个爽，然后我们就回家了。</p><p>一开始估计到湾区要晚上十点了，根据湾区这店家的服务意识，大部分店已经关门了，于是我们就去了一家烧烤店，<span class="exturl" data-url="aHR0cHM6Ly9tYXBzLmFwcC5nb28uZ2wvWW1OejNNTlQyS21OSzVBTDk=">友情客串</span>，最晚开到凌晨十二点半，在湾区可以算得上是劳模了。虽然比预计时间早到湾区，但是还是决定吃了这家。我觉得他们家的牛髓味道相当不错。</p><p>这次多亏了我的小伙伴 wzc 这一路带我。我这一路玩下来很过瘾，但是他可能没咋爬爽。他一开始以为拉了个王者，没想到是个青铜。我在用登山杖吭哧瘪肚的时候，他的登山杖还在包后面挂着没用呢……</p><h1 id="经验">经验</h1><p>装备：</p><ul><li>登山杖还是可以用的。下山的时候还挺有用的。</li><li>还可以整一双登山鞋。我这次穿的鞋被 wzc 戏称为“鞋底都被磨平了”，好像是这个道理……<del>（没爬完 Upper Yosemite Fall Trail 的借口又多了一个）</del></li></ul><p>资料</p><ul><li>我参考了很多<span class="exturl" data-url="aHR0cHM6Ly93d3cubWVpbHZ0b25nLmNvbS92aWV3dGhyZWFkLnBocD90aWQ9NA==">美旅通</span>上的攻略，里面还有很多其它地方的攻略贴。</li><li>NPS 官方也有很多资料，包括<span class="exturl" data-url="aHR0cHM6Ly9ucGdhbGxlcnkubnBzLmdvdi9IRkMvU2VhcmNoUmVzdWx0cz9hbGxGaWVsZHNGb3JtYXQ9QWxsV29yZHMmY29sbGVjdGlvbj1IRkMrQ2FydG9ncmFwaHkmdW5pdGNvZGU9WU9TRSZ2aWV3PWxpc3QmUGFnZVNpemU9MjQ=">地图</span>（开头的地图就来自这里），<span class="exturl" data-url="aHR0cHM6Ly93d3cubnBzLmdvdi95b3NlL3BsYW55b3VydmlzaXQvdmFsbGV5aGlrZXMuaHRt">day hikes</span> 的介绍，还有 <span class="exturl" data-url="aHR0cHM6Ly93d3cubnBzLmdvdi95b3NlL2xlYXJuL3Bob3Rvc211bHRpbWVkaWEvd2ViY2Ftcy5odG0=">webcam</span>（实时观察 Yosemite Fall 的流量）。我最喜欢的是下面这张图，可以直观感受到每个 trail 的难度：</li></ul><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="https://www.nps.gov/yose/planyourvisit/images/finalcombinedprofile_1.jpg">      <img data-src="https://www.nps.gov/yose/planyourvisit/images/finalcombinedprofile_1.jpg" alt="<p>Yosemite trail 难度一览</p>"/>      <figcaption aria-hidden="true"><p>Yosemite trail 难度一览</p></figcaption>    </div>  </div></figure><p>最后总结：菜就多练。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;几个星期前，一位小伙伴 wzc 问我有啥娱乐活动。聊着聊着，我们就约起了 Yosemite。于是几天前，我们终于趁着还不需要预约的时候，赶个 ddl 去了趟 Yosemite.&lt;/p&gt;
&lt;p&gt;TL;DR 实在是妙啊！不愧是国家公园啊！瀑布量大管饱。&lt;/p&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.roosephu.net/categories/Life/"/>
    
    <category term="Travel" scheme="https://www.roosephu.net/categories/Life/Travel/"/>
    
    
  </entry>
  
  <entry>
    <title>Changelog - 网站小更新</title>
    <link href="https://www.roosephu.net/2025/06/04/blog-update/"/>
    <id>https://www.roosephu.net/2025/06/04/blog-update/</id>
    <published>2025-06-04T12:25:16.000Z</published>
    <updated>2025-06-04T12:25:16.000Z</updated>
    
    <content type="html"><![CDATA[<p>我最近又准备小翻新一下博客了。虽然东西没写多少，但是主题更新的快呀哈哈哈哈哈<del>毕竟差生文具多</del></p><span id="more"></span><h1 id="vps-迁移到-hostinger">VPS 迁移到 Hostinger</h1><p>之前我在用 DigitalOcean，因为很久之前 GitHub 整了个 <span class="exturl" data-url="aHR0cHM6Ly9lZHVjYXRpb24uZ2l0aHViLmNvbS9wYWNr">GitHub Student Developer Pack</span>，给了一些 DigitalOcean 的 credit，之后就懒得切换了。但是 DigitalOcean 给的硬盘有点小，装了系统就不剩多少空间了，于是我最近决定换一个 VPS。</p><p>看了一圈之后决定换到 Hostinger，也没啥，主要是觉得便宜 233333 基本上同价位的硬盘比 DigitalOcean 高一倍，而且还有新人优惠，更加便宜了。</p><p>切换 VPS 比我想象中的要简单一些，主要就是 DNS 那边换一下 IP 就行。看了一下，老 VPS 上没有啥值得留念的文件，干脆就断舍离了。新的系统需要重新配置，但是正好重新学习一个。</p><h1 id="nginx-换成-caddy">Nginx 换成 Caddy</h1><p>我之前一直懒得搞 https，靠着 Cloudflare 来假装自己有 https。后来看到了 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NhZGR5c2VydmVyL2NhZGR5">Caddy</span> 这个 project，<span class="exturl" data-url="aHR0cHM6Ly9jYWRkeXNlcnZlci5jb20vZG9jcy9hdXRvbWF0aWMtaHR0cHM=">自带 https</span>，顿时就来了兴趣。</p><p>nginx 对我就两个用处：</p><ol type="1"><li>reverse proxy；</li><li>serve 静态网页，例如这个网站就是一个静态网站。</li></ol><p>这两个 feature 都挺 standard 的，太 standard 了导致根本没啥可说的，比 sample 还 sample。另外 Caddy 还支持 zstd，我就加上去了。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">roosephu.net &#123;</span><br><span class="line">    root * /a/b/c</span><br><span class="line">    file_server</span><br><span class="line">    encode zstd gzip</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="中文字体切割">中文字体切割</h1><p>我之前也<a href="/2023/01/08/theme-update/#细调">提到过</a>，我现在在用 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2x4Z3cvTHhnd0JyaWdodA==">LxgwBright</span> 这款字体，是中文<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2x4Z3cvTHhnd1dlbkthaQ==">霞骛文楷</span>英文 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL0NhdGhhcnNpc0ZvbnRzL1lzYWJlYXU=">Ysabeau Office</span> 的混合字体。字体质量上除了粗体不太明显之外，我觉得没有缺点。</p><p>但是，中文字体部署在网页上都有一个相同的问题：字体文件太大了……光是一个 Regular 型号就有 11MB，一来下载慢，用户需要等一阵才能看到，二来也浪费带宽。很多常用的英文字体没这烦恼，本来字体就好，还可以直接 Google Fonts 就好。霞骛文楷虽然有 Google Fonts，但是只有繁体中文版，而且我需要的还是 LxgwBright……</p><p>经过一些学习，我才知道原来有个技术叫做<em>字体切割</em>，大概思路是把一个大字体文件切成很多小文件，每个文件只包含部分字体。这样浏览器可以通过多个并行的请求来获取字体。由于汉语常用字没那么多，所以请求的数量就还好。我随便看了几个，都在 50 个文件以内，文件平均大小不到 50KB，所以相比之前确实有十足长进。</p><p>那最后的问题就是怎么切了……我最后找到了一个库 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL0tvbmdoYVlhby9jbi1mb250LXNwbGl0">cn-font-split</span>，可以在 nodejs 上跑，也可以通过 WASM <span class="exturl" data-url="aHR0cHM6Ly9jaGluZXNlLWZvbnQubmV0bGlmeS5hcHAvemgtY24vb25saW5lLXNwbGl0">直接在浏览器上跑</span>，点击下鼠标就搞定了。LxgwBright 的每个 ttf 会被切成一堆小文件，还会附赠一个 <code>result.css</code>，可以直接 <code>@import</code> ：</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">@import url(<span class="string">&#x27;/assets/fonts/LXGWBright-MediumItalic/result.css&#x27;</span>);</span><br><span class="line">@import url(<span class="string">&#x27;/assets/fonts/LXGWBright-Medium/result.css&#x27;</span>);</span><br><span class="line">@import url(<span class="string">&#x27;/assets/fonts/LXGWBright-LightItalic/result.css&#x27;</span>);</span><br><span class="line">@import url(<span class="string">&#x27;/assets/fonts/LXGWBright-Light/result.css&#x27;</span>);</span><br><span class="line">@import url(<span class="string">&#x27;/assets/fonts/LXGWBright-Regular/result.css&#x27;</span>);</span><br><span class="line">@import url(<span class="string">&#x27;/assets/fonts/LXGWBright-Italic/result.css&#x27;</span>);</span><br></pre></td></tr></table></figure><h1 id="评论系统切换为-isso">评论系统切换为 isso</h1><p>由于这网站就是一个静态网站，没有后端，而且我也不打算切换成 WordPress 这种大框架（再加上 WordPress 最近的一系列 drama 让我更加不想用它），那得找一个自带后端的评论系统。之前在用 Disqus，感觉还挺好的，但是 Disqus 似乎<span class="exturl" data-url="aHR0cHM6Ly93ZWIuYXJjaGl2ZS5vcmcvd2ViLzIwMjEwMjA1MTEwMzM5L2h0dHBzOi8vc3VwdW5rYXZpbmRhLmJsb2cvZGlzcXVz">有一些奇怪的行为</span>，虽然这文章的作者似乎和 Disqus 有 conflict of interests，但是我觉得他说的还在理，所以我想切换成一个更加注重 privacy 的评论系统，要是能 self-hosted 那就更好了。</p><p>我找了一圈，找到了几个选项：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL0FydGFsa0pTL0FydGFsaw==">Artalk</span>，<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2RldnJzaTBuL2NoaXJweQ==">Chirpy</span>， <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NvcmFscHJvamVjdC90YWxr">Coral</span>， <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2lzc28tY29tbWVudHMvaXNzby8=">isso</span>，<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3VtcHV0dW4vcmVtYXJrNDI=">Remark42</span>。为什么最后决定用 isso 呢？因为这几个中，<code>hexo-theme-next</code> 只支持 isso……</p><p>那就开干。先写个配置文件 <code>isso.cfg</code>，可以对着 <span class="exturl" data-url="aHR0cHM6Ly9pc3NvLWNvbW1lbnRzLmRlL2RvY3MvcmVmZXJlbmNlL3NlcnZlci1jb25maWcv">Server Configuration</span> 写。给个示范如下：</p><figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="section">[general]</span></span><br><span class="line"><span class="attr">dbpath</span> = comments.db</span><br><span class="line"><span class="attr">host</span> =</span><br><span class="line">    https://roosephu.net</span><br><span class="line"></span><br><span class="line"><span class="section">[server]</span></span><br><span class="line"><span class="attr">listen</span> = http://localhost:<span class="number">1234</span>/  <span class="comment"># your favorite port</span></span><br><span class="line"><span class="attr">trusted-proxies</span> = localhost</span><br></pre></td></tr></table></figure><p>然后再装 isso。为啥把装放在后面？因为 isso 是用 Python 写的，这个我熟啊，零帧起手拿起 <code>uv</code> 就能跑：</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line"><span class="comment"># install uv</span></span><br><span class="line">curl -LsSf https://astral.sh/uv/install.sh | sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># run isso</span></span><br><span class="line">uvx --with setuptools isso -c isso.cfg</span><br></pre></td></tr></table></figure><p>最后再在 Caddy 里配置一下 reverse proxy 即可：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">isso.roosephu.net &#123;</span><br><span class="line">    reverse_proxy :1234  # your favorite port</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;我最近又准备小翻新一下博客了。虽然东西没写多少，但是主题更新的快呀哈哈哈哈哈&lt;del&gt;毕竟差生文具多&lt;/del&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.roosephu.net/categories/Tech/"/>
    
    <category term="Changelog" scheme="https://www.roosephu.net/categories/Tech/Changelog/"/>
    
    
  </entry>
  
  <entry>
    <title>Sum-free subset 老题</title>
    <link href="https://www.roosephu.net/2025/05/24/sum-free-subset/"/>
    <id>https://www.roosephu.net/2025/05/24/sum-free-subset/</id>
    <published>2025-05-24T11:15:28.000Z</published>
    <updated>2025-05-24T11:15:28.000Z</updated>
    
    <content type="html"><![CDATA[<div class="note default"><p>一个集合 <span class="math inline">\(S\)</span> 被称为 sum-free set 当且仅当 <span class="math inline">\(\forall a, b \in S, a + b \not\in S\)</span>。</p><p>试证明：对于任何一个集合 <span class="math inline">\(A \subseteq \ZZ\backslash\{0\}\)</span>，其最大的 sum-free subset 大小至少为 <span class="math inline">\(|A| / 3\)</span>.</p></div><p><del>（有一个和题面差不多长度的解答）</del></p><details class="note success"><summary><p>解答</p></summary><p>对于 <span class="math inline">\(t \sim U[0, 1]\)</span>，令 <span class="math inline">\(A_t := \{x \in A: 1/3 &lt; xt \bmod 1 &lt; 2/3 \}\)</span>。显然 <span class="math inline">\(A_t\)</span> 为 sum-free set. 最后再注意到 <span class="math inline">\(\max_t |A_t| \geq \mathbb{E}_t [|A_t|] = |A|/3\)</span> 即可。</p><p>解法来自 Erdos。</p></details>]]></content>
    
    
      
      
    <summary type="html">&lt;div class=&quot;note default&quot;&gt;&lt;p&gt;一个集合 &lt;span class=&quot;math inline&quot;&gt;&#92;(S&#92;)&lt;/span&gt; 被称为 sum-free set 当且仅当 &lt;span class=&quot;math inline&quot;&gt;&#92;(&#92;forall a, b &#92;in </summary>
      
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    
  </entry>
  
  <entry>
    <title>Ten Days in the Zed Editor</title>
    <link href="https://www.roosephu.net/2025/05/18/Ten-Days-in-Zed-Editor/"/>
    <id>https://www.roosephu.net/2025/05/18/Ten-Days-in-Zed-Editor/</id>
    <published>2025-05-18T22:45:39.000Z</published>
    <updated>2025-05-18T22:45:39.000Z</updated>
    
    <content type="html"><![CDATA[<p>Recently I'm playing with a new code editor, <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2">Zed</span>. I used it for about ten days and here write a review to share my experiences with it. This review is based on the latest version (v0.186.9).</p><h1 id="tldr">TL;DR</h1><p>My feelings about Zed are mixed: as an <em>editor</em>, it's pretty good; as a <em>(Python) code editor</em>, it's pretty bad; as an <em>AI code editor</em>, it's reasonable. As of its current state (v0.186.9), it's hard for me to recommend it to colleagues due to its poor Python experience, but I think I'll keep an eye on it.</p><p>Update (<span class="citation" data-cites="2025-05-19">@2025-05-19</span>): After tuning LSP configurations, the Python experience is much better. <a href="/2025/05/18/Ten-Days-in-Zed-Editor/#zed-as-a-python-code-editor-ide">See below</a>.</p><span id="more"></span><h1 id="metadata">Metadata</h1><p>Zed is very actively maintained. Their <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9ncmFwaHMvY29tbWl0LWFjdGl2aXR5">commit activity</span> shows that there are more than 200 commits almost every week. <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9yZWxlYXNlcw==">Their releases</span> are also very frequent: one release every one or two business days. It's still pre-1.0, so they have the chance to make breaking changes, and the so-called "Stable Release" is almost nightly. Take a random example: this <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9wdWxsLzMwNjQ1">PR</span> was submitted on May 13th, was merged on May 14th, and then a new release, v0.186.8, was published on the same day. It does sound a bit aggressive to me... Maybe after 1.0, the Stable version can be less aggressive?</p><p>They released their <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L3JvYWRtYXA=">roadmap</span>.</p><h1 id="zed-as-a-text-editor">Zed as a <em>Text Editor</em></h1><p>It runs pretty smoothly, which is not surprising. It's one of the main selling points, so they have to do it well.</p><p>I really like their text-based configuration method.</p><ul><li>The auto-complete works amazingly well. Zed also checks the schema and quickly prompts.</li><li>It also formats the configuration file automatically, so the configuration always looks clean. However, I wish it didn't remove the trailing comma.</li><li>One missing point is that it doesn't sync settings. This has been <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9kaXNjdXNzaW9ucy82NTY5">discussed</span> for a long time, but there's still no official solution.</li></ul><p>It has built-in <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2RvY3MvZ2l0">git support</span> (<span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvZ2l0">announcement</span>). Works as expected.</p><ul><li>I have an annoying issue: after it restarts, the git gutter doesn't work for certain files. I have to close the file and reopen it. I'm not sure if it's related to my specific setup.</li><li>Currently, there is no conflict resolver. I really like the one in VSCode.</li><li>I'm also using <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2pqLXZjcy9qag==">jj</span> recently. There is no jj integration, but it's totally understandable—even VSCode doesn't have it.</li></ul><p>It supports remote development, which is essential for work. The overall experience is pretty good.</p><ul><li>Read the <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2RvY3MvcmVtb3RlLWRldmVsb3BtZW50I3plZC1zZXR0aW5ncw==">section on settings</span> before you proceed. I spent a lot of time figuring out why Ruff doesn't work, why I can't hide certain files from the project panel, etc., and it turns out the reason is that I should configure them in the server setting instead of the local setting.</li><li>One issue is that I don't know how to open a file in the current window.</li><li>Another issue is that I sometimes add multiple folders to the same workspace, but after it restarts, the extra folders are gone.</li></ul><p>The key binding setting is very flexible. The format is similar to VSCode.</p><ul><li>Rant: project panel is <code>cmd-shift-e</code>, outline panel <code>cmd-shift-b</code>, but why is the git panel <code>ctrl-shift-g</code>?</li><li>A note is that to use <code>cmd-b</code> in the terminal, you should send the escape string instead of <code>cmd-b</code>.</li></ul><p>It has <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2RvY3MvbXVsdGlidWZmZXJz">multibuffers</span>, where multiple files are shown in the same buffer. Some usages include (a) <code>git diff</code>, (b) multi-cursor over multiple files (<span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2RvY3MvbXVsdGlidWZmZXJzI2VkaXRpbmctaW4tYS1tdWx0aWJ1ZmZlcg==">demo</span>), (c) changes from an AI agent, (d) all calls to the function.</p><p>It has <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2RvY3Mvdmlt">built-in vim mode</span>, and it's very well integrated with the editor.</p><ul><li>It also integrates a few popular vim plugins, e.g., <code>vim-surround</code>, <code>vim-textobj-entire</code>. I still miss <code>flash.nvim</code>. Zed has <code>vim-sneak</code> emulated (hidden deep in the docs), but <code>vim-easymotion</code>, <code>leap.nvim</code>, or my (<em>current</em>) favorite, <code>flash.nvim</code> (<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvMTQ4MDE=">tracking issue</span>) are missing.</li><li>The search experience is quite similar to vim: <code>/xxx&lt;enter&gt;</code>. Now you need to use <code>n/N</code> to find the next/previous occurrence. This is exactly how vim works, although I'm pretty used to using <code>&lt;enter&gt;</code> to go to the next occurrence (which doesn't work anymore).</li><li>Off-topic: Among all editors with vim integration, VSCode has the best experience so far due to <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3ZzY29kZS1uZW92aW0vdnNjb2RlLW5lb3ZpbQ==">vscode-noevim</span>. It runs a neovim instance so all neovim's plugins can run—no emulation needed! But recently I've seen a lot of <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3ZzY29kZS1uZW92aW0vdnNjb2RlLW5lb3ZpbS9pc3N1ZXMvMjQwNw==">this issue</span>, which is very annoying... Though, Zed has a blog post on <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvemVkLWRlY29kZWQtdmlt">why it doesn't embed neovim</span>.</li><li>It also has a nice write-up on <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvdmltLTIwMjU=">its plan to improve vim mode</span>.</li></ul><p>Some mini-features that I miss:</p><ul><li>Sticky scroll in the editor (<span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vdXBkYXRlcy92MV83MSNfc3RpY2t5LXNjcm9sbA==">here</span>, <span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vdXBkYXRlcy92MV84MiNfc3RpY2t5LXNjcm9sbA==">here</span>), <span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vdXBkYXRlcy92MV84MSNfbm90ZWJvb2stc3RpY2t5LXNjcm9sbA==">notebook</span>, and <span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vdXBkYXRlcy92MV84NSNfc3RpY2t5LXNjcm9sbC1pbi10cmVlcw==">file tree</span> (<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvNTM0NA==">tracking issue</span>).</li><li>Minimap, which was released in <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9yZWxlYXNlcy90YWcvdjAuMTg3LjAtcHJl">v10.187.0-pre</span> and will be in the stable release soon.</li></ul><h1 id="zed-as-a-python-code-editor-ide">Zed as a <em>(Python) Code Editor (IDE)</em></h1><p>My main programming language for work is Python (I'm doing Machine Learning research), so here I focus on support for Python. But the result is disappointing...</p><p>The biggest issue is that the Python LSP is extremely slow (&gt;5 seconds to see the auto-complete popup, <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvMjczNDM=">tracking issue</span>), making auto-complete unusable. I can't believe the speed of auto-complete for Python and Zed configuration is from the same editor...</p><ul><li>Changing <code>basedpyright.analysis.diagnosticMode</code> from <code>workspace</code> to <code>openFilesOnly</code> mitigates it a little bit, but it's still too slow.</li><li>I tried to <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvMjczNDMjaXNzdWVjb21tZW50LTI3NTM3MDcxODU=">disable PyLSP</span>, but it's still too slow.</li><li>My hypothesis is that I have two (or more) LSP requests: one for the last edit and one for autocomplete. One of Zed or LSP is handling them sequentially—either the LSP can only give the autocomplete list after reanalysis is done, or Zed waits for the LSP's response for the edit before sending the autocomplete request. But I could be totally wrong—I haven't read a single line of Zed's code.</li></ul><p>Update (<span class="citation" data-cites="2025-05-19">@2025-05-19</span>): It turns out that the reason why Python LSP is extremely slow is due to basedpyright: after turning off the auto import feature (<code>basedpyright.analysis.autoImportCompletions=false</code>), the performance is much better and is now acceptable. <strong>I strongly suggest the extension to turn it off by default, as <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL21pY3Jvc29mdC9weWxhbmNlLXJlbGVhc2U/dGFiPXJlYWRtZS1vdi1maWxlI3NldHRpbmdzLWFuZC1jdXN0b21pemF0aW9u">in Pylance</span>.</strong></p><p>Other features (inlay hints, type checking, go to definition) work pretty well.</p><ul><li>To make <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmJhc2VkcHlyaWdodC5jb20vbGF0ZXN0L2JlbmVmaXRzLW92ZXItcHlyaWdodC9weWxhbmNlLWZlYXR1cmVzLyNpbmxheS1oaW50cw==">inlay hints</span> work, use <span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmJhc2VkcHlyaWdodC5jb20vbGF0ZXN0Lw==">basedpyright</span> as the Python LSP. I tried very hard to make it work with pyright, until I realized that pyright doesn't come with inlay hints. The one that works in VSCode is actually pylance, which is closed-source.</li><li>To use basedpyright in Zed, I followed the guide <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL20xZ3Vlci9iYXNlZHB5cmlnaHQtemVk">here</span>, but basically you just need to install the Zed extension, install the basedpyright binaries, and configure the path.</li><li>There is also a <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9kaXNjdXNzaW9ucy8yMzM3Ng==">discussion</span> on the Python experience, and people are complaining about it.</li><li>I really wish <code>astral-sh/ty</code> can finish soon. Meta also released <span class="exturl" data-url="aHR0cHM6Ly9weXJlZmx5Lm9yZw==">Pyrefly</span> a few days ago, which is also written in Rust and is <em>IDE first</em>. The community acts so fast that we now have an <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1leHRlbnNpb25zL3B5cmVmbHk=">Zed extension</span>, but I haven't tried it.</li><li>One missing feature is to attach icons for each auto-completion item (<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvNDk0Mw==">tracking issue</span>).</li><li>An annoying bug is that basedpyright complains about receiving "<em>redundant open text document command</em>" during restart ( <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvMjUyNTY=">tracking issue</span>). I suspect it's related to the Git gutter issue that I've mentioned above. For more context, I'm using remote development but I'm not sure if it's related.</li></ul><p>Syntax highlighting works out of the box (thanks to tree-sitter), but <span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vYXBpL2xhbmd1YWdlLWV4dGVuc2lvbnMvc2VtYW50aWMtaGlnaGxpZ2h0LWd1aWRl">semantic highlighting</span> is not ready (<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvNzQ1MA==">tracking issue</span>).</p><ul><li>Sometimes syntax highlighting is incorrect...</li><li>The <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9kaXNjdXNzaW9ucy8yMzk1Mg==">PR</span> for semantic highlighting hasn't been merged, but I do see some sort of semantic highlighting (i.e., <code>f"&#123;x&#125;"</code> and <code>"&#123;x&#125;"</code> are different).</li></ul><p>One missing feature is notebook support, which is on their roadmap but has low priority. However, this is important to me as I always have a notebook open for interactive data analysis and experiment management.</p><h1 id="zed-as-an-ai-code-editor">Zed as an <em>AI Code Editor</em></h1><p>It has <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvZWRpdC1wcmVkaWN0aW9u">Edit Prediction</span> (I like the short demo), which is essentially the Cursor Tab feature.</p><ul><li>An issue that occurs for me is the conflict between tab for indentation and Edit Prediction. The document says that "Zed requires a modifier key to accept predictions when your cursor isn't at the right indentation level." However, my experience is different: I just want to add one level of indentation, while <code>&lt;tab&gt;</code> accepts all predictions...</li><li>To use the free 2000 Edit Predictions, you need to click to enroll in the Free plan.</li><li>I'm super interested in this blog post—I also work for an AI company, but not a <em>generative</em> AI company.</li></ul><p>The integration with model providers is ok, but it can still be improved.</p><ul><li>Integration includes internal model/completion providers, external completion providers (GitHub Copilot, Supermaven), local model providers (Ollama, LM Studio), and external model providers (OpenAI, Anthropic, Google, etc.) through BYOK.</li><li>I tried the Ollama integration and found it a little disappointing. It doesn't stream text, so I have to wait until all output is generated.<ul><li>I tried local <code>Qwen2.5-Coder-7B</code>, but the result was miserable. The generated code is incomplete: it ends with <code>... (other code remains the same)</code>. The output path is wrong, so Zed can't diff it and I have to manually copy over the generated code.</li><li>I also tried local <code>Qwen3-30B-A3B</code>, but it's very slow on my MBP.</li></ul></li></ul><p>Agentic editing and tool use are also supported.</p><ul><li>One issue is that the <code>&lt;thinking&gt;</code> token is not hidden (<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvMjIzNzM=">tracking issue</span>).</li><li>I like that the user prompts are editable in Agent Panel. My suggestion is that we use a button to enter the edit mode instead of clicking the text, because sometimes I just want to copy it.</li><li>Another issue is that the Agent Panel doesn't render MathJax, but I agree that's such a niche feature request...</li></ul><p>I do like the feature of using LLMs to generate commit messages. I just don't like writing commit messages...</p><ul><li>One improvement would be to allow custom prompts (<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3plZC1pbmR1c3RyaWVzL3plZC9pc3N1ZXMvMjY4MjM=">tracking issue</span>).</li></ul><p>As for data privacy, to my understanding, if we don't opt in, Zed does not retain any data (<em>"Zed does not persistently store user content or use user content to evaluate and/or improve our AI features"</em>) for both Agent Panel and Edit Prediction, but third-party services for Agent Panel might do so (inferred from <em>"Anthropic may not train models on Customer Content from paid Services."</em>).</p><ul><li>I do appreciate that the default is opt-out.<br /></li><li>For comparison, both <span class="exturl" data-url="aHR0cHM6Ly93d3cuY3Vyc29yLmNvbS9wcml2YWN5">Cursor (in Privacy Mode)</span> and <span class="exturl" data-url="aHR0cHM6Ly93aW5kc3VyZi5jb20vc2VjdXJpdHkjemVyby1kYXRhLXJldGVudGlvbg==">Windsurf (in zero-data retention mode)</span> have a zero-data retention policy that prevents third-party services from retaining data, if I understand it correctly. I wish Zed could also have a zero-data retention policy for the Agent Panel.</li><li>For Edit Prediction, Zed itself doesn't retain any data (when not opted in), but <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvZWRpdC1wcmVkaWN0aW9uI21pbmltaXppbmctbGF0ZW5jeS1zZXJ2aW5nLXRoZS1tb2RlbA==">Zed uses Baseten to serve the model</span>, and it's not clear whether Baseten retains any data.</li><li>Another improvement is to have an option to disable opt-in in project settings in case the user enables it by accident.</li></ul><p>In terms of the quality of generated code, it's hard for me to evaluate and compare as I haven't used many AI code editors before, and during my evaluation, I was using GitHub Copilot as the service provider instead of Zed's own services... I only found GitHub Copilot mediocre. I also used Cursor for a little bit and was impressed by the Tab completion feature. Honestly, I feel like Zed's Edit Prediction is slightly slower and worse than Cursor Tab, but I say that with pretty low confidence.</p><h1 id="random-thoughts">Random Thoughts</h1><p>As a programmer, I'm a heavy user of code editors. I've used (Neo)Vim, Emacs, Atom, VSCode, etc. When I first heard of Zed, I wasn't that interested. I kind of forgot what exactly I read, but all I remember is that it's an editor with Rope and it's written in Rust, both of which are buzzwords. I later found their blog post, <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvY3JkdHM=">How CRDTs make multiplayer text editing part of Zed's DNA</span>, but given the prior art, the <span class="exturl" data-url="aHR0cHM6Ly94aS1lZGl0b3IuaW8vZG9jcy9yb3BlX3NjaWVuY2VfMDAuaHRtbA==">Rope science</span> series, Zed's blog post didn't really impress me (but the animation is pretty cool!). When I first heard "Rust" and "Rope", I was thinking, isn't the <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3hpLWVkaXRvci94aS1lZGl0b3I=">Xi-editor</span> doing the same thing?</p><p>I tried Zed on and off for a few sessions, each of which took only half an hour or so. The reason I didn't invest my time in it is that I have concerns about its future: What if the product goes unmaintained? It's hard to monetize a text editor. The market is competitive and there are many tough-to-beat free options, like the OG Vim/Emacs and modern options like VSCode. Moreover, it also competes with full IDEs like Visual Studio and JetBrains products. So before they joined the AI Code Editor army, I didn't see a good business model (B2B is just hard). Maybe they can commercialize the product with certain features (e.g., collaborative editing), but I'm not convinced. Until recently, they announced being the <span class="exturl" data-url="aHR0cHM6Ly96ZWQuZGV2L2Jsb2cvZmFzdGVzdC1haS1jb2RlLWVkaXRvcg==">fastest AI code editor</span>. I finally pulled the trigger, as this might be a viable business model (B2C). AI code editor is also a very competitive market (just to name a few: Cursor, Windsurf, OpenAI Codex, Claude Code, Augment Code, Fitten Code, Tabby, GitHub Copilot, Devin), but now Zed has its own strength: they understand the editor down to every line of code and every feature, and they have full control.</p><p>Regarding the future of AI code editors, I do have some thoughts, but they're pretty immature and I haven't investigated enough. It's possible that the problems have been studied intensively in the industry while I live in too small a bubble to see it. Perhaps I should revisit this later after using more AI code editors for longer to think more about the features, integrations (of AI/LSP/Editor), business model, research, etc.</p><p>I also read two more posts on others' experiences with Zed <a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a><a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> after writing this post, so the opinions are my own.</p><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr /><ol><li id="fn1"><p><span class="exturl" data-url="aHR0cHM6Ly95b3lvLWNvZGUuY29tL2Vhcmx5LWZlZWRiYWNrLW9uLXplZC1lZGl0b3Iv">Early feedback on Zed editor</span><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><span class="exturl" data-url="aHR0cHM6Ly9kYXZpZGdhc3F1ZXouY29tL3RyeWluZy16ZWQtZWRpdG9yLw==">Trying Zed Editor</span><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;Recently I&#39;m playing with a new code editor, &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly96ZWQuZGV2&quot;&gt;Zed&lt;/span&gt;. I used it for about ten days and here write a review to share my experiences with it. This review is based on the latest version (v0.186.9).&lt;/p&gt;
&lt;h1 id=&quot;tldr&quot;&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;My feelings about Zed are mixed: as an &lt;em&gt;editor&lt;/em&gt;, it&#39;s pretty good; as a &lt;em&gt;(Python) code editor&lt;/em&gt;, it&#39;s pretty bad; as an &lt;em&gt;AI code editor&lt;/em&gt;, it&#39;s reasonable. As of its current state (v0.186.9), it&#39;s hard for me to recommend it to colleagues due to its poor Python experience, but I think I&#39;ll keep an eye on it.&lt;/p&gt;
&lt;p&gt;Update (&lt;span class=&quot;citation&quot; data-cites=&quot;2025-05-19&quot;&gt;@2025-05-19&lt;/span&gt;): After tuning LSP configurations, the Python experience is much better. &lt;a href=&quot;/2025/05/18/Ten-Days-in-Zed-Editor/#zed-as-a-python-code-editor-ide&quot;&gt;See below&lt;/a&gt;.&lt;/p&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.roosephu.net/categories/Tech/"/>
    
    
  </entry>
  
  <entry>
    <title>Integer and Rational Solutions of a Binary Quadratic Equation (0): General binary quadratic equation</title>
    <link href="https://www.roosephu.net/2025/03/23/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-0-General-Binary-Quadratic-Equation/"/>
    <id>https://www.roosephu.net/2025/03/23/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-0-General-Binary-Quadratic-Equation/</id>
    <published>2025-03-24T02:16:59.000Z</published>
    <updated>2025-03-24T02:16:59.000Z</updated>
    
    <content type="html"><![CDATA[<p>In this post, we are interested in finding all <strong><em>integer</em></strong> and <strong><em>rational</em></strong> solutions for a general binary quadratic equation:<span class="math display">\[\begin{equation}ax^2 + bxy + cy^2 + dx + ey + f = 0,\label{eq:general}\end{equation}\]</span>for any integer coefficients <span class="math inline">\(a, b, c, d, e, f\)</span>. The equation might degenerate; we only point it out when it does but do not solve it, as it's typically easier to solve a degenerate equation.</p><p>This post mainly serves as an entry point for a series of posts and also contains a little bit of the story behind the posts. That is why this post has index 0 in the title.</p><span id="more"></span><h1 id="the-story">The story</h1><p>As readers might know, I have been practicing Project Euler for years. Project Euler contains many problems related to number theory, some of which involve solving binary Diophantine equations. One of the most famous examples of quadratic Diophantine equations is the Pythagorean triples (<span class="math inline">\(x^2 + y^2 = z^2\)</span>).</p><p>One day, I suddenly had an idea: What's the general method for solving quadratic equations? How can we enumerate the solutions? Given that there might be infinite solutions, probably we want to enumerate <em>bounded</em> solutions efficiently (mostly for solving Project Euler problems). So I spent time researching, reading numerous papers, notes, books and blog posts... Until I thought I was done and ready to summarize what I had learned into posts.</p><p>While finishing my writing, I found <span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL3BocC9tYWluX3BlbGwuaHRtbA==">Quadratic diophantine equations BCMATH programs</span>, and immediately realized that my literature review was so incomplete. The website covers most related topics and is maintained by Keith Matthews, who seems to be a domain expert, with regular updates. Reading his papers made me question the necessity of my posts...</p><p>Nevertheless, I decided to proceed with my writing. Perhaps it's for my own understanding, or perhaps I simply don't want to admit I haven't published anything on my blog for two months (so I even break the topic down into many small parts)...</p><p>What's next? I still have two incomplete things, but it depends on whether my interests fade.</p><ul><li>Implement the algorithms, which is a perfect chance to learn a new programming language, just like what I did for my previous side project (Analytic prime counting, <span class="exturl" data-url="aHR0cDovL2xvY2FsaG9zdDo0MDAwLzIwMjEvMDMvMDgvYW5hbHl0aWMtcGkv">Part 1</span> and <a href="/2021/03/15/analytic-pi-2/">Part 2</a>).</li><li>More efficient enumeration of primitive solutions of the Legendre equation. Although <a href="/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/#connection-to-integer-solutions-of-a-homogenous-ternary-quadratic-equation">an upper bound of the GCD</a> exists, I still want to make it more efficient by removing GCD. The requirement of enumerating the primitive input pair <span class="math inline">\((u, v)\)</span> probably can't be removed, though.</li></ul><h1 id="common-techniques">Common techniques</h1><p>Suppose we want to solve <span class="math inline">\(f(\mathbf{x}) = n\)</span>.</p><ul><li><em>(Homogenization)</em> If <span class="math inline">\(f\)</span> is not homogeneous, it may be beneficial to homogenize it;</li><li><em>(Diagonalization)</em> If <span class="math inline">\(f(\mathbf{x}) = x^\T Q \mathbf{x}\)</span> is quadratic but <span class="math inline">\(Q\)</span> is not diagonal, diagonalize it;</li><li><em>(Primitive solutions)</em> Only consider the primitive solutions. For most cases, an algorithm for primitive solutions can be easily used to find non-primitive solutions (<span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVHVyaW5nX3JlZHVjdGlvbg==">Turing reduction</span>);</li><li><em>(Partition of solution space)</em> Consider the equation <span class="math inline">\(f(\mathbf{x}) \equiv 0 \pmod{n}\)</span>. The solution gives us strong constraints on <span class="math inline">\(\mathbf{x}\)</span>, which is enough to partition the solution space.</li></ul><h1 id="integer-solutions">Integer solutions</h1><p>Let <span class="math inline">\(\Delta := b^2 - 4ac\)</span> be the <em>discriminant</em> of the equation.</p><ul><li><p><em>(Case 1)</em> If <span class="math inline">\(a = c = 0\)</span>:</p><ul><li><em>(Case 1.1)</em> If <span class="math inline">\(b = 0\)</span>: it simplifies to a linear case <span class="math inline">\(dx + ey + f = 0\)</span>;</li><li><em>(Case 1.2)</em> If <span class="math inline">\(b \neq 0\)</span>: This results in a degenerate case: <span class="math inline">\((bx + e)(by + d) = de - bf\)</span>. To solve, factor <span class="math inline">\(de - bf\)</span> to express it in terms of <span class="math inline">\(bx + e\)</span> and <span class="math inline">\(by + d\)</span>.</li></ul></li><li><p><em>(Case 2)</em> If <span class="math inline">\(\Delta = 0\)</span> while <span class="math inline">\(a \neq 0\)</span> or <span class="math inline">\(c \neq 0\)</span>: Without loss of generality (W.L.O.G.), we assume <span class="math inline">\(a \neq 0\)</span>. Note<span class="math display">\[  4a(a x^2 + bxy + cy^2 + dx + ey + f) = (2ax + by + d)^2 + (4ae - 2bd)y - (d^2 - 4af).  \]</span>Thus:</p><ul><li><em>(Case 2.1)</em> If <span class="math inline">\(2ae = bd\)</span>: Solve <span class="math inline">\((2ax+by+d)^2 = d^2 - 4af\)</span>;</li><li><em>(Case 2.2)</em> If <span class="math inline">\(2ae \neq bd\)</span>: Solve<span class="math display">\[  (2ax+by+d)^2 \equiv d^2 - 4af \pmod{|4ae - 2bd|}  \]</span><a href="/2025/03/10/Integer-Solutions-of-a-Binary-Quadratic-Equation-Quadratic-Residue/">using quadratic residue</a>.</li></ul></li><li><p><em>(Case 3)</em> If <span class="math inline">\(\Delta \neq 0\)</span>, while <span class="math inline">\(a \neq 0\)</span> or <span class="math inline">\(c \neq 0\)</span>. W.L.O.G. we assume <span class="math inline">\(a \neq 0\)</span>. Note<span class="math display">\[  4 a \Delta (ax^2+bxy+cy^2 + dx + ey + f) = x&#39;^2 - \Delta y&#39;^2 - 4an,  \]</span>where<span class="math display">\[  \begin{aligned}  x&#39; &amp;= \Delta y - 2ae + bd,\\  y&#39; &amp; = 2 a x + by + d, \\  n &amp; = ae^2 - bde + cd^2 + \Delta f. \\  \end{aligned}  \]</span>Thus:</p><ul><li><em>(Case 3.1)</em> If <span class="math inline">\(\Delta &gt; 0\)</span> and is not a perfect square, we <a href="/2025/03/15/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-4-Pell-equation-and-generalized-Pell-equation/">solve the generalized Pell equation <span class="math inline">\(x&#39;^2 - |\Delta| y&#39;^2 = 4an\)</span></a>;</li><li><em>(Case 3.2)</em> If <span class="math inline">\(\Delta &gt; 0\)</span> and is a square, we solve <span class="math inline">\((x&#39; - \sqrt{\Delta}y&#39;)(x&#39; + \sqrt{ \Delta } y&#39;) = 4an\)</span>.</li><li><em>(Case 3.3)</em> If <span class="math inline">\(\Delta &lt; 0\)</span>, we <a href="/2025/03/11/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-3-ax-2-by-2-c/">solve the ellipse case <span class="math inline">\(x&#39;^2 + |\Delta| y&#39;^2 = 4an\)</span></a>.</li></ul></li><li><p><em>(Alternative Case 3)</em> If <span class="math inline">\(\Delta \neq 0\)</span>, note<span class="math display">\[  \Delta^2 (ax^2 + bxy + cy^2 + dx + ey + f) = ax&#39;^2 + bx&#39;y&#39; + cy&#39;^2 + \Delta n,  \]</span>where<span class="math display">\[  \begin{aligned}  x&#39; &amp;= \Delta x - 2cd + be, \\  y&#39; &amp;= \Delta y - 2ae + bd, \\  n &amp;= ae^2 - bed + cd^2 + \Delta f.  \end{aligned}  \]</span>Then we can use the methods about <a href="/2025/03/22/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-5-Binary-Quadratic-Form/">solving binary quadratic forms</a> to find all integer solutions to <span class="math inline">\(ax&#39;^2 + bx&#39;y&#39; + cy&#39;^2 = n\)</span>. The advantage of the method is that we don't need to diagonalize the equation, although many methods implicitly include this step.</p></li></ul><p>Note that many changes of variable are not unimodular, so we need to verify whether the result is an integer solution.</p><h1 id="rational-solutions">Rational solutions</h1><p>We write the equation in the matrix form:<span class="math display">\[\mathbf{x}^\T Q \mathbf{x} = 0, \quadQ := \begin{pmatrix}2a &amp; b &amp; d \\b &amp; 2c &amp; e \\d &amp; e &amp; 2f \\\end{pmatrix}, \quad\mathbf{x} := \begin{pmatrix} x \\ y \\ 1 \end{pmatrix}.\]</span></p><p><em>(Diagonalization)</em> The matrix <span class="math inline">\(Q\)</span> may not be diagonal. If so, we can diagonalize it by finding an invertible matrix <span class="math inline">\(P \in \QQ^{3 \times 3}\)</span> and a diagonal matrix <span class="math inline">\(D \in \ZZ^{3 \times 3}\)</span> such that<span class="math display">\[Q = P^\T D P.\]</span>It follows that<span class="math display">\[\mathbf{x}^\T Q \mathbf{x} = 0 \quad \Longleftrightarrow \quad (P \mathbf{x})^\T D (P \mathbf{x}) = 0.\]</span>One possible approach is to use the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2hvbGVza3lfZGVjb21wb3NpdGlvbiNMRExfZGVjb21wb3NpdGlvbl8y">LDL decomposition</span>. However, the matrix <span class="math inline">\(Q\)</span> may not have a LDL decomposition if, at some step (e.g., <span class="math inline">\((x+y)^2 + yz\)</span>), the diagonal element becomes zero. If this occurs, we can conclude that the equation at that step is actually linear in the variable (e.g., <span class="math inline">\(y\)</span> in the example). Thus we can always treat other variables (<span class="math inline">\(x&#39; = x+y, z\)</span>) as free variables and compute <span class="math inline">\(y\)</span> in the end.</p><p><em>(Legendre equation)</em> Let <span class="math inline">\(Q = \diag(a&#39;, b&#39;, c&#39;)\)</span>, we consider the primitive solutions to the Legendre equation <span class="math inline">\(a&#39;x&#39;^2 + b&#39; y&#39;^2 + c&#39;z&#39;^2 = 0\)</span>. For any primitive solution <span class="math inline">\(\mathbf{x}&#39; = (x&#39;, y&#39;, z&#39;)\)</span> to the Legendre equation, write <span class="math inline">\((x, y, z) = P \mathbf{x&#39;}\)</span>. If <span class="math inline">\(z \neq 0\)</span>, then <span class="math inline">\((x/z, y/z)\)</span> is a rational solution to the original equation.</p><p><em>(Solving Legendre equation)</em> For the Legendre equation <span class="math inline">\(a&#39;x&#39;^2 + b&#39; y&#39;^2 + c&#39;z&#39;^2 = 0\)</span>, we assume <span class="math inline">\(|a&#39;| \geq |b&#39;| \geq |c&#39;|\)</span> W.L.O.G. We analyze the number of zero coefficients among <span class="math inline">\(a&#39;, b&#39;, c&#39;\)</span>.</p><ol type="1"><li>There are 3 zeros. Any <span class="math inline">\((x&#39;, y&#39;, z&#39;)\)</span> for any <span class="math inline">\(x&#39;, y&#39;, z&#39;\)</span> is a solution;</li><li>There are 2 zeros. Any <span class="math inline">\((0, y&#39;, z&#39;)\)</span> for any <span class="math inline">\(y&#39;, z&#39;\)</span> is a solution;</li><li>There is 1 zero. Any <span class="math inline">\((ty&#39;, y&#39;, z&#39;)\)</span> for any <span class="math inline">\(y&#39;, z&#39;\)</span>, and any rational solution <span class="math inline">\(t\)</span> to <span class="math inline">\(a&#39; t^2 + b&#39; = 0\)</span>, is a solution.</li><li>There is no zero. We <a href="/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/#find-a-single-non-trivial-integer-solution-to-legendre-equation-in-normal-form">find any non-trivial integer solution <span class="math inline">\(\mathbf{x&#39;}\)</span> to the Legendre equation <span class="math inline">\(a&#39;x^2 + b&#39;y^2 + c&#39;z^2 = 0\)</span></a>, and <a href="/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/#all-non-trivial-solutions-from-a-single-non-trivial-solution">every rational solution can be parameterized by <span class="math inline">\(\mathbf{x}&#39;\)</span></a>.</li></ol><h1 id="homework-fracxyz-fracyzx-fraczxy4">Homework: <span class="math inline">\(\frac{x}{y+z} + \frac{y}{z+x} + \frac{z}{x+y}=4\)</span></h1><figure><img data-src="https://i.redd.it/p92108lekoq11.jpg" alt="How hard it can be?" /><figcaption aria-hidden="true">How hard it can be?</figcaption></figure><p>Answer: See <span class="exturl" data-url="aHR0cHM6Ly93d3cucXVvcmEuY29tL0hvdy1kby15b3UtZmluZC10aGUtcG9zaXRpdmUtaW50ZWdlci1zb2x1dGlvbnMtdG8tZnJhYy14LXkrei0rLWZyYWMteS16K3gtKy1mcmFjLXoteCt5LTQ=">Alon Amit's excellent post</span>.</p><h1 id="references">References</h1><p>General resources from the Internet:</p><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL3BocC9tYWluX3BlbGwuaHRtbA==">Quadratic diophantine equations BCMATH programs</span> by Keith Matthews.<ul><li>Also check out <span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL3BocC9jaGFuZ2VzLmh0bWw=">the history of his algorithm</span> for his line of work.</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxwZXJ0cm9uLmNvbS5hci9NRVRIT0RTLkhUTQ==">Methods to solve quadratic Diophantine equations</span> by Dario Alejandro Alpern;<ul><li>The validity of the following statement is uncertain:</li></ul><blockquote><p>If <span class="math inline">\(4F^2&lt;B^2-4AC\)</span>, the solutions of the equation will be amongst the convergents of the continued fraction of the roots of the equation <span class="math inline">\(At^2+ Bt + C = 0\)</span>.</p></blockquote></li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL01hbmdhcmEvZGlvcGhhbnRpbmU=">Sander Verdonschot's repo</span>, which implements many Keith Matthews' papers in Java;</li><li><span class="exturl" data-url="aHR0cHM6Ly90aGlsaW5hYXRzeW1weS53b3JkcHJlc3MuY29tLw==">Thilina Rathnayake's blog</span> when developing the Diophantine module of SciPy;</li><li><span class="exturl" data-url="aHR0cHM6Ly9tYXRod29ybGQud29sZnJhbS5jb20vRGlvcGhhbnRpbmVFcXVhdGlvbjJuZFBvd2Vycy5odG1s">Diophantine Equation--2nd Powers from Wolfram MathWorld</span>;</li><li><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLnN5bXB5Lm9yZy9sYXRlc3QvbW9kdWxlcy9zb2x2ZXJzL2Rpb3BoYW50aW5lLmh0bWw=">SciPy's doc on Diophantine equations</span>;</li><li>Writings in <span class="exturl" data-url="aHR0cHM6Ly9rY29ucmFkLm1hdGgudWNvbm4uZWR1L2JsdXJicy8=">Keith Conrad's websites</span>. It may be elementary, but it is sufficient for amateurs like me.</li></ul><p>Textbooks and notes:</p><ul><li>[Dickson19] : Dickson, L.E., 1919. <em>History of the Theory of Numbers</em> (No. 256). Carnegie Institution of Washington.</li><li>[Mordell69] : Mordell, L.J., 1969. <em>Diophantine Equations: Diophantine Equations</em> (Vol. 30)</li><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL1BERlMvZ2VuZXJhbF9xdWFkcmF0aWNfc29sdXRpb24ucGRm">Matthews15</span>: Matthews, K., 2015. Solving the Diophantine Equation <span class="math inline">\(ax^2 + bxy + cy^2 + dx + ey + f = 0\)</span>.</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;In this post, we are interested in finding all &lt;strong&gt;&lt;em&gt;integer&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;rational&lt;/em&gt;&lt;/strong&gt; solutions for a general binary quadratic equation:
&lt;span class=&quot;math display&quot;&gt;&#92;[
&#92;begin{equation}
ax^2 + bxy + cy^2 + dx + ey + f = 0,
&#92;label{eq:general}
&#92;end{equation}
&#92;]&lt;/span&gt;
for any integer coefficients &lt;span class=&quot;math inline&quot;&gt;&#92;(a, b, c, d, e, f&#92;)&lt;/span&gt;. The equation might degenerate; we only point it out when it does but do not solve it, as it&#39;s typically easier to solve a degenerate equation.&lt;/p&gt;
&lt;p&gt;This post mainly serves as an entry point for a series of posts and also contains a little bit of the story behind the posts. That is why this post has index 0 in the title.&lt;/p&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    <category term="Number Theory" scheme="https://www.roosephu.net/categories/CS-Math/Number-Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>Integer and Rational Solutions of a Binary Quadratic Equation (5): Binary quadratic form</title>
    <link href="https://www.roosephu.net/2025/03/22/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-5-Binary-Quadratic-Form/"/>
    <id>https://www.roosephu.net/2025/03/22/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-5-Binary-Quadratic-Form/</id>
    <published>2025-03-22T11:12:20.000Z</published>
    <updated>2025-03-22T11:12:20.000Z</updated>
    
    <content type="html"><![CDATA[<p>In this post, we are interested in finding all <em><strong>integer</strong></em> solutions of the following equation:<span class="math display">\[\begin{equation}ax^2 + bxy + cy^2 = n.%\label{eq:ibqf}\end{equation}\]</span>where <span class="math inline">\(acn \neq 0\)</span>, <span class="math inline">\(b^2 - 4ac \neq 0\)</span>. This equation can certainly be solved using methods from the <a href="/2025/03/15/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-4-Pell-equation-and-generalized-Pell-equation/#generalized-pell-equation-x2--d-y2-n">generalized Pell equation</a>, but here, we explore other methods.</p><span id="more"></span><p>The function <span class="math inline">\(ax^2 + bxy + cy^2\)</span> is also called the <em>binary quadratic form</em> (BQF), which we denote as <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> for brevity. The <em>discriminant</em> of <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> is defined as <span class="math inline">\(\Delta := b^2 - 4ac\)</span>.</p><h1 id="theory-of-binary-quadratic-forms">Theory of binary quadratic forms</h1><p><span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQmluYXJ5X3F1YWRyYXRpY19mb3Jt">Binary quadratic forms</span> have been studied for centuries and many textbooks discuss them. The goal of this section is to provide a brief introduction. Advanced topics (e.g., composition) will not be discussed.</p><p><em>(Equivalence)</em> We define an equivalence relation between BQFs: <span class="math inline">\(\left&lt; a,b,c \right&gt; \sim \left&lt; a&#39;,b&#39;,c&#39; \right&gt;\)</span> if and only if there exists an <span class="math inline">\(M \in \ZZ^{2\times 2}\)</span> such that <span class="math inline">\(\det M = 1\)</span> and<span class="math display">\[\begin{equation}M^\T \begin{pmatrix}a &amp; b/2 \\b / 2 &amp; c \\\end{pmatrix} M = \begin{pmatrix}a&#39; &amp; b&#39; / 2 \\b&#39; / 2 &amp; c&#39; \\\end{pmatrix},%\label{eq:equivalence}\end{equation}\]</span>The matrix determinants of both sides show that <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> and <span class="math inline">\(\left&lt; a&#39;,b&#39;,c&#39; \right&gt;\)</span> have the same discriminant.</p><p><em>(Representing numbers)</em> One way to understand this equivalence is by examining the set of numbers it represents. We say <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> represents <span class="math inline">\(n\)</span> if the equation <span class="math inline">\(ax^2 + bxy + cy^2 = n\)</span> for some <span class="math inline">\(x, y \in \ZZ\)</span>, and it <em>properly</em> represents <span class="math inline">\(n\)</span> if <span class="math inline">\(\gcd(x, y) = 1\)</span>. If <span class="math inline">\(\left&lt; a,b,c \right&gt; \sim \left&lt; a&#39;,b&#39;,c&#39; \right&gt;\)</span> and <span class="math inline">\(\left&lt; a&#39;,b&#39;,c&#39; \right&gt;\)</span> represents <span class="math inline">\(n\)</span> by <span class="math inline">\([x, y]\)</span>, then <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> represents <span class="math inline">\(n\)</span> through <span class="math inline">\(M [x, y]\)</span>, which is a simple change of variables (under the constraint that <span class="math inline">\(\det M = 1\)</span>).</p><p><em>(Positive definite)</em> A BQF <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> is <em>positive definite</em> if for any <span class="math inline">\((x, y) \neq 0\)</span>, <span class="math inline">\(ax^2 + bxy + cy^2 &gt; 0\)</span>. An alternative definition states that <span class="math inline">\(a &gt; 0, c &gt; 0\)</span> and <span class="math inline">\(\Delta &lt; 0\)</span>.</p><p><em>(Lagrange-reduced form)</em> A positive definite BQF <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> is <em>(Lagrange-)reduced</em> if it meets both of the following conditions: (1) <span class="math inline">\(|b| \leq a \leq c\)</span>, (2) if <span class="math inline">\(|b| = a\)</span> or <span class="math inline">\(a = c\)</span>, then <span class="math inline">\(b \geq 0\)</span>. Every positive definite BQF is equivalent to a unique reduced BQF.</p><p><em>(Reduction)</em> We have two rules to reduce a positive definite BQF, similar to the Euclidean algorithm:<span class="math display">\[\begin{equation}\left&lt; a,b,c \right&gt; \sim \left&lt; c,-b,a \right&gt;, \quad M = \begin{pmatrix} 0 &amp; 1 \\ -1 &amp; 0\end{pmatrix}.\label{R.1}\tag{R.1}\end{equation}\]</span>and for any <span class="math inline">\(t \in \ZZ\)</span>,<span class="math display">\[\begin{equation}\left&lt; a,b,c \right&gt; \sim \left&lt;a, b - 2at, at^2 - bt + c\right&gt;, \quad M = \begin{pmatrix} 1 &amp; -t \\ 0 &amp; 1 \end{pmatrix},\label{R.2}\tag{R.2}\end{equation}\]</span>A special case of <span class="math inline">\(\eqref{R.2}\)</span> is <span class="math inline">\(\left&lt; a,a,c \right&gt; \sim \left&lt; a,-a,c \right&gt;\)</span> for <span class="math inline">\(t = 1\)</span>.</p><p>We can always apply <span class="math inline">\(\eqref{R.1}\)</span> to ensure that <span class="math inline">\(a \leq c\)</span>, and <span class="math inline">\(\eqref{R.2}\)</span> to ensure <span class="math inline">\(|b| \leq a\)</span> by setting <span class="math inline">\(t = \left\lfloor  \frac{b}{2a}  \right\rceil\)</span>. Both <span class="math inline">\(\eqref{R.1}\)</span> and <span class="math inline">\(\eqref{R.2}\)</span> reduces either <span class="math inline">\(a\)</span> or <span class="math inline">\(|b|\)</span> while keeping the other the same, so the process must terminate with a BQF <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> where <span class="math inline">\(|b| \leq a \leq c\)</span>. Finally, if <span class="math inline">\(a = c\)</span> or <span class="math inline">\(a = |b|\)</span>, we choose the one with <span class="math inline">\(b \geq 0\)</span>.</p><p>See <span class="exturl" data-url="aHR0cHM6Ly93c3RlaW4ub3JnL2VkdS8xMjQvbGVjdHVyZXMvbGVjdHVyZTIzL2xlY3R1cmUyMy5wZGY=">William Stein's lecture notes</span> and [Manguba-Glover18, Chapter 2] for more details.</p><h1 id="normalization">Normalization</h1><p><em>(Normalization)</em> The equation is first normalized as follows:</p><ul><li>Assume <span class="math inline">\(\gcd(a, b, c) = 1\)</span>. Otherwise, we divide both sides by <span class="math inline">\(\gcd(a, b, c)\)</span>.</li><li>Assume <span class="math inline">\(\gcd(a, n) = 1\)</span>. Otherwise, we can always find <span class="math inline">\(u, s, v, r \in \ZZ\)</span> such that <span class="math inline">\(us - vr = 1\)</span> and <span class="math inline">\(\gcd(au^2 + buv + cv^2, n) = 1\)</span>, and then apply <span class="math inline">\(\begin{pmatrix} u  &amp; r \\ v &amp; s\end{pmatrix}\)</span> to <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span>. See <span class="exturl" data-url="aHR0cHM6Ly9tYXRoLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8yODQ1MzQ0L3ByaW1pdGl2ZS1iaW5hcnktcXVhZHJhdGljLWZvcm1z">here</span> for how to find <span class="math inline">\((u, v)\)</span>. The high level idea is that for any prime <span class="math inline">\(p\)</span>, it's enough to try <span class="math inline">\((1, 0), (0, 1), (1, 1)\)</span>, and we use CRT to combine the results.</li></ul><p><em>(Primitive solutions)</em> We only care about the primitive solutions <span class="math inline">\((x, y)\)</span> where <span class="math inline">\(g := \gcd(x, y) = 1\)</span>. If <span class="math inline">\(g \neq 1\)</span>, we find the primitive solutions of <span class="math inline">\(ax^2 + bxy + cy^2 = n/g^2\)</span>. It's also clear that <span class="math inline">\(\gcd(y, n) = 1\)</span> for primitive solutions <span class="math inline">\((x, y)\)</span>.</p><h1 id="ellipse-case-delta-0">Ellipse case (<span class="math inline">\(\Delta &lt; 0\)</span>)</h1><p>(This section is mostly taken from [Matthews15].)</p><p>We assume the BQF <span class="math inline">\(\left&lt; a,b,c \right&gt;\)</span> is positive definite. Otherwise, we negate <span class="math inline">\(a, b, c\)</span> and <span class="math inline">\(n\)</span>.</p><p>For a primitive solution <span class="math inline">\((x, y)\)</span> to <span class="math inline">\(ax^2 + bxy + cy^2 = n\)</span>, define <span class="math inline">\(\lambda := x y^{-1} \bmod{|n|}\)</span>, which certainly satisfies <span class="math inline">\(a\lambda^2 + b\lambda + c \equiv 0 \pmod{|n|}\)</span>. We write <span class="math inline">\(x = x&#39;n - \lambda y\)</span>, hence<span class="math display">\[ax^2 + bxy + cy^2 = n, \quad \Longleftrightarrow \quad an x&#39;^2 + (b-2\lambda a) x&#39;y + \frac{a\lambda^2 + b\lambda + c}{n}y^2 = 1.\]</span>Note that these two BQFs have the same discriminant.</p><p>The theorem below determines the minimum positive value of a reduced positive definite BQF:</p><div class="note default"><p><em>([Matthews15, Lemma 0.1], [Manguba-Glover18, Lemma 1])</em> For a <em>reduced</em> positive definite BQF <span class="math inline">\(\left&lt; a, b, c \right&gt;\)</span>, the minimum positive value of <span class="math inline">\(ax^2 + bxy + cy^2\)</span> for <span class="math inline">\(x, y \in \ZZ\)</span> is <span class="math inline">\(a\)</span>, and is achieved when <span class="math inline">\((x, y)\)</span> is:</p><ul><li><span class="math inline">\((\pm 1, 0)\)</span> if <span class="math inline">\(a &lt; c\)</span>;</li><li><span class="math inline">\((\pm 1, 0), (0, \pm 1)\)</span> if <span class="math inline">\(b &lt; a = c\)</span>;</li><li><span class="math inline">\((\pm1, \mp1)\)</span> if <span class="math inline">\(b = a = c\)</span>.</li></ul></div><p>After reducing <span class="math inline">\(\left&lt;an, b-2 \lambda a, \frac{a\lambda^2+b\lambda+c}{n} \right&gt;\)</span> to <span class="math inline">\(\left&lt; a&#39;, b&#39;, c&#39; \right&gt;\)</span> using some <span class="math inline">\(M_{\lambda}\)</span>, we can use the theorem above to determine the solutions of <span class="math inline">\(a&#39;x^2 + b&#39;xy + c&#39;y^2 = 1\)</span>.</p><p>Here is an algorithm to solve the case:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">solve_bqf_neg_disc_primitive</span>(<span class="params">a, b, c, n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;find primitive solutions to ax^2 + bxy + cy^2 = n. </span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    Assume gcd(a, b, c) = 1 and gcd(c, n) = 1, b^2 - 4ac &lt; 0.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    Δ = b*b - <span class="number">4</span>*a*c</span><br><span class="line">    <span class="keyword">assert</span> Δ &lt; <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> λ <span class="keyword">in</span> sqrtmod_all(Δ, n):</span><br><span class="line">        λ = invmod(<span class="number">2</span>*a, n) * (λ - b) % n</span><br><span class="line"></span><br><span class="line">        (a2, b2, c2), M = bqf_neg_disc_reduce((a*n, b - <span class="number">2</span>*a*λ, (a*λ*λ + b*λ + c) // n))</span><br><span class="line">        <span class="keyword">if</span> a2 != <span class="number">1</span>:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># transform back to the original equation</span></span><br><span class="line">        M = [[n, -λ], [<span class="number">0</span>, <span class="number">1</span>]] @ M</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> a2 &lt; c2:</span><br><span class="line">            sols = [[<span class="number">1</span>, <span class="number">0</span>], [-<span class="number">1</span>, <span class="number">0</span>]]</span><br><span class="line">        <span class="keyword">elif</span> b2 &lt; a2:</span><br><span class="line">            sols = [[<span class="number">1</span>, <span class="number">0</span>], [-<span class="number">1</span>, <span class="number">0</span>], [<span class="number">0</span>, <span class="number">1</span>], [<span class="number">0</span>, -<span class="number">1</span>]]</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            sols = [[<span class="number">1</span>, -<span class="number">1</span>], [-<span class="number">1</span>, <span class="number">1</span>]]</span><br><span class="line"></span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">from</span> (M @ s <span class="keyword">for</span> s <span class="keyword">in</span> sols)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="parabola-case-delta-0">Parabola case (<span class="math inline">\(\Delta &gt; 0\)</span>)</h1><p>(This section is mostly taken from [Matthews15] and [MRS17].)</p><h2 id="structure-and-transformation">Structure and transformation</h2><p><em>(Equivalence relation)</em> [Matthews23, Section 2] Let <span class="math inline">\((x^*, y^*)\)</span> be the fundamental solution to <span class="math inline">\(x^2 - \Delta y^2 = 4\)</span>, and <span class="math inline">\((x, y)\)</span> be a solution to <span class="math inline">\(ax^2 + bxy + cy^2 = n\)</span>, then any <span class="math inline">\((x&#39;, y&#39;)\)</span> such that<span class="math display">\[\begin{pmatrix}x&#39;  \\y&#39; \\\end{pmatrix}= \pm U^k \begin{pmatrix}x  \\y\end{pmatrix}, \quad U := \begin{pmatrix}\frac{x^* - by^*}{2}  &amp; -cy^*  \\ay^*  &amp; \frac{x + by^*}{2}\end{pmatrix}.\]</span>for <span class="math inline">\(k \in \ZZ\)</span> is also a solution to <span class="math inline">\(ax^2 + bxy + cy^2 = n\)</span>. Hence we can use the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR3JvdXBfYWN0aW9uI09yYml0c19hbmRfc3RhYmlsaXplcnM=">associated equivalence relation</span> from the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR3JvdXBfYWN0aW9uIw==">group action</span> of a group <span class="math inline">\(\left\{ \pm U^k: k \in \ZZ \right\}\)</span> on the set of all solutions, which is the <em>Stolt equivalence</em>.</p><p><em>(Partition of solution space)</em> Similarly as the ellipse case, we define <span class="math inline">\(\lambda := x y^{-1} \bmod{|n|}\)</span> for a primitive solution <span class="math inline">\((x, y)\)</span> and do the same transformation: write <span class="math inline">\(x = x&#39;n - \lambda y\)</span>, hence<span class="math display">\[ax^2 + bxy + cy^2 = n, \quad \Longleftrightarrow \quad an x&#39;^2 + (b-2\lambda a) x&#39;y + \frac{a\lambda^2 + b\lambda + c}{n}y^2 = 1.\]</span>One can also show ([Stolt57, Theorem 5]) that two primitive solutions are Stolt equivalent if and only if they have the same <span class="math inline">\(\lambda\)</span>, implying that the number of Stolt equivalent classes is the finite.</p><h2 id="find-stolt-fundamental-solutions">Find Stolt fundamental solutions</h2><p>Our goal in this subsection is to find the <em>Stolt fundamental solution</em> for a specific <span class="math inline">\(\lambda\)</span>, which solves the equation<span class="math display">\[an x&#39;^2 + (b-2\lambda a) x&#39;y + \frac{a\lambda^2 + b\lambda + c}{n}y^2 = 1,\]</span>with minimum non-negative <span class="math inline">\(y\)</span> (and maximum <span class="math inline">\(x\)</span> for tie-breaking).</p><p>With abuse of notation, we study the equation <span class="math inline">\(ax^2 + bxy + cy^2 = n\)</span> where <span class="math inline">\(\Delta &gt; 0\)</span> is not a square, <span class="math inline">\(a &gt; 0\)</span> and <span class="math inline">\(n = \pm 1\)</span>. Additionally, we require <span class="math inline">\(y &gt; 0\)</span>. Since <span class="math inline">\(\Delta \equiv 1 \pmod 4\)</span> and <span class="math inline">\(\Delta\)</span> is not a square, we have <span class="math inline">\(\Delta \geq 5\)</span>.</p><p>We first present a very interesting result in [Serret66]:</p><div class="note default"><p><em>([Serret66])</em> Let <span class="math inline">\(0 &lt; n &lt; \sqrt{ \Delta } / 2\)</span>, and let <span class="math inline">\((x, y)\)</span> be a primitive solution to <span class="math inline">\(ax^2 + bxy + cy^2 = n\)</span> with <span class="math inline">\(y &gt; 0\)</span> where <span class="math inline">\(a &gt; 0\)</span>. In addition, let <span class="math inline">\(\omega, \omega&#39; = (-b \pm \sqrt{ \Delta }) / 2a\)</span> be the roots of <span class="math inline">\(ar^2 + br + c = 0\)</span> where <span class="math inline">\(\omega &lt; \omega&#39;\)</span>. Then <span class="math inline">\(p/q\)</span> is a convergent to either <span class="math inline">\(\omega\)</span> or <span class="math inline">\(\omega&#39;\)</span>.</p></div><p>[MRS17] gives a simple proof: Note <span class="math inline">\(a(x / y - \omega)(x / y - \omega&#39;) = n / y^2\)</span>. If <span class="math inline">\(x / y &gt; \omega&#39;\)</span>,<span class="math display">\[\frac{x}{y} - \omega&#39; = \frac{n}{a (x / y - \omega) y^2} &lt; \frac{\sqrt{ \Delta } / 2}{a(\omega&#39; - \omega )y^2} = \frac{1}{2y^2},\]</span>then by <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGlyaWNobGV0JTI3c19hcHByb3hpbWF0aW9uX3RoZW9yZW0=">Dirichlet's approximation theorem</span>, we conclude that <span class="math inline">\(x / y\)</span> is a convergent to <span class="math inline">\(\omega&#39;\)</span>. Similarly, if <span class="math inline">\(x / y &lt; \omega\)</span>, it's a convergent to <span class="math inline">\(\omega\)</span>.</p><p>However, the theorem is no longer true for negative <span class="math inline">\(n\)</span>. [Pavone86] generalized the theorem for<span class="math display">\[|n| =  \mu :=\min\limits_{\substack{(x, y) \in \ZZ^2 \\ (x, y) \neq (0, 0)}} |ax^2 + bxy + cy^2|,\]</span>where he proved that Serret's theorem still holds unless <span class="math inline">\(\Delta = 5\)</span>, <span class="math inline">\(n &lt; 0\)</span>, in which case <span class="math inline">\(x / y\)</span> can also be a special <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2ltcGxlX2NvbnRpbnVlZF9mcmFjdGlvbiNTZW1pY29udmVyZ2VudHM=">semiconvergent</span> of both <span class="math inline">\(\omega\)</span> and <span class="math inline">\(\omega&#39;\)</span>. [MRS17] further generalizes [Pavone86]'s result, allowing <span class="math inline">\(|n| &lt; \sqrt{ \Delta } / 2\)</span>.</p><p>Anyway, [Pavone86]'s result is enough for us since we only care about the case <span class="math inline">\(n = \pm 1\)</span>. One way to find the fundamental solution is to examine the candidates from the following categories to see if they solve the equation, and pick the most fundamental one:</p><ol type="1"><li>Check the smallest convergent of <span class="math inline">\(\omega\)</span> before the first period ends;</li><li>Check the smallest convergent of <span class="math inline">\(\omega&#39;\)</span> before the first period ends;</li><li>If <span class="math inline">\(\Delta = 5\)</span> and <span class="math inline">\(n &lt; 0\)</span>, also check the special semi-convergent.</li></ol><p>[Matthews02] also designed an algorithm based on [Pavone86], but added many optimizations.</p><h1 id="references">References</h1><p>General:</p><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL1BERlMvcXVhZHIucGRm">Robertson09</span>: Robertson, J., 2009. Computing in quadratic orders. <em>at jpr2718. org</em>.</li></ul><p>Ellipse case (<span class="math inline">\(\Delta &lt; 0\)</span>):</p><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL1BERlMvcG9zcmVwLnBkZg==">Matthews15</span>: Matthews, K.R., 2015. <em>On a transformation of Lagrange</em> [online]</li><li><span class="exturl" data-url="aHR0cHM6Ly9jcy51d2F0ZXJsb28uY2Evam91cm5hbHMvSklTL1ZPTDE3L01hdHRoZXdzL21hdHQxMC5wZGY=">Matthews14</span>: Matthews, K.R., 2014. Lagrange's Algorithm Revisited: Solving <span class="math inline">\(at^2+ btu+ cu^2= n\)</span> in the case of negative discriminant. <em>Journal of Integer Sequences</em>, <em>17</em>(11).<ul><li>It uses continued fraction and is more complicated. Also need to handle a few exceptional cases.</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9tYXRoLmhhd2FpaS5lZHUvfmttYW5ndWJhL21hc3RlcnNwcm9qZWN0LnBkZg==">Manguba-Glover18</span>: Manguba-Glover, K., 2018. <em>Reduction Theory of Binary Quadratic Forms</em> (Doctoral dissertation, University of Hawai ‘i at Manoa).</li></ul><p>Parabola case (<span class="math inline">\(\Delta &gt; 0\)</span>):</p><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL1BERlMvc3RvbHQucGRm">Matthews23</span>: Matthews, K., 2023. Finding the Fundamental Solutions of <span class="math inline">\(ax^2+ bxy+ cy^2= n\)</span>.<ul><li>Notes on Stolt equivalence.</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9wcm9qZWN0ZXVjbGlkLm9yZy9qb3VybmFscy9yb2NreS1tb3VudGFpbi1qb3VybmFsLW9mLW1hdGhlbWF0aWNzL3ZvbHVtZS01MS9pc3N1ZS00L09uLXNvbHZpbmctYS1iaW5hcnktcXVhZHJhdGljLURpb3BoYW50aW5lLWVxdWF0aW9uLzEwLjEyMTYvcm1qLTIwMjEtNTEtMTM2OS5zaG9ydA==">MR21</span>: Matthews, K.R. and Robertson, J.P., 2021. On solving a binary quadratic Diophantine equation. <em>Rocky Mountain Journal of Mathematics</em>, <em>51</em>(4), pp.1369-1385.<ul><li>Unfortunately it's paywalled so I haven't read it yet. My guess is that it's pretty similar to [Matthews23].</li></ul></li><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1kYW0ub3JnL2l0ZW0/aWQ9SlROQl8yMDAyX18xNF8xXzI1N18w">Matthews02</span>: Matthews, K., 2002. The diophantine equation <span class="math inline">\(ax^ 2+ bxy+ cy^ 2= N\)</span>, <span class="math inline">\(D= b^ 2-4ac&gt;0\)</span>. <em>Journal de théorie des nombres de Bordeaux</em>, <em>14</em>(1), pp.257-270.<ul><li>Continued fraction-based algorithm.</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuaW1wYW4ucGwvZW4vcHVibGlzaGluZy1ob3VzZS9qb3VybmFscy1hbmQtc2VyaWVzL2FjdGEtYXJpdGhtZXRpY2EvYWxsLzE2OS8zLzgyMzQzL29uLWZ1bmRhbWVudGFsLXNvbHV0aW9ucy1vZi1iaW5hcnktcXVhZHJhdGljLWZvcm0tZXF1YXRpb25z">MRS15</span>: Matthews, K.R., Robertson, J.P. and Srinivasan, A., 2015. On fundamental solutions of binary quadratic form equations.<ul><li>This bounds the scale of the Stolt fundamental solutions.</li></ul></li><li><span class="exturl" data-url="aHR0cHM6Ly9jcy51d2F0ZXJsb28uY2Evam91cm5hbHMvSklTL1ZPTDIwL01hdHRoZXdzL21hdHRoZXdzMTEuaHRtbA==">MRS17</span>: Matthews, K.R., Robertson, J.P. and Srinivasan, A., 2017. Extending Theorems of Serret and Pavone. <em>J. Integer Seq.</em>, <em>20</em>(10), pp.17-10.</li><li>[Serret66] : Serret, J.A., 1866. <em>Cours d'algèbre supérieure</em> (Vol. 1). Gauthier Villars.</li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS8wMDIyMzE0WDg2OTAwOTZY">Pavone86</span>: Pavone, M., 1986. A Remark on a Theorem of Serret. <em>Journal of Number Theory</em>, <em>23</em>(2), pp.268-278.</li><li><span class="exturl" data-url="aHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9hcnRpY2xlLzEwLjEwMDcvQkYwMjU4OTQzMA==">Stolt57</span>: Stolt, B., 1957. On a Diophantine equation of the second degree. Arkiv för Matematik, 3(4), pp.381-390.</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;In this post, we are interested in finding all &lt;em&gt;&lt;strong&gt;integer&lt;/strong&gt;&lt;/em&gt; solutions of the following equation:
&lt;span class=&quot;math display&quot;&gt;&#92;[
&#92;begin{equation}
ax^2 + bxy + cy^2 = n.
%&#92;label{eq:ibqf}
&#92;end{equation}
&#92;]&lt;/span&gt;
where &lt;span class=&quot;math inline&quot;&gt;&#92;(acn &#92;neq 0&#92;)&lt;/span&gt;, &lt;span class=&quot;math inline&quot;&gt;&#92;(b^2 - 4ac &#92;neq 0&#92;)&lt;/span&gt;. This equation can certainly be solved using methods from the &lt;a href=&quot;/2025/03/15/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-4-Pell-equation-and-generalized-Pell-equation/#generalized-pell-equation-x2--d-y2-n&quot;&gt;generalized Pell equation&lt;/a&gt;, but here, we explore other methods.&lt;/p&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    <category term="Number Theory" scheme="https://www.roosephu.net/categories/CS-Math/Number-Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>Integer and Rational Solutions of a Binary Quadratic Equation (4): Pell equation and generalized Pell equation</title>
    <link href="https://www.roosephu.net/2025/03/15/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-4-Pell-equation-and-generalized-Pell-equation/"/>
    <id>https://www.roosephu.net/2025/03/15/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-4-Pell-equation-and-generalized-Pell-equation/</id>
    <published>2025-03-15T12:03:16.000Z</published>
    <updated>2025-03-15T12:03:16.000Z</updated>
    
    <content type="html"><![CDATA[<p>In this post, we are interested in finding all <em><strong>integer</strong></em> solutions of <em>Pell equation</em><span class="math display">\[\begin{equation}x^2 - d y^2 = 1, \label{eq:pell} % \tag{Pell Equation}\end{equation}\]</span>and <em>generalized Pell equation</em><span class="math display">\[\begin{equation} x^2 - d y^2 = n, \label{eq:generalized-pell} % \tag{Generalized Pell Equation}\end{equation}\]</span>for a non-square integer <span class="math inline">\(d &gt; 0\)</span> and <span class="math inline">\(n \neq 0\)</span>.</p><span id="more"></span><h1 id="connection-to-zzsqrt-d">Connection to <span class="math inline">\(\ZZ[\sqrt{ d }]\)</span></h1><p>For any element <span class="math inline">\(s = x + y \sqrt{ d }\)</span> in <span class="math inline">\(\ZZ[\sqrt{ d }]\)</span>, we define <span class="math inline">\(\bar{s} := x - y \sqrt{ d }\)</span>. It is easy to show that <span class="math inline">\(\bar{s} \bar{t} = \overline{st}\)</span> for any <span class="math inline">\(s, t \in \ZZ[\sqrt{d}]\)</span>.</p><p>Note that for <span class="math inline">\(s = x + y \sqrt{ d }\)</span>, <span class="math inline">\(x^2 - dy^2 = s \bar{s}.\)</span> Now we define<span class="math display">\[S_{n} := \{ s \in \ZZ[\sqrt{ d }]: s \bar{s} = n \}.\]</span>so solving Pell equations is equivalent to calculating <span class="math inline">\(S_1\)</span> and solving generalized Pell equations is equivalent to calculating <span class="math inline">\(S_n\)</span>.</p><p>Most references I found online use the <span class="math inline">\((x, y)\)</span> notation, but in certain cases, it might be cleaner to present the results using operations in <span class="math inline">\(\ZZ[\sqrt{ d }]\)</span>, especially when presenting the structure of solutions.</p><h1 id="primitive-solutions">Primitive solutions</h1><p><em>(Primitive solutions)</em> A solution <span class="math inline">\((x, y)\)</span> is <em>primitive</em> if <span class="math inline">\(\gcd(x, y) = 1\)</span>. If <span class="math inline">\((x, y)\)</span> is primitive, <span class="math inline">\(\gcd(x, n) = \gcd(y, n) = 1\)</span>.</p><p>Similar to <a href="/2025/03/11/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-3-ax-2-by-2-c/">solving <span class="math inline">\(ax^2 + by^2 = n\)</span></a>, once we have an algorithm to find all primitive solutions, we can derive all solutions. Let <span class="math inline">\(S&#39;_n\)</span> be the set of all primitive solutions of <span class="math inline">\(x^2 - d y^2 = n\)</span>:<span class="math display">\[S&#39;_{n}:= \left\{ s\in \ZZ[\sqrt{ d }]: s \bar{s} = n, \quad s = x + y \sqrt{ d }, \quad \gcd(x, y) = 1 \right\},\]</span>then it is straightforward to see that<span class="math display">\[S_{n} = \left\{ gs: g \in \NN, \quad g^2 | n, \quad s \in S&#39;_{n / g^2} \right\} .\]</span>Clearly, <span class="math inline">\(S_1 = S&#39;_1\)</span>.</p><h1 id="pell-equation-x2---d-y2-1">Pell equation <span class="math inline">\(x^2 - d y^2 = 1\)</span></h1><p>The Pell equation has been studied extensively over time. See [Lenstra02] for a comprehensive overview. There are so many tutorials (e.g. [Conrad22A] and [Conrad22B]) on solving Pell equations, so here I only list a few key conclusions and the algorithm.</p><p><em>(Structure of solutions)</em> It follows from <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGlyaWNobGV0JTI3c191bml0X3RoZW9yZW0=">Dirichlet's unit theorem</span> that <span class="math inline">\(S_{1}\)</span> has the structure<span class="math display">\[S_{1} = \{ \pm s^{*k}: k \in \ZZ \},\]</span>for a unique <em>fundamental solution</em> <span class="math inline">\(s^* := x^* + y^* \sqrt{d}\)</span> where <span class="math inline">\(x^* &gt; 0, y^* &gt; 0\)</span>. Note <span class="math inline">\(n\)</span> can be negative, as <span class="math inline">\(\bar{s} = s^{*-1}\)</span>. See [Conrad22A, Theorem 5.3] for proof.</p><p><em>(Fundamental solution).</em> It is also shown that <span class="math inline">\(x^*/y^*\)</span> must be a <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2ltcGxlX2NvbnRpbnVlZF9mcmFjdGlvbiNDb252ZXJnZW50cw==">convergent</span> of <span class="math inline">\(\sqrt{d}\)</span>. We can use the <em>PQa</em> algorithm below to find all convergents. See [Conrad22B, Theorem 5.1] for proof.</p><p><em>(PQa algorithm)</em> Given <span class="math inline">\(p_0, q_0\)</span> and <span class="math inline">\(d\)</span> such that <span class="math inline">\(p_0^2 \equiv d \pmod {q_{0}}\)</span>, the <em>PQa</em> algorithm computes the continued fraction <span class="math inline">\([ a_0, a_1, a_2, \dots]\)</span> of <span class="math inline">\((p_0 + \sqrt{d}) / q_0\)</span>. It works as follows:<span class="math display">\[\begin{aligned}a_i &amp; = \floor{(p_i + \sqrt{d}) / q_i}, \\p_{i+1} &amp; = a_i q_i - p_i, \\q_{i+1} &amp; = (d -p_{i+1}^2)/q_i. \\\end{aligned}\]</span>Note that the division for calculating <span class="math inline">\(q_{i+1}\)</span> is always exact.</p><p>Below is an implementation of the PQa algorithm optimized for solving the Pell equation. See [Robertson04] for more details.</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">pell</span>(<span class="params">d</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;find the fundamental solution of x^2 - dy^2 = 1 and -1&quot;&quot;&quot;</span></span><br><span class="line">    p, q = <span class="number">0</span>, <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    sqrt_d = <span class="built_in">int</span>(d ** <span class="number">0.5</span>)</span><br><span class="line">    x0, x1 = <span class="number">0</span>, <span class="number">1</span></span><br><span class="line">    y0, y1 = <span class="number">1</span>, <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    l = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> l == <span class="number">0</span> <span class="keyword">or</span> q != <span class="number">1</span>:</span><br><span class="line">        a = (p + sqrt_d) // q</span><br><span class="line">        p = a * q - p</span><br><span class="line">        q = (d - p ** <span class="number">2</span>) // q</span><br><span class="line">        l += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        x0, x1 = x1, a * x1 + x0</span><br><span class="line">        y0, y1 = y1, a * y1 + y0</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> l % <span class="number">2</span> == <span class="number">1</span>:</span><br><span class="line">        pos = x1 * x1 + y1 * y1 * d, x1 * y1 * <span class="number">2</span></span><br><span class="line">        neg = x1, y1</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        pos = x1, y1</span><br><span class="line">        neg = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> pos, neg</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="generalized-pell-equation-x2--d-y2-n">Generalized Pell equation <span class="math inline">\(x^2 -d y^2 = n\)</span></h1><p>(The idea in this section is taken from [Matthews00].)</p><p><em>(Nagell equivalence)</em> For any <span class="math inline">\(s \in S&#39;_{n}\)</span> and <span class="math inline">\(t \in S&#39;_1\)</span>, we observe<span class="math display">\[(s t) \overline{(st)} = (s \overline{s})(t \overline{t}) = n, \quad (st) \bar{t} = s \in S&#39;_{n},\]</span>implying that <span class="math inline">\(s t \in S&#39;_{n}\)</span>. This motivates us to define equivalence relation, the <em>Nagell equivalence</em>, for elements in <span class="math inline">\(S&#39;_{n}\)</span>: Two solutions <span class="math inline">\(s \sim s&#39;\)</span> if and only if there exists an element <span class="math inline">\(t \in S&#39;_{1}\)</span>, such that <span class="math inline">\(st = s&#39;\)</span>.</p><p><em>(Partition of solution space)</em> Let <span class="math inline">\(s = x + y \sqrt{ d }\)</span> and <span class="math inline">\(s&#39; = x&#39; + y&#39; \sqrt{ d }\)</span> be two primitive solutions, and define <span class="math inline">\(\lambda(s) := -x y^{-1} \bmod |n|\)</span>. Since <span class="math inline">\(\lambda(s)^2 \equiv d \pmod {|n|}\)</span>, it follows that<span class="math display">\[\begin{aligned}s \sim s&#39; \quad &amp; \Longleftrightarrow \quad s s&#39;^{-1} \in \ZZ[\sqrt{ d }] \\&amp; \Longleftrightarrow \quad x x&#39; \equiv yy&#39; d \pmod{|n|}, \quad xy&#39; \equiv x&#39; y \pmod {|n|} \\&amp; \Longleftrightarrow \quad \tau(s) = \tau(s&#39;).\end{aligned}\]</span>So an alternative definition of Nagall equivalence for primitive solutions is that <span class="math inline">\(s \sim s&#39;\)</span> if and only if <span class="math inline">\(\lambda(s) = \lambda(s&#39;)\)</span>.</p><p><em>(Structure of solutions)</em> The alternative definition of Nagall equivalence directly implies the number of equivalent classes in <span class="math inline">\(S&#39;_n\)</span> is finite, that is, there exists a finite set <span class="math inline">\(F_{n} \subset \ZZ[\sqrt{ d }]\)</span> such that<span class="math display">\[S&#39;_{n} = \left\{f t: f \in F_{n}, t \in S&#39;_{1} \right\} = \left\{ \pm f s^{*n}: F \in F_{n, }n \in \ZZ \right\} ,\]</span>where <span class="math inline">\(s^*\)</span> is the fundamental solution of <span class="math inline">\(x^2 - dy^2 = 1\)</span>.</p><p><em>(Finding a minimum positive solution)</em> Now we focus on one equivalence class, represented by <span class="math inline">\(\lambda \equiv -x y^{-1} \pmod{|n|}\)</span> (with abuse of notation) where <span class="math inline">\(-|n| / 2 &lt; \lambda \leq |n| / 2\)</span>. If we write <span class="math inline">\(x = x&#39; |n| - \lambda y\)</span>, it is proved in [Matthews00, Theorem 1] that <span class="math inline">\(x&#39; / y\)</span> is a convergent of <span class="math inline">\((\lambda + \sqrt{ d }) / |n|\)</span>. Thus, we can find a <em>minimum positive solution</em> <span class="math inline">\(s = x + y \sqrt{ d }\)</span> with smallest positive <span class="math inline">\(x\)</span> and <span class="math inline">\(y\)</span> using the <em>PQa</em> algorithm.</p><p><em>(Finding a fundamental solution)</em> It might be more convenient to find a <em>fundamental solution</em> for an equivalence class, with smallest positive <span class="math inline">\(y\)</span>. If multiple solutions have the same smallest positive <span class="math inline">\(y\)</span>, choose the one with positive <span class="math inline">\(x\)</span>. A fundamental solution can be found as follows:</p><ul><li>If <span class="math inline">\(\lambda \in \{ 0, n / 2\}\)</span>, the minimum positive solution is the fundamental solution;</li><li>Otherwise, let <span class="math inline">\((x, y)\)</span> and <span class="math inline">\((x&#39;, y&#39;)\)</span> be the minimum positive solutions of <span class="math inline">\(\lambda\)</span> and <span class="math inline">\(-\lambda\)</span>. If <span class="math inline">\(y &lt; y&#39;\)</span>, the fundamental solutions are <span class="math inline">\((x, y)\)</span> and <span class="math inline">\((-x, y)\)</span>; otherwise they're <span class="math inline">\((-x&#39;, y&#39;)\)</span> and <span class="math inline">\((x&#39;, y&#39;)\)</span>.</li></ul><p>The algorithm is almost the same as [Mollin01, Algorithm 4.1]. The <em>Lagrange-Matthews-Mollin</em> (LMM) algorithm, proposed in [Matthews00, Section 5], improves the process by considering properties of the continued fraction of <span class="math inline">\((\lambda + \sqrt{ d }) / |n|\)</span>.</p><p>Here is the pseudocode:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">solve_generalized_pell_primitive</span>(<span class="params">d, n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;find all fundamental solutions of x^2 - d y^2 = n&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> λ <span class="keyword">in</span> sqrtmod_all(d, n):</span><br><span class="line">        <span class="keyword">if</span> (s := PQa(d, λ, n)) <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> λ == <span class="number">0</span> <span class="keyword">or</span> λ * <span class="number">2</span> == n:</span><br><span class="line">            <span class="keyword">yield</span> s</span><br><span class="line">        <span class="keyword">elif</span> λ * <span class="number">2</span> &lt; n:</span><br><span class="line">            p1, q1 = s</span><br><span class="line">            p2, q2 = PQa(d, -λ, n)</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> q1 &lt; q2:</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> (p1, q1), (-p1, q1)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> (p2, q2), (-p2, q2)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="a-special-generalized-pell-equation-x2---dy2-4">A special generalized Pell equation <span class="math inline">\(x^2 - dy^2 = 4\)</span></h1><p>We also discuss special generalized Pell equation <span class="math inline">\(x^2 - dy^2 = 4\)</span>, as it also shares many properties as the Pell equation <span class="math inline">\(x^2 - dy^2 = 1\)</span>.</p><p>As we have mentioned above, the set <span class="math inline">\(S_1\)</span> has a special structure <span class="math inline">\(S_1 = \{s_{1}^{*n}: n \in \ZZ\}\)</span> for a fundamental solution <span class="math inline">\(s_{1}^*\)</span>. Similarly, the solutions for <span class="math inline">\(x^2 - dy^2 = 4\)</span> have a similar structure<span class="math display">\[T_{4} = \left\{ \pm t_{4}^{*k}: k \in \ZZ \right\}, \quad T_{4} := \left\{ \frac{x+y\sqrt{ d }}{2} : x^2 - dy^2 = 4 \right\}.\]</span>for a fundamental solution <span class="math inline">\(t_4^* = \frac{x^* + y^* \sqrt{ d }}{2}\)</span> where <span class="math inline">\((x^*, y^*)\)</span> is minimum positive solution to <span class="math inline">\(x^2 - dy^2 = 4\)</span> [Stolt57, p.4]. There is actually a connection between <span class="math inline">\(s_{1}^{*}\)</span> and <span class="math inline">\(t_4^{*}\)</span>: <span class="math inline">\(s_1^{*} = t_{4}^{*k}\)</span> for <span class="math inline">\(k \in \left\{ 1,2,3 \right\}\)</span> ([Stolt57, p.4], [Matthews23, Theorem 1.1]).</p><p><em>(Equivalence relation)</em> Observing that <em>Negall equivalence</em> is the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR3JvdXBfYWN0aW9uI09yYml0c19hbmRfc3RhYmlsaXplcnM=">associated equivalence relation</span> of the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR3JvdXBfYWN0aW9u">group action</span> of group <span class="math inline">\(\{\pm s_1^{*k}: k \in \ZZ\}\)</span> on the set <span class="math inline">\(S&#39;_n\)</span>. As <span class="math inline">\(T_4\)</span> is also a group, we can define a similar equivalence relation, the <em>Stolt equivalence</em>. In some sense, the Stolt equivalence is more fundamental than the Negall equivalence, in the sense that <span class="math inline">\(s_1^*\)</span> can be generated by <span class="math inline">\(t_4^*\)</span> but <span class="math inline">\(t_4^*\)</span> might not be generated by <span class="math inline">\(s_1^*\)</span>. As a consequence, the Stolt equivalence might have fewer equivalence classes than the Negall equivalence.</p><p>To find the fundamental solution <span class="math inline">\(t_4^*\)</span>, check out [Johnson04] for an efficient algorithm implemented below:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">pell4</span>(<span class="params">d</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;find the mininum positive solution to x^2 - d y^2 = 4 or -4&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> d % <span class="number">4</span> == <span class="number">1</span>:</span><br><span class="line">        p, q = <span class="number">1</span>, <span class="number">2</span></span><br><span class="line"></span><br><span class="line">        sqrt_d = <span class="built_in">int</span>(d ** <span class="number">0.5</span>)</span><br><span class="line">        x0, x1 = -<span class="number">1</span>, <span class="number">2</span></span><br><span class="line">        y0, y1 = <span class="number">1</span>, <span class="number">0</span></span><br><span class="line"></span><br><span class="line">        l = <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> l == <span class="number">0</span> <span class="keyword">or</span> q != <span class="number">2</span>:</span><br><span class="line">            a = (p + sqrt_d) // q</span><br><span class="line">            p = a * q - p</span><br><span class="line">            q = (d - p ** <span class="number">2</span>) // q</span><br><span class="line">            l += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            x0, x1 = x1, a * x1 + x0</span><br><span class="line">            y0, y1 = y1, a * y1 + y0</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> l % <span class="number">2</span> == <span class="number">1</span>:</span><br><span class="line">            pos = (x1 * x1 + y1 * y1 * d) // <span class="number">2</span>, x1 * y1</span><br><span class="line">            neg = x1, y1</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            pos = x1, y1</span><br><span class="line">            neg = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">elif</span> d % <span class="number">4</span> == <span class="number">0</span>:</span><br><span class="line">        pos, neg = pell(d // <span class="number">4</span>)</span><br><span class="line">        pos = (pos[<span class="number">0</span>] * <span class="number">2</span>, pos[<span class="number">1</span>])</span><br><span class="line">        <span class="keyword">if</span> neg:</span><br><span class="line">            neg = (neg[<span class="number">0</span>] * <span class="number">2</span>, neg[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        pos, neg = pell(d)</span><br><span class="line">        pos = (pos[<span class="number">0</span>] * <span class="number">2</span>, pos[<span class="number">1</span>] * <span class="number">2</span>)</span><br><span class="line">        <span class="keyword">if</span> neg:</span><br><span class="line">            neg = (neg[<span class="number">0</span>] * <span class="number">2</span>, neg[<span class="number">1</span>] * <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> pos, neg</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="references">References</h1><ul><li><p><span class="exturl" data-url="aHR0cHM6Ly90aGlsaW5hYXRzeW1weS53b3JkcHJlc3MuY29tLzIwMTMvMDYvMjkvcXVhZHJhdGljLWRpb3BoYW50aW5lLWVxdWF0aW9uLWkv">Rathnayake13</span>: Rathnayake T., 2013. Quadratic Diophantine equation – I [online]</p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYW1zLm9yZy9ub3RpY2VzLzIwMDIwMi9mZWEtbGVuc3RyYS5wZGY=">Lenstra02</span>: Lenstra Jr, H.W., 2002. Solving the Pell equation. <em>Notices of the AMS</em>, <em>49</em>(2), pp.182-192.</p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9rY29ucmFkLm1hdGgudWNvbm4uZWR1L2JsdXJicy91Z3JhZG51bXRoeS9wZWxsZXFuMS5wZGY=">Conrad22A</span>: Conrad, K., 2022. <em>Pell’s equation, I</em> [online]</p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9rY29ucmFkLm1hdGgudWNvbm4uZWR1L2JsdXJicy91Z3JhZG51bXRoeS9wZWxsZXFuMi5wZGY=">Conrad22B</span>: Conrad, K., 2022. <em>Pell’s equation, II</em> [online]</p></li><li><p><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL1BERlMvcGF0el9pbXByb3ZlZC5wZGY=">Matthews00</span>: Matthews, K., 2000. The Diophantine Equation <span class="math inline">\(x^ 2-Dy^ 2= N\)</span>, <span class="math inline">\(D&gt; 0\)</span>. <em>Expositiones Mathematicae</em>, <em>18</em>(4), pp.323-332.</p><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy5udW1iZXJ0aGVvcnkub3JnL1BERlMvdGFsa19lZGluYnVyZ2hfMjAwNC5wZGY=">slides</span>.</li></ul></li><li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wdWJsaWNhdGlvbi8zNTQ0NDc1MzVfT05fVEhFX1NUVURZX09GX1FVQURSQVRJQ19ESU9QSEFOVElORV9FUVVBVElPTlM=">Tamang21</span>: Tamang, B.B., 2021. <em>On the study of quadratic Diophantine equations</em> (Doctoral dissertation, Tribhuvan University Kathmandu).</p><ul><li>Discussed this topic in more details and shows more examples.</li></ul></li><li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMDcyMzA4NjkwMTgwMDE1Mw==">Mollin01</span>: Mollin, R.A., 2001. Simple continued fraction solutions for Diophantine equations. <em>Expositiones Mathematicae</em>, <em>19</em>(1), pp.55-73.</p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9jaXRlc2VlcnguaXN0LnBzdS5lZHUvZG9jdW1lbnQ/cmVwaWQ9cmVwMSZ0eXBlPXBkZiZkb2k9NWFjMzRhMzQ0ZWUzNDY4NTUxODRmZjk0OWVlYWVkMTg2ODViMTU1Yw==">Robertson04</span>: Robertson, J.P., 2004. Solving the generalized Pell equation <span class="math inline">\(x^2− Dy^2= N\)</span>. <em>Unpublished manuscript</em>.</p><ul><li>It gives a complete description of the LMM algorithm.</li></ul></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9hcnRpY2xlLzEwLjEwMDcvQkYwMjU4OTQzMA==">Stolt57</span>: Stolt, B., 1957. On a Diophantine equation of the second degree. Arkiv för Matematik, 3(4), pp.381-390.</p></li></ul><hr /><p>Update <span class="citation" data-cites="2025-03-20">@2025-03-20</span>: Added the discussion of <span class="math inline">\(x^2 - dy^2 = 4\)</span> and the optimized algorithm for the Pell equation.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;In this post, we are interested in finding all &lt;em&gt;&lt;strong&gt;integer&lt;/strong&gt;&lt;/em&gt; solutions of &lt;em&gt;Pell equation&lt;/em&gt;
&lt;span class=&quot;math display&quot;&gt;&#92;[
&#92;begin{equation}
x^2 - d y^2 = 1, &#92;label{eq:pell} % &#92;tag{Pell Equation}
&#92;end{equation}
&#92;]&lt;/span&gt;
and &lt;em&gt;generalized Pell equation&lt;/em&gt;
&lt;span class=&quot;math display&quot;&gt;&#92;[
&#92;begin{equation} x^2 - d y^2 = n, &#92;label{eq:generalized-pell} % &#92;tag{Generalized Pell Equation}
&#92;end{equation}
&#92;]&lt;/span&gt;
for a non-square integer &lt;span class=&quot;math inline&quot;&gt;&#92;(d &amp;gt; 0&#92;)&lt;/span&gt; and &lt;span class=&quot;math inline&quot;&gt;&#92;(n &#92;neq 0&#92;)&lt;/span&gt;.&lt;/p&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    <category term="Number Theory" scheme="https://www.roosephu.net/categories/CS-Math/Number-Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>Integer and Rational Solutions of a Binary Quadratic Equation (3): $ax^2+by^2=n$</title>
    <link href="https://www.roosephu.net/2025/03/11/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-3-ax-2-by-2-c/"/>
    <id>https://www.roosephu.net/2025/03/11/Integer-and-Rational-Solutions-of-a-Binary-Quadratic-Equation-3-ax-2-by-2-c/</id>
    <published>2025-03-11T09:45:32.000Z</published>
    <updated>2025-03-11T09:45:32.000Z</updated>
    
    <content type="html"><![CDATA[<p>In this post, we're interested in finding all <em><strong>integer</strong></em> solutions of a binary quadratic equation:<span class="math display">\[\begin{equation}ax^2 + by^2 = n,\label{eq:ellipse}\end{equation}\]</span>for <span class="math inline">\(a, b, n &gt; 0\)</span>.</p><span id="more"></span><h1 id="normalization">Normalization</h1><p><em>(Pairwise coprime)</em> Equation <span class="math inline">\(\eqref{eq:ellipse}\)</span> is <em>reduced</em> if <span class="math inline">\(a, b, n\)</span> are pairwise coprime. We can reduce it as follows:</p><ul><li>If <span class="math inline">\(p | a\)</span> and <span class="math inline">\(p | b\)</span>, then <span class="math inline">\(p\)</span> must divide <span class="math inline">\(n\)</span> (otherwise there is no solution), so we can reduce it to <span class="math inline">\((a / p, b / p, n / p)\)</span>.</li><li>If <span class="math inline">\(p | a\)</span> and <span class="math inline">\(p | n\)</span> but <span class="math inline">\(p \nmid b\)</span>, then <span class="math inline">\(p | y\)</span>, so we can reduce it to <span class="math inline">\((a / p, yp, n / p)\)</span>.</li><li>Similarly for <span class="math inline">\(p | b\)</span> and <span class="math inline">\(p | n\)</span> but <span class="math inline">\(p \nmid a\)</span>.</li></ul><p>Every time we make a reduction, <span class="math inline">\(|abn|\)</span> decreases so it must terminate with a reduced equation.</p><p><em>(Primitive solutions)</em> A solution <span class="math inline">\((x, y)\)</span> is <em>primitive</em> if and only if <span class="math inline">\(\gcd(x, y) = 1\)</span>. It's trivial to use an algorithm which only finds all primitive solutions to find all solutions: If <span class="math inline">\(g := \gcd(x, y) &gt; 1\)</span>, then <span class="math inline">\((x / g, y / g)\)</span> is a primitive solution to the equation <span class="math inline">\(ax^2 + by^2 = n/g^2\)</span>.</p><p>From this point forward, we assume that Equation <span class="math inline">\(\eqref{eq:ellipse}\)</span> is reduced unless stated otherwise, and we focus on the primitive solutions:</p><div class="note default"><p>For pairwise coprime integers <span class="math inline">\(a, b, n &gt; 0\)</span>, find all <em>primitive</em> solutions <span class="math inline">\((x, y)\)</span> such that</p><p><span class="math display">\[ax^2 + by^2 = n,\]</span></p></div><h1 id="method-1-lattice">Method 1: Lattice</h1><p><em>(Partition of solution space)</em> We take the idea from a method on <a href="/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/">solving Legendre equations</a>. Let <span class="math inline">\(\mathcal{S}\)</span> be a set of integer points in <span class="math inline">\(\ZZ^2\)</span>:<span class="math display">\[S = \{(x, y): a x^2 + by^2 \equiv 0 \pmod n, \quad \gcd(x, y) = 1\}.\]</span>Obviously, <span class="math inline">\(S\)</span> contains all solutions to Equation <span class="math inline">\(\eqref{eq:ellipse}\)</span>. We may partition <span class="math inline">\(S\)</span> into subsets: Let <span class="math inline">\(\Lambda_{g} = \{\lambda: \lambda^2 \equiv -ab \pmod{n}\}\)</span>, then<span class="math display">\[S = \bigcup_{\lambda \in \Lambda} S_{\lambda}, \quad S_{\lambda} := \{(x, y): a x \equiv \lambda y \pmod{n}, \quad \gcd(x, y) = 1\}.\]</span>Next, we need to determine the solutions in each individual <span class="math inline">\(S_{\lambda}\)</span>.</p><p><em>(Lattice)</em> To find the solutions in <span class="math inline">\(S_{\lambda}\)</span>, we do it in a superset of <span class="math inline">\(S_{\lambda}\)</span>, denoted as <span class="math inline">\(L_{\lambda}\)</span>, and check if the solution is in <span class="math inline">\(L_{\lambda}\)</span>. The superset <span class="math inline">\(L_{\lambda}\)</span> is defined as:<span class="math display">\[L_{\lambda} := \{(x, y): a x \equiv \lambda y \pmod{n}\}.\]</span>It turns out that <span class="math inline">\(L_{\lambda}\)</span> is a <em>lattice</em> with basis <span class="math inline">\(\mathbf{u} = (n, 0)\)</span>, <span class="math inline">\(\mathbf{v} = (\lambda a^{-1} \bmod{n}, 1)\)</span>. It's also easy to check that every point <span class="math inline">\((x, y)\)</span> in <span class="math inline">\(L_{\lambda}\)</span> satisfies <span class="math inline">\(ax^2 + by^2 \equiv 0 \pmod n\)</span>, thus, checking all minimizers of <span class="math inline">\(ax^2 + by^2\)</span> in <span class="math inline">\(L_{\lambda}\)</span> suffices to find all solutions to Equation <span class="math inline">\(\eqref{eq:ellipse}\)</span>.</p><p><em>(Shortest vector)</em> As in <a href="/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/">solving Legendre equation</a>, we can use the same lemma for 3D lattice to efficiently find the shortest vector after reducing the basis by either <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGVuc3RyYSVFMiU4MCU5M0xlbnN0cmElRTIlODAlOTNMb3YlQzMlQTFzel9sYXR0aWNlX2Jhc2lzX3JlZHVjdGlvbl9hbGdvcml0aG0=">Lenstra–Lenstra–Lovász algorithm</span> or <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGF0dGljZV9yZWR1Y3Rpb24jSW5fdHdvX2RpbWVuc2lvbnM=">Lagrange-Gauss algorithm</span>.</p><div class="note default"><p>Let <span class="math inline">\(\mathcal{L} = \{\mathbf{w} \mathbf{B}: \mathbf{w} \in \ZZ^2\}\)</span> be a 2D lattice where the (row-based) basis <span class="math inline">\(\mathbf{B}\)</span> is LLL-reduced. Then all shortest vectors in <span class="math inline">\(\mathcal{L}\)</span> have form of <span class="math inline">\(\mathbf{w} \mathbf{B}\)</span> where <span class="math inline">\(\mathbf{w} \in \{-1, 0, 1\}^2\)</span>.</p></div><p>The following is the pseudocode for the complete algorithm. We need an efficient algorithm to solve <span class="math inline">\(\lambda^2 \equiv -ab \pmod n\)</span>, which was <a href="/2025/03/09/Integer-Solutions-of-a-Binary-Quadratic-Equation-Quadratic-Residue/">covered before</a>. It's also interesting to explore whether it is possible to avoid repeatly solving <span class="math inline">\(\lambda\)</span>'s but that is left for future work. (Well, probably there won't be any future work because the other method is much easier...)</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">solve_primitive</span>(<span class="params">a, b, n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;find all *primitive* solutions of (x, y) such that a x^2 + b y^2 = n&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">assert</span> gcd(a, b) == <span class="number">1</span> <span class="keyword">and</span> gcd(b, n) == <span class="number">1</span> <span class="keyword">and</span> gcd(a, n) == <span class="number">1</span></span><br><span class="line">    <span class="keyword">assert</span> <span class="number">1</span> &lt;= a <span class="keyword">and</span> <span class="number">1</span> &lt;= b <span class="keyword">and</span> <span class="number">1</span> &lt;= n</span><br><span class="line"></span><br><span class="line">    dot = <span class="keyword">lambda</span> u, v: <span class="built_in">sum</span>(u * v * [a, b])</span><br><span class="line">    <span class="keyword">for</span> λ <span class="keyword">in</span> sqrtmod_all(-a * b, n):</span><br><span class="line">        B = np.array([</span><br><span class="line">            [n, <span class="number">0</span>],</span><br><span class="line">            [λ * invmod(a, n) % n, <span class="number">1</span>],</span><br><span class="line">        ])</span><br><span class="line">        B = LLL(B, dot=dot)</span><br><span class="line">        <span class="keyword">for</span> w <span class="keyword">in</span> product([-<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>], [-<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>]): </span><br><span class="line">            <span class="keyword">if</span> w != (<span class="number">0</span>, <span class="number">0</span>) <span class="keyword">and</span> gcd(x := w @ B) == <span class="number">1</span> <span class="keyword">and</span> dot(x, x) == n: </span><br><span class="line">                 <span class="keyword">yield</span> x</span><br></pre></td></tr></table></figure><h1 id="method-2-the-hardy-muskat-williams-algorithm">Method 2: The <em>Hardy-Muskat-Williams</em> algorithm</h1><p>The most relevant paper is [HMW90], which introduces the <em>Hardy-Muskat-Williams</em> (HMW) algorithm. For pairwise copime integers <span class="math inline">\(a, b, n &gt; 0\)</span> such that <span class="math inline">\(n &gt; a + b\)</span>, it finds all <em>primitive</em> solutions <span class="math inline">\((x, y)\)</span> to <span class="math inline">\(ax^2 + by^2 = n\)</span> with <span class="math inline">\(x \geq 1, y \geq 1\)</span>.</p><p><em>(Parition of solutions)</em> Similarly, the HMW algorithm partitions the solutions using a linear congruence: For any <span class="math inline">\(0 &lt; \lambda &lt; n / 2\)</span> such that <span class="math inline">\(a \lambda^2 + b \equiv 0 \pmod n\)</span>, define<span class="math display">\[S_\lambda = \{(x, y): a x^2 + b y^2 = n, \quad \gcd(x, y) = 1, \quad x \equiv \pm \lambda y \pmod n, \quad x \geq 1, \quad y \geq 1 \},\]</span>where pairs are unordered when <span class="math inline">\(ab = 1\)</span>, that is, <span class="math inline">\((x, y)\)</span> and <span class="math inline">\((y, x)\)</span> is considered the same solution when <span class="math inline">\(ab = 1\)</span>. It can be shown that <span class="math inline">\(|S_\lambda| \leq 1\)</span>.</p><p><em>(Structure of <span class="math inline">\(S_\lambda\)</span>)</em> The HMW algorithm is based on the following elegant theorem:</p><div class="note default"><p><em>(Theorem 1, [HMW90])</em> Run the Euclidean algorithm on <span class="math inline">\(n\)</span> and <span class="math inline">\(\lambda\)</span> and let <span class="math inline">\(r_i\)</span> be all the reminders:</p><p><span class="math display">\[r_{-1} (=n) &gt; r_0 (= \lambda) &gt; r_1 &gt; \dots &gt; r_{s-1} ( = 1) &gt; r_s ( = 0),\]</span></p><p>i.e., <span class="math inline">\(r_{i+1} = r_{i-1} \bmod r_{i}\)</span>. Let <span class="math inline">\(r_k\)</span> be the first remainder such that <span class="math inline">\(a r_k^2 &lt; n\)</span>. If <span class="math inline">\(S_\lambda\)</span> is not empty, then</p><p><span class="math display">\[S_\lambda = \{ (x, y ) \}, \quad x := r_k, \quad y:= \sqrt{(n - a r_k^2) / b}.\]</span></p><p>If <span class="math inline">\(ab = 1\)</span>, then <span class="math inline">\(y = r_{k+1}\)</span>.</p></div><p>The implementation is surprisingly easy:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">solve_primitive</span>(<span class="params">a, b, n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;find all positive primitive (unordered if a = b = 1) solutions such that ax^2 + by^2 = n&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> t <span class="keyword">in</span> sqrtmod_all(-b * invmod(a, n) % n):</span><br><span class="line">        <span class="keyword">if</span> t &gt;= n // <span class="number">2</span>: <span class="keyword">continue</span></span><br><span class="line">        p, x = n, t</span><br><span class="line">        <span class="keyword">while</span> a * x * x &gt;= n:</span><br><span class="line">            p, x = x, p % x</span><br><span class="line">        y = <span class="built_in">int</span>(sqrt((n - a * x * x) // b))</span><br><span class="line">        <span class="keyword">if</span> a * x * x + b * y * y == n:</span><br><span class="line">            <span class="keyword">yield</span> x, y</span><br></pre></td></tr></table></figure><p>A special case of the HMW algorithm is <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29ybmFjY2hpYSUyN3NfYWxnb3JpdGht">Cornacchia's algorithm</span> where <span class="math inline">\(a = 1\)</span>. [Basilla04] proved the correctness of Cornacchia's algorithm using same lattice as ours.</p><h1 id="a-failed-attempt">A failed attempt</h1><p>The <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQnJhaG1hZ3VwdGElRTIlODAlOTNGaWJvbmFjY2lfaWRlbnRpdHk=">Brahmagupta–Fibonacci identity</span> states that<span class="math display">\[(x_1 + d y_1^2)(x_2 + d y_2^2) = (x_1 x_2 - n y_1 y_2)^2 + n (x_1 y_2 + x_2 y_1)^2,\]</span>so the attempt is to find all prime factors of <span class="math inline">\(n\)</span> and solve subproblems for each prime factors. However, not all solutions can be generated this way. A simple counterexample is <span class="math inline">\(1^2 + 5 \times 1^2 = 6\)</span>, while <span class="math inline">\(x^2 + 5 y^2 = 2\)</span> and <span class="math inline">\(x^2 + 5 y^2 = 3\)</span> have no solutions.</p><p>It's also related to the fact that <span class="math inline">\(\ZZ[\sqrt{ -D }]\)</span> is not a UFD for <span class="math inline">\(D \geq 3\)</span>.</p><h1 id="reference">Reference</h1><p>Due to an incomplete literature review, I initially overlooked a significant portion of previous work before arriving with the lattice algorithm. Later I came across Cornacchia's algorithm algorithm, which can be further generalized to the HMW algorithm.</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly90aGlsaW5hYXRzeW1weS53b3JkcHJlc3MuY29tLzIwMTMvMDcvMDYvc29sdmluZy10aGUtZ2VuZXJhbGl6ZWQtcGVsbC1lcXVhdGlvbi8=">Rathnayake13</span>: Rathnayake T., 2013. Improving the solving process using Cornacchia [online]</li><li><span class="exturl" data-url="aHR0cHM6Ly9wcm9qZWN0ZXVjbGlkLm9yZy9qb3VybmFscy9wcm9jZWVkaW5ncy1vZi10aGUtamFwYW4tYWNhZGVteS1zZXJpZXMtYS1tYXRoZW1hdGljYWwtc2NpZW5jZXMvdm9sdW1lLTgwL2lzc3VlLTUvT24tdGhlLXNvbHV0aW9uLW9mLXgyLS1keTItLW0vMTAuMzc5Mi9wamFhLjgwLjQwLmZ1bGw=">Basilla04</span>: Basilla, J.M., 2004. On the solution of <span class="math inline">\(x^2+dy^2=m\)</span>.</li><li><span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGF0dGljZV9yZWR1Y3Rpb24=">Lattice Reduction</span>: https://en.wikipedia.org/wiki/Lattice_reduction</li><li><span class="exturl" data-url="aHR0cHM6Ly9tYXRoLm1pdC5lZHUvfmFwb3N0L2NvdXJzZXMvMTguMjA0LTIwMTYvMTguMjA0X1hpbnl1ZV9EZW5nX2ZpbmFsX3BhcGVyLnBkZg==">Deng16</span>: Deng, X., 2016. An introduction to lenstra-lenstra-lovasz lattice basis reduction algorithm. <em>Massachusetts Institute of Technology (MIT)</em>.</li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuZGkuZW5zLmZyL35wbmd1eWVuL05ndXllbl9IZXJtaXRlQ29uc3RhbnQucGRm">Nguyen09</span>: Nguyen, P.Q., 2009. Hermite’s constant and lattice algorithms. In <em>The LLL Algorithm: Survey and Applications</em> (pp. 19-69). Berlin, Heidelberg: Springer Berlin Heidelberg.</li><li><span class="exturl" data-url="aHR0cHM6Ly9wZW9wbGUubWF0aC5jYXJsZXRvbi5jYS9+d2lsbGlhbXMvcGFwZXJzL3BkZi8xNjUucGRm">HMW90</span>: Hardy, K., Muskat, J.B. and Williams, K.S., 1990. A deterministic algorithm for solving <span class="math inline">\(n = fu^2 + gv^2\)</span> in coprime integers 𝑢 and 𝑣. <em>Mathematics of Computation</em>, <em>55</em>(191), pp.327-343.</li></ul><hr /><p>Update <span class="citation" data-cites="2025-03-13">@2025-03-13</span>: Added the Hardy-Muskat-Williams algorithm.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;In this post, we&#39;re interested in finding all &lt;em&gt;&lt;strong&gt;integer&lt;/strong&gt;&lt;/em&gt; solutions of a binary quadratic equation:
&lt;span class=&quot;math display&quot;&gt;&#92;[
&#92;begin{equation}
ax^2 + by^2 = n,
&#92;label{eq:ellipse}
&#92;end{equation}
&#92;]&lt;/span&gt;
for &lt;span class=&quot;math inline&quot;&gt;&#92;(a, b, n &amp;gt; 0&#92;)&lt;/span&gt;.&lt;/p&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    <category term="Number Theory" scheme="https://www.roosephu.net/categories/CS-Math/Number-Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>Integer and Rational Solutions of a Binary Quadratic Equation (2): Quadratic Residue</title>
    <link href="https://www.roosephu.net/2025/03/10/Integer-Solutions-of-a-Binary-Quadratic-Equation-Quadratic-Residue/"/>
    <id>https://www.roosephu.net/2025/03/10/Integer-Solutions-of-a-Binary-Quadratic-Equation-Quadratic-Residue/</id>
    <published>2025-03-10T07:21:17.000Z</published>
    <updated>2025-03-10T07:21:17.000Z</updated>
    
    <content type="html"><![CDATA[<p>In this post, we're interested in finding any or all <strong><em>integer</em></strong> solutions of a binary quadratic equation:<span class="math display">\[x^2 - ny - a = 0.\]</span>We assume that the equation is non-degenerate in the sense that <span class="math inline">\(n \neq 0\)</span>.</p><span id="more"></span><h1 id="normalization">Normalization</h1><p>We first normalize the problem to simplify its properties.</p><ul><li>Assume <span class="math inline">\(n &gt; 0\)</span>. Otherwise, reduce it to <span class="math inline">\(x^2 - (-n)(-y) - a = 0\)</span>.</li><li>Assume <span class="math inline">\(n = p^e\)</span> is a prime power. Otherwise, write <span class="math inline">\(n = \prod\limits_{i=1}^k p_i^{e_i}\)</span> and solve <span class="math inline">\(x^2 \equiv a \pmod {p_i^{e_i}}\)</span> for each <span class="math inline">\(p_i^{e_i}\)</span> and use CRT to combine all results.</li><li>Assume <span class="math inline">\(a \neq 0\)</span>. If <span class="math inline">\(a = 0\)</span>, all solutions are <span class="math inline">\(\{p^i \bmod p^e: e \leq 2i \leq 2 e\}\)</span>.</li><li>Assume <span class="math inline">\(p\)</span> does not divide <span class="math inline">\(a\)</span>. Otherwise, write <span class="math inline">\(a = a&#39; p^{e&#39;}\)</span> where <span class="math inline">\(p\)</span> does not divide <span class="math inline">\(a&#39;\)</span>.<ul><li>If <span class="math inline">\(e&#39;\)</span> is odd, there exists no solution.</li><li>If <span class="math inline">\(e&#39;\)</span> is even, <span class="math inline">\(x\)</span> must be a multiple of <span class="math inline">\(p^{e&#39;/2}\)</span>, so reduce it to <span class="math inline">\((x/p^{e&#39;/2})^2 \equiv a&#39; \pmod{p^{e-e&#39;}}\)</span>.</li></ul></li></ul><p>After normalization, the problem that we're interested in is:</p><div class="note default"><p>For a prime <span class="math inline">\(p\)</span>, an integer <span class="math inline">\(a \neq 0\)</span> such that <span class="math inline">\(p\)</span> does not divide <span class="math inline">\(a\)</span>, find one or all integer <span class="math inline">\(x\)</span> such that</p><p><span class="math display">\[\begin{equation}x^2 \equiv a \pmod{p^e}.\label{eq:quad-residue}\end{equation}\]</span></p></div><p>We say that <span class="math inline">\(x\)</span> is a <em>quadratic residue modulo <span class="math inline">\(n\)</span></em> if <span class="math inline">\(x^2 \equiv a \pmod{n}\)</span> has a solution, and a <em>quadratic non-residue modulo <span class="math inline">\(n\)</span></em> otherwise.</p><h1 id="the-case-of-p-2">The case of <span class="math inline">\(p = 2\)</span></h1><p><em>(Solubility)</em> For <span class="math inline">\(p = 2\)</span>, <span class="math inline">\(a\)</span> is a quadratic residue modulo <span class="math inline">\(p^e\)</span> if and only if <span class="math inline">\(a \equiv 1 \pmod 8\)</span>.</p><p>To find one or all solutions, we first consider the two easy cases:</p><ul><li>If <span class="math inline">\(e = 1\)</span>, certainly the case is <span class="math inline">\(a \equiv 1 \pmod 2\)</span> and all solutions are <span class="math inline">\(x \equiv 1 \pmod 2\)</span>.</li><li>If <span class="math inline">\(e = 2\)</span>:<ul><li>If <span class="math inline">\(a \equiv 1 \pmod{4}\)</span>, all solutions are <span class="math inline">\(x \equiv 1 \pmod 2\)</span>.</li><li>Otherwise, there is no solution.</li></ul></li></ul><p>Now we assume <span class="math inline">\(e &gt; 2\)</span>.</p><p><em>(Any solution)</em> Observe that:<span class="math display">\[\left(x + \frac{x^2 - a}{2} \right)^2 - a = (x+1)(x^2 - a) + \left( \frac{x^2 - a}{2} \right)^2.\]</span>So if <span class="math inline">\(x_{e-1}\)</span> satisfies <span class="math inline">\(x^2 \equiv a \pmod{2^{e-1}}\)</span>, then <span class="math inline">\(x_e = x_{e-1} + \frac{x_{e-1}^2 - a}{2}\)</span> satisfies <span class="math inline">\(x^2 \equiv a \pmod{2^e}\)</span>. Thus, we can begin with <span class="math inline">\(x_2 = 1\)</span>.</p><p><em>(All solutions)</em> Let <span class="math inline">\(x^*\)</span> be any solution to <span class="math inline">\(x^2 \equiv a \pmod{2^e}\)</span>. It is clear that <span class="math inline">\(x^*\)</span> must be odd. Consider <span class="math inline">\(\frac{x^* - x}{2}\)</span> and <span class="math inline">\(\frac{x^* + x}{2}\)</span>. The sum is <span class="math inline">\(x^*\)</span>, so only one of them is even. The product is <span class="math inline">\(\frac{x^{*2} - x^2}{4} \equiv 0 \pmod{2^{e-2}}\)</span>, thus one of them is 0 modulo <span class="math inline">\(2^{e-2}\)</span>. Thus, the set of all solutions is:<span class="math display">\[x \in \{\pm x^*\} \pmod{2^{e-1}}.\]</span></p><h1 id="the-case-of-an-odd-prime-p">The case of an odd prime <span class="math inline">\(p\)</span></h1><p><em>(Solubility)</em> The solubility is often determined by the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSmFjb2JpX3N5bWJvbA==">Jacobi symbol</span>, which is the generalization of the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGVnZW5kcmVfc3ltYm9s">Legendre symbol</span>: For an odd prime <span class="math inline">\(p\)</span>, <span class="math inline">\(a\)</span> is a quadratic residue modulo <span class="math inline">\(p^e\)</span> if and only if<span class="math display">\[\left( \frac{a}{p^e} \right) = 1, \quad \left( \frac{a}{p^e} \right) := \left( \frac{a}{p} \right)^e,\]</span>where <span class="math inline">\(\left( \frac{a}{p} \right)\)</span> is the Legendre symbol.</p><p>Note that this does not apply to <span class="math inline">\(p = 2\)</span> (e.g., <span class="math inline">\(x^2 \equiv 3 \pmod 4\)</span>) or general composite numbers (e.g., <span class="math inline">\(x^2 \equiv 8 \pmod {15}\)</span>).</p><p><em>(Modulo a prime (<span class="math inline">\(e=1\)</span>))</em> There exist well-known algorithms for finding a solution to <span class="math inline">\(x^2 \equiv a \pmod p\)</span>, e.g., the <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVG9uZWxsaSVFMiU4MCU5M1NoYW5rc19hbGdvcml0aG0=">Tonelli-Shanks algorithm</span> and <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2lwb2xsYSUyN3NfYWxnb3JpdGht">Cipolla's algorithm</span>.</p><p><em>(Modulo a prime power (<span class="math inline">\(e &gt; 1\)</span>))</em> Let <span class="math inline">\(x&#39;\)</span> be any solution to <span class="math inline">\(x^2 \equiv a \pmod{p}\)</span>. Tonelli shows that<span class="math display">\[x^* \equiv x&#39;^{p^{e - 1}} \cdot a^{(p^e - 2 p^{e-1} + 1) / 2} \pmod{p^e},\]</span>is a solution to Equation <span class="math inline">\(\eqref{eq:quad-residue}\)</span>.</p><p>Alternatively, Cipolla's algorithm can be extended to find a solution to Equation <span class="math inline">\(\eqref{eq:quad-residue}\)</span> directly: Let <span class="math inline">\(u\)</span> be any integer such that <span class="math inline">\(u^2 - a\)</span> is a quadratic non-residue modulo <span class="math inline">\(p\)</span>, then<span class="math display">\[x^* \equiv \frac{1}{2} a^{(p^e - 2 p^{e-1} + 1) / 2} \left(\left(u + \sqrt{u^2 - a}\right)^s + \left(u - \sqrt{u^2 - a}\right)^s \right). \pmod{p^e}\]</span>where <span class="math inline">\(s := p^{e-1} (p+1)/2\)</span>.</p><p>Moreover, it's also possible to use the identity<span class="math display">\[\left( \frac{x^2 + a}{2x} \right)^2 - a = \frac{(x^2 - a)^2}{4x^2}\]</span>to generate a solution from <span class="math inline">\(x&#39;\)</span> by iterating it for <span class="math inline">\(\log e\)</span> times, but a division modulo <span class="math inline">\(p^e\)</span> takes <span class="math inline">\(O(\log p^e)\)</span> time so it won't be faster.</p><p><em>(All solutions)</em> Similarly, let <span class="math inline">\(x^*\)</span> be any solution to <span class="math inline">\(x^2 \equiv a \pmod{p^e}\)</span>. Since <span class="math inline">\((x^* - x)(x^* +x) \equiv 0 \pmod {p^e}\)</span>, and at most one of <span class="math inline">\(x^* - x\)</span> and <span class="math inline">\(x^* + x\)</span> is a multiple of <span class="math inline">\(p\)</span>, we conclude that all solutions are<span class="math display">\[x \in \{\pm x^*\} \pmod{p^e}.\]</span></p><h1 id="references">References</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVG9uZWxsaSVFMiU4MCU5M1NoYW5rc19hbGdvcml0aG0=">Tonelli-Shanks algorithm</span></li><li><span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2lwb2xsYSUyN3NfYWxnb3JpdGht">Cipolla's algorithm</span></li><li>[Dickson]: Dickson, L.E., 1919. <em>History of the Theory of Numbers</em> (No. 256). Carnegie Institution of Washington.</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;In this post, we&#39;re interested in finding any or all &lt;strong&gt;&lt;em&gt;integer&lt;/em&gt;&lt;/strong&gt; solutions of a binary quadratic equation:
&lt;span class=&quot;math display&quot;&gt;&#92;[
x^2 - ny - a = 0.
&#92;]&lt;/span&gt;
We assume that the equation is non-degenerate in the sense that &lt;span class=&quot;math inline&quot;&gt;&#92;(n &#92;neq 0&#92;)&lt;/span&gt;.&lt;/p&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    <category term="Number Theory" scheme="https://www.roosephu.net/categories/CS-Math/Number-Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>Integer and Rational Solutions to a Binary Quadratic Equation (1): Legendre equation</title>
    <link href="https://www.roosephu.net/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/"/>
    <id>https://www.roosephu.net/2025/03/08/Rational-Solutions-to-a-Binary-Quadratic-Equation/</id>
    <published>2025-03-08T12:38:17.000Z</published>
    <updated>2025-03-08T12:38:17.000Z</updated>
    
    <content type="html"><![CDATA[<p>In this post, we're interested in finding any or all <strong><em>rational</em></strong> solutions of the following equation:<span class="math display">\[ax^2 + by^2 + c = 0,\]</span>or alternatively, all <strong><em>integer</em></strong> solutions to the Legendre equation:<span class="math display">\[\begin{equation}ax^2 + by^2 + cz^2 = 0,\label{eq:legendre}% \tag{Legendre Equation}\end{equation}\]</span>where <span class="math inline">\(abc \neq 0\)</span>.</p><span id="more"></span><h1 id="legendre-equation-and-normal-form">Legendre equation and normal form</h1><p><em>(Normalization)</em> We say the Legendre equation is in <em>normal form</em> if <span class="math inline">\(abc\)</span> is square-free. The following transformation by Gauss reduces a Legendre equation to normal form: Write <span class="math inline">\(bc = \alpha^2 s, ca=\beta^2 t, ab=\gamma^2 u\)</span> for square-free integers <span class="math inline">\(s, t, u\)</span> and integers <span class="math inline">\(\alpha, \beta, \gamma\)</span>, then<span class="math display">\[\frac{a \alpha}{\beta \gamma} (x/\alpha)^2 + \frac{b \beta}{\gamma \alpha} (y/\beta)^2 + \frac{c \gamma}{\alpha \beta} (z/\gamma)^2 = 0,\]</span>and <span class="math inline">\(\frac{a \alpha}{\beta \gamma}, \frac{b \beta}{\gamma \alpha}, \frac{c\gamma}{\alpha \beta}\)</span> are square-free and pair-wise coprime.</p><p><em>(Solubility)</em> Legendre gave an elegant theorem to determine the solubility of an equation in normal form:</p><div class="note default"><p>(<em>Legendre</em>) Equation <span class="math inline">\(\eqref{eq:legendre}\)</span> in normal form has a non-trivial integer solution iff. <span class="math inline">\(a, b, c\)</span> have different sign, and <span class="math inline">\(-bc, -ca, -ab\)</span> are quadratic residues modulo <span class="math inline">\(a, b, c\)</span> respectively.</p></div><p>The <code>if</code> direction follows by considering modulo <span class="math inline">\(a, b, c\)</span> individually.</p><h1 id="find-a-single-non-trivial-integer-solution">Find a single non-trivial integer solution</h1><h2 id="brute-force">Brute-force</h2><p>Holtz proved that there exists a small non-trivial solution:</p><div class="note default"><p><em>(Small solution, Holzer)</em> If Equation <span class="math inline">\(\eqref{eq:legendre}\)</span> in normal form has a non-trivial solution, then there exists a non-trivial solution <span class="math inline">\((\hat x, \hat y, \hat z)\)</span> such that:</p><p><span class="math display">\[\hat x^2 \leq |bc|, \quad \hat y^2 \leq |ca|, \quad \hat z^2 \leq |ab|.\]</span></p></div><p>Additionally, <span class="math inline">\(|bc|\)</span> is a square if and only if <span class="math inline">\(|bc| = 1\)</span>, since <span class="math inline">\(bc\)</span> is square-free. The same holds for <span class="math inline">\(|ca|\)</span> and <span class="math inline">\(|ab|\)</span>.</p><p>See [Mordell, Chapter 7, Theorem 5], [CR03] and [CM97] for more details.</p><h2 id="legendres-descent">Legendre's descent</h2><p>(This subsection is mostly taken from [Aitken, Section 5].)</p><p>Apparently, <span class="math inline">\(ax^2 + by^2 + cz^2 = 0\)</span> is equivalent to <span class="math inline">\((-ac) x^2 + (-bc) y^2 = (cz)^2\)</span>. <em>Legendre's descent</em> is based on the following observation:<span class="math display">\[ax^2 + by^2 = z^2 \quad \Longleftrightarrow \quad a ( ux + z)^2 + b&#39; (b y)^2 = (uz + ax)^2.\]</span>for <span class="math inline">\(u^2 = a + b&#39;b\)</span>. It allows us to reduce the problem <span class="math inline">\(ax^2 + by^2 = z^2\)</span> to another problem <span class="math inline">\(ax^2 + b&#39; y^2 = z^2\)</span>. When <span class="math inline">\(|b| \geq \max(2, |a|)\)</span>, the other problem is smaller as we can always choose <span class="math inline">\(|u| \leq |b|/2\)</span> so <span class="math inline">\(|b&#39;| \leq (u^2 + |a|) / |b| \leq |b|/4+1 &lt; |b|\)</span>.</p><p>The algorithm runs as follows:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># given a, b, find (x, y, z) such that a x^2 + b y^2 = z^2</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">solve</span>(<span class="params">a, b</span>):</span><br><span class="line">    <span class="keyword">assert</span> is_squarefree(a) <span class="keyword">and</span> is_squarefree(b)</span><br><span class="line">    <span class="keyword">assert</span> a &gt;= <span class="number">0</span> <span class="keyword">or</span> b &gt;= <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">abs</span>(a) &gt; <span class="built_in">abs</span>(b):</span><br><span class="line">        y, x, z = solve(b, a)</span><br><span class="line">        <span class="keyword">return</span> (x, y, z)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> a == <span class="number">0</span>: <span class="keyword">return</span> (<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>)</span><br><span class="line">    <span class="keyword">if</span> b == <span class="number">1</span>: <span class="keyword">return</span> (<span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>)</span><br><span class="line">    <span class="keyword">if</span> a == <span class="number">1</span>: <span class="keyword">return</span> (<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># find u such that u^2 == a (mod b) and -b / 2 &lt;= u &lt; b / 2</span></span><br><span class="line">    <span class="comment"># WHAT IF u DOES NOT EXIST?</span></span><br><span class="line">    u = sqrtmod(a, b)</span><br><span class="line">    u = <span class="built_in">min</span>(u, b - u)</span><br><span class="line">    b2 = (u * u - a) // b</span><br><span class="line"></span><br><span class="line">    <span class="comment"># b2 = sqf_b2 * sq_b2^2 where sqf_b2 is square-free</span></span><br><span class="line">    sqf_b2, sq_b2 = squarefree(b2)</span><br><span class="line"></span><br><span class="line">    x2, y2, z2 = solve(a, sqf_b2)</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        u * x2 + z2,</span><br><span class="line">        sqf_b2 * sq_b2 * y2,</span><br><span class="line">        u * z2 + a * x2,</span><br><span class="line">    )</span><br></pre></td></tr></table></figure><p>The key point of the algorithm is that <span class="math inline">\(u\)</span> must exist during all recursions. It's proved that if <span class="math inline">\(ax^2 + by^2 + cz^2 = 0\)</span> is in normal form, <span class="math inline">\(u\)</span> always exists when calling <span class="math inline">\(\operatorname*{solve}(-ac, -bc)\)</span>.</p><p>See [CR03, Section 2.3], [Aitken] and [Hillgarter] for more details. [CR03] also optimizes the method but it's beyond the scope of the post.</p><h2 id="lattice-based-algorithm">Lattice-based algorithm</h2><p>(This section is taken from [CR03, Section 2.6].)</p><p>Another idea arises when going deeper in Legendre's theorem.</p><p>We first discuss the case where any of <span class="math inline">\(|ab|, |bc|, |ca|\)</span> is 1. W.L.O.G. we assume <span class="math inline">\(|ab| = 1\)</span>.</p><ul><li>If <span class="math inline">\(a \neq b\)</span>, then <span class="math inline">\((x, y, z) = (1, 1, 0)\)</span> is a non-trivial solution.</li><li>If <span class="math inline">\(a = b\)</span>, we can use <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29ybmFjY2hpYSUyN3NfYWxnb3JpdGht">Cornacchia's algorithm</span> to solve <span class="math inline">\(x^2 + y^2 = -c / a\)</span>, and <span class="math inline">\((x, y, 1)\)</span> is a non-trivial solution.</li></ul><p>Now we consider the general case. We define a set of points <span class="math inline">\(\mathcal{L} \subset \ZZ^3\)</span>:<span class="math display">\[\begin{aligned}\mathcal{L} = \{ (x, y, z): \ &amp; by \equiv \lambda_a z \pmod{|a|},  \\&amp; cz \equiv \lambda_b x \pmod{|b|}, \\&amp; ax \equiv \lambda_c y \pmod{|c|},\\&amp; a x^2 + b y^2 + c z^2 \equiv 0 \pmod{|2abc|}\},\end{aligned}\]</span></p><p>where <span class="math inline">\(\lambda_a\)</span> is an arbitrary solution to <span class="math inline">\(\lambda^2 \equiv -bc \pmod{|a|}\)</span>, similarly for <span class="math inline">\(\lambda_b\)</span> and <span class="math inline">\(\lambda_c\)</span>. Let <span class="math inline">\((\bar x, \bar y, \bar z)\)</span> be the non-zero point in <span class="math inline">\(\mathcal{L}\)</span> minimizing <span class="math inline">\(|a| x^2 + |b| y^2 + |c| z^2\)</span>. It can be proved that there is a small solution <span class="math inline">\((\hat x, \hat y, \hat z)\)</span> in <span class="math inline">\(\mathcal{L}\)</span>, which can be used to bound <span class="math inline">\(|a\bar x^2 + b\bar y^2 + c \bar z^2|\)</span>:<span class="math display">\[\left |a \bar x^2 + b \bar y^2 + c \bar z^2 \right| \leq |a| \hat x^2 + |b| \hat y^2 + |c| \hat z^2 &lt; 2|abc|.\]</span>so we conclude that <span class="math inline">\(a \bar x^2 + b \bar y^2 + c \bar z^2 = 0\)</span> and the minimizer itself satisfies Holzer's condition.</p><p>The final step is to determine the minimizer. [CR03, Section 2.6, p.17] shows that <span class="math inline">\(\mathcal{L}\)</span> is a lattice, that is, <span class="math inline">\(\mathcal{L} = \{\mathbf{w} \mathbf{B}: \mathbf{w} \in \ZZ^3 \}\)</span> for a (row-based) basis <span class="math inline">\(\mathbf{B} \in \ZZ^{3 \times 3}\)</span>. It also gives an explicit expression for a basis <span class="math inline">\(\mathbf{B}\)</span>. Moreover, the shortest vector in a 3D lattice can be found efficiently using <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGVuc3RyYSVFMiU4MCU5M0xlbnN0cmElRTIlODAlOTNMb3YlQzMlQTFzel9sYXR0aWNlX2Jhc2lzX3JlZHVjdGlvbl9hbGdvcml0aG0=">LLL</span>, as long as we have an arbitrary basis <span class="math inline">\(\mathbf{B}\)</span> for <span class="math inline">\(\mathcal{L}\)</span>:</p><div class="note default"><p><em>([CR03, Lemma 2.7, p.16])</em> Let <span class="math inline">\(\mathcal{L} = \{\mathbf{w} \mathbf{B}: \mathbf{w} \in \ZZ^3\}\)</span> be a 3D lattice where <span class="math inline">\(\mathbf{B}\)</span> is LLL-reduced. Then the shortest vector in <span class="math inline">\(\mathcal{L}\)</span> is in the form of <span class="math inline">\(\mathbf{w}\mathbf{B}\)</span> where <span class="math inline">\(\mathbf{w} \in \{-1, 0, 1\}^3\)</span>.</p></div><p>Here is the pseudocode:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">solve</span>(<span class="params">a, b, c</span>):</span><br><span class="line">    <span class="keyword">assert</span> (<span class="built_in">abs</span>(a) == <span class="number">1</span>) + (<span class="built_in">abs</span>(b) == <span class="number">1</span>) + (<span class="built_in">abs</span>(c) == <span class="number">1</span>) &lt; <span class="number">2</span></span><br><span class="line"></span><br><span class="line">    f = <span class="keyword">lambda</span> v: <span class="built_in">sum</span>(v * v * [a, b, c])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_B</span>():</span><br><span class="line">        λ_a = sqrtmod(-b * c, a)</span><br><span class="line">        λ_b = sqrtmod(-a * b, c)</span><br><span class="line">        λ_c = sqrtmod(-a * c, b)</span><br><span class="line"></span><br><span class="line">        u, a_2 = invmod(b, c), invmod(a, b * c)</span><br><span class="line">        v, b_2 = (<span class="number">1</span> - u * b) // c, (<span class="number">1</span> - a * a_2) // (b * c)</span><br><span class="line"></span><br><span class="line">        α = b_2 * c * λ_a % a</span><br><span class="line">        β = u * a_2 * b * λ_b % (b * c)</span><br><span class="line">        γ = v * a_2 * c * λ_c % (b * c)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># the basis of &#123;(x, y, z): a x^2 + b y^2 + c z^2 = 0 mod abc&#125;</span></span><br><span class="line">        B1 = np.array([</span><br><span class="line">            [b * c, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">            [a * β, a, <span class="number">0</span>],</span><br><span class="line">            [α * β + γ, α, <span class="number">1</span>],</span><br><span class="line">        ])</span><br><span class="line"></span><br><span class="line">        <span class="comment"># the basis of &#123;(x, y, z): a x^2 + b y^2 + c z^2 = 0 mod 2abc&#125;</span></span><br><span class="line">        eps = <span class="keyword">lambda</span> v: f(v) // (a * b * c) % <span class="number">2</span></span><br><span class="line">        r = <span class="built_in">next</span>(r <span class="keyword">for</span> r <span class="keyword">in</span> B1 <span class="keyword">if</span> eps(r) != <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> np.array([v <span class="keyword">if</span> eps(v) == <span class="number">0</span> <span class="keyword">else</span> v + r <span class="keyword">for</span> v <span class="keyword">in</span> B])</span><br><span class="line"></span><br><span class="line">    B = LLL(get_B(), dot=<span class="keyword">lambda</span> u, v: <span class="built_in">sum</span>(u * v * np.<span class="built_in">abs</span>([a, b, c])))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> w <span class="keyword">in</span> product([-<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>], [-<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>], [-<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>]):</span><br><span class="line">        <span class="keyword">if</span> w != (<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>) <span class="keyword">and</span> f(v := w @ B) == <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">return</span> v</span><br><span class="line"></span><br><span class="line">    <span class="comment"># unreachable!</span></span><br></pre></td></tr></table></figure><p>Check out [CR03, Section 2.6] and [CM97] for more details. [CM97] uses a different lattice and [CR03] improves upon it.</p><h1 id="a-single-non-trivial-rational-solution-to-mathbfxtop-q-mathbfx-0-with-q-in-zzd-times-d">A single non-trivial rational solution to <span class="math inline">\(\mathbf{x}^\top Q \mathbf{x} = 0\)</span> with <span class="math inline">\(Q \in \ZZ^{d \times d}\)</span></h1><p>Assume <span class="math inline">\(\det Q \neq 0\)</span> and <span class="math inline">\(Q\)</span> is symmetric. We here only list a few references below:</p><ul><li>[Simon05] removes the need of diagonalization, which often bloats the size of coefficients. It gives an algorithm where <span class="math inline">\(d = 3\)</span>. It works by reducing <span class="math inline">\(Q\)</span> repeatedly until <span class="math inline">\(\det Q = \pm 1\)</span>, and then solving the case where <span class="math inline">\(\det Q = \pm 1\)</span>.</li><li>[Simon06] extends [Simon05] and gives an algorithm for <span class="math inline">\(d &gt; 3\)</span>. However, I have not read the paper.</li></ul><h1 id="all-non-trivial-solutions-from-a-single-non-trivial-solution">All non-trivial solutions from a single non-trivial solution</h1><p>Given a single non-trivial solution <span class="math inline">\(\mathbf{\hat x} \in \QQ^d\)</span> to a quadratic equation <span class="math inline">\(F(\mathbf{x}) = 0\)</span>, we parametrize <span class="math inline">\(\mathbf{x} \in \QQ^d\)</span> by <span class="math inline">\(\mathbf{x} = \mathbf{\hat x} - t\mathbf{u}\)</span>, where <span class="math inline">\(\mathbf{u} \in \ZZ^d, t \in \QQ\)</span> and <span class="math inline">\(\mathbf{u}\)</span> is primitive (i.e., <span class="math inline">\(\gcd(\mathbf{u}) = 1\)</span>).</p><p>Since <span class="math inline">\(F(\mathbf{\hat x}) = 0\)</span>, we solve <span class="math inline">\(t\)</span> in <span class="math inline">\(F(\mathbf{\hat x} - t\mathbf{u}) = 0\)</span> using the second-order Taylor expansion of <span class="math inline">\(F\)</span> at <span class="math inline">\(\mathbf{\hat x}\)</span>:<span class="math display">\[F(\mathbf{\hat x}) - t \mathbf{u}^\T \nabla F(\mathbf{\hat x}) + \frac{t^2}{2} \mathbf{u}^\T \nabla^2 F(\mathbf{\hat x}) \mathbf{u} = 0 \quad \Longrightarrow \quad t = \frac{\mathbf{u}^\T \nabla F(\mathbf{\hat x})}{\frac{1}{2} \mathbf{u}^\T \nabla^2 F(\mathbf{\hat x}) \mathbf{u}}.\]</span>Since <span class="math inline">\(\mathbf{u}\)</span> and <span class="math inline">\(-\mathbf{u}\)</span> produce the same solution, we require that <span class="math inline">\(\mathbf{u}\)</span> is <em>lexicographically</em> larger than <span class="math inline">\(-\mathbf{u}\)</span>. Note this parametrization also generates the initial solution <span class="math inline">\(\mathbf{\hat x}\)</span> when <span class="math inline">\(\mathbf{u}^\top \nabla F(\mathbf{\hat x}) = 0\)</span>, i.e., <span class="math inline">\(\mathbf{u}\)</span> is tangent to the curve.</p><h1 id="connection-to-integer-solutions-of-a-homogenous-ternary-quadratic-equation">Connection to integer solutions of a homogenous ternary quadratic equation</h1><p>A homogeneous polynomial <span class="math inline">\(F(\mathbf{x})\)</span> of degree <span class="math inline">\(d\)</span> satisfies <span class="math inline">\(F(\lambda \mathbf{x}) = \lambda^d F(\mathbf{x})\)</span>, so every rational solution can be normalized to a primitive integer solution. Furthermore, any primitive integer solution <span class="math inline">\(\mathbf{x}\)</span> with <span class="math inline">\(\mathbf{x}_1 \neq 0\)</span> is equivalent to a rational solution of a heterogeneous equation <span class="math inline">\(F([1, \mathbf{x&#39;}]) = 0\)</span> for <span class="math inline">\(\mathbf{x}&#39; \in \QQ^{d-1}\)</span>.</p><p>When the goal is to enumerate integral solutions, a good strategy is to first find a non-trivial rational solution to <span class="math inline">\(F(\mathbf{x}) = 0\)</span> and then apply the parametrization trick to a heterogeneous equation to avoid duplicates.</p><p>Consider the following example. We consider the homogenous equation <span class="math inline">\(ax^2+bxy+cy^2=d z^2\)</span> in <span class="math inline">\(\ZZ^3\)</span> with a non-trivial primitive solution <span class="math inline">\((x_0, y_0, z_0)\)</span> where <span class="math inline">\(z_0 \neq 0\)</span>, which is equivalent rational solutions to the heterogenous equation <span class="math inline">\(a x^2 + b xy + c y^2 - d = 0\)</span> with a non-trivial solution <span class="math inline">\(\mathbf{\hat x} = (x_0 / z_0, y_0 / z_0)\)</span>. Write <span class="math inline">\(\mathbf{u} = [u, v]\)</span> and apply the results above:<span class="math display">\[\begin{aligned}\mathbf{u}^\top \nabla F(\mathbf{\hat x}) &amp; = z_0^{-1}(u (2ax_0 + b y_0) + v(b x_0 + 2c y_0) ), \\\frac{1}{2} \mathbf{u}^\T \nabla^2 F(\mathbf{\hat x}) \mathbf{u} &amp; = a u^2 + b uv + c v^2. \\\end{aligned}\]</span>After simplification, we have<span class="math display">\[\begin{pmatrix}x \\ y \\ z\end{pmatrix} =A \mathbf{r}, \quadA := \begin{pmatrix}ax_0 + by_0 &amp; 2cy_0 &amp; -cx_0 \\-ay_0 &amp; 2ax_0 &amp; c y_0 + b x_0 \\a z_0 &amp; b z_0 &amp; c z_0 \\\end{pmatrix}, \quad \mathbf{r} := \begin{pmatrix}u^2 \\ uv \\ v^2\end{pmatrix},\]</span>which are exactly the Equations (8-10) in <span class="exturl" data-url="aHR0cHM6Ly9tYXRod29ybGQud29sZnJhbS5jb20vRGlvcGhhbnRpbmVFcXVhdGlvbjJuZFBvd2Vycy5odG1s">Wolfram MathWorld</span>. The equations might not generate primitive solutions directly, but they do produce all primitive tuples after normalization.</p><p>To use the equations to generate <em>bounded</em> primitive solutions, the common factor <span class="math inline">\(g := \gcd(A \mathbf{r})\)</span> needs to be determined. It can be shown that <span class="math inline">\(g\)</span> divides <span class="math inline">\(\det A\)</span> by writing <span class="math inline">\(\mathbf{r}\)</span> using <span class="math inline">\(A \mathbf{r}\)</span>:<span class="math display">\[\mathbf{r} = A^{-1} A \mathbf{r} = \frac{\adj A}{\det A} A \mathbf{r},\]</span>and observing that if <span class="math inline">\(g\)</span> doesn't divide <span class="math inline">\(\det A\)</span>, <span class="math inline">\(\gcd(\mathbf{r})\)</span> can't be 1.</p><p>In our case, <span class="math inline">\(\det A = -\Delta d z_0^3\)</span> where <span class="math inline">\(\Delta = b^2 - 4ac\)</span>. The bound can be slightly improved to <span class="math inline">\(\Delta d z_0^2\)</span> by finding the LCM of denominators in <span class="math inline">\(A^{-1}\)</span>:<span class="math display">\[A^{-1} = \frac{1}{\Delta d z_0^2}\begin{pmatrix}\Delta x_0 + cT &amp; cS &amp; -2cdz_0 \\-aS &amp; -cT &amp; b d z_0 \\aT &amp; \Delta y_0 + a S &amp; -2adz_0\end{pmatrix},\quad\begin{cases}S := 2cy_0+bx_0, \\T := 2ax_0+by_0.\end{cases}\]</span>If <span class="math inline">\(b = 0\)</span>, the denominator can be further reduced to <span class="math inline">\(2acdz_0^2\)</span> instead of <span class="math inline">\(4acdz_0^2\)</span>.</p><h1 id="references">References</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly90aGlsaW5hYXRzeW1weS53b3JkcHJlc3MuY29tLzIwMTMvMDcvMjAvc29sdmluZy10ZXJuYXJ5LXF1YWRyYXRpYy1mb3Jtcy1ieS1kZXNjZW50LW1ldGhvZC8=">Rathnayake13</span>: Rathnayake T., 2013. Solving Ternary Quadratic forms by descent method [online]</li><li>[Mordell]: Mordell, L.J., 1969. <em>Diophantine Equations: Diophantine Equations</em> (Vol. 30). Academic press.</li><li><span class="exturl" data-url="aHR0cHM6Ly9qb2huY3JlbW9uYS5naXRodWIuaW8vcGFwZXJzL2Nvbmljcy5wZGY=">CR03</span>: Cremona, J. and Rusin, D., 2003. Efficient solution of rational conics. <em>Mathematics of Computation</em>, <em>72</em>(243), pp.1417-1441.</li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuYW1zLm9yZy9qb3VybmFscy9tY29tLzIwMDUtNzQtMjUxL1MwMDI1LTU3MTgtMDUtMDE3MjktMS9TMDAyNS01NzE4LTA1LTAxNzI5LTEucGRm">Simon05</span>: Simon, D., 2005. Solving quadratic equations using reduced unimodular quadratic forms. <em>Mathematics of Computation</em>, <em>74</em>(251), pp.1531-1543.</li><li><span class="exturl" data-url="aHR0cHM6Ly9zaW1vbmQudXNlcnMubG1uby5jbnJzLmZyL21hdGhzL0RpbTQucGRm">Simon06</span>: Simon, D., <em>Quadratic equations in dimensions 4, 5 and more. Preprint, 2005</em> [online]</li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cubWF0aC5rLXN0YXRlLmVkdS9+Y29jaHJhbmUvcmVzZWFyY2gvbGVnZW5kcmUucGRm">CM97</span>: Cochrane, T. and Mitchell, P., 1998. Small solutions of the Legendre equation. <em>Journal of Number Theory</em>, <em>70</em>(1), pp.62-66.</li><li><span class="exturl" data-url="aHR0cHM6Ly9wdWJsaWMuY3N1c20uZWR1L2FpdGtlbl9odG1sL25vdGVzL2xlZ2VuZHJlLnBkZg==">Aitken</span>: Wayne Aitken, Legrendre's Theorem, Legrange's Descent</li><li><span class="exturl" data-url="aHR0cHM6Ly93d3czLnJpc2Muamt1LmF0L3B1YmxpY2F0aW9ucy9kb3dubG9hZC9yaXNjXzEzNTUvUmF0aW9uYWwlMjBQb2ludHMlMjBvbiUyMENvbmljcy5wZGY=">Hillgarter</span>: Hillgarter, E., 1996. <em>Rational points on conics</em>. na.</li><li><a href="Minimal%20Solutions%20of%20Diophantine%20Equations">Holzer</a>: Holzer, L., 1950. Minimal solutions of diophantine equations. <em>Canadian Journal of Mathematics</em>, <em>2</em>, pp.238-244.<ul><li>It's weird that I can't find the first name of Holzer...</li></ul></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;In this post, we&#39;re interested in finding any or all &lt;strong&gt;&lt;em&gt;rational&lt;/em&gt;&lt;/strong&gt; solutions of the following equation:
&lt;span class=&quot;math display&quot;&gt;&#92;[
ax^2 + by^2 + c = 0,
&#92;]&lt;/span&gt;
or alternatively, all &lt;strong&gt;&lt;em&gt;integer&lt;/em&gt;&lt;/strong&gt; solutions to the Legendre equation:
&lt;span class=&quot;math display&quot;&gt;&#92;[
&#92;begin{equation}
ax^2 + by^2 + cz^2 = 0,
&#92;label{eq:legendre}
% &#92;tag{Legendre Equation}
&#92;end{equation}
&#92;]&lt;/span&gt;
where &lt;span class=&quot;math inline&quot;&gt;&#92;(abc &#92;neq 0&#92;)&lt;/span&gt;.&lt;/p&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    <category term="Number Theory" scheme="https://www.roosephu.net/categories/CS-Math/Number-Theory/"/>
    
    
  </entry>
  
  <entry>
    <title>六个解谜游戏</title>
    <link href="https://www.roosephu.net/2025/01/05/6-puzzle-games/"/>
    <id>https://www.roosephu.net/2025/01/05/6-puzzle-games/</id>
    <published>2025-01-06T05:15:44.000Z</published>
    <updated>2025-01-06T05:15:44.000Z</updated>
    
    <content type="html"><![CDATA[<p>转眼间 2024 年<del>快</del>已经过去了……隔了好久没写娱乐相关的文章了，随便写几个作为<del>今</del>去年的年终总结得了……（然后震惊地发现我上次写游戏已经是四年前了……）</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9vYnJhZGlubi5jb20v">Return of the Obra Dinn</span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cudGhlZ29sZGVuaWRvbC5jb20v">The Case of the Golden Idol</span> 和 <span class="exturl" data-url="aHR0cHM6Ly93d3cucmlzZW9mdGhlZ29sZGVuaWRvbC5jb20v">The Rise of the Golden Idol</span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cucGF0cmlja3NwYXJhYm94LmNvbS8=">Patrick's Parabox</span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuY29jb29uZ2FtZS5jb20v">COCOON</span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuYW5pbWFsd2VsbC5uZXQv">ANIMAL WELL</span></li></ul><p>（可能有略微剧透，手动狗头）</p><span id="more"></span><h1 id="return-of-the-obra-dinn">Return of the Obra Dinn</h1><p>早就听说过这款游戏的大名。作者 Lucas Pope 之前开发的 Papers, Please 让他一举成名<del>顺便赚了不少钱可以没有后顾之忧的做新的游戏</del>。</p><p>在这个游戏里，你扮演一个侦探，任务在 Obra Dinn 这艘船上寻找线索，判断出每个人各是谁，死因是什么。这种侦探游戏的难点是如何设计线索。我之前玩过 The Room 系列或者 Gorogoa，玩法是一个线性叙事，不停和物品交互获取新的线索，揭开一个谜题后开始下一个，点点点式的交互才是玩法核心，没啥推理成分。Return of the Obra Dinn 则不同，它给你的主要线索是若干个快照，一个快照就是一个静态场景，你可以仔细看，但是没法交互，所以不存在那种哪个地方没点到就解锁不了下一关的痛苦。而且 Return of the Obra Dinn 是一个非线性叙事，你不需要任何推理结果就找到所有线索，剩下的就是推理了，推理才是游戏的玩法核心。</p><p>那这种玩法会出现一个问题：太静态了，和“游戏”不太搭边。如果只是一些图片的话，那么我是不是可以打印出来离线玩，需要这个程序干什么？我能想到两个原因。第一是交互式验证，这个只能通过程序来实现。第二是沉浸感，游戏通过一些设计（例如叙事、音效）能让你沉浸在这么一个角色中，增强体验。</p><p>这游戏视觉风格特别独特：1-bit 黑白风格，并且经过 dithering 处理。作者还在 <span class="exturl" data-url="aHR0cHM6Ly9mb3J1bXMudGlnc291cmNlLmNvbS9pbmRleC5waHA/dG9waWM9NDA4MzIubXNnMTM2Mzc0MiNtc2cxMzYzNzQy">论坛上</span> 讲述了他如何优化效果的（据称花了 100h+ 来调）：</p><figure><img data-src="http://3909.co/dev/od/img/Dither2-SplitCompare.gif" alt="优化前后效果图" /><figcaption aria-hidden="true">优化前后效果图</figcaption></figure><p>这游戏其他方面都还行我就不多说了。剧情 consistent，对话听起来 authentic，音乐简短有力，建模中规中矩不丢分。</p><p>但是玩下来吧，我有几个用户体验不太满意的地方：</p><ul><li>为什么查看记忆一定要走来走去呢……特别还是某些记忆得要经过另外一些记忆才能访问。虽然我觉得这样增加了沉浸感，但是用户体验真不太行。一个 random idea 是加一个自动寻路来打开记忆。</li><li>退出一个记忆为啥不能快捷键搞定，还得走到一个特定地方……</li><li>难度偏大了……不过这有一部分原因是我没注意到一些细节，导致没法区分一些人，只能靠猜。</li></ul><p>最后给大家两个小 tips：</p><ul><li>游戏的英语 transcript 非常口语化，一堆不明所以的短语，我读起来有点吃力。中文翻译相当正宗，推荐使用。</li><li>仔细想想如何最大程度利用游戏的验证机制。</li></ul><div class="video-container"><iframe src="https://www.youtube.com/embed/ILolesm8kFY" frameborder="0" loading="lazy" allowfullscreen></iframe></div><h1 id="the-case-of-the-golden-idol-和-the-rise-of-the-golden-idol">The Case of the Golden Idol 和 The Rise of the Golden Idol</h1><p>这俩游戏是同一个系列的两款作品，The Case of the Golden Idol 是一代，The Rise of the Golden Idol 是二代，玩法一样，但是二代优化了用户体验。</p><p>这俩游戏玩法和 Return of the Obra Dinn 类似，但是谜题的颗粒度不同。Gorogoa 非常 fine-grained，一个小谜题就一分钟；Return of the Obra Dinn 非常 coarse-grained，整个游戏就一个大谜题；the Golden-Idol 系列则取了一个中间，10-20min 一个。对于一个谜题，the Golden Idol 系列和 Return of the Obra Dinn 类似，你需要找到线索，直到游戏告诉你找到了所有线索，剩下的就是推理了。当然，推理的过程中你也需要不停回去看各个场景。另一个不同点是解谜要求不同：Return of the Obra Dinn 要求填补 <em>这张脸</em> 的名字是 <code>谁</code>，死于 <code>死因</code>，不要求你补完故事，而 the Golden Idol 系列需要填补剧情，<em>这张脸</em> 是 <code>谁谁谁</code>，<code>谁谁谁</code> 做了 <code>什么事</code> ，所以 <code>谁谁谁</code> 做了 <code>什么事</code>，从而 <code>什么东西</code> <code>怎么怎么</code> 了。官方还有 <span class="exturl" data-url="aHR0cHM6Ly93d3cudGhlZ29sZGVuaWRvbC5jb20vdHJpYWw=">网页试玩版</span> 让你体验具体玩法。要比较 the Golden Idol 系列和 Return of the Obra Dinn 的话，我还是更喜欢 the Golden Idol 系列一些，可能是我玩 Return of the Obra Dinn 时解谜不顺吧，解完了感觉缺了一堆细节不清楚……但是我还是得承认像 Return of the Obra Dinn 一样设计一个特别大的谜题是一件特别难的事。</p><p>剧情方面，我和 zrp 一致同意写剧本的人有点东西，故事荒诞离奇，出人意料但又不无道理。二代的剧情比一代更上一层楼，特别是最后一章直接升华了，也揭示了为什么采取回忆录这样的表现方式。</p><p>另外这游戏汉化非常辣鸡，还是玩英文版比较好。</p><p>最后放一个官方 trailer，但是感觉没啥用，不如直接去网页试玩版上手试试。</p><div class="video-container"><iframe src="https://www.youtube.com/embed/Qqciwm7GD1E" frameborder="0" loading="lazy" allowfullscreen></iframe></div><h1 id="patricks-parabox">Patrick's Parabox</h1><p>之前 <a href="/2020/12/27/puzzle-games/">聊过三个推箱子类游戏（Snakebird, Baba is You, Stephen's Sausage Roll）</a>，这里再来一个推箱子类游戏。</p><p>游戏的主要卖点就是它的玩法：每个箱子是一个可以进入的空间，而这个空间里可以包含其他箱子，有种递归的感觉。作者在玩法上做了充分的扩展，依次引入了很多小规则。前面的小规则还好，后面的规则有点头晕。具体例子可以看官方 trailer：</p><div class="video-container"><iframe src="https://www.youtube.com/embed/0zprrHCe5as" frameborder="0" loading="lazy" allowfullscreen></iframe></div><p>游戏里的一个小设计是场景跳跃也用了这个界面，就是进入子菜单或者进入下一关等于进入一个小房间。游戏难度适中，有简单的也有难的。整体而言，这是一个很纯粹的解谜向游戏，喜欢 puzzle game 的话不妨一试。</p><h1 id="cocoon">COCOON</h1><p>COCOON 作者是 Jeppe Carlsen，之前是 INSIDE 和 LIMBO 的主设计师。这游戏还是 <span class="exturl" data-url="aHR0cHM6Ly9hbm5hcHVybmFpbnRlcmFjdGl2ZS5jb20v">Annapurna Interactive</span> 发行的，我很喜欢他们发行的游戏的风格，之前的 Gorogoa 和 Journey 都让我眼前一亮。COCOON 的设计理念就和 Journey 类似，没有文字引导，也是小清新的画风。</p><p>COCOON 的玩法是你可以举着一个球四处跑，然后用球来激活开关。每个球是一个独立的世界，所以也可以递归，例如红球的世界里包含绿球，绿球的世界里又可以访问红球——等等是不是感觉很眼熟？没错，我就是写 Patrick's Parabox 的时候突然想到了 COCOON 这才加了进来。要比较两个游戏的话，COCOON 专注于表现力，例如视觉、音效，甚至它还有一个小剧情，而 Patrick's Parabox 更注重解谜，谜题更多更妙。这基本上就是我喜欢的两个套路了：一个是艺术气息，一个是挑战智慧。不过 COCOON 游戏体量不大，五个小时左右就能搞定。</p><p>COCOON 的 trailer 没说啥，可以看看它的视觉效果。它里面确实说明了一个球套一个球的玩法，还有一些有的没的的小机关。</p><div class="video-container"><iframe src="https://www.youtube.com/embed/pf-tfzLNFBw" frameborder="0" loading="lazy" allowfullscreen></iframe></div><h1 id="animal-well">ANIMAL WELL</h1><p>最后一个是 2024 年的重头戏，盘点 2024 年的独立游戏就不可能绕过的 ANIMAL WELL。这款游戏有着强烈的个人艺术气息，作者把这游戏当成一份艺术品在打磨。很难想象，一个 2024 年的游戏，包含了一个物理引擎，大小居然只有 33MB。游戏画面是像素风格，作者一个人几乎包揽了所有，包括引擎、玩法、地图、音乐、美术、谜题，而且完成度非常高。</p><p>游戏的主要玩法是类银河恶魔城 + 解密。需要打怪，但是非常简单，但是有些移动方式是需要卡操作。这游戏设计理念和 FEZ 类似，作者在游戏里藏了很多机关，找到谜题也是解谜的一部分。游戏非线性，你可以自己选择地图去探索并解锁相关道具，甚至道具都有意想不到的用法（spoiler alert：你有很多种移动方式）。</p><p>根据 <span class="exturl" data-url="aHR0cHM6Ly9rb3Rha3UuY29tL2FuaW1hbC13ZWxsLWd1aWRlLXRpcHMtbGF5ZXJzLWV4cGxhaW5lZC0xODUxNTMyNTE1">相关资料</span>，ANIMAL WELL 的解谜有四个级别：</p><ol type="1"><li>探索主要区域，获取主要道具，完成主要任务；</li><li>探索隐藏区域，完成隐藏任务。FEZ 玩家应该很熟悉了。</li><li>ARG 风格解谜：每个玩家只能拿到答案的一部分，需要凑齐所有部分才能完成解谜；</li><li>多周目：特定条件下的多周目会提供一些线索，合起来又可以找到一个隐藏区域。</li></ol><p>甚至还有个网站 <span class="exturl" data-url="aHR0cHM6Ly9oYXNldmVyeXRoaW5naW5hbmltYWx3ZWxsYmVlbmZvdW5keWV0LmNvbS8=">https://haseverythinginanimalwellbeenfoundyet.com/</span>……gsh 还给了我两个 B 站链接：<span class="exturl" data-url="aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tL3ZpZGVvL0JWMU5aNDIxNDdIaA==">级别 3 的攻略</span> 和 <span class="exturl" data-url="aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tL3ZpZGVvL0JWMUx6NDIxWTd5MQ==">级别 4 的攻略</span>。</p><div class="video-container"><iframe src="https://www.youtube.com/embed/39mqGkp8XQM" frameborder="0" loading="lazy" allowfullscreen></iframe></div>]]></content>
    
    
    <summary type="html">&lt;p&gt;转眼间 2024 年&lt;del&gt;快&lt;/del&gt;已经过去了……隔了好久没写娱乐相关的文章了，随便写几个作为&lt;del&gt;今&lt;/del&gt;去年的年终总结得了……（然后震惊地发现我上次写游戏已经是四年前了……）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9vYnJhZGlubi5jb20v&quot;&gt;Return of the Obra Dinn&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cudGhlZ29sZGVuaWRvbC5jb20v&quot;&gt;The Case of the Golden Idol&lt;/span&gt; 和 &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cucmlzZW9mdGhlZ29sZGVuaWRvbC5jb20v&quot;&gt;The Rise of the Golden Idol&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cucGF0cmlja3NwYXJhYm94LmNvbS8=&quot;&gt;Patrick&#39;s Parabox&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cuY29jb29uZ2FtZS5jb20v&quot;&gt;COCOON&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cuYW5pbWFsd2VsbC5uZXQv&quot;&gt;ANIMAL WELL&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（可能有略微剧透，手动狗头）&lt;/p&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.roosephu.net/categories/Life/"/>
    
    <category term="Game" scheme="https://www.roosephu.net/categories/Life/Game/"/>
    
    
  </entry>
  
  <entry>
    <title>论 First Principle</title>
    <link href="https://www.roosephu.net/2024/12/30/on-First-Principles/"/>
    <id>https://www.roosephu.net/2024/12/30/on-First-Principles/</id>
    <published>2024-12-30T09:10:47.000Z</published>
    <updated>2024-12-30T09:10:47.000Z</updated>
    
    <content type="html"><![CDATA[<p>前几天和 hls bibi 了一下。hls 推荐了 <span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1GNVdaQ3BUTkVDOA==">这个视频</span> 给我。我是以一个批判的角度看视频的，就像我之前 review paper 一样，先把屁股坐住了再来找论据，所以大部分观点都是 biased 的，可能就是在 nitpicking。</p><span id="more"></span><p>先抛出我对 <em>First Principle</em> 的理解：在需要 innovate 的场合，挑选合适的 assumption 来定义问题，然后 end-to-end 来解决问题。</p><h1 id="如何定义问题assumption-的合理性">如何定义问题？（assumption 的合理性）</h1><p><em>First Principle</em> 说，对于解决一个问题，我们应该从基本公理出发来思考，而不是从类比、经验来思考：</p><blockquote><p>[…] You kind of boil things down to the most fundamental truths, and say ok what do we sure is true or as sure as possible is true, and reason up from there. […]</p><p>[…] people take too many things as they assume too many things to be true without a sufficient basis in that belief. It's very important that people closely analyze what is supposed to be true […]</p></blockquote><p>我的翻译就是：挑选合适的 assumption 来定义问题。</p><p>视频里 Elon 有一个电池的例子：电池成本的 lower bound 是你去市场上购买各种化学原料的成本之和。这个例子我觉得可以进一步，为什么要从市场上买呢？直接买矿石自己提炼不更有效吗？生产过程中的一些 byproduct 说不定也有用。或许还可以再进一步：为什么电动车需要独立的电池？我们可以把电池和发动机合在一起可以吗？</p><p>（hls 表示这里是在综合考虑成本和可行性。钢铁行业作为一个成立了几百年的行业，竞争极为激烈，行业利润较为稀薄，想象力非常有限，把钱投在这上面不划算。这个信息依赖于对钢铁行业的了解。尽管如此，我上面那段话有一定的讽刺意味。）</p><p>这个例子还 assume 了 R&amp;D 成本可以忽略<del>，which is probably not true</del>。还有我也挺好奇的，Elon 的公司需要律师或者会计吗？从 <span class="exturl" data-url="aHR0cHM6Ly9jYXJlZXJzLnguY29tL2Vu">X Careers</span> 来看，可能还是需要的……律师的理论输出速度不知道能不能设成打字速度……这么说来 Elon 这么喜欢 AI 是有道理的，毕竟 AI 的理论输出速度我只能用 non-deterministic Turing machine 来 bound——啊不对，可能还会有比 non-deterministic Turing machine 更加强大的 model……</p><p>（hls 告诉我在市场足够大的情况下 R&amp;D 是一次性投入，可以忽略，例如车企。我查了一下比较大的车企，gross margin 通常为 10%-20%，而 R&amp;D 为 5% 左右，对比起 variable cost 的 80%+ 来说确实是个小头。）</p><h1 id="如何解决问题method-的合理性">如何解决问题？（method 的合理性）</h1><p><em>First Principle</em> 强调减少中间环节，每加一个环节都会阻碍 efficiency：</p><blockquote><p>[…] the second thing is try to delete the whatever the step is, the part or the process step […]</p></blockquote><p>就是我们有了一个 solution 后，试图去减少其中的步骤来去除冗余。</p><p>视频里 Elon explicitly 提到了一个 anti-pattern:</p><blockquote><p>[…] through most of our life, we get through life by reasoning by analogy, which essentially means kind of copying what other people do with slight variations. […]</p><p>[…] don't just follow the trend […]</p></blockquote><p>这句话可以 apply 到一件具体的事上。那如果我们直接把它 apply 到 <em>First Principle</em> 自己呢？如果我们 get through our life by applying <em>First Principle</em> with slight variations 呢？例如，大部分人思考问题都是 hierarchical thinking，Elon 他思考方式会有什么本质不同的 variation 吗？不同粒度的 hierarchical thinking 算不算 slight variation？Elon 会直接 end-to-end thinking，跑一个全局最短路吗？</p><p>我个人认为，一个合适的 abstraction，是必要的——那什么叫做 <em>合适</em> 呢？有人吐槽有一些 engineer，太喜欢 abstraction 了，做啥都要 abstract。我曾经瞄过一眼 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3JheS1wcm9qZWN0L3JheS90cmVlL21hc3Rlci9ybGxpYg==">RLlib 的代码</span>，看看能不能在此基础上定制我的 research code，不好意思抽象太多直接告辞……另一方面，编程语言是越来越 abstract 的。从早期的汇编，到 C，到 Java/Python，到后来的 Go/Rust/Zig。当年 Knuth <a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> 和 Dijkstra<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a><a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> 为是否需要 <code>goto</code> 而争论了一番：<code>while/for</code> 这类抽象是否足够了？</p><p>开个小玩笑（手动 doge）：数学家可能会对此嗤之以鼻，因为他们最擅长 reduction，解决一个问题的方法就是把它 reduce 成一个 solved problem。</p><h1 id="哪里适用goal-的合理性">哪里适用？（goal 的合理性）</h1><p>在需要 innovation 的地方，我们不应该被常规思路限制住。这点在视频里也有说明：</p><blockquote><p>[…] When you want to do something new, you have to apply the physical approach. The physics is about how to discover new things that are counterintuitive. […]</p><p>[…] if you're trying to do something new, it's the best way to think. […]</p></blockquote><p>只是，我们需要在所有地方 innovate 吗？</p><p>视频中提到：</p><blockquote><p>[…] they'll say we do that because it's always done that way, or they'll not do it because nobody's ever done that way so it must not be good […]</p></blockquote><p><em>it's always done that way</em> 或者 <em>nobody's ever done that way</em> 这两句话确实不太行，但是为此否定 prior experience 就太草率了。我更加关心的问题是：没人做过，是因为有 strong reason 大家不做，还是没人想到在这里 innovate，还是有人做了我们不知道？别人做出过哪些尝试，有哪些经验教训可以汲取？如果无法回答这些问题，盲目地 assume 我们不应该 follow 别人的做法，在不懂的地方强行 innovate，难免会重复别人踩过的坑。难道真的是人类从历史中学到的唯一教训就是人类不能从历史中学到任何教训？</p><p>所以我觉得这里还需要再加一个前提：在一个我们充分了解的领域。spot 哪里能 improve，这个问题同样非常难。这和之前说的 <em>如何定义问题</em> 非常类似，后者关注 assumption 的合理性，前者关注 goal 的合理性。如果学不会热力学三定律，<em>First Principle</em> 也不能帮我造永动机呀……</p><p>这不免令我想到那句从小听到大的经典名言：</p><div class="note warning"><p>学而不思则罔，思而不学则殆。</p></div><h1 id="总结">总结</h1><p>In conclusion，我不认为 <em>First Principle</em> is nonsense。我觉得 <em>First Principle</em> 有一点是有道理的：不要被 convention 束缚住，因为一件事是 convention 就认为它好。但是只想着 <em>First Principle</em> 就是 nonsense 了。而且，即使 <em>First Principle</em> 了，或许还可以更加 <em>First Principle</em>。用 <em>First Principle</em> 来分析是需要 mental energy，需要时间去研究，这都是有限的资源，所以我们需要把 <em>First Principle</em> 留在关键的地方，ROI 大的地方。</p><p>Bonus：请用 <em>First Principle</em> 来分析 <em>First Principle</em> 的合理性。</p><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr /><ol><li id="fn1"><p>Structured Programming with go to Statements<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p>Go To Statement Considered Harmful<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn3"><p>Notes On Structured Programming<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;前几天和 hls bibi 了一下。hls 推荐了 &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1GNVdaQ3BUTkVDOA==&quot;&gt;这个视频&lt;/span&gt; 给我。我是以一个批判的角度看视频的，就像我之前 review paper 一样，先把屁股坐住了再来找论据，所以大部分观点都是 biased 的，可能就是在 nitpicking。&lt;/p&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.roosephu.net/categories/Tech/"/>
    
    
  </entry>
  
  <entry>
    <title>The Optimal Order to Purchase Vampire Survivors Power Ups</title>
    <link href="https://www.roosephu.net/2024/10/20/optimal-order-to-purchase-Vampire-Survivors-Power-Ups/"/>
    <id>https://www.roosephu.net/2024/10/20/optimal-order-to-purchase-Vampire-Survivors-Power-Ups/</id>
    <published>2024-10-21T02:52:13.000Z</published>
    <updated>2024-10-21T02:52:13.000Z</updated>
    
    <content type="html"><![CDATA[<div class="note warning"><p><em>The cost for Power Ups has changed since version 0.7, so this post is no longer applicable.</em></p></div><p><span class="math inline">\(\newcommand{\eps}{\varepsilon} \newcommand{\R}{\mathbb{R}}\)</span> <span class="exturl" data-url="aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2FwcC8xNzk0NjgwL1ZhbXBpcmVfU3Vydml2b3JzLw==">Vampire Survivors</span> is a game released in early access near the end of 2021. It received 110k recommendations in one month, which is pretty amazing given its minimal aesthetics and seemingly low production costs. I also played it a lot back then, but I won't talk about its gameplay here. Instead, this post discusses the (old) Power Up mechanism and some of the math behind it.</p><p>The Power Up mechanism in Vampire Survivors is similar to Kingdom Rush's Upgrades, where you spend the gold earned during gameplay to make permanent gains (e.g., increase move speed by x%). As per the notes in <span class="exturl" data-url="aHR0cHM6Ly92YW1waXJlLXN1cnZpdm9ycy5mYW5kb20uY29tL3dpa2kvUG93ZXJVcHM/b2xkaWQ9MzU2NQ==">Fandom</span>, the cost for a Power Up is defined as:<span class="math display">\[Price = \text{InitialPrice} \cdot (1 + \text{Bought}) \cdot \left(1 + \frac{\text{TotalBought}}{10} \right),\]</span>where <span class="math inline">\(\text{InitialPrice}\)</span> is the initial price of the Power Up, <span class="math inline">\(\text{Bought}\)</span> is the number of purchased ranks for this Power Up, and <span class="math inline">\(\text{TotalBought}\)</span> is the total number of purchased ranks among all Power Ups. What's interesting is that the order in which you buy Power Ups actually matters!</p><p>Intuitively, we should purchase expensive Power Ups first. For example, <span class="exturl" data-url="aHR0cHM6Ly93d3cuaWduLmNvbS93aWtpcy92YW1waXJlLXN1cnZpdm9ycy9Qb3dlclVwX09yZGVy">IGN recommends maxing out Power Ups with the highest initial costs first.</span> However, this is not optimal. Suppose we have two Power Ups, A and B. A has 5 ranks with an initial price of 1, while B has only 1 rank with an initial price of 2. If we max out A first, the total price is:<span class="math display">\[1 \times 1 + 1.1 \times 2 + 1.2 \times 3 + 1.3 \times 4 + 1.4 \times 5 + 1.5 \times 2 = 22,\]</span>and if we max out B first, the total price is:<span class="math display">\[1 \times 2 + 1.1 \times 1 + 1.2 \times 2 + 1.3 \times 3 + 1.4 \times 4 + 1.5 \times 5 = 22.5.\]</span></p><p>So, here is our central topic today:</p><div class="note info"><p>What is the optimal order for buying all Power Ups?</p></div><span id="more"></span><h1 id="a-general-formulation-and-its-hardness">A General Formulation and Its Hardness</h1><p>The first attempt is to formulate the problem as a directed acyclic graph (DAG). More specifically, each rank <span class="math inline">\(j\)</span> of a certain Power Up <span class="math inline">\(i\)</span> is a node <span class="math inline">\(v_{i, j}\)</span>, and it has a weight defined as<span class="math display">\[w(v_{i, j}) = \frac{1}{10} \text{InitialPrice}_i \cdot (1 + j),\]</span>with edges <span class="math inline">\(v_{i, j-1} \to v_{i, j}\)</span>, representing that <span class="math inline">\(v_{i, j}\)</span> depends on <span class="math inline">\(v_{i, j - 1}\)</span>. A valid order of purchasing Power Ups is a topological sort of all nodes, and our goal is to find the optimal topological sort. We may notice that the graph has a special property (i.e., it only contains chains), but for now, let's focus on a general graph.</p><section id="weighted-topological-sort" class="note info"><h2><em>Weighted Topological Sort</em></h2><p>Given a directed acyclic graph <span class="math inline">\(G = (V, E)\)</span> and a node weight function <span class="math inline">\(w: V \to \mathbb{N}\)</span>, find a topological ordering <span class="math inline">\(v_{i_1}, \dots, v_{i_n}\)</span> to minimize <span class="math inline">\(\sum\limits_{i=1}^n i w(v_{a_i}).\)</span></p></section><p>An equivalent objective can also be defined in terms of <em>completion time</em> <span class="math inline">\(c(v)\)</span>. The completion time of a specific node <span class="math inline">\(v&#39;\)</span> in an ordering <span class="math inline">\(v_{a_1}, \dots, v_{a_n}\)</span> is the <span class="math inline">\(i\)</span> such that <span class="math inline">\(v&#39; = v_{a_i}\)</span>, so the objective can also be written as<span class="math display">\[\sum_{i} i w(v_{a_i}) = \sum_{v \in V} c(v) w(v).\]</span></p><p>One might try to solve the general problem but may struggle to make significant progress. The reason is simple: it's NP-hard! We present a proof, which is essentially a combination of Theorem 4.15 and Exercise 4.19 from <span class="exturl" data-url="aHR0cHM6Ly9lbGVtZW50c29mc2NoZWR1bGluZy5ubA==">Elements of Scheduling</span> with (very) minor modifications.</p><section id="proof-of-np-hardness-of-weighted-topological-sort" class="note success"><h2>Proof of NP-hardness of <em>Weighted Topological Sort</em></h2><p>We start with <em>Linear Arrangement</em>, an NP-hard problem, which will be defined below and used for reduction.</p><section id="linear-arrangement" class="note info"><h3>Linear Arrangement</h3><p>Given an undirected graph <span class="math inline">\(G = (V, E)\)</span>, find a one-to-one mapping <span class="math inline">\(f: V \to [n]\)</span> to minimize <span class="math inline">\(\sum_{(u, v) \in E} |f(u) - f(v)|\)</span>.</p></section><p>One can easily show that <em>Linear Arrangement</em> is equivalent to minimizing <span class="math inline">\(\sum_{(u, v) \in E} \max(f(u), f(v))\)</span>, as <span class="math inline">\(|f(u) - f(v)| = 2 \max(f(u), f(v)) - f(u) - f(v)\)</span>. We now reduce the <em>Linear Arrangement</em> instance to a <em>Weighted Topological Sort</em> instance.</p><div class="note info"><p>Let <span class="math inline">\(K = |E|^2\)</span>. Construct a graph <span class="math inline">\(G&#39; = (V&#39;, E&#39;)\)</span> from <span class="math inline">\(G = (V, E)\)</span> as follows:</p><ul><li>For each vertex <span class="math inline">\(v_i \in V\)</span>:<ul><li>Add <span class="math inline">\(K\)</span> nodes <span class="math inline">\(V_i&#39;\)</span> to <span class="math inline">\(V&#39;\)</span>: <span class="math inline">\(v_i^{(j)}\)</span> for <span class="math inline">\(j \in \{1, \dots, K\}\)</span>;</li><li>Add <span class="math inline">\(K - 1\)</span> dependencies: <span class="math inline">\(v_i^{(j)} \to v_i^{(j + 1)}\)</span> for <span class="math inline">\(j \in \{1, \dots, K - 1\}\)</span>.</li></ul></li><li>For each edge <span class="math inline">\(e = (v_i, v_j) \in E\)</span>:<ul><li>Add <span class="math inline">\(e\)</span> to <span class="math inline">\(V&#39;\)</span>;</li><li>Add two dependencies: <span class="math inline">\(v^{(K)}_i \to e\)</span> and <span class="math inline">\(v^{(K)}_j \to e\)</span>.We also define the vertex weight as <span class="math inline">\(w(v_{i}^{(j)}) = 0\)</span> and <span class="math inline">\(w(e) = 1\)</span> for each edge <span class="math inline">\(e\)</span>.</li></ul></li></ul></div><p>Let <span class="math inline">\(OPT\)</span> be the optimal <span class="math inline">\(\sum_{(u, v) \in E} \max(f(u), f(v))\)</span> in the <em>Linear Arrangement</em> instance, and let <span class="math inline">\(f^*\)</span> be the corresponding mapping. Similarly, let <span class="math inline">\(OPT&#39;\)</span> be the minimum total cost in the constructed instance, and let <span class="math inline">\(v&#39;^*\)</span> be the corresponding solution.</p><p>We first show that <span class="math inline">\(OPT&#39; &lt; K \cdot OPT + K\)</span>. We determine the order of <span class="math inline">\(V&#39;_i\)</span> as <span class="math display">\[\left[V&#39;_{f^{*-1}(1)}, V&#39;_{f^{*-1}(2)}, \dots, V&#39;_{f^{*-1}(n)} \right]\]</span> and then insert all edges <span class="math inline">\(e \in E\)</span> as early as possible. Since at most <span class="math inline">\(|E|\)</span> edges are inserted, the completion time for an edge <span class="math inline">\(e\)</span> is bounded by <span class="math inline">\(c(e) &lt; K \max(f^*(u), f^*(v)) + |E|\)</span>, which gives a total cost of<span class="math display">\[OPT&#39; &lt; \sum_{e = (u, v) \in E} \left(K \max(f^*(u), f^*(v)) + |E| \right) = K \cdot OPT + K.\]</span></p><p>Now, we construct an optimal solution <span class="math inline">\(f\)</span> from <span class="math inline">\(v&#39;^*\)</span>. We consider the subsequence <span class="math inline">\(\left\{v_{a_i}^{(K)} \right\}_i\)</span> in <span class="math inline">\(v&#39;^*\)</span> and define <span class="math inline">\(f\left(v_{a_i} \right) = i\)</span>. The completion time <span class="math inline">\(c(e)\)</span> for an edge <span class="math inline">\(e = (u, v)\)</span> has a lower bound of <span class="math inline">\(c_e &gt; K \max(f(u), f(v))\)</span>, which in turn provides an upper bound on <span class="math inline">\(\sum_e \max(f(u), f(v))\)</span>:<span class="math display">\[OPT \leq \sum_{(u, v) \in E} \max(f(u), f(v)) &lt; \frac{1}{K} \sum_{e \in E} c_e = \frac{1}{K} OPT&#39; &lt; OPT + 1.\]</span>Note that <span class="math inline">\(f\)</span> maps nodes to integers, so the first inequality must be an equality, which means <span class="math inline">\(f\)</span> is an optimal mapping for the <em>Linear Arrangement</em> instance.</p></section><h1 id="the-optimal-order-for-buying-power-ups">The Optimal Order for Buying Power Ups?</h1><p>The result above is definitely frustrating. However, we might want to take a step back. Remember that our goal is only to find the optimal order to buy Power Ups? It's sufficient to consider only chains instead of a general graph.</p><section id="weighted-topological-sort-for-chains" class="note info"><h2><em>Weighted Topological Sort for Chains</em></h2><p>Given a directed acyclic graph <span class="math inline">\(G = (V, E)\)</span> and a node weight function <span class="math inline">\(w: V \to \mathbb{N}\)</span>, find a topological ordering <span class="math inline">\(v_{i_1}, \dots, v_{i_n}\)</span> to minimize <span class="math inline">\(\sum\limits_{i=1}^n i w(v_{a_i}).\)</span>The graph <span class="math inline">\(G\)</span> contains only <span class="math inline">\(k\)</span> chains:<span class="math display">\[\begin{cases}V = \{v _{i, j}\} _{i \in \{1, \dots, k\}, j \in \{1, \dots, n_i\}}, \\E = \{v _{i, j} \to v _{i, j + 1}\} _{i \in \{1, \dots, k\}, j \in \{1, \dots, n_i - 1\}}.\end{cases}\]</span></p></section><p>It turns out that there is an efficient (polynomial time) algorithm!</p><section id="a-polynomial-algorithm-for-weighted-topological-sort-for-chains" class="note success"><h2>A polynomial algorithm for <em>Weighted Topological Sort for Chains</em></h2><p>Let <span class="math inline">\(v^*\)</span> be the optimal ordering. Let a segment be an interval of nodes from the same chain. Since <span class="math inline">\(v^*\)</span> is optimal, swapping adjacent segments that come from different chains won't improve the objective. This can be formulated as: let <span class="math inline">\(v_{i_1, \{l_1, \dots, r_1\}}\)</span> and <span class="math inline">\(v_{i_2, \{l_2, \dots, r_2\}}\)</span> be adjacent segments where <span class="math inline">\(i_1 \neq i_2\)</span>, then:<span class="math display">\[\sum_{j=l_1}^{r_1} (j - l_1) w(v_{i_1, j}) + \sum_{j=l_2}^{r_2} (j - l_2 + r_1 - l_1 + 1) w(v_{i_2, j}) \leq \sum_{j=l_2}^{r_2} (j - l_2) w(v_{i_2, a}) + \sum_{j=l_1}^{r_1} (j - l_1 + r_2 - l_2 + 1) w(v_{i_1, j}),\]</span>After simplification, it becomes:<span class="math display">\[\frac{\sum\limits_{a=l_2}^{r_2} w(v_{i_2, a})}{r_2 - l_2 + 1} \leq \frac{\sum\limits_{a=l_1}^{r_1} w(v_{i_1, a})}{r_1 - l_1 + 1}.\]</span></p><p>We define the weight of a segment <span class="math inline">\(w(v_{i, \{l, \dots, r\}}) := \frac{1}{r - l + 1} \sum\limits_{j=l}^r w(v_{i, j})\)</span>, so the inequality above simply means that <span class="math inline">\(w(v_{i_1, \{l_1, \dots, r_1\}}) \geq w(v_{i_2, \{l_2, \dots, r_2\}})\)</span>, and all maximal segments in <span class="math inline">\(v^*\)</span> are sorted in descending order.</p><p>Now, let us consider the chain <span class="math inline">\(V_i\)</span>. We start with <span class="math inline">\(n_i\)</span> chains, each containing only one node. Now we're trying to merge adjacent nodes: If there exist two adjacent segments <span class="math inline">\(v_{i, \{a, \dots, b\}}\)</span> and <span class="math inline">\(v_{i, \{b + 1, \dots, c\}}\)</span> such that $w(v_{i, {a, , b}}) &lt; $ <span class="math inline">\(w(v_{i, \{b + 1, \dots, c\}})\)</span>, we can merge them into one segment <span class="math inline">\(v_{i, \{a, \dots, c\}}\)</span>. The reason is: If there is another segment <span class="math inline">\(v_{i&#39;, \{l&#39;, \dots, r&#39;\}}\)</span>, one of the following must be true:<span class="math display">\[w(v_{i&#39;, \{l&#39;, \dots, r&#39;\}}) &gt; w(v_{i, \{a, \dots, b\}}), \quad \text{or} \quad w(v_{i&#39;, \{l&#39;, \dots, r&#39;\}}) &lt; w(v_{i, \{b + 1, \dots, c\}}),\]</span>which contradicts our previous observation. The process of merging can be summarized in the following pseudocode:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">&gt;<span class="keyword">def</span> <span class="title function_">merge_segments</span>(<span class="params">chain</span>):</span><br><span class="line">   segments = []</span><br><span class="line">   <span class="keyword">for</span> v <span class="keyword">in</span> chain:</span><br><span class="line">       cur_seg = [v]</span><br><span class="line">       <span class="keyword">while</span> segments <span class="keyword">and</span> w(segments[-<span class="number">1</span>]) &lt; w(cur_seg):</span><br><span class="line">           cur_seg = segments.pop() + cur_seg</span><br><span class="line">       segments.append(cur_seg)</span><br><span class="line">   <span class="keyword">return</span> segments</span><br></pre></td></tr></table></figure><p>After merging segments, all segments in chain <span class="math inline">\(i\)</span> are sorted in descending order of <span class="math inline">\(w\)</span>. This naturally leads to a greedy algorithm: The best candidate in each chain will be the current first segment, so we don't have any dependency issues!</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">&gt;<span class="keyword">def</span> <span class="title function_">optimal_order</span>(<span class="params">chains</span>):</span><br><span class="line">   segments = [seg <span class="keyword">for</span> chain <span class="keyword">in</span> chains <span class="keyword">for</span> seg <span class="keyword">in</span> merge_segments(chain)]</span><br><span class="line">   segments = <span class="built_in">sorted</span>(segment, key=w)</span><br><span class="line">   <span class="keyword">return</span> [v <span class="keyword">for</span> seg <span class="keyword">in</span> segments <span class="keyword">for</span> v <span class="keyword">in</span> seg]</span><br></pre></td></tr></table></figure><p>The code above is inefficient in its representation of segments and can be easily optimized to <span class="math inline">\(O(n \log n)\)</span>, where <span class="math inline">\(n = |V|\)</span>.</p></section><h1 id="how-about-a-general-completion-time-weight-function">How About a General Completion Time Weight Function?</h1><p>We have considered the objective in the form of <span class="math inline">\(\sum_{v} c(v) w(v) = \sum_{v} \mathnormal{Id}(c(v)) w(v)\)</span>, where <span class="math inline">\(Id\)</span> is the identity function. Is it possible to generalize this to a more complex completion time weight function <span class="math inline">\(f: \mathbb{N} \to \R\)</span> instead of the identity function?</p><p>Unfortunately, this is also NP-hard, even if the graph contains only chains. We will reduce the <em>3-Partition</em> problem to it.</p><section id="partition" class="note info"><h2><em>3-Partition</em></h2><p>Let <span class="math inline">\(S\)</span> be a multiset containing <span class="math inline">\(3n\)</span> positive integers. Can we partition <span class="math inline">\(S\)</span> into <span class="math inline">\(n\)</span> multisets, each containing 3 elements with a sum <span class="math inline">\(s = \frac{1}{n} \sum\limits_{e \in S} e\)</span>?</p></section><p>Note that <em>3-Partition</em> is strongly NP-complete: it's NP-complete even when all numbers are bounded by a polynomial of <span class="math inline">\(n\)</span>.</p><section id="proof-of-np-hardness-of-general-completion-time-weighted-topological-sort" class="note success"><h2>Proof of NP-hardness of <em>General Completion Time Weighted Topological Sort</em></h2><p>Given an instance of the <em>3-Partition</em> problem (where we assume every element in <span class="math inline">\(S\)</span> is strictly greater than 1), we construct the following problem:</p><section id="instance-of-general-completion-time-weighted-topological-sort" class="note info"><h3>Instance of <em>General Completion Time Weighted Topological Sort</em></h3><ul><li>The graph <span class="math inline">\(G\)</span> contains <span class="math inline">\(3n\)</span> chains, where the <span class="math inline">\(i\)</span>-th chain <span class="math inline">\(V_i\)</span> has <span class="math inline">\(S_i\)</span> nodes.</li><li>The weight function <span class="math inline">\(w\)</span> and completion time weight function <span class="math inline">\(f\)</span> are:<span class="math display">\[w(v_{i, j}) = \begin{cases} 1 &amp; j = 1 \\ 0 &amp; j = S_i \\ 2 &amp; \text{otherwise} \end{cases}, \quad f(t) = \begin{cases} 1 &amp; t \bmod s \in \{1, 2, 3\} \\ 2 &amp; t \bmod s \in \{0, s - 1, s - 2\} \\ 0 &amp; \text{otherwise} \end{cases}\]</span></li></ul></section><p>From the <em>Rearrangement Inequality</em>, <span class="math inline">\(3n\)</span> is a lower bound of the objective, and it is only attained if <span class="math inline">\(c(v_{i, 1}) \bmod s \in \{1, 2, 3\}\)</span> and <span class="math inline">\(c(v_{i, S_i}) \bmod s \in \{0, s - 1, s - 2\}\)</span> for all chains <span class="math inline">\(i\)</span>. In this case, the optimal ordering can be split into <span class="math inline">\(n\)</span> intervals evenly, each consisting of 3 complete chains, which means the <em>3-Partition</em> instance is satisfiable.</p></section><h1 id="are-chains-and-identity-completion-time-weight-function-the-limit">Are Chains and Identity Completion Time Weight Function the Limit?</h1><p>We found an efficient algorithm for <em>Weighted Topological Sort for Chains</em>, but it failed for two extensions:</p><ul><li>A general graph;</li><li>A general completion time weight function,</li></ul><p>both of which have been proven NP-hard. So what is the right problem to focus on for a successful generalization?</p><p>Section 4.3 of <em>Elements of Scheduling</em> shows that the following extensions still have polynomial time algorithms:</p><ul><li>A <em>series-parallel graph</em>;</li><li>A cost function with a <em>preference order on sequences</em>.</li></ul><p>We refer readers to the book for more details.</p><h1 id="notes">Notes</h1><p>In fact, this post has been in draft for more than a year. The reason I didn’t post it earlier is that… I realized I was completely wrong when it was almost finished. I thought it should align with a classic problem:</p><section id="a-classic-problem" class="note info"><h2>A Classic Problem</h2><p>Given a DAG, the task is to sort all vertices topologically, such that vertex 1 has the smallest index. Break ties by minimizing the index of vertex 2, 3, and so on, until <span class="math inline">\(n\)</span>.</p></section><p>which can be seen as a special case (<span class="math inline">\(w(i)= \eps^i\)</span>) of the <em>Weighted Topological Sort</em> problem. The analysis, however, is entirely different. When I realized this, the post was nearly done, except for the (wrong) proof. It was awkward to admit I couldn’t solve it anymore! I then modeled the problem as the <em>Weighted Topological Sort</em> problem and spent quite some time trying to solve it. Unfortunately, I made no progress in designing an efficient algorithm. Recently, I tried again and, after changing some keywords, I found a relevant paper. After checking out a few more papers and reading their Introduction and Related Work sections, I discovered the right keywords, which led me to <em>Elements of Scheduling</em> and a satisfying solution.</p><p>As for writing, I feel it’s better to present and solve the <em>Weighted Topological Sort</em> problem first and then attempt to generalize it. However, I’m too lazy to reorder the sections, as it already took me a lot of effort just to finish the post…</p><h1 id="references">References</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly9ob21lcy5kaS51bmltaS5pdC9yaWdoaW5pL0RpZGF0dGljYS9Mb2dpc3RpY2EvTWF0ZXJpYWxlTG9nL1M4YiUyMC0lMjBTY2hlZHVsaW5nJTIwc2luZ2xlJTIwbWFjaGluZS5wZGY=">Scheduling (single machine)</span></li><li><span class="exturl" data-url="aHR0cHM6Ly9lbGVtZW50c29mc2NoZWR1bGluZy5ubA==">Elements of Scheduling</span></li></ul>]]></content>
    
    
    <summary type="html">&lt;div class=&quot;note warning&quot;&gt;
&lt;p&gt;&lt;em&gt;The cost for Power Ups has changed since version 0.7, so this post is no longer applicable.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span class=&quot;math inline&quot;&gt;&#92;(&#92;newcommand{&#92;eps}{&#92;varepsilon} &#92;newcommand{&#92;R}{&#92;mathbb{R}}&#92;)&lt;/span&gt; &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2FwcC8xNzk0NjgwL1ZhbXBpcmVfU3Vydml2b3JzLw==&quot;&gt;Vampire Survivors&lt;/span&gt; is a game released in early access near the end of 2021. It received 110k recommendations in one month, which is pretty amazing given its minimal aesthetics and seemingly low production costs. I also played it a lot back then, but I won&#39;t talk about its gameplay here. Instead, this post discusses the (old) Power Up mechanism and some of the math behind it.&lt;/p&gt;
&lt;p&gt;The Power Up mechanism in Vampire Survivors is similar to Kingdom Rush&#39;s Upgrades, where you spend the gold earned during gameplay to make permanent gains (e.g., increase move speed by x%). As per the notes in &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly92YW1waXJlLXN1cnZpdm9ycy5mYW5kb20uY29tL3dpa2kvUG93ZXJVcHM/b2xkaWQ9MzU2NQ==&quot;&gt;Fandom&lt;/span&gt;, the cost for a Power Up is defined as:
&lt;span class=&quot;math display&quot;&gt;&#92;[
Price = &#92;text{InitialPrice} &#92;cdot (1 + &#92;text{Bought}) &#92;cdot &#92;left(1 + &#92;frac{&#92;text{TotalBought}}{10} &#92;right),
&#92;]&lt;/span&gt;
where &lt;span class=&quot;math inline&quot;&gt;&#92;(&#92;text{InitialPrice}&#92;)&lt;/span&gt; is the initial price of the Power Up, &lt;span class=&quot;math inline&quot;&gt;&#92;(&#92;text{Bought}&#92;)&lt;/span&gt; is the number of purchased ranks for this Power Up, and &lt;span class=&quot;math inline&quot;&gt;&#92;(&#92;text{TotalBought}&#92;)&lt;/span&gt; is the total number of purchased ranks among all Power Ups. What&#39;s interesting is that the order in which you buy Power Ups actually matters!&lt;/p&gt;
&lt;p&gt;Intuitively, we should purchase expensive Power Ups first. For example, &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cuaWduLmNvbS93aWtpcy92YW1waXJlLXN1cnZpdm9ycy9Qb3dlclVwX09yZGVy&quot;&gt;IGN recommends maxing out Power Ups with the highest initial costs first.&lt;/span&gt; However, this is not optimal. Suppose we have two Power Ups, A and B. A has 5 ranks with an initial price of 1, while B has only 1 rank with an initial price of 2. If we max out A first, the total price is:
&lt;span class=&quot;math display&quot;&gt;&#92;[
1 &#92;times 1 + 1.1 &#92;times 2 + 1.2 &#92;times 3 + 1.3 &#92;times 4 + 1.4 &#92;times 5 + 1.5 &#92;times 2 = 22,
&#92;]&lt;/span&gt;
and if we max out B first, the total price is:
&lt;span class=&quot;math display&quot;&gt;&#92;[
1 &#92;times 2 + 1.1 &#92;times 1 + 1.2 &#92;times 2 + 1.3 &#92;times 3 + 1.4 &#92;times 4 + 1.5 &#92;times 5 = 22.5.
&#92;]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So, here is our central topic today:&lt;/p&gt;
&lt;div class=&quot;note info&quot;&gt;
&lt;p&gt;What is the optimal order for buying all Power Ups?&lt;/p&gt;
&lt;/div&gt;</summary>
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    
  </entry>
  
  <entry>
    <title>Lake Tahoe 游记</title>
    <link href="https://www.roosephu.net/2024/10/12/trip-to-Lake-Tahoe/"/>
    <id>https://www.roosephu.net/2024/10/12/trip-to-Lake-Tahoe/</id>
    <published>2024-10-12T23:20:42.000Z</published>
    <updated>2024-10-12T23:20:42.000Z</updated>
    
    <content type="html"><![CDATA[<p>几个星期前，我和朋友们去了趟湾区的后花园 Lake Tahoe，这里就随便写点东西记录一下。由于时间比较短，我们只过了一个周末，所以显得这篇文章尤为流水帐 23333</p><span id="more"></span><h1 id="section">9.14</h1><p>由于早上要赶路，我们八点钟就起床了。熟悉我的人都知道，这个时间点对我特别早，然而这天我却因为还在倒时差，早早就起床了。中午在 Sacramento 吃的桂林米粉，Top Choice Restaurant，味道挺不错的，而且这价格真是太让人感动了，放在湾区怕是要翻个倍。</p><p>下午我们搞了个 kayak，六个人分成三组，我和胡老师一组。划之前我们又在开玩笑说会不会翻船。脸哥信心满满，这次绝对不会翻船。然而天公不作美，天气预报告诉我们下午的风不小。这次我们租的是 Tahoe 特色透明小船，因为 Tahoe 的湖水特别清澈，透明小船就会有一种悬浮的感觉。我不由想到当年的图形学大作业了，我写了一个 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Jvb3NlcGh1L2FtY21jcHBtL3RyZWUvbWFzdGVy">AMCMCPPM 算法（自适应马尔可夫链蒙特卡洛渐进式光子映射，特别适合用来装逼）</span>，里面还有一张 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Jvb3NlcGh1L2FtY21jcHBtL2Jsb2IvbWFzdGVyL3dhdGVyLWNhdXN0aWMvVHVuZ3N0ZW5SZW5kZXIucG5n">渲染图</span> ，就和我在 Tahoe 看到的一样。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/kayak.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/kayak.webp" alt="<p>透明 Kayak</p>"/>      <figcaption aria-hidden="true"><p>透明 Kayak</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3004.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3004.webp" alt="<p>Lake Tahoe (from Sand Harbor)</p>"/>      <figcaption aria-hidden="true"><p>Lake Tahoe (from Sand Harbor)</p></figcaption>    </div>  </div></figure><p>和我们同行的还有两个导游，估计是来保护我们的。而导游的 kayak 很特别，可以用脚踩着划，所以我们就看到导游一边刷手机，一边划 kayak，而我和胡老师死命划，划的手臂累死了，却只能望其项背。不过速度不重要，重要的是，我和胡老师友谊的小船，在不断进水！一个浪打过来，船头迎浪而上，浪过以后，船头重重打到水面上，激起的浪花落入船中，而我们还没有办法把水排出去。更要命的是，这还是个正反馈，已经进的水越多，船就越沉；船越沉，就越容易进水，再这样下去，我和胡老师就要上演泰坦尼克号了……我们试图在湖中泼一下水出去，但是看起来效果不大，最后我和胡老师一致决定：离目的地不远了，赶紧在船沉之前划到目的地，然后下船排水。可喜可贺，我和胡老师终于逃离了这艘 sinking boat……</p><p>返程下船的时候岸边浪很大，胡老师在前排我在后排，导游帮我们抓着 kayak 我们就可以下船了。在这里我犯了一个致命错误，初中物理书上经常提到的，为什么水比看上去的浅？我看着挺浅的，就自信一脚踏下去，直到没过了腰才后悔莫及，甚至一个浪冲过来，我的拖鞋都差点无了……</p><p>整体而言，这次 kayak 体验挺好的，风景也不错，但是问题在于周围没啥设施，我们走出来后脚底一堆沙子，但是周围没有一个水龙头可以冲一下脚，就很难受……</p><p>划了 kayak 后，我们就去吃饭了。由于我们也不期望 Incline Village 这边有啥好吃的餐厅，我们就在 Google Map 上随便看看了。环顾一圈，我们找到了一家 <span class="exturl" data-url="aHR0cHM6Ly9nLmNvL2tncy9iNUpmeUxl">炸鸡店</span>，评分高达 4.9，堪称是 Incline Village 评分最高的餐馆。于是我们就一致决定是它了。没想到的是，这家炸鸡店做的特别慢，而我们还准备去 Cave Rock 去看日落，就只好在车上吃了。</p><p>Cave Rock 那边不是很好停车，我们几个人先下车之后有人去停了车。Cave Rock 那里一堆大石头，坡度不小，手脚并用才好上去，幸亏这里石头很实，踩上去不会晃悠。由于就在湖边上，拍的日落还挺好看的。刚日落的时候云是灿烂的金黄色，太阳落下去后云变成了赤陶色，甚至有种不真实的模糊感。lzy 和 tjc 来的比较晚，想爬上去的时候已经比较黑了，担心安全就没上去了，特别是下山比上山还危险。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3054.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3054.webp" alt="<p>爬 Cave Rock</p>"/>      <figcaption aria-hidden="true"><p>爬 Cave Rock</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3051.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3051.webp" alt="<p>Cave Rock 上的晚霞</p>"/>      <figcaption aria-hidden="true"><p>Cave Rock 上的晚霞</p></figcaption>    </div>  </div></figure><p>爬完 Cave Rock 后我们就去买了点东西，给明天的活动准备一下，然后租的 AirBnB 休息了。说起来这一段时间刚好 Incline Village 有山火，好像就在我们租的 AirBnB 旁边。经过 lzy 缜密分析查资料，最后决定风向应该是远离我们的方向。要是突然风向变了，睡到一半被烟薰起来就尴尬了。</p><h1 id="section-1">9.15</h1><p>胡老师起了个大早，开始给大家做早餐。昨天晚上胡老师机智地买了 bacon，这样就可以不用买油了，可以用 bacon 的油来煎鸡蛋。我也来大显身手，然后煎糊了 sausage……</p><p>今天的第一站是 Ed Z'berg Sugar Pine Point State Park。公园地上一堆松果，你说的我都懂，可是为啥松果这么大？胡老师大为震撼，决定捡一个松果给汤姐做礼物。胡老师又注意到这松果上有些粘液，还有种奇特的芬芳，然后意识到这就是传说中的松脂吗……而且这个公园叫做 Sugar Pine Point State Park，那想必这里的松树都是 Sugar Pine 咯，有没有勇士来尝尝是不是真的是甜的 2333</p><p>这个公园里还有一个网红景点，是一段废弃铁轨延伸进清澈的湖水里。说实话第一次看的时候感觉就这？但是拍了照之后<del>再加几个滤镜修修图</del>感觉还挺不错的……</p><p>第二站是 Emerald Bay State Park。今天的风明显比昨天大，我在 Vikingsholm Trail Head 被风吹的不行了，但是这里的风景确实不错，从这里能看到 Fannette Island。理论上附近还有一个 Eagle Fall，到了之后实在让我大跌眼镜，就这？？？别说是 Fall 了，你说这是个 Creek 我都将信将疑……</p><p>然后我们就去了 Heavenly。这里有个大缆车，可以通往一个高处的观景台（Heaven？）。不得不说，站得高看得远，湖景能一览无余，虽然票价要 $75……但从另外一方面来说，高处不胜寒，我还穿了一件短袖，风还呜呜的吹，冻死我了。缆车的终点是一个游乐园，我们只玩了其中一个项目，就是一个可以自己控制速度的过山车。说是能自己控制速度，但是对我这种不高的人不太友好，我需要弯腰用力才能把速度加满，不然就只能慢慢游了。</p><p>不知不觉已经到晚饭时间了。晚上我们吃的是 Edgewood Restaurant，一家 fine-dining 餐馆。这里环境挺好的，一个很大的落地窗可以看外面的湖景。说起来我一直在思考为啥湖面上有两种颜色，一种是正常的蓝色，一种是棕色，还成带状分布，最后得出结论：这带状分布的棕色是镜子反光出来的我们背后的墙上那一条棕色装饰带……</p><p>吃完饭后我们就跑路了，天黑了不好开夜路。路上我们正聊着天（<span class="citation" data-cites="胡老师和">@胡老师和</span> tjc），我突然发现外面的晚霞超好看，脸哥便拍下了这张晚霞照片，比昨天的还好看，梦幻的粉红色：</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3056.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3056.webp" alt="<p>Sugar Pine 树上有一些小地衣</p>"/>      <figcaption aria-hidden="true"><p>Sugar Pine 树上有一些小地衣</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3058.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3058.webp" alt="<p>铁轨，湖，与云</p>"/>      <figcaption aria-hidden="true"><p>铁轨，湖，与云</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3071.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3071.webp" alt="<p>Lake Tahoe (from Emerald Bay State Park)</p>"/>      <figcaption aria-hidden="true"><p>Lake Tahoe (from Emerald Bay State Park)</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3074.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/IMG_3074.webp" alt="<p>Lake Tahoe (from Heavenly)</p>"/>      <figcaption aria-hidden="true"><p>Lake Tahoe (from Heavenly)</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-10-12-trip-to-Lake-Tahoe/sunset%202.webp">      <img data-src="/images/2024-10-12-trip-to-Lake-Tahoe/sunset%202.webp" alt="<p>回家路上拍的晚霞</p>"/>      <figcaption aria-hidden="true"><p>回家路上拍的晚霞</p></figcaption>    </div>  </div></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;几个星期前，我和朋友们去了趟湾区的后花园 Lake Tahoe，这里就随便写点东西记录一下。由于时间比较短，我们只过了一个周末，所以显得这篇文章尤为流水帐 23333&lt;/p&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.roosephu.net/categories/Life/"/>
    
    <category term="Travel" scheme="https://www.roosephu.net/categories/Life/Travel/"/>
    
    
  </entry>
  
  <entry>
    <title>Magic Numbers</title>
    <link href="https://www.roosephu.net/2024/09/30/magic-numbers/"/>
    <id>https://www.roosephu.net/2024/09/30/magic-numbers/</id>
    <published>2024-09-30T07:48:09.000Z</published>
    <updated>2024-09-30T07:48:09.000Z</updated>
    
    <content type="html"><![CDATA[<p>很多文件格式都是以一个 <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFnaWNfbnVtYmVyXyhwcm9ncmFtbWluZyk=">Magic number</span> 开始，例如 Java 编译后的字节码文件就是以 <code>CAFEBABE</code> 开头。Wikipedia 上还有一个 <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFnaWNfbnVtYmVyXyhwcm9ncmFtbWluZykjRGVidWdfdmFsdWVz">表</span>，里面放了很多好玩的 magic number，例如 <code>DEADBEEF</code> （还有个音乐播放器叫这个名字）和 <code>FEE1DEAD</code> （linux 的 reboot syscall 中用到）。于是我就心血来潮，能不能整出类似的看起来像英文词组的 magic numbers 。</p><span id="more"></span><p>首先我们要限定一下范围：</p><ul><li>字符串长度限制为恰好为 8；</li><li>能被拆成一个或多个英文单词。由于英文单词也没有准确的定义，和之前一样，我这里采用 unix 自带的字典。</li><li>字符集允许 <code>0-9A-F</code> 。这字符集有点小，但是我们可以整一些字符的 alias<ul><li><code>O =&gt; o</code></li><li><code>I =&gt; 1</code></li><li><code>L =&gt; 1</code>。由于 <code>I</code> 和 <code>L</code> 都映射到了 <code>1</code>，所以我限制这两个字母不能同时出现</li><li><code>ATE =&gt; 8</code></li><li>或许可以允许 <code>S =&gt; 5</code>？</li><li>或许可以允许 <code>K =&gt; C</code>？</li><li>或许可以允许 <code>G =&gt; 9</code>？</li></ul></li><li>每个拆分的单词长度在 3-8 之间。</li></ul><p>事实上，我们可以先看一下那些单词可以在这些规则下表示出来。</p><p>跑了之后发现里面这样 unix 自带字典 <code>/usr/share/dict/words</code> 太大，23w 个词，太多词我不认识了……为了解决这个问题我决定按照词频排序，于是我找到了 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3JzcGVlci93b3JkZnJlcS8=">wordfreq</span> 这个 repo。虽然他因为 AI generated content 污染决定不更新词频统计了，但是我们还是相信它吧。于是我们把词限定在这两个交集：可以去除一些人名地名、过去分词、专有名词，还可以按照词频排序。</p><p>我先看看有那些词替换之后直接就是 8 个字符。这种词总共只有 8 个：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">ACC01ADE =&gt; accolade</span><br><span class="line">F01DAB1E =&gt; foldable</span><br><span class="line">CA11AB1E =&gt; callable</span><br><span class="line">DEADFA11 =&gt; deadfall</span><br><span class="line">CAB00D1E =&gt; caboodle</span><br><span class="line">D011FACE =&gt; dollface</span><br><span class="line">C0C0B010 =&gt; cocobolo</span><br><span class="line">C0CC1D1A =&gt; coccidia</span><br></pre></td></tr></table></figure><p>但是如果你允许 <code>S =&gt; 5</code> 的话，small list 都有一些了：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">BA5EBA11 =&gt; baseball</span><br><span class="line">DECEA5ED =&gt; deceased</span><br><span class="line">A55E55ED =&gt; assessed</span><br><span class="line">C01055A1 =&gt; colossal</span><br><span class="line">5E1F1E55 =&gt; selfless</span><br><span class="line">D15EA5ED =&gt; diseased</span><br><span class="line">BA5E1E55 =&gt; baseless</span><br><span class="line">5CAFF01D =&gt; scaffold</span><br></pre></td></tr></table></figure><p>剩下要组词的话，由于限制 8 个字符，所以只能是一个 3-5 字符的词 + 一个 5-3 字符的词。这样的词总共只有 300 个左右，列表如下：</p><details class="note info"><summary><p>3-5 字符的词</p></summary><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">A11      =&gt; all</span><br><span class="line">D1D      =&gt; did</span><br><span class="line">0FF      =&gt; off</span><br><span class="line">01D      =&gt; old</span><br><span class="line">FEE1     =&gt; feel</span><br><span class="line">BAD      =&gt; bad</span><br><span class="line">CA11     =&gt; call</span><br><span class="line">FACE     =&gt; face</span><br><span class="line">AB1E     =&gt; able</span><br><span class="line">10CA1    =&gt; local</span><br><span class="line">F00D     =&gt; food</span><br><span class="line">1DEA     =&gt; idea</span><br><span class="line">DEA1     =&gt; deal</span><br><span class="line">1EAD     =&gt; lead</span><br><span class="line">DEAD     =&gt; dead</span><br><span class="line">ADDED    =&gt; added</span><br><span class="line">C001     =&gt; cool</span><br><span class="line">FA11     =&gt; fall</span><br><span class="line">1ED      =&gt; led</span><br><span class="line">B100D    =&gt; blood</span><br><span class="line">ADD      =&gt; add</span><br><span class="line">C0DE     =&gt; code</span><br><span class="line">BED      =&gt; bed</span><br><span class="line">D1E      =&gt; die</span><br><span class="line">BA11     =&gt; ball</span><br><span class="line">C01D     =&gt; cold</span><br><span class="line">1CE      =&gt; ice</span><br><span class="line">DAD      =&gt; dad</span><br><span class="line">CE11     =&gt; cell</span><br><span class="line">FE11     =&gt; fell</span><br><span class="line">A1D      =&gt; aid</span><br><span class="line">1EE      =&gt; lee</span><br><span class="line">FEED     =&gt; feed</span><br><span class="line">DEB8     =&gt; debate</span><br><span class="line">B0B      =&gt; bob</span><br><span class="line">BE11     =&gt; bell</span><br><span class="line">10AD     =&gt; load</span><br><span class="line">CAB1E    =&gt; cable</span><br><span class="line">FED      =&gt; fed</span><br><span class="line">FEE      =&gt; fee</span><br><span class="line">C0A1     =&gt; coal</span><br><span class="line">FACED    =&gt; faced</span><br><span class="line">AC1D     =&gt; acid</span><br><span class="line">1ABE1    =&gt; label</span><br><span class="line">1AB      =&gt; lab</span><br><span class="line">0DD      =&gt; odd</span><br><span class="line">B1D      =&gt; bid</span><br><span class="line">D0C      =&gt; doc</span><br><span class="line">F100D    =&gt; flood</span><br><span class="line">BEEF     =&gt; beef</span><br><span class="line">F001     =&gt; fool</span><br><span class="line">B01D     =&gt; bold</span><br><span class="line">1EAF     =&gt; leaf</span><br><span class="line">B1ADE    =&gt; blade</span><br><span class="line">C01E     =&gt; cole</span><br><span class="line">ACE      =&gt; ace</span><br><span class="line">BABE     =&gt; babe</span><br><span class="line">BEE      =&gt; bee</span><br><span class="line">F01D     =&gt; fold</span><br><span class="line">D011     =&gt; doll</span><br><span class="line">F1ED     =&gt; fled</span><br><span class="line">CA1      =&gt; cal</span><br><span class="line">DEAF     =&gt; deaf</span><br><span class="line">DA1E     =&gt; dale</span><br><span class="line">CAB      =&gt; cab</span><br><span class="line">C01      =&gt; col</span><br><span class="line">10C8     =&gt; locate</span><br><span class="line">B00      =&gt; boo</span><br><span class="line">1AD      =&gt; lad</span><br><span class="line">C0D      =&gt; cod</span><br><span class="line">FADE     =&gt; fade</span><br><span class="line">DEE      =&gt; dee</span><br><span class="line">D0E      =&gt; doe</span><br><span class="line">BA1D     =&gt; bald</span><br><span class="line">1ACE     =&gt; lace</span><br><span class="line">F1EE     =&gt; flee</span><br><span class="line">BE11E    =&gt; belle</span><br><span class="line">D1CE     =&gt; dice</span><br><span class="line">DEED     =&gt; deed</span><br><span class="line">C01A     =&gt; cola</span><br><span class="line">FADED    =&gt; faded</span><br><span class="line">B1EED    =&gt; bleed</span><br><span class="line">CA1F     =&gt; calf</span><br><span class="line">A1DE     =&gt; aide</span><br><span class="line">A1E      =&gt; ale</span><br><span class="line">DE11     =&gt; dell</span><br><span class="line">F0CA1    =&gt; focal</span><br><span class="line">A1EC     =&gt; alec</span><br><span class="line">E1F      =&gt; elf</span><br><span class="line">C0CA     =&gt; coca</span><br><span class="line">C0C0A    =&gt; cocoa</span><br><span class="line">BA1E     =&gt; bale</span><br><span class="line">B10C     =&gt; bloc</span><br><span class="line">AB1DE    =&gt; abide</span><br><span class="line">BABA     =&gt; baba</span><br><span class="line">BAE      =&gt; bae</span><br><span class="line">C1AD     =&gt; clad</span><br><span class="line">1EA      =&gt; lea</span><br><span class="line">AD0BE    =&gt; adobe</span><br><span class="line">A1A      =&gt; ala</span><br><span class="line">F0E      =&gt; foe</span><br><span class="line">C0C0     =&gt; coco</span><br><span class="line">10AF     =&gt; loaf</span><br><span class="line">1CED     =&gt; iced</span><br><span class="line">BEAD     =&gt; bead</span><br><span class="line">E11E     =&gt; elle</span><br><span class="line">BE1      =&gt; bel</span><br><span class="line">F1EA     =&gt; flea</span><br><span class="line">B00B     =&gt; boob</span><br><span class="line">CAD      =&gt; cad</span><br><span class="line">F1FE     =&gt; fife</span><br><span class="line">D0DD     =&gt; dodd</span><br><span class="line">0B1      =&gt; obi</span><br><span class="line">D0D      =&gt; dod</span><br><span class="line">F00      =&gt; foo</span><br><span class="line">A1BA     =&gt; alba</span><br><span class="line">D01E     =&gt; dole</span><br><span class="line">ABA      =&gt; aba</span><br><span class="line">DAB      =&gt; dab</span><br><span class="line">1AC      =&gt; lac</span><br><span class="line">0DE      =&gt; ode</span><br><span class="line">C00      =&gt; coo</span><br><span class="line">10BE     =&gt; lobe</span><br><span class="line">100      =&gt; loo</span><br><span class="line">CE110    =&gt; cello</span><br><span class="line">1ACED    =&gt; laced</span><br><span class="line">DAE      =&gt; dae</span><br><span class="line">F1DE     =&gt; fide</span><br><span class="line">FAD      =&gt; fad</span><br><span class="line">DA1      =&gt; dal</span><br><span class="line">DEB      =&gt; deb</span><br><span class="line">EE1      =&gt; eel</span><br><span class="line">B1ED     =&gt; bled</span><br><span class="line">10C0     =&gt; loco</span><br><span class="line">AD0      =&gt; ado</span><br><span class="line">B0A      =&gt; boa</span><br><span class="line">FE1      =&gt; fei</span><br><span class="line">DADE     =&gt; dade</span><br><span class="line">FAB1E    =&gt; fable</span><br><span class="line">C0E      =&gt; coe</span><br><span class="line">1DE      =&gt; ide</span><br><span class="line">AB0DE    =&gt; abode</span><br><span class="line">BA1      =&gt; bal</span><br><span class="line">A1F      =&gt; alf</span><br><span class="line">BAC      =&gt; bac</span><br><span class="line">F0B      =&gt; fob</span><br><span class="line">DADA     =&gt; dada</span><br><span class="line">0DA      =&gt; oda</span><br><span class="line">A1FA     =&gt; alfa</span><br><span class="line">B10B     =&gt; blob</span><br><span class="line">A10E     =&gt; aloe</span><br><span class="line">D10DE    =&gt; diode</span><br><span class="line">B0D      =&gt; bod</span><br><span class="line">CABA1    =&gt; cabal</span><br><span class="line">C0B      =&gt; cob</span><br><span class="line">EBB      =&gt; ebb</span><br><span class="line">B0DE     =&gt; bode</span><br><span class="line">B0B0     =&gt; bobo</span><br><span class="line">DECA1    =&gt; decal</span><br><span class="line">FECA1    =&gt; fecal</span><br></pre></td></tr></table></figure></details><p>要组词的话其实挺简单的，因为这里面有足够的动词名词形容词：</p><ul><li>动词：<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">FEE1 =&gt; feel</span><br><span class="line">CA11 =&gt; call</span><br><span class="line">DEA1 =&gt; deal</span><br><span class="line">FA11 =&gt; fall</span><br><span class="line">FEED =&gt; feed</span><br></pre></td></tr></table></figure></li><li>名词<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">FACE =&gt; face</span><br><span class="line">FOOD =&gt; food</span><br><span class="line">1DEA =&gt; idea</span><br><span class="line">1CE  =&gt;  ice</span><br><span class="line">BEEF =&gt; beef</span><br></pre></td></tr></table></figure></li><li>形容词<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">A11  =&gt;  all</span><br><span class="line">01D  =&gt;  old</span><br><span class="line">BAD  =&gt;  bad</span><br><span class="line">C001 =&gt; cool</span><br><span class="line">DEAD =&gt; dead</span><br></pre></td></tr></table></figure></li></ul><p>如果只看 <code>wordfreq</code> small list 里的，那更少，只有 160 个。我自己稍微组了一些词：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">FEE1BAAD =&gt; feel bad       这个其实不太可行，因为我用的 BAAD，也可以考虑 FEE15BAD</span><br><span class="line">A10EFOOD =&gt; aloe food</span><br><span class="line">DEB81DEA =&gt; debate idea</span><br><span class="line">BADCAB1E =&gt; bad cable</span><br><span class="line">ADD1ABE1 =&gt; add label</span><br><span class="line">DEADF001 =&gt; dead fool      差一点变成了 deadpool</span><br><span class="line">1EAFFA11 =&gt; leaf fall      让我想起了 e.e. cummings 的诗 l(a</span><br></pre></td></tr></table></figure><p>如果我们不允许任何 alias，只用 <code>A-F</code> 来组词，也是能组出一些的：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">DEADBEEF =&gt; dead beef    经典</span><br><span class="line">FEEDBABE =&gt; feed babe    张嘴，啊～</span><br><span class="line">FADEDDAD =&gt; faded dad    我去买个橘子？</span><br></pre></td></tr></table></figure><p>我挑了一些词：</p><details class="note info"><summary><p>部分只包含 <code>A-F</code> 的词</p></summary><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">BAD      =&gt; bad</span><br><span class="line">FACE     =&gt; face</span><br><span class="line">DEAD     =&gt; dead</span><br><span class="line">ADDED    =&gt; added</span><br><span class="line">ADD      =&gt; add</span><br><span class="line">BED      =&gt; bed</span><br><span class="line">DAD      =&gt; dad</span><br><span class="line">FEED     =&gt; feed</span><br><span class="line">DECADE   =&gt; decade</span><br><span class="line">FED      =&gt; fed</span><br><span class="line">FEE      =&gt; fee</span><br><span class="line">FACED    =&gt; faced</span><br><span class="line">BEEF     =&gt; beef</span><br><span class="line">ACE      =&gt; ace</span><br><span class="line">BABE     =&gt; babe</span><br><span class="line">BEE      =&gt; bee</span><br><span class="line">DEAF     =&gt; deaf</span><br><span class="line">CAB      =&gt; cab</span><br><span class="line">FADE     =&gt; fade</span><br><span class="line">DEED     =&gt; deed</span><br><span class="line">FADED    =&gt; faded</span><br><span class="line">BEAD     =&gt; bead</span><br><span class="line">FACADE   =&gt; facade</span><br><span class="line">DAB      =&gt; dab</span><br><span class="line">FAD      =&gt; fad</span><br><span class="line">BEADED   =&gt; beaded</span><br><span class="line">EBB      =&gt; ebb</span><br></pre></td></tr></table></figure></details><p>可以看到长度为 5 的词很少，所以 3+5 和 5+3 这种搭配不好组词，只能 4+4 的组。</p><p>代码不长我就直接放这里了：</p><details class="note info"><summary><p>代码</p></summary><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> wordfreq</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">transform</span>(<span class="params">x</span>):</span><br><span class="line">    result = x.upper()</span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> x:</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> c.isalpha():</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;I&quot;</span> <span class="keyword">in</span> result <span class="keyword">and</span> <span class="string">&quot;L&quot;</span> <span class="keyword">in</span> result:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># if result.endswith(&quot;ATE&quot;):</span></span><br><span class="line">    <span class="comment">#     result = result[:-3] + &quot;8&quot;</span></span><br><span class="line">    <span class="comment"># result = result.replace(&quot;L&quot;, &quot;1&quot;)</span></span><br><span class="line">    <span class="comment"># result = result.replace(&quot;O&quot;, &quot;0&quot;)</span></span><br><span class="line">    <span class="comment"># result = result.replace(&quot;I&quot;, &quot;1&quot;)</span></span><br><span class="line">    <span class="comment"># result = result.replace(&quot;S&quot;, &quot;5&quot;)</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> result:</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> c.isdigit() <span class="keyword">and</span> c &gt; <span class="string">&quot;F&quot;</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">dictionary = <span class="built_in">set</span>(Path(<span class="string">&quot;/usr/share/dict/words&quot;</span>).read_text().splitlines())</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">words = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> w <span class="keyword">in</span> wordfreq.iter_wordlist(<span class="string">&quot;en&quot;</span>, wordlist=<span class="string">&quot;small&quot;</span>):</span><br><span class="line">    <span class="keyword">if</span> w <span class="keyword">not</span> <span class="keyword">in</span> dictionary:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    w2 = transform(w)</span><br><span class="line">    <span class="keyword">if</span> w2 <span class="keyword">and</span> w2 <span class="keyword">not</span> <span class="keyword">in</span> words <span class="keyword">and</span> <span class="number">3</span> &lt;= <span class="built_in">len</span>(w2) &lt;= <span class="number">8</span>:</span><br><span class="line">        words[w2] = w</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> w2, w <span class="keyword">in</span> words.items():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;w:<span class="number">12</span>&#125;</span> =&gt; <span class="subst">&#123;w2:<span class="number">12</span>&#125;</span>&quot;</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure></details>]]></content>
    
    
    <summary type="html">&lt;p&gt;很多文件格式都是以一个 &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFnaWNfbnVtYmVyXyhwcm9ncmFtbWluZyk=&quot;&gt;Magic number&lt;/span&gt; 开始，例如 Java 编译后的字节码文件就是以 &lt;code&gt;CAFEBABE&lt;/code&gt; 开头。Wikipedia 上还有一个 &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWFnaWNfbnVtYmVyXyhwcm9ncmFtbWluZykjRGVidWdfdmFsdWVz&quot;&gt;表&lt;/span&gt;，里面放了很多好玩的 magic number，例如 &lt;code&gt;DEADBEEF&lt;/code&gt; （还有个音乐播放器叫这个名字）和 &lt;code&gt;FEE1DEAD&lt;/code&gt; （linux 的 reboot syscall 中用到）。于是我就心血来潮，能不能整出类似的看起来像英文词组的 magic numbers 。&lt;/p&gt;</summary>
    
    
    
    <category term="Tech" scheme="https://www.roosephu.net/categories/Tech/"/>
    
    
  </entry>
  
  <entry>
    <title>平铺矩形</title>
    <link href="https://www.roosephu.net/2024/09/25/tiling-a-rectangle/"/>
    <id>https://www.roosephu.net/2024/09/25/tiling-a-rectangle/</id>
    <published>2024-09-26T05:55:56.000Z</published>
    <updated>2024-09-26T05:55:56.000Z</updated>
    
    <content type="html"><![CDATA[<div class="note default"><p>试证明：如果一个大矩形能被有限个小矩形（大小不一定相同）平铺，且小矩形至少一条边为整数，则大矩形至少有一条变为整数。</p></div><details class="note success"><summary><p>解答</p></summary><p>注意到积分 <span class="math display">\[\int_{a}^b e^{2 \pi i x} \mathrm{d} x = e^{2 \pi i a} \left(e^{2 \pi i (b - a)} - 1 \right)\]</span> 为 0 当且仅当 <span class="math inline">\(b - a \in \mathbb{Z}\)</span>。对于平面上一个矩形区域 <span class="math inline">\(R\)</span>，我们考虑如下积分：<span class="math display">\[I(R) = \iint_R e^{2\pi i (x + y)} \mathrm{d}  x \mathrm{d}  y,\]</span> 这个积分值为 0 当且仅当 <span class="math inline">\(R\)</span> 至少有一边长为整数。令 <span class="math inline">\(R^*\)</span> 为大矩形，<span class="math inline">\(R_{1, \dots, n}\)</span> 为小矩形，则有 <span class="math display">\[I(R^*) = \sum_{i} I(R_i) = 0,\]</span> 故 <span class="math inline">\(R^*\)</span> 至少有一条边长为整数。</p><p>事实上这个题还有很多种证明方法（14 种！），但我最喜欢的还是这个基于积分的证明。</p><p>参考资料：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly93d3cubGF0cm9pa2EuY29tL21hdGhvbWFuL2V4b3Mvd2Fnb24taW50ZWdlci1yZWN0YW5nbGUucGRm">Fourteen Proofs of a Result About Tiling a Rectangle, Stan Wagon.</span></li><li><span class="exturl" data-url="aHR0cDovL3d3dy5pbmZlcmVuY2Uub3JnLnVrL21hY2theS9yZWN0YW5nbGVzLw==">Simple Proofs of a Rectangle Tiling Theorem</span></li><li><span class="exturl" data-url="aHR0cHM6Ly9tYXRoLmRhcnRtb3V0aC5lZHUvbmV3cy1yZXNvdXJjZXMvZWxlY3Ryb25pYy9wdXp6bGVib29rL2Jvb2svY2gyNC5wZGY=">Mathematical Puzzles, Revised Edition, Chapter 24, Integral Rectangles</span></li></ul></details>]]></content>
    
    
      
      
    <summary type="html">&lt;div class=&quot;note default&quot;&gt;&lt;p&gt;试证明：如果一个大矩形能被有限个小矩形（大小不一定相同）平铺，且小矩形至少一条边为整数，则大矩形至少有一条变为整数。&lt;/p&gt;
&lt;/div&gt;
&lt;details class=&quot;note success&quot;&gt;&lt;summary&gt;&lt;p</summary>
      
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    
  </entry>
  
  <entry>
    <title>夏威夷旅游小记</title>
    <link href="https://www.roosephu.net/2024/08/28/trip-to-Hawaii/"/>
    <id>https://www.roosephu.net/2024/08/28/trip-to-Hawaii/</id>
    <published>2024-08-28T08:25:49.000Z</published>
    <updated>2024-08-28T08:25:49.000Z</updated>
    
    <content type="html"><![CDATA[<p>今年上半年，ydl 找到我，说他和 maggie 订婚了，诚邀我去 Hawaii 参加婚礼。这我怎么能不参加呢？况且我还没去过 Hawaii，自然应该趁这个机会小小旅游一下。这里就随手记一下流水帐。这次旅游之后，终于知道 Hawaii, O'ahu, Waikiki, Honolulu 之间的关系了……</p><p>后来发现一个有趣的 trivia：夏威夷的 state fish 叫做 Humuhumunukunukuapua'a，虽然看起来很长，但是读起来非常有节奏感 23333</p><span id="more"></span><h1 id="准备">准备</h1><p>出之前自然应该先订一下酒店。看了一下 ydl 他们推荐的酒店 Hyatt Regency Waikiki Beach，价格有点小贵，于是问了一下 ydl 还有谁来吗，结果发现 zw 也来，那我们又可以愉快的做室友了 23333</p><p>去之前肯定要稍微做一下攻略，刚好公司里有一个土生土长的 Waikiki 人，于是我就愉快的向他取了取经，拿到了一份《O'ahu 旅游宝典》可以直接抄答案了。</p><h1 id="周六">周六</h1><p>由于我在 Hawaii 只待三天，都在 O'ahu 上，我就准备特种兵一下，早去晚会，maximize 在这里的时间。为此，我订了一个早上七点钟起飞的航班，早上五点钟就得挣扎着起床……飞机上也没睡好，整个脖子疼。这里给个建议：从美西去夏威夷最好坐飞机右侧，因为来的时候是从东向西飞，机场在岛南侧，所以坐在右侧能从天上看到岛的全貌。</p><p>到了夏威夷后，九点多，打个车到酒店大概十点多，然后震惊的发现，我打的这辆车下一个乘客就是 zhy 和 ljw……不过我当时脸盲真没认出来只看着眼熟哈哈哈哈哈囧……到了酒店后前台告诉我房间还没准备好，说好了会发短信告诉我，行李箱可以先寄存在酒店里面，妙啊，这样我就可以在 Waikiki 大走特走欣赏一下美丽的城市风景了。令人尴尬的是，就在楼下的 Bell Desk 我愣是找了半天才找到，而且还是找的侧门，出了门之后一看，BELL DESK 几个字母就大大方方的写在那里。脱下飞机上穿的长裤，换上一条短裤，Waikiki 我来啦！</p><p>出门就是 Waikiki Beach，但是里面人太多了（而且我也不会游泳）。拍了几张游客照我就想跑路了。接下来我就面临人生两大问题之一：中午吃什么？这个时候自然要问问万能的 xhs 啦～xhs 告诉我，这里可以吃日料，但是一个个都需要预订，很显然，我完全没有做准备。不要慌，我还有 wp 的 O'ahu 旅游宝典。wp 推荐的第一个餐馆叫做 Gina's BBQ。可以啊，让我体验一下什么才是正宗的 Hawaiian BBQ！我准备一路走过去，只要走四五十分钟就到了哦～于是打定主意，我就走了过去。这一路上都没啥树，在大太阳下走着晒死我了，路过了几家日料店，没有位置，路过了一个 Taco Bell，有啥好吃的，路过了一个 Mango Mango，都到夏威夷了怎么能吃这种大路货！最后终于走到了 Gina's BBQ，等等，为啥店名下面一个大大的 Korean BBQ？？？我再看了一眼菜单，没错，就是 Korean BBQ……不知道点啥就点了个 Gina's special，里面肉挺多的，但是问题是，我去哪里吃……这个店就一个门店，没有吃饭的地方啊……不慌，我看夏威夷这么多的公园，找个公园里的桌子不成问题！于是我又原路走回，这一路上都没啥树，在大太阳下走着晒死我了，路过了几家日料店，没有位置，路过了一个 Mango Mango，都到夏威夷了怎么能吃这种……啊真香！热的时候吃冷饮太香了！我路上试图招一个空地上的长桌来吃饭，但是问题是这些桌子全在草坪里，蚂蚁苍蝇来开会，我实在吃不下去，不能再现 SIGYAO30 '22 @ LA 的三和大神荣光了，直到——我有一次路过 Taco Bell。虽然我没买东西，但是 Taco Bell 有室外的水泥餐桌凳子，看起来不赶人，重点是，还有遮阳伞！我没有丝毫犹豫，直接坐在了 Taco Bell 的凳子上开始吃饭，吃饱喝足后旁边还有个垃圾桶，一气呵成！</p><p>之后我准备往 Diamond Head 那边走走看，拍几张游客照。走到一半收到短信说房间好了可以去 check in 了。那我自然应该往酒店走了。到酒店取出之前的行李后，ydl 就过来了，打了声招呼，我就去 check in 了，zw 到了之后一起去房间。</p><p>稍微修整了一下后我们就去下面玩，因为 zw 来的时候堵车了，原因就是有一个 festival 封路。既然来都来了，不如我们就去逛逛吧。这个 festival 叫做 Kanikapila Waikiki Festival，就在我们酒店旁边，但是去了之后有点大路货……所谓的 festival 就是一堆小贩在街边支个摊子，然后卖一些有的没的的东西。有些东西还稍微有点特色，例如自称 Hawaii 本地蜂蜜，但是有些东西就毫无特色了，例如烤鱿鱼，写着名字的竹子……</p><p>回到酒店后遇到了人生两大问题的第二个问题：晚上吃什么？经过 zw 一顿搜索，我们决定吃一家日式烧烤店，叫做 Aburiya Ibushi，打电话订了今天晚上最后一个位置。味道整体还可以，小菜里的 Cold Tofu 感觉很妙，上面淋的那层肉沫味道很好，wasyugyu sirloin steak 感觉也很好，不肥，入口即化，对比起来 beef flank 就差一些，不过看价格就看得出来 2333</p><p>晚上回到宾馆后在考虑明天搞些啥活动。我提议去去爬 Diamond Head 山，毕竟这个火山坑空中看起来超帅。但是这个 trail 需要预订，我们晚上预订的时候，7am-12pm 都已经被订完了，只剩下 6am-7am 或者 12pm 以后，但是 12pm 之后有点晚了，一来太热了我怕了，二来我们明天下午两点多需要在酒店集合坐车去婚礼现场。zw 表示我们可以早起特一特，于是我们就订了 6am-7am 这个时间。</p><h1 id="周日">周日</h1><p>昨晚订的 trail，哭着也要爬完……但是 Diamond Head Summit Trail 这个还好，主要是比较短，往返就一个小时多一点。去的路上和司机 bibi 了一下，司机上来就问我们有没有预订，然后说一半以上的游客没有预订……但是机智如我们早已在昨天晚上订好了 23333 进了公园后就开始爬山。这个 trail 不长，单程只有 30-40min，一下子就爬完了。我发现我爬楼梯超级菜，稍微一爬膝盖就软了，不太对劲。虽然一开始是因为只有 6-7am 有时间我们才订的，但是爬的时候感觉就得这个时候爬，没有特别晒，温度也不高，天气很好，我估计九十点钟就太热了。不过我觉得山顶的景色一般吧，从山顶看 Waikiki 既没有一片高楼带来的现代气息，也没有纯粹自然的风景气息。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-08-28-trip-to-Hawaii/IMG_2867.webp">      <img data-src="/images/2024-08-28-trip-to-Hawaii/IMG_2867.webp" alt="<p>爬 Diamond Head Summit Trail 时看到的彩虹</p>"/>      <figcaption aria-hidden="true"><p>爬 Diamond Head Summit Trail 时看到的彩虹</p></figcaption>    </div>  </div></figure><p>爬了这个 trail 后我们就去打车回去了，补 wan 补 shou 觉 ji。中午吃的 Paia Fish Market，感觉还行，zw 说鱼非常新鲜但是我不知道什么新不新鲜……</p><p>下午说好的两点十五集合，我和 zw 就两点十分下去了，到了之后发现咋没人呢……有人和我打了个招呼，随便聊了两句但是我没认出来是谁，超级尴尬。到了两点半，人来齐了，我们开车前往 Kualoa Ranch，听说这还是拍侏罗纪公园的地方，但是我也没看过侏罗纪公园……车上我对着群里的名字一个一个找，终于想起了之前和我打招呼的是谁了……</p><p>之后婚礼正式开始了。这真是一个举办婚礼的好地方。我们在去婚礼现场的路上看到海上有一个小岛，感觉挺有意思的，查了一下叫做 China Man Hat Island。但是有意思的不只是这个，如果你从一个合适的角度去看婚礼花拱，你会发现这个岛恰好就在花拱中间，位置太妙了！</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-08-28-trip-to-Hawaii/IMG_2884.webp">      <img data-src="/images/2024-08-28-trip-to-Hawaii/IMG_2884.webp" alt="<p>China Man Hat Island</p>"/>      <figcaption aria-hidden="true"><p>China Man Hat Island</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-08-28-trip-to-Hawaii/IMG_2885.webp">      <img data-src="/images/2024-08-28-trip-to-Hawaii/IMG_2885.webp" alt="<p>花拱与 China Man Hat Island</p>"/>      <figcaption aria-hidden="true"><p>花拱与 China Man Hat Island</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-08-28-trip-to-Hawaii/IMG_2887.webp">      <img data-src="/images/2024-08-28-trip-to-Hawaii/IMG_2887.webp" alt="<p>Kualoa Ranch</p>"/>      <figcaption aria-hidden="true"><p>Kualoa Ranch</p></figcaption>    </div>  </div></figure><p>婚礼进行的时候天有点阴，要是阳光再明媚一点就好了，不过另一方面想，我们就没那么晒了是吧哈哈哈。婚礼快要结束的时候，下起了点点细雨；婚礼刚刚结束的时候，这点点细雨变成了瓢泼大雨，也是天公给足了面子。我一开始仓促跑向避雨棚，忘记带小礼品了，这怎么能忘记呢！于是我像风一样跑了回去，又像风一样跑了回来。然而，这个时候我没有注意到，摄像师正拍着 ydl 保护 yfy 从容不迫地从婚礼现场来到避雨棚。希望将来他们回顾这份温馨的录像时，忽略掉一位一闪而过挡住了镜头的风一样的男子吧……</p><p>婚礼还配置了一个 photo booth，每次可以 1-3 个人一起拍摄三张照片。photo booth 还提供了很多塑料道具可以选择。我印象最深刻的一张是我、zw 还有 ydl 三个人的一张，ydl 坐中间，我和 zw 坐两边，zw 拿着手枪指着我，我拿着菜刀砍向 zw，ydl 则在中间惊慌失措。</p><p>晚上到了宾馆之后我和 zw 再来规划明天的行程。看了一下，周围似乎没啥好玩的了，有个 aquarium 但是似乎评价不是很高。红警玩家 zw 推荐了珍珠港，因为 Arizona Memorial 在红警里出现过，那我们就去那里了。这里有个问题是 Arizona Memorial 的摆渡船需要预约，而我们没有预约上，但是 anyway 我们还是决定去那里看一下拍个队，随缘吧。说起来这边的 researvation 就很迷，只能预约要么明天的，要么两个星期之后的。预约明天的基本上都被黄牛抢了……</p><h1 id="周一">周一</h1><p>我们本可以选择去大大大早起去 Pearl Harbor 排 Arizona Memorial 的队，但是经过前两天的特种兵旅游，我们决定躺了，吃完饭再去。卡着点 checkout 后，我们去 Sato's Seafood 吃了一个 Poke Bowl。我点了一个 spicy salmon 感觉酱调的非常不错，就是鱼有点少。这家店店内没有桌椅，就在店门口有一两个小桌子，随意配上三四把椅子，我又回忆起了前几天吃 Gina's BBQ 的场景……</p><p>吃完饭后打个车去了 Pearl Harbor。门口存了个包后我们就进去排队，幸亏这里的排队是签到制，登记了名字之后不需要排在那里，不过我对排上队不报啥希望了，一来我们吃了饭才来，里面好多人，二来我要赶飞机，如果一个小时内排不上队的话我就看不了了。之后就去里面的珍珠港事件纪念馆随便看看。正当我觉得马上到时间了看不上 Arizona Memorial 的时候，zw 突然发现排到了，哟太凑巧了。</p><p>Arizona Memorial 建立在珍珠港事件中被摧毁的 USS Arizona 残骸上，在纪念馆还能看到一些残骸。它不大，里面没有多少东西可以参观。我有时候看到水面上还有一些漂浮的五光十色的东西，zw 告诉我那是不停冒出来的机油，漂浮在水面上，不同地方油层厚度不同，在阳光下反射的光的颜色也不同，这东西还有一个名字，叫做 <span class="exturl" data-url="aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVVNTX0FyaXpvbmFfTWVtb3JpYWwjSGlzdG9yeQ==">Tears of the Arizona</span>。Arizona Memorial 附近还有另外一艘战舰，是退役的 USS Missouri，就是在这艘船上，日本正式投降了。将 USS Missouri 停在珍珠港内，算是给故事添上一个句号吧。</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-08-28-trip-to-Hawaii/IMG_2893.webp">      <img data-src="/images/2024-08-28-trip-to-Hawaii/IMG_2893.webp" alt="<p>Arizona Memorial</p>"/>      <figcaption aria-hidden="true"><p>Arizona Memorial</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-08-28-trip-to-Hawaii/IMG_2902.webp">      <img data-src="/images/2024-08-28-trip-to-Hawaii/IMG_2902.webp" alt="<p>Tears of the Arizona</p>"/>      <figcaption aria-hidden="true"><p>Tears of the Arizona</p></figcaption>    </div>  </div></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;今年上半年，ydl 找到我，说他和 maggie 订婚了，诚邀我去 Hawaii 参加婚礼。这我怎么能不参加呢？况且我还没去过 Hawaii，自然应该趁这个机会小小旅游一下。这里就随手记一下流水帐。这次旅游之后，终于知道 Hawaii, O&#39;ahu, Waikiki, Honolulu 之间的关系了……&lt;/p&gt;
&lt;p&gt;后来发现一个有趣的 trivia：夏威夷的 state fish 叫做 Humuhumunukunukuapua&#39;a，虽然看起来很长，但是读起来非常有节奏感 23333&lt;/p&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.roosephu.net/categories/Life/"/>
    
    <category term="Travel" scheme="https://www.roosephu.net/categories/Life/Travel/"/>
    
    
  </entry>
  
  <entry>
    <title>IMO 2024 P1 &amp; P5</title>
    <link href="https://www.roosephu.net/2024/07/23/IMO-2024-P1-P5/"/>
    <id>https://www.roosephu.net/2024/07/23/IMO-2024-P1-P5/</id>
    <published>2024-07-23T08:36:35.000Z</published>
    <updated>2024-07-23T08:36:35.000Z</updated>
    
    <content type="html"><![CDATA[<p>听说今年这两道题比较简单，我就来蹭蹭热度了。 P1 是一个不太难的题，但是我很可能把它想复杂了；P5 是一个非常巧妙的 brainteaser 。</p><h1 id="p1">P1</h1><div class="note default"><p>求所有实数 <span class="math inline">\(\alpha\)</span> 满足：对任意正整数 <span class="math inline">\(n\)</span>，整数 <span class="math inline">\(\sum\limits_{i=1}^n \lfloor i \alpha \rfloor\)</span> 均为 <span class="math inline">\(n\)</span> 的倍数。</p></div><details class="note success"><summary><p>解答</p></summary><p>令 <span class="math inline">\(S\)</span> 表示所有满足要求的 <span class="math inline">\(\alpha\)</span> 构成的集合。先来个小观察：如果 <span class="math inline">\(\alpha \in S\)</span>，那么 <span class="math inline">\(2 + \alpha \in S\)</span>：对于任何 <span class="math inline">\(n \in \mathbb{N}^+\)</span>，有 <span class="math display">\[\sum_{i=1}^n \lfloor i (\alpha + 2) \rfloor = \sum_{i=1}^n \left( \lfloor i \alpha \rfloor + 2i \right) = \sum_{i=1}^n \lfloor i \alpha \rfloor + n (n + 1)\]</span></p><p>于是我们就只需要研究 <span class="math inline">\([0, 2)\)</span> 里的 <span class="math inline">\(\alpha\)</span> 即可。</p><ol type="1"><li>显然有 <span class="math inline">\(0 \in S\)</span>;</li><li>如果 <span class="math inline">\(0 &lt; \alpha &lt; 1\)</span>，考虑 <span class="math inline">\(n = \lceil \alpha^{-1} \rceil \geq 2\)</span>，有 <span class="math inline">\(\sum\limits_{i=1}^n \lfloor i \alpha \rfloor = 1\)</span>，明显不满足要求；</li><li>显然有 <span class="math inline">\(1 \not\in S\)</span>：令 <span class="math inline">\(n = 2\)</span> 即可。</li><li>如果 <span class="math inline">\(1 &lt; \alpha &lt; 2\)</span>:<ol type="1"><li>如果 <span class="math inline">\(\alpha \not\in \mathbb{Q}\)</span>，对于任何 <span class="math inline">\(n \in \mathbb{N}^+\)</span> 有 <span class="math inline">\(\sum\limits_{i=1}^n \lfloor i (2 - \alpha) \rfloor = \sum\limits_{i=1}^n (2i - 1 - \lfloor i \alpha \rfloor)\)</span> ，故 <span class="math inline">\(2-\alpha \in S\)</span>，又有 <span class="math inline">\(0 &lt; 2 - \alpha &lt; 1\)</span>，矛盾。</li><li>如果 <span class="math inline">\(\alpha \in \mathbb{Q}\)</span>，不妨令 <span class="math inline">\(\alpha = p / q\)</span> 且 <span class="math inline">\(gcd(p, q) = 1, q &gt; 1\)</span>。有 <span class="math display">\[\sum_{i=1}^{q-1} \left\lfloor i \frac{p}{q} \right \rfloor = \frac{1}{2} \sum_{i=1}^{q-1} \left( \left\lfloor i \frac{p}{q} \right \rfloor + \left\lfloor (q - i) \frac{p}{q} \right \rfloor \right) = \frac{p - 1}{2} (q - 1) \equiv 0 \pmod {q - 1}.\]</span> 可知 <span class="math inline">\(p\)</span> 为奇数。又考虑 <span class="math display">\[\sum_{i=1}^q \left\lfloor i \frac{p}{q} \right \rfloor = p + \frac{1}{2} \sum_{i=1}^q \left\lfloor i \frac{p}{q} \right \rfloor = p + \frac{p - 1}{2} (q - 1) \equiv 0 \pmod{q}.\]</span> 可得 <span class="math display">\[\frac{p + 1}{2} \equiv 0 \pmod{q},\]</span> 而这是不可能的，因为 <span class="math inline">\(0 &lt; \frac{p + 1}{2} &lt; q\)</span>.</li></ol></li></ol><p>综上所述，<span class="math inline">\([0, 2) \cap S = \{0\}\)</span>，故 <span class="math inline">\(S = \{2k: k \in \mathbb{Z}\}\)</span>.</p><p>（这个对 <span class="math inline">\(\alpha \in \mathbb{Q}\)</span> 的讨论好丑好想优化掉……）</p></details><h1 id="p5">P5</h1><div class="note default"><p>憨豆特工在一个 2024 行 2023 列的方格表上做游戏。方格表中恰有 2022 个方格各藏有一个坏人。初始时，憨豆不知道坏人的位置，但是他知道除了第一行和最后一行之外，每行恰有一个坏人，且每列至多有一个坏人。</p><p>憨豆想从第一行移动到最后一行，并进行若干轮尝试. 在每一轮尝试中，憨豆可以在第一行中任意选取一个方格出发并不断移动，他每次可以移动到与当前所在方格有公共边的方格内。（他允许移动到之前已经到达过的方格。）若憨豆移动到一个有坏人的方格，则此轮尝试结束，并且他被传送回第一行开始新的一轮尝试。坏人在整个游戏过程中不移动，并且憨豆可以记住每个他经过的方格内是否有坏人。若憨豆到达最后一行的任意一个方格，则游戏结束。</p><p>求最小的正整数 <span class="math inline">\(n\)</span>，使得不论坏人的位置如何分布，憨豆总有策略可以确保他能够经过不超过 <span class="math inline">\(n\)</span> 轮尝试到达最后一行。</p></div><details class="note success"><summary><p>解答</p></summary><p>以下给出一个 <span class="math inline">\(n = 3\)</span> 的构造。所有的图里蓝色为坏人，黄色表示可行路径。</p><p>我们先确定第二行的坏人在哪里。这一步直接扫就好了。有两种情况：</p><ol type="1"><li>Case 1：坏人不在第一列或最后一列。如图所示，A 点和 B 点同行，所以至多有一个坏人。不妨设 B 点没坏人，按照黄色路径走即可。<img data-src="/images/2024-07-23-IMO-2024-P1-P5/Case%201.png" alt="Case 1" /></li><li>Case 2：坏人在第一列或最后一列。不妨设坏人在第一列，那我们便从上到下依次遍历下图的所有绿色方块，遇到第一个坏人就停止。<img data-src="/images/2024-07-23-IMO-2024-P1-P5/Case%202.png" alt="Case 2" /><ol type="1"><li>Case 2-1：我们没有找到任何一个坏人，那么我们可以直接走最后一列即可；</li><li>Case 2-2：我们找到了一个坏人，那么按照下面的黄色路径走即可：<img data-src="/images/2024-07-23-IMO-2024-P1-P5/Case%202-2.png" alt="Case 2-2" />A 点左上方的点没有坏人是因为 A 点是我们从上往下扫找到的第一个坏人。</li></ol></li></ol><p>至于 <span class="math inline">\(n\)</span> 为啥不能小于 3：我们就考虑一个 adversarial 的放坏人的方式，</p><ul><li>第一次探索时我把坏人放在你探索的第一个方块上（在第二行）；</li><li>第二次探索时我把坏人放在你探索的第一个位于第三行的方块上，这个方块一定和我第一次放坏人的方块不在同一列上，因为你只能从第二行的非坏人方格到第三行。</li></ul></details>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;听说今年这两道题比较简单，我就来蹭蹭热度了。 P1 是一个不太难的题，但是我很可能把它想复杂了；P5 是一个非常巧妙的 brainteaser 。&lt;/p&gt;
&lt;h1 id=&quot;p1&quot;&gt;P1&lt;/h1&gt;
&lt;div class=&quot;note default&quot;&gt;&lt;p&gt;求所有实数 &lt;spa</summary>
      
    
    
    
    <category term="CS/Math" scheme="https://www.roosephu.net/categories/CS-Math/"/>
    
    
  </entry>
  
  <entry>
    <title>SIGYAO30 2024 @ Chicago</title>
    <link href="https://www.roosephu.net/2024/07/04/SIGYAO30-2024-Chicago/"/>
    <id>https://www.roosephu.net/2024/07/04/SIGYAO30-2024-Chicago/</id>
    <published>2024-07-04T23:25:59.000Z</published>
    <updated>2024-07-04T23:25:59.000Z</updated>
    
    <content type="html"><![CDATA[<p>又是一年 SIGYAO30。今年 SIGYAO30 在 Chicago，一来是因为之前美东美西都举办过 SIGYAO30 了，二来去年商定举办地点的时候，刚好 Chicago 有三个人，于是我们就决定放在 Chicago 了。事实上，我对芝加哥 downtown 印象挺好的，现代的城市，干净的街道，便捷的生活，低廉的物价，丰富的娱乐，多样的美食，就是冬天有点冷，南边不安全……</p><p>七年前才毕业，到现在居然已经毕业七年了。上次在芝加哥还是上次。半年不见，如隔六月。这次来芝加哥体验了好多游客项目，我在芝加哥住了一年半，但凡我稍微出门逛逛，也不至于一点也没逛。</p><span id="more"></span><h1 id="周六">周六</h1><p>早上起了个大早去坐河上的小船，真是年度坐船游戏吗 233333 不过说起来，我之前还真没坐过芝加哥的旅游小船……为啥这船上都挂着彩虹旗？这船就沿着 Riverwalk 一路开，还有个导游讲解芝加哥建筑，毕竟这是 Architecture tour. 今天温度比我想象中搞，更加不幸的是我刚好坐在了某个热风出口（我也不知道是啥）。。</p><p>下午我们我们去逛了 Shedd Aquarium 和 Field Museum。里面人好多啊，一不小心人就不见了。这个水族馆我没啥印象，没有特别感觉。Field Museum 很有趣，一楼展厅放着一个大大的恐龙骨架，我记得 ORD 里也有一个恐龙骨架。芝加哥这么喜欢恐龙骨架吗……另一个有趣的东西就是硅化木头了。之后我们要去一个高贵的罚款餐厅 RPM Seafood 吃饭，现在问题来了，怎么去呢？cxq 表示应该走过去：“你可以的”。wjb 回应：“不我不可以，要走你走。”于是有四个人选择了逃避，打了个 uber。其实我也倾向走，主要是 loop 湖边这一片区域，是我最喜欢芝加哥的部分了，贴近自然又富有人文气息，不走一下太可惜了。</p><p>我们在 Shedd Aquarium 附近拍了个合影，Google Map 在 Adler Planetarium 附近放了一个旅客拍照点，然而我以为放在 Shedd Aquarium 那边了。水族馆那边也能拍，但是背景里就多了一些树。</p><p>晚上吃的 RPM Seafood，为了点到最低消费，脸哥疯狂点小吃，这里的小吃真的很好吃。Entrée 我点了个和牛，比起小吃那就相当一般了，主要是我之前吃小吃吃饱了，主食还没上我就打出了 gg，而且我设想中和牛比较贵，只有一小块，和小吃一样精致，但是这里上来就是一大片牛肉。这个 private room 还有一个阳台，阳台下芝加哥河，我吃到一半实在吃撑了还去阳台上逛了逛消化一下。</p><p>之后我们去了 360 TILT 看夜景。我之前来过一次这里，感觉去过一次没必要再去第二次了……这次我没去坐 TILT，排队的人太多了……不过芝加哥夜景还可以的。</p><h1 id="周日">周日</h1><p>星期六的特种兵大旅游后，我们一堆人打出了 gg。由于 360 TILT 和 Skydeck 感觉差不多，再加上 Skydeck 十点半就要集合，所以我直接选择了睡大觉。后来想想我这个决策失误，因为 360 我去过一次了，而 Skydeck 我还没去过。</p><p>中午我们吃的 HaiSous，这可是我在芝加哥最喜欢的越南餐厅。吃了啥没啥印象了，只记得来的路上在讨论某人的冲击 x 猜想荣获 x 奖项的沉淀人生了。吃完饭后我和 cls、脸哥还有 jzp 去了 Millenium Park，准备看看芝加哥的地标，大豆子 Cloud Gate。说来也巧，昨天和 pty 来 Millenium Park 的时候，这大豆子还被围了起来没有开放，但是功夫不负有心人，脸哥不停地刷小红书，终于刷出了 Cloud Gate 重新开放的消息，这才得以让我们参观一下。Cloud Gate 我去过挺多次的了，但是这次 Cloud Gate 翻修后更光滑了。</p><p>下午我们去了 Lucky Strike。一开始我还不知道，到了之后发现这不就是我之前去过的 AMC 二楼嘛。于是我就和 cls 开始菜鸡互啄打起了桌球。我上次打桌球还是两年前，但是看起来和 cls 是旗鼓相当的对手。第一局，我上来先送三个自由球，但是后来奋勇拼搏，和 cls 打得难解难分，让过来围观的 zw 惊叹到：“这两个球你们打了多久了还没进？”最后在我不懈努力和 cls 友情乌龙的帮助下，我险胜 cls，真不愧是卧龙凤雏的巅峰对决！第二局，cxq 决定 2v2。他首先给我们展示了他丰富的教学经验，五分钟桌球从入门到入土，成功把 wjb 带到桌上。我和 cls 桌球双雄组一队，强强联手，挑战 cxq/wjb 这一对普渡侠侣，默契无双！我进了一球，cxq 开局不利！cls 进了一球，cxq 深陷苦战！cxq 给我们进了一球，cxq 昏招频出！我和 cls 把所有花球都打进袋了，cxq 败局已定！cls 把黑球白球同时打入袋中，cxq 发表获胜感言！</p><p>在 avec river north 吃完饭我们就跑路去 MDW 了。听闻另一辆车上 cxq 又和司机进行了 py 交易。cxq 不停规劝我们坐 Caltrain 回家，但是我们到 SFO 已经是晚上十一点多了，我还是有点害怕……cxq 表示 Caltrain 上一个人都没有，安全得很，但我和脸哥还是表示你自己去吧……见 Caltrain 不成，cxq 又心生一计，表示我们可以去租车。他上飞机之前提前预订了 Hertz 的车，表示打车的价格不能超过这个钱，不然我们就可以先坐 BART 到 Millbrae 然后租个车，明天再去还车。然后他就说起了 SJC 那里就特别好，等车的地方旁边就可以租车，可以脚踏两只船。他说的非常对，但是我和脸哥仍然不为所动。cxq 又心生一计，表示我们即使打车也不应该在机场打，因为 <span class="exturl" data-url="aHR0cHM6Ly93d3cuc2ZnYXRlLmNvbS90ZWNoL2FydGljbGUvdWJlci1seWZ0LXNmby1haXJwb3J0LWZlZXMtMTg1OTg2MTQucGhw">这样 Uber/Lyft 要给 SFO 交钱</span>，所以我们应该先坐 BART 到 Millbrae 再打车，可以省几块钱。我和脸哥十分感动，并选择了直接在 SFO 打车。</p><h1 id="结语">结语</h1><p>最后我来放几张图吧：</p><figure>  <div class="f-carousel">    <div class="f-carousel__slide" data-thumb-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2809.webp">      <img data-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2809.webp" alt="<p>MDW 安检排队厅</p>"/>      <figcaption aria-hidden="true"><p>MDW 安检排队厅</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2788.webp">      <img data-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2788.webp" alt="<p>第一次坐芝加哥这个旅游小船</p>"/>      <figcaption aria-hidden="true"><p>第一次坐芝加哥这个旅游小船</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2804.webp">      <img data-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2804.webp" alt="<p>百看不厌的大豆子</p>"/>      <figcaption aria-hidden="true"><p>百看不厌的大豆子</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2803.webp">      <img data-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2803.webp" alt="<p>这表情和吐水是什么鬼啊哈哈哈哈</p>"/>      <figcaption aria-hidden="true"><p>这表情和吐水是什么鬼啊哈哈哈哈</p></figcaption>    </div>    <div class="f-carousel__slide" data-thumb-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2792.webp">      <img data-src="/images/2024-07-04-SIGYAO30-2024-Chicago/IMG_2792.webp" alt="<p>一块石化的木头（Silicified wood）：木头内部逐渐石化被硅替换</p>"/>      <figcaption aria-hidden="true"><p>一块石化的木头（Silicified wood）：木头内部逐渐石化被硅替换</p></figcaption>    </div>  </div></figure><p>整体而言这次 SIGYAO30 比较赶，芝加哥 downtown 本来就有很多地方可以去，而且我们时间也不够，大部分人星期天晚上就跑路了，另外还有一个原因是我们住的酒店，没有豪华包间，所以没有一个比较大的可供大家聚在一起的地方。不过整体而言，这是一次成功的 SIGYAO30，没有翻船，明年再聚！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;又是一年 SIGYAO30。今年 SIGYAO30 在 Chicago，一来是因为之前美东美西都举办过 SIGYAO30 了，二来去年商定举办地点的时候，刚好 Chicago 有三个人，于是我们就决定放在 Chicago 了。事实上，我对芝加哥 downtown 印象挺好的，现代的城市，干净的街道，便捷的生活，低廉的物价，丰富的娱乐，多样的美食，就是冬天有点冷，南边不安全……&lt;/p&gt;
&lt;p&gt;七年前才毕业，到现在居然已经毕业七年了。上次在芝加哥还是上次。半年不见，如隔六月。这次来芝加哥体验了好多游客项目，我在芝加哥住了一年半，但凡我稍微出门逛逛，也不至于一点也没逛。&lt;/p&gt;</summary>
    
    
    
    <category term="Life" scheme="https://www.roosephu.net/categories/Life/"/>
    
    <category term="Travel" scheme="https://www.roosephu.net/categories/Life/Travel/"/>
    
    
  </entry>
  
</feed>
