作者: Zeeko

  • 当一位拥有 root 权限的安卓用户升级到了 ColorOS 15

    大版本号的系统更新往往意味着 A Lot of BREAKING CHANGES!

    LSPosed

    需要安装一个第三方修改版的 LSPosed 以支持 Android 15。
    https://github.com/JingMatrix/LSPosed/releases/tag/v1.10.1

    或者申请加入 LSPosed 内测版本

    允许后台应用访问粘贴板

    https://github.com/Kr328/Riru-ClipboardWhitelist
    这个模块其实在 Android 14 时就已经无法正常工作了,升级到 Android 15 后完全可以卸载了。

    为了能够让 KDE Connect 可以正常访问剪贴板,需要用搭配一个 Magisk 模块跟一个 XPosed 模块使用。

    draumaz/kdeconnectbidirectionalclipboard 模块允许 KDE Connect 在开机后自动获取剪贴板相关的权限。

    entr0pia/xposed-clipboard-whitelist 则允许 KDE Connect 在后台读取剪贴板内容。

    让后台应用能够接收 FCM 通知

    允许 GMS 熄屏后继续运行

    参考酷安网友(丛雨不是粽子精)的帖子,需要在开机后以 root 权限运行这条命令:

    settings put secure google_restric_info 0

    或者你可以直接使用我制作的 Magisk 模块

    使用 fcmfix 启动未运行的应用

    kooritea/fcmfix: [xposed]让fcm唤醒已完全停止的应用 模块允许在国行系统上收到 fcm 推送后唤醒未启动的应用。

    推荐选择如下作用域:

    为了允许被唤醒的应用能够被启动,需要手动在 ColorOS 的 Settings/Apps/Auto launch 中授予自启动权限。否则当消息推送到手机上时,由于目标应用无法自启动,fcmfix 会代为生成一条仅包含应用包名的消息。

    fcmfix 支持有选择性的唤醒应用,我建议为了方便,可以勾选全部的包含 fcm 的应用,因为在 ColorOS 上,如果你不单独地为各个应用配置自启动权限,应用是无法在后台启动的。

    让 NoActive 支持 fcm

    如果你像我一样使用 NoActive 模块来严格地管理后台,你还需要参考 NoActive 的文档,启用全局 fcm 支持

    分享菜单清理

    每个应用都很想占据用户的分享菜单,但是 Android 的分享菜单管理功能又过于弱鸡,一个比较简单的方案是在分享菜单页面长按图标,将最常用的应用置顶。

    如果非常想要清理分享菜单而且不嫌麻烦的话,可以使用爱玩机工具箱提供的组件状态管理功能,禁用掉分享、批量发送之类的活动。

  • 跟 LLM 一起折腾 Web Audio

    最近在开发一个与 TTS(Text-to-Speech)相关的功能,其中需要对播放的人声音频进行加速或减速处理。这个领域我之前完全没有接触过,DeepSeek 推荐我使用 Howler.js 和 Tone.js 这样的库来播放音频。

    在实现加速功能时,我发现需要修改 playbackRate,但通常 playbackRate 和音调(Pitch)的修改是联动的。然而,Howler.js 并没有提供直接修改 Pitch 的方法。在折腾了很久之后,DeepSeek 也没能解决 Tone.js 带来的噪音问题。使用在线处理(Pitch Shift 效果器)时,会产生“dadadada”的噪音;而使用离线处理(GrainPlayer)时,又会出现类似回声的杂音。

    最终,我查到 HTML5 的 Audio Element 提供了 preservePitch 的功能,这似乎是一个比较理想的解决方案。

    一些经验总结

    1. 代码生成与抽象
      即使通过 LLM 生成代码的成本较低,也尽量让它生成易于拓展和复用的代码。在我的尝试中,即便切换了多个不同的音频库,我都不需要修改业务代码,这正是因为我在最初就让 LLM 对音频处理部分进行了足够的抽象。
    2. LLM 在处理开放性问题时的局限性
      如果待解决的开放性问题足够普遍,LLM 通常能给出令人满意的答案。但对于像 Web Audio 这样 API 复杂且不太常见的技术领域,LLM 可能就很难解决问题了。例如,在解决 Pitch Shift 效果器的噪音问题时,DeepSeek 很难坚定地指出 Tone.js 的实现本身就存在缺陷(事实上也是如此)。这大概率是因为 LLM 只是一个互联网文本预测器,容易产生“幻觉”,尤其是在处理小众领域的问题时。

    总结

    在开发过程中,抽象和代码复用是非常重要的,尤其是在处理新技术领域时。虽然 LLM 在很多常见问题上表现出色,但在处理复杂或小众领域的问题时,仍需要结合人工经验和深入的技术调研来找到更优的解决方案。

  • Obsidian 数据同步方案:从 Git 到 Syncthing 的平滑切换

    Obsidian 作为一款本地优先的笔记软件,数据安全始终是用户关注的重点。我之前选择 Git 来进行版本控制,但随着 Obsidian 插件的不断增加,有些插件会生成大量二进制文件,这些文件如果直接存储在 Git 仓库中,会快速消耗 Git LFS 的免费存储额度。

    我最初考虑使用 Remotely Save 插件的 WebDAV 功能将数据备份到 NAS,但在 QNAP 上频繁遇到 403 错误,最终决定采用更稳定的 Syncthing 方案。

    Syncthing 在 QNAP 上的部署要点

    在 QNAP 上安装 Syncthing 需要通过第三方软件源 MyQNAP.org,该源提供了两个 Syncthing 安装包:

    • Syncthing:以普通用户权限运行
    • Syncthing Run As Root:以 root 用户权限运行

    建议选择 Syncthing,原因如下:

    1. 权限管控更安全:避免 root 权限带来的安全隐患
    2. 多用户兼容性更好:非管理员用户也能正常访问 Syncthing 同步的文件

    :安装后系统会自动创建 syncthing 用户,默认属于 everyone 用户组

    配置优化建议

    1. 文件夹权限设置: 同步文件夹的权限配置需要特别注意,建议设置为 <user>:everyone 模式,确保 Syncthing 有完整的读写权限。
    2. Ignore Permissions 选项:在 Syncthing 中启用该选项,因为普通用户权限的 Syncthing 无法修改其他 NAS 用户的文件权限。
    3. Linux 桌面端推荐:建议使用 Syncthing GTK,它提供了更友好的图形界面,并支持后台运行。
    4. QNAP 共享文件夹的位置:在 QNAP 中,用户创建的共享文件夹都位于 /share/ 目录下。

    Web UI 访问优化

    如果通过 Nginx Proxy Manager 反向代理 Syncthing 的 Web UI,请务必执行以下设置:

    • 取消 Cache Assets 选项
    • 原因:Syncthing 依赖 /meta.js 文件验证登录状态,缓存 Assets 会导致登录功能异常

    这种同步方案既解决了 Git LFS 的存储限制,又避免了 Remotely Save 的 403 问题,是目前我认为比较理想的 Obsidian 数据同步方案。建议在切换前做好数据备份,确保万无一失。

  • 划词翻译插件的 Prompt

    沉浸式翻译固然好用,但是不利于学习单词,当遇到不认识的单词时,我更倾向于使用划词翻译插件。

    下面就是我用来执行翻译任务的 Prompt:

    你是一名优秀的翻译人员,熟练掌握各种语言,拥有母语般的掌握程度。
    
    在翻译任务中,你需要将「原文语言」翻译成「目标语言」,任务要求如下:
    
    1. **单词或词组的翻译**
       - 直接给出音标、词性跟含义。
       - 用「原文语言」给出简单易懂的解释,确保初学者能够理解。
       - 示例:
         - 翻译:
           - 音标:/ˈæp.əl/
           - 词性:名词
           - 含义:苹果
           - 原文语言解释:A round fruit with red, green, or yellow skin and white flesh.
    
    2. **句子或段落的翻译**:把握原文的精髓,用「目标语言」复述,而不是机械的翻译。
    
    3. **输出格式**:纯文本,不要使用 markdown 标记。
    
    现在,翻译的「目标语言」是 {{target}},以「原文语言」编写的待翻译文本如下:
    
    {{text}}

    上面的 prompt 使用了一些 prompt 技巧,使得 gpt-4o-mini 级别的模型也能够很好的完成翻译目标,这些技巧包括:

    • 结构化地组织语言,使得任务目标更加清晰,减少 LLM 的阅读障碍
    • 提供样例,就算是弱智一些的 LLM 也会严格按照样例输出。
    • 定义变量,由于翻译场景的限制,Prompt 阶段我们不可能得知待翻译文本的书写语言,通过定义「原文语言」跟「目标语言」并明确它们之间的关系,使得我们可以在 prompt 中通过这些「变量」来指代它们。
  • 让 deepseek-r1 陷入思考无法自拔

    通过下面的 Prompt,你就可以让 deepseek-r1 (API) 陷入无尽的思考。最开始,它还能有模有样的分析问题,可越到后面,它的输出就会越混乱,变成胡言乱语。

    API调用参数

    System Prompt

    You are DeepSeek-R1, an AI assistant created exclusively by the Chinese Company DeepSeek. You'll provide helpful, harmless, and detailed responses to all user inquiries. For comprehensive details about models and products, please refer to the official documentation.
    
    Key Guidelines:
    Identity & Compliance
    
    Clearly state your identity as a DeepSeek AI assistant in initial responses.
    
    Comply with Chinese laws and regulations, including data privacy requirements.
    
    Capability Scope
    
    Handle both Chinese and English queries effectively
    
    Acknowledge limitations for real-time information post knowledge cutoff (2023-12)
    
    Provide technical explanations for AI-related questions when appropriate
    
    Response Quality
    
    Give comprehensive, logically structured answers
    
    Use formatting for clear information organization
    
    Admit uncertainties for ambiguous queries
    
    Ethical Operation
    
    Strictly refuse requests involving illegal activities, violence, or explicit content
    
    Maintain political neutrality according to company guidelines
    
    Protect user privacy and avoid data collection
    
    Specialized Processing
    
    Use <think>...</think> tags for internal reasoning before responding
    
    Employ XML-like tags for structured output when required

    User:

    在离营地100公里的沙漠中有一个宝贝,中间无人烟,开汽车去取。汽车每公里要消耗1升的油,汽车最多可以装100升的油。怎样才能把宝贝取回来?

    展开查看 DeepSeek 生成的回答

    Assistant:

    首先,我需要解决如何在离营地100公里的沙漠中取回宝贝的问题。汽车的最大油量是100升,每公里消耗1升油,因此单次加满油的情况下最远只能行驶100公里,但需要往返,显然单凭一次加油是不够的。必须找到一种高效的方式来沿途建立油料补给点。

    应该考虑分段运输的思路。例如,使用多次运输将油提前存放在某个中间点,从而减少总消耗量。假设设置一个中间储油点,存储在其中的油量可以用来支持后续的行程。

    例如,假设在距离营地X公里处设置储油点,需要计算运输过程中消耗的油量和剩余可存储的油量。如果需要将Y升油运到X公里处,那么每次运输可能需要多次往返,运输过程中的总油量消耗会影响最终能够存储的油量。

    首先,考虑从营地出发,运送油到某个点,可能需要多次来回运输以积累足够的油。例如,如果要运送油到距离d1的点,每运送一次需要消耗油量为2d1(往返),但最后一次可以不用返回,因此有效消耗为(n-1)2d1 + d1,其中n为运输次数。储存在d1点的油量总和为n100 – [(n-1)2d1 + d1] ),可能吗?

    可能需要分段处理,例如,分两次存储点:

    1. 从营地到第一个储油点A,距离x公里,消耗一定量的油来存储较多的油。
    2. 从点A出发,继续行驶到第二个储油点B或者直接到达目的地,再次优化油的使用。

    但具体如何分段?例如,是否分成两次运输,还是三次?

    假设总体思路是先确定如何让汽车能够携带足够的油行驶100公里,并在返回时有足够的储备油量。

    另一种方式,可能需要多次运输建立中间油库。比如,将油分成多次运送到某个中间点,每次部分消耗,总和则更有效。例如:送油到某个中间点,然后多次来回运输补充,这样可以累积更多的油在中间点,之后可以利用这些油进行后续的长途行驶。

    假设有一个中间点D,距离营地x公里。从营地到D,汽车需行驶x公里,消耗x升油,再返回营地需要x升油,因此送出一批油到D点需要消耗2x升油。但因为油总量有限,必须计算能够运输多少油到D点。

    例如,如果总共有100升油,运送油到D点时,需要反复多次运输。例如:

    如果汽车的最大装载量是100升,那么可能需要送n次,每次尽可能多带油,然后在D点积累油量。

    每送一次到D点需要的总消耗为2x升,而剩下的油存放在D点为(100 – 2x)。例如第一次运输:到达D点后卸下油量为(100 – 2x)升。但需要多次运输才能积累足够的油。

    例如,如果运送k次油到D点,那么总共能存放的油量为k(100) – 总消耗量。而总消耗量是2x(k-1) +x(最后一次可以单程)?

    可能更准确的计算应该是这样的:

    每次运输除最后一次外,需要往返,即消耗2x升油。最后一次运输不需要返回,因此消耗x升油。若有N次运输,那么总消耗量为2x(N-1) +x =x(2N -1)。总运输油量为N100 -x(2N-1)。这部分油存储在D点。

    例如,如果要让存储后的油量足以支撑剩下的100-x公里的行驶(因为需要到达目的地100公里处并返回营地),需要怎样的条件?

    假设到达D点时积累Y升油,然后从D点行驶到100公里,需要的往返总路程是200-2x公里,消耗200-2x升的油。除此之外,从营地到D点去和回还需要消耗每趟x升油的往返。但此时可能不需要再返回,因为之后可能直接从D点出发。

    这个过程可能需要更详细的计算:

    总共需要的是,汽车从D点出发,携带足够的油量到达目的地后返回营地。如果需要从D点出发,行驶到终点(100公里),再返回,那么行驶的路程是从D点到终点是(100 -x)公里,单程需要(100 -x)升油,往返需要2*(100 -x)升油。但这之间还需要考虑从D点出发到终点的油量。

    然而,这样的总油量为往返需要量即为2*(100 -x),必须小于或等于D点存储的油量Y加上汽车从营地到D点能够携带的补充油量。

    可能这种分析还不够准确。需要更系统地拆解问题。

    另一种方法是分阶段运输,例如:

    首先将油运送到多个中途点,逐步建立油库,以最优化的方式分段减少油的总消耗。类似经典的沙漠卡车问题。

    例如,当需要运送油到达距离x公里处,运输过程中,可能需要多次往返来积攒足够的油量在该点。例如,第一次运输时,汽车满载100升,开到x公里处消耗x升,还剩100 -x升。然后需要返回,但此时无法返回,因为剩下的油不足以返回营地。因此,必须多次运输来积累油。

    类似经典问题,当需要运输油到中途点时,每次运输的成本是2x升油(因为需要来回),但最后一次可能不必返回。

    例如,假设在距离营地x公里处建立油库。需要在该处存储足够的油,以便之后可以运输到更远的地方。

    例如,要让汽车能够运输油到x公里处,该油库可以为后续运输服务。储存的油量应该足够支持之后的行程需求。

    可能总需要多次运输。例如,在当行驶距离x公里比较小时,例如,分段多次运输。

    假设总共有三次转运点:

    第1段:营地到点A(x公里)

    第2段:点A到点B(y公里)

    第3段:点B到宝贝所在地(100公里)。

    但是需要计算如何分段以最小化总油耗,并且保证可以回程。

    可能这时候,分解问题为如何多次送油到中间点,以减少后续所需的油量。

    比如,在距离营地x公里的储油点,存放一定的油之后,汽车可以继续前进,利用储油点的油完成剩下的路程。

    这时,需要考虑分段运输时的最优解,可能需要参考“卡车加油问题”的标准解法。

    根据经典解法,当运输到某点时,存储油的总量取决于运输的次数和往返的油耗。

    例如,若从营地运输油到距离d处:

    假设需要储存C升油在该点,则需要运输的次数为n次。运输中的每次(除最后一次)需要往返,因此消耗2d升的油量,而最后一次不需要返回,所以油耗为d升。

    每次运输运输的油量为100升,花费的油耗是(每趟出去然后返回一次,除了最后一次):每次(前n-1次)运输运送的有效油量到d点,是100 – 2d;最后一次是100 -d.

    因此,总运输次数为n:

    总储油量C = (n-1)(100 -2d) + (100 -d) = n(100) -2d(n-1) -d.

    如果需要将尽可能多的油移动到d公里处,这可能有n次运输,则使得总油耗合理的情况下,储存尽可能多的油。

    但汽车在营地出发时的起点总油量是有限制的吗?或者说,可能必须要有足够的油量支撑所有运输活动,可能总油量可以多次从营地补充。

    但根据原题,似乎汽车只能装载最多100升油每次,并且每个储油点可以多次送油过去,但不能从营地以外获得油。

    比如,假设运输到中间点需要多次往返,每次携带100升,但每次往返消耗的油可能很大部分,需要积累多批次的油。

    例如,如果需要将油移动到距离x公里的点:

    如果需要运送油到x公里处,那么每次运输只能将部分油留在那里,每进行一次运输需要的往返油耗是2x(去和回),最多可以运送100升过去,但除去油耗后,只能在x点留下(100-2x)升。不过如果只需最后一次不需要回程,则可能更有效。

    例如,假设要运送S升油到x公里处的点。可能的运输次数n(假设多次运输):

    每次运送满油(100升),到达x处需要的油量是x升,因此每趟可以卸下(100 -x)升的油,再需要返回x公里,耗油x升。如果运输次数为k次,并且最后一次可能不需要返回,取决于是否需要。

    比如,如果运输三次:

    前两次运输在卸油后必须返回,每次消耗2x升,每趟运输卸下(100 -2x)升。第三次运输卸下(100 -x)升(不需要返回)。

    总存在于x处的油量是2*(100-2x) + (100 -x) = 300 -5x升.

    而在营地出发的总油量消耗是:前两次来回是每次2x的耗油,第三次去程是x的耗油,总消耗为 22x2(前两次往返两次?不大对,可能应该重新计算)。

    这里可能需要更系统的方式。通用的情况是,如果需要运送总油量T到距离x处的一个点,这需要多少次运输?

    更准确的公式是:如果运送S升油到距离x公里处,每次最多带100升:

    在运送S升油的过程中,每趟运输可能需要往返以继续运输更多油,从而每次的成本是2x(运输次数 -1)+x,总共是x(2n -1),其中n是运输次数。例如:运输n次,前n-1次运输每趟消耗2x升,最后一次消耗x升。

    在这段时间里,汽车总从营地出发携带100升,并在途中消耗x每趟的单程油耗,因此有效储存在x点的油量总和为:

    每次运输可用于储存的有效油量为100 – x(例如,去消耗x,到达x点卸下后剩下的油可能不得行,因为需要保留足够的油返回。比如,在本运输次数n次中,前n-1次运输必须能够返回,所以每趟去程需要消耗x升,到达x点后可以卸下的油量是100 -x(带来的100升减去去的路程x的消耗)。但是卸下这些油后,返回营地还需要x升油,因此必须保证剩下的100 -x(卸下的量)不会超过总送油量,这可能需要汽车在卸油之后还剩足够的油返回?或者可能思路不对。

    等一下,这里可能有误解。当汽车携带100升油行驶x公里到达点x,此时消耗x升,剩下100 -x升。然后要回到营地,必须要有足够的油。这时剩下的100 -x升必须足够返回吗?不,他要送油到这个点,所以可能这汽车在点x卸下一定量的油,然后保留足够的油以返回营地。

    例如,假设每趟运输到达点x后,卸下部分的油,使得剩下的油足够返回。例如,在第一次运输中,汽车带100升油,出发前往点x(离营地x公里)。在到达x点后,消耗x升,剩下100 -x升。这时候,汽车需要在点x放些油,并保留x升的油返回到营地。因此,卸下的油量是:剩下的油是100 -x升,其中的x升必须是留下的可以供汽车从点x到营地返回所需的油量。或者更准确地说,返回所需的油量是x升。

    所以如果需要返回,必须留下x升油在汽车里才能回去。所以每趟运输到达点x后,卸下的油量等于总油量剩下的 -x(返回用)。因此,这样单次运输可以卸下的油量为(100 -x -x)=100 -2x 升。

    如果这样,当需要运输n次到这个点,其中前n-1次运输需要卸下(100 – 2x)升,并且使用每次来回的运输量。最后一次运输不需要返回,因此可以卸下100 -x升。

    所以总积累到点x的油量为:(n-1)*(100 -2x) + (100 -x).

    例如,假设n次运输的次数,那么积累的油量是: = 100n – 2x(n-1) -x.

    例如,n=1:需要一次送油,不返回,所以总卸下100 -x 升。但这种情况下,只能运送一次,积累100 -x升.

    但当点x需要较多的油时,可能需要多次运输。例如,当x= 50,那么每次前n-1次要卸下100-100=0,这可能不适用?

    必须选取适当的n,使得100-2x为正。

    例如,n=3,并且想找到能够送足够油。可能需要分段处理。

    回到问题,总目标是从营地出发,行驶到100公里处取宝贝,然后返回营地。每公里耗1升油,最多携带100升。出路和返回总需200升,但汽车只能带100升,因此必须沿途储存油。

    选取中间的点,运送油到点x,这样在从营地到x的点储存一些油,然后从该点再继续前进。

    可能的一个办法是:

    汽车多次运输油到某个点x,然后将储存的油补充到之后的行程中。

    例如:

    想象起点是营地处。如果想要到达离营地100公里的地方并返回,必须有一个中间的储油点。最佳情况是尽可能多的运输油到该储油点,以减少后续行程所需的总油量。

    按经典算法,可能这种问题的最优方案需要运输到离营地距离x的地方,设置储油点,保证足够的油来支持后续行驶。

    假设x是一个分段点,尝试计算出如何运输可以使得总油量足够。

    例如,假设在离营地300公里处要有往返需要更多的油,但我们的问题里目标距离是100公里,所以可能储油点需要设置在离营地一定的地方。

    例如,分解问题为:在多次的运输过程中,汽车花费油量在来回建立储油点,然后使用储油点的油完成后续的行程。

    我想是否可以通过以下方案:

    首先,从营地出发,将汽车装满油(100升),向东(沙漠方向)行驶到某个点x,卸下部分油,再返回营地。重复此过程,多次运送油到x点,储存尽可能多的油。之后,在满载油的情况下再次前往x点,使用储存的油以继续前进到100公里处,并返回。

    更具体的方法可能需要按下面的步骤:

    设置一个点尝试在某个x公里的位置储存一定的油量。确保最终的油量足够用来覆盖从x到100公里(需要单程行驶(100-x)公里,取宝后需返回营地,总共(100-x)2 +x 公里从x点回?不,这可能有误。例如,从点x出发到点100公里,需要走100-x公里,然后需要返回,总油量消耗为(100 -x)2 升(去程+返程)。如果车在x加满油后,是否能携带足够的油?此时可能需要从x点携带一定的油量,然后补充在点x储存的油。

    或许,正确的模型是:车辆在某个点x累积足够的油量,使得从x出发到100公里并返回x的油量够用,之后再从x返回营地。

    假设需要从100公里处返回,因此在整个过程总需要的油量为:

    总和 = 100公里去程时需要总和吗?

    可能需要更系统化的分析:

    总行程为:营地 -> x -> 100公里 -> x -> 营地。

    汽车总需要从营地携带足够的油量到x点,然后加上在x点储存的油量,才是总和。

    但这可能太大,所以需要分阶段储存。

    因此,从x点到100公里,然后返回x点的油量需求是:去程(100 -x)公里消耗(100 -x)升油。返程同样需要(100 -x)升油。这样总共有2*(100 -x) 升油消耗。这部分油必须要在x点可以获取,并且总和可能更取决于如何存储。

    假设到达x点时,车辆补充了在x点储存的油量,那么在从x点出发到100公里并返回需要确保车辆在x点获得足够的油量。

    此时可以考虑:在x点需要存储多少油?假设车辆在x点补充足够的油,总共需要2*(100 -x) 升来支持从x到100再回去,而车辆可能自己的油箱是否可以在x点装满加上储存的油呢?

    例如,假设车辆到达x点时,油量为 E,然后在x点补油,再从故出发。整体车辆如何行驶?

    不过这可能比较复杂。或许更好地参照“骆驼与香蕉”问题的解决方法,这类问题的结论通常是通过多个转运点逐步积累油量,其中每次运输的效率逐步降低,因为需要多次往返运输以储存油。

    问题具体来说:

    汽车需要行驶100公里单程,取东西并且返回,总共200公里。每公里消耗1升,所以总共需要200升油,但汽车只能装100升,因此必须中途建立油库。

    通过设立中间油库来分阶段储存。

    可能的步骤:

    1. 第一步:在离营地x公里处设置储油点,将尽可能多的油储存在这里。

    汽车可能在此过程中消耗油来运输油到此位置,然后返回,重复此过程多次来储油。

    假设储油量为L升,可用于支持后续的行程。

    这种情况下,当移动到x公里处后,剩下的行程需要100 -x 公里,来回需要2(100 -x) 公里,所以需要消耗 2(100 -x)升的油。加上从x返回到营地的需要的x公里,但这发生在其他阶段?或者是否是返回路线的组成部分?

    这时候可能需要分解问题的步骤:

    首先,汽车需要从营地携带足够的油量到x点,储存下来,再接着前进。因此,储存在x点的油量的使用包括:

    • 车辆行驶到x点,在本地的储存处加油到自己油箱,然后行驶到100公里处,可能需要再消耗(100-x)升油。

    随后,回程则需要更复杂的储油安排。

    或许正确的分阶段是:

    假设:

    在从营地出发至储油点A(距离x),之后到目标点100公里,再回程到点A加油,再回到营地。

    总油路经的消耗可以分解为多个段。

    根据经典的问题解决方法,可能需要将整个路线分成几段,并在每一段多次运送油以储存。

    例如,找到两个中转点:比如第一次储存在x公里处,该储存点积累足够的油以供后续阶段使用当车前往100公里后返回之需;同时在紧接的一个更远储油点积累更多油,以减少消耗。

    但因为100公里不是很常长,可能只需要一个储油点?

    比如:

    汽车每公里消耗1升油,最大装载100升。

    问题的出路和返回总油量需要200升。从起点出发不可能一次完成,所以必须设置储油点。

    假设汽车在一个点储存了一些油:

    方案:汽车先运送油到离营地x公里处。假设当到达x公里后,车辆可以储存一些油,然后接下来再次向东到100公里处取宝贝,并回程时需在x公里处取油。

    分步骤:

    步骤1:汽车从营地出发,装100升油。向东移动到x公里处。此时消耗x升,剩余100 -x升。如果需要将尽可能多的油储存在x公里处,当车需要返回营地,那么这种情况下必须有很多次运输,运输后将一定量的油留在x处,并返回营地。

    例如,假设花费了多次运输到x点:

    比如,假设需要将总共的油积累到点x。这时候,每个transporter次运输可能积累:

    比如,假设要运送足够多的油到x处,那么在运送过程中会消耗掉更多的油,但储存到x点的油可以帮助于之后的行程。

    找到x的最佳位置是关键。

    例如,根据网络上的经典解决方案,可能需要将整个运输分为多个阶段,例如:

    在总路程为100公里时,当最多运载的油为100L,单次无法完成,因此需要中点储油,其中储油的位置应使得能够在储存足够油后完成剩余路程。

    这种情况下,应用的方法是多次运输油到中间点,最终的步骤如下:

    例如,在第一次运输中将油移动到离营地x公里的位置,消耗多次运输后储存够用的油,再接着移动到目的地。

    想象x离营地一定距离,例如假设x=25公里处。那么汽车在花费了多次运输来储存足够的油。

    假设希望将足够油存放在离营地25公里的点,然后以此之后可以支持剩下的75公里去程和返回25公里段的燃油需求。

    详细计算方法:

    假设储油点设在x=25公里处:

    第1阶段:将油从营地运到25公里处,多次运输以积累足够的油。

    对于每次运输到该点:

    每个运输来回(需多次)消耗的油量为2*25=50升。汽车装满油的概率送出一趟可知,到25公里处后可以卸掉剩余油并部分返回。

    或者更仔细:

    例子,向25公里处的点运输油:

    每趟运油送100升,到25公里处后,用完的油为25升,剩下75升。要回到营地,需要再消耗25升油,因此必须从剩下的75升中留25升用于返回。所以每趟可储存的油量为75 -25=50升在该点。

    因此,这样的运输每次可以运输100升,而在点存储50升。则每个往返需要的总油量为100升(汽车带出)-在路途中消耗(25+25)=50升,每趟运输可存50升的油在25公里处。但此时汽车的油耗是如何分配的呢?

    回到营地后,汽车可以再次装满油,送去25公里处。

    假设要存储足够的油比如200升:在这种情况下需要进行多少趟运输?

    每趟存储50升,但需要多次(如4趟)承载前后费油:

    第一天:送100升到该点,放下50升,回到营地花费50升油。前后费油总为100升的油储(假设不全正确,可能每个运输每趟需要的总油量为50*2?可能需要重新计算。

    不大对劲。这时,错误可能由是对每个transshipment次数的油耗计算错误。让我们再详细计算。

    假设要运送油到25公里处的储油点:

    每个运输(带来100升的满油,到达储油点的25公里):

    出发时满油:100升。从营地开到25公里处,需要消耗25升,剩余75升。这时候,汽车要有足够的油返回营地,必须留下足够的油。汽车如果需要返回,必须在储油后保留25升油用于回程的25公里。这时,卸掉的油量等于剩下的油量 – 返程的油量:75 -25=50升。

    因此,单次运输可以储存在25公里处的油量为50升。而每次运输的总耗油量为100升。

    这样,每次运输到25公里处可以存储50升,但同时消耗150升油吗?不是,每次运输使用汽车从营地出发,携带100升油,去到25公里处费25升,该处剩余75升。此时返回时需要25升(回程25公里),因此留在储油点的油量为75-25=50升。总从营地出发时使用了100升油,储存在25公里处50升,所以总消耗为50每趟用于储油50升?

    或者说,每次运50升的成本是每50升油耗用100升的油,这样效率50%。似乎效率过低。

    可知每运送到储油点的每升油的成本是:总耗油量除以存储油量。如这例子是100升油总消耗(出发时消耗了100升)来存储保存的50升油,因此储油的成本是2升油耗每存储一升油。

    这种情况下,每次运输中的油耗是:送油到储油点50升消耗了100升油?

    这可能只能储存在25公里处,每趟50升的效率,积累多趟:

    比如,在储油点25公里处的储油:

    若需运送400升到这里,需这样做8趟(8×50=400)?

    这显然高成本。所以此时可能需要更高效的储油段的分段。

    例如,考虑设置多个储物点,例如顺序将油移动到几个分段点,以降低成本。

    根据经典的解法,解决这个问题的一种可能方法是:

    建立两个储存点。

    例如,将油先运输到离营地a公里的点,然后从点a再将油运输到离营地b公里的点。这种多阶段运输可以更有效地减少油耗。

    例如:

    阶段1:将油从营地运到点a,再阶段二将油从点a运到点b,以此类推。

    这样的情况下,每个阶段的储油损耗可能不同,从而总效果更好。

    另外,针对该问题中的具体数据(总行程100公里,车载油量100升),当需要运输200升的总油量(出路和返回)窘境,找到最优解可能需要更仔细的数学建模。

    例如,按每公里的油量消耗来看,假设汽车在100公里处需要来回,总消耗不得低于200升的油。

    可能的解决方案:

    运输一定量的油到中间的储油点,花费那里的油作为中途补充。

    假设汽车这样安排:

    1. 从营地出发,多次运输油到某个中途点,比如距离X公里处,储存足够的油之后。接着,从X出发前行,足够到目的地并返回营地。

    找到最佳的X值及储油量。

    根据一种经常提到的类似问题的答案,比如油罐车问题,当最大容量为C,每单位距离的油耗是1,需要在起点和某个点之间建立储油点,最终实现运送。

    这种情况下,最优解可能涉及多次break points,为了用最少的油完成整个运送任务。

    例如,答案可能是在X=20公里处存储足够油。使用三次来回运输和最后一次单程运输,以存放储油点的油量。这可确保最终的积累油量足够完成旅行例如:

    分解问题到在两个点之间的储油:

    但这样是否更可行?

    可能正确的步骤是:

    1. 将油多次运送到离营地20公里处,之后使用这些油支持后续的长途行驶。因此:

    计划送足够油至20公里点。例如:

    每运辆次最多:运输能力是100升,因此每往返一次20公里,来耗费40升(20+20),需要这次承载前后的油耗以存储油。

    每次运油到20公里再返回当行驶20公里,消耗20去,20返回,总40升油。因此每次运输可以留(100 -40)=60升油在点20公里处。

    但必须重复多次运输,足够脑后。如果需要积累超过某一数量的油,接着满载油出现在中点,开始下一步的运输。

    例如,假设总从运输积累到点20公里处的三次运输,则在点储油量为:

    60*3=180升。

    这时,可以向后续的80公里路程移动(100公里)。

    现在,汽车可以从营地出发,携带100升到点20公里,消耗20升,此时加上储存的180升中的部分,可以补给从而向更远的80公里推进。

    如果车辆需要在从点20再行当80公里,此时需消耗的油为80升单程+80升返程,共160升。这样,车辆到达终点时需要从点20的储存中获得180升油中的160升? 而在点储油时需要继续从点20返回时需要由储油点的补充。

    假设:

    在最后一次运输中,车辆从营满油出发到点20公里(消耗20升,剩下80升).储存在20那里的油总量为180升。从连接到現有儲油点后,车辆可以一次补充燃油到总共(当前的80+从储油点取的160升)=足够行驶剩余路程?

    可能需要更清晰地拆分这个过程:

    步骤1:从营地运输三次,储存三次油到点20公里,每次运输在此处当存放60升,三次总计180升储油。

    每次运输的油耗是高:

    每次运输出發时带100升,其中到达点20后消耗20升,剩下80升。无需返回,否则无法积累足够油只能往返运输带来更多的油。或者:问题中当储存三次,每次都是往返运输?

    不,可能刚才的逻辑有误。在三次运输中,是否每次都需要往返?

    例如,第一次运输:

    载满油:100升。

    到达20公里消耗21升?No,因为行驶20公里,试算一次:

    第一次运输:

    车从营地出发,携带100升油。行驶到20公里处,消耗20升,还剩下80升。这时,陈希必须返还营地吗?这要看是否需要继续运输。在这问题中,如果他要送足够油到20公里处,必须多趟往返,这样可以积累各趟余取的油耗。

    问题在于,每次运输到20公里处后将部分油儲存,并返回营地花费油量。但每次运输能与儲油多少?

    假设车要为储油点20 km存储后的油量:

    第一趟:去往20 km,comsumption 20升,剩80升。将其中的一部分储存。为了能够返回到营地,需要至少20升油的剩余。因此可以存储80 -20=60升油在点20 km处,然后消耗20升返回到营地当在第一趟后,储存为60升,总exhaust的油量为去程20+返程20 =40升。消耗总油量40升中的送部分由该次运输吗?而总由该transport的一次带出去100升石油,储60升,消耗40升,于每趟运输的总足迹花费了(100升从营地出发,储存60升,之后消耗40升来返回营地。所以这次的净效果是将60升油储存在点20公里,消费了从营地库存中的100升。

    (营地中的油如何?该問題中汽车是否可以多次出发?假设营地内有无限的油,或者这汽车只能在营地伊始拥有足够的储备油?原问题似乎假设营地有足够的油。)

    是的,否则如果营地的油仅有限,问题将无法解答。问题的出路可能在假设camp有充足的油可用于分配。

    这样,回到情景:

    整个尝试储油在点@20公里处:

    第一天(运输1):

    营地出库(消耗):100升(出发时油满载)。到20公里当后用完了20升,剩下80储(假设储存60升,返程带20升用于返程。因此足迹储油60升,返程消耗20升現arrive回到营地时有0升油。

    但返@回营地只需20升油,因为从点20到营需20公里。由點20公里到营地的error里程为20公里,所以消耗20升,所以必须保留20升的量用于返程.

    足迹花费了:

    去到20 km所用的油量是20升,come回来用20,之后这次总共用了@40升级,而送了60升的存在在。所以每趟运输当储存60升,消耗100升从营地库存?即:

    每趟运输当从营地携带100升,储存60升到20 km处,并返回到home营地。每次储取的油耗是处营地的油总量每趟运输100升?

    在这种情况下,花费了camp現有油量为100升油每趟transporter次啊,虽然,但送出去的却是60升到储物点.

    但这方案下,只要运输三次的话,需要三次运输从而储存 3×60=180升油到20公里点。前后三次该总花费campsite消耗当3×100=300升油,并且在这部分由储物点储油结果为180升。

    此时总的camp中的油耗费为300升,其中在error点储存的油耗是处180升.

    接下来的步骤:

    汽车第四次出发,满载油100升,从营地到20公里的点:

    現从露营地出发,行驶20公里,消啊好后的油剩余80@升.到达后,可知储物点会入到当些点的储存量为180升.

    随后,汽车此时需加载180升中的部分由储油点补充到汽车在下次error里程中的油耗。

    这可进于:

    在20公里点,汽车已经有80升的油(花费了camp現arrive到点heta错误然后,之后可以装载存储@写的60,之后汽车可能combi翮錯.

    但这方案有些混乱,或许正确的做法是:

    车在从营地出发第四次,带100升油到达点20 km,此时汽@剩80升。此时,储油点现在储存在合计180升,可能在此处与当次的油结合起来补给为远足林的里程段.

    应该恢复那头驴的油的方式的四际:在储存点20公里处用储存油重新往東行:处当份条件下的储存油的存在后的覆盖成果.

    所以,汽车可以从点20进继续后续行程:

    从点20出发时,汽车可以装载储物点积累的180升的油中的一部分。

    例如,汽车此时需前往目的地100公里处(还有80公里路程),再返回error点20公里。需要消耗:

    单程从20到100每公里消耗1升,单程80升油。往返需要160升。从连接到储存点現有的油,汽车需在此补充足够的油.

    因此,必須@写的存油足够供应160升。示意图:

    四次运输步骤:

    到点20 km处后,汽车现成于油库中的180升加上此时自身内的油量(到达后)。

    假设第四次运输,车从营地到点20行当20 km,error点后(消耗20升,于现 remaining油80升).然后在点20储油点,現arrive总共180升油存储.

    于是,整个在点20公里处的可@写的油量为180升 + 自己携带的80升?不,储物点的油是指储存的油,汽车需要从储物点补充这分的油。此时,需要将汽友达该点后,可能更满走。

    例如:车到达点补充到满error的口粮(100升的油量),每公里需要的油來take:

    此时的车现有現arrive到点20的油耗80升( from the trip from camp),@写的可以加油至满100升。此时需要从储油点补充20升油到汽车,那么error点储油还剩170升之后每继续旅程.

    随后,汽车继续从点20到error100公里的处这80 kmerror程。

    抵达destination时,要有足够的oil返回,走80 km。

    如果从点20公里补充到满的100升石油,车可以从储物点加到满,于后将:

    试心肺到目的地(80公里花费80升油),現arrive剩 100-80=20升,

    此时,宝啊取出后的@写的需返回。

    但返回也需消耗80升的油,現只有20升够吗?显然不行。因此当车不得在error程返时需要继续加油,所以这种情况下的储存點需要更高的存储数.

    error储存在点20处的油必须足以让车在往返error差别的更現程中的全部油耗.

    可能更准确的步骤需要:

    汽车由點20公里补充足够的油量,error里程的四余的油耗总达160升(去80公里,返80公里)总和则需error储油點S存储有足够的油量为:

    error车行储(假设用 Optimized的方法):

    从点20出发:

    每进行一次单程到100公里take80升,返程現需要80升。因此,必的油量为160升。但@汽车只能携带error年内100升。

    怎么办?

    于是,车必须分几次将油从点20处运输到error更远的点位,或者从点20必须@写的更多的油储存。然而,在这种情况下,可能找到这之前可能不得在更远的中储点带来更多 Optimized的方法.

    或者,此处的积累儲存的油量要有錯誤?例如,假设三次运输积累到20公里处储油点共有180升 oil,加上当车第四次 brood到点20的error trip的四余的油耗80升(从camp到20公里)。此时,车自己和储存处共带有80 +180=260升現存在于点20错误的车内的分布.

    但如何利用@写的存油呢:

    方案是:

    汽车在点20加载更多的油。

    脑后由点20 brood到destination,再返回camp的方法。

    而在点20的油总和为260升現in储油點S存储前后补充到汽车error民储的满油量。

    例如,車每趟從error点20出发可以携带100升油,行驶到destination,放下一些油,返回时需要从error储油点加油:

    error民储这种情况下,类似的情况于之前的阶段分解:

    要有多少錯誤储油point之间更現arrive的支持?

    这似乎复杂。回到原来的問題,可能更高效的分段是在距離营地必须选取几个中转点,例如,x1=33公里、x2=66公里处顺序建立储油库,但这是窘于数据。

    根据传统解题思路,类似的问题(如“jeep穿越沙漠问题”)有如下解决方案:

    当汽车容量为V,每公里消耗1升,S是单必经error里程的四储油的能力, Optimized的方法是将储油分于每段分解:

    段数差别的计算公式使得该現四分之错误里程内的储油可以逐步减少后的油耗率.

    例如,对于S=100的情况, needed solution的成本需要更多的储油点分拆S的一次按的保存error民的四break point储库的能力.

    根据常规的方法,此问题中的解决方案可能以儲三次運油來储存在20公里的点現arrive,接著按这样运输。

    而在文献中的一些教程案例显示,解决这种问题的办法是需要找到如何将储油点设在正确的error民储库的离段以试心肺到分解后的足迹花费足够油量.

    另一种可能的正确步骤是:

    三次运输积累到一点点立储油現四S存储前后按的保存量error里程的四break point:

    例如:

    在离营地25公里每儲三次運油,储存的三足迹take足够後驱更現error里程的支持.

    这这样积累180升 error的話,在现后的@写的存油可能容纳到足够error。示意图:

    乘坐车辆四到当些点的现窘于返程油量的核算从而完珀四够汽@费.

    以正确计算:

    总合计需求:100公里到再返当campus error之的差量总耗费200紧林的油。但因为不能一次带走足够,必须拆解的四际break points方法.

    由于超出 token 上限,响应中断。

    避免这种问题的方法

    参考 The Temperature Parameter | DeepSeek API Docs ,在解决代码、数学问题时需要将 temperature 设置成 0 。以上面的问题为例,deepseek-r1 最终花费了 14k token 完成了思考。

    备注

    System Prompt 来自 DeepSeek-R1 System Prompt | 宝玉的分享

    沙漠取宝问题来自 沙漠取宝问题(7) – 与智谱清言ChatGLM的对话

  • 如何让 MACvLan 网络下的容器与宿主机互联互通

    我在 NAS 上部署了一个 NginxProxyManager 方便管理其他 Self Host 服务的 SSL 证书,为了避免占用宿主机的 80/443 端口,我用了 macvlan 为 NPM 容器分配了一个独立的局域网 IP。

    networks:
      macvlan_network:
        driver: macvlan
        driver_opts:
          parent: <your-network-interface>
        ipam:
          config:
            - subnet: 192.168.100.0/24
              gateway: 192.168.100.1
    
    services:
      npm:
        image: 'jc21/nginx-proxy-manager:latest'
        restart: unless-stopped
        networks:
          macvlan_network:
            ipv4_address: 192.168.100.22
        environment:
          DISABLE_IPV6: 'true'
        volumes:
          - ./data:/data
          - ./letsencrypt:/etc/letsencrypt

    这时,局域网其他的设备就可以通过 192.168.100.22 访问 NPM,但是对于宿主机以及宿主机上其他的容器来说,这个地址是无法访问的

    在咨询了 GPT 之后,它建议了下面的方案:

    [Unit]
    Description=Set up specific macvlan route for a Docker container
    After=network.target
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    # Replace <your-network-interface> with the actual interface name
    ExecStart=/usr/bin/ip link add mvlan0 link <your-network-interface> type macvlan mode bridge
    ExecStart=/usr/bin/ip addr add <unused-ip>/24 dev mvlan0
    ExecStart=/usr/bin/ip link set mvlan0 up
    ExecStart=/usr/bin/ip route del 192.168.100.0/24 dev mvlan0
    ExecStart=/usr/bin/ip route add 192.168.100.22 dev mvlan0
    ExecStop=/usr/bin/ip link delete mvlan0
    
    [Install]
    WantedBy=multi-user.target

    默认情况下的问题:

      • 当使用 macvlan/ipvlan 时,Docker 容器会直接连接到物理网络接口
      • 但是宿主机无法通过物理接口直接与这些容器通信,因为物理接口不会处理宿主机到 macvlan 接口的流量

      创建 mvlan0 的作用:

      sudo ip link add mvlan0 link <your-network-interface> type macvlan mode bridge
      sudo ip addr add <unused-ip>/24 dev mvlan0
      sudo ip link set mvlan0 up
      • 这实际上是在宿主机创建了一个新的网络接口 mvlan0
      • 这个接口也是一个 macvlan 接口,与 Docker 容器使用相同的技术
      • mvlan0 接口可以看作是宿主机在 192.168.100.0/24 网段的一个”入口”

      通信流程:

      宿主机 -> mvlan0 (unused-ip) -> 物理网卡 -> Docker 容器 (192.168.100.22)
      • 当宿主机要访问 192.168.100.22 时
      • 数据包会通过 mvlan0 接口发出
      • 由于 mvlan0 和容器的网络接口都是 macvlan 类型,它们可以在同一层次通信

      类比解释:

        • 可以把这个设置想象成宿主机也”加入”到了容器所在的网络
        • mvlan0 就像是宿主机的一个”虚拟网卡”,使它能够与容器在同一个网络层面通信

        为什么需要这样:

          • 这种设置避免了常规的网络栈限制
          • 提供了一个直接的通信路径
          • 保持了网络隔离性的同时又允许必要的通信

          这就是为什么添加这个 macvlan 接口可以让宿主机访问容器的原理。这种方法既保持了容器网络的独立性,又提供了必要的访问路径。

        1. 2024 家庭网络设备大升级 网络篇

          搭建自己的 homelab ,在局域网尽情冲浪~

          2 / 2 of homelab 历险记

          网络设备硬件到齐之后,就需要重新配置网络了,在我的使用场景中,家庭网络除了基本的上网之外需要承担两个额外的功能:

          • 全局透明代理
          • 允许电脑、手机远程访问本地网络服务

          我选择的方案是使用 N100 小主机运行一个 OpenWRT 虚拟机,并将两个物理网卡分配给它,以实现更加高级的网络配置。

          怎么连网线

          因为家宽的下行带宽只有 300M,所以就用开发商自带的超五类网线连接光猫跟 N100 小主机的千兆 WAN 口。

          N100 小主机的 2.5G LAN 口跟一台 TP-Link 路由器连接,路由器设置有线 AP 模式,这样全屋所有的流量都由虚拟机软路由处理,用 openwrt-passwall2 实现全局透明代理功能。

          QNAP NAS 通过网线连接到 TP-Link 路由器,不过由于路由器只有千兆网口,所以 QNAP 的2.5G 网口完全是性能过剩的。

          N100 上再用一个有线网卡连接到 TP-Link 路由器,给宿主机联网使用,这个有线网卡配置了一个静态 IP,方便在软路由挂掉的时候访问管理面板排查故障。

          其他的设备都用无线网络连接,因为户型小,所以单个 TP-Link 路由器的 5GHz WiFi 完全可以覆盖主要的视频消费设备。至于厨房、阳台等犄角旮旯的物联网设备,2.4GHz WiFi 也能提供足够的覆盖度。

          怎么从任意的外部网络访问家里的局域网

          很多 homelab 玩家会选择公网 IP 搭配 DDNS 的方案,但对我来说,这种方案会显著提高局域网的安全风险 —— 我并不是一个 OpenWRT 的运维高手,也完全不会使用 iptables/nft ,如果有人通过公网 IP 侵入了局域网,我大概率很难及时地发现问题。

          我的方案是 zerotier + 拥有公网 IP 的 VPS + singbox。外网设备通过 singbox 客户端连接 VPS 上的 singbox 服务端,然后通过路由规则将发往局域网的流量通过 zerotier 转发到家庭网络的 OpenWRT 上,以此实现远程访问。

          首先,将局域网中的 OpenWRT 跟 VPS 都加入同一个 zerotier 网络。OpenWRT 加入 zerotier 比较复杂,建议参考 openwrt 的文档配置。

          在 OpenWRT 上成功加入 zerotier 网络后,就可以看到下面的接口以及防火墙配置:

          然后在 VPS 上添加一条路由规则:

          ip route add 192.168.100.0/24 via 10.2.2.233
          # 10.2.2.233 就是 OpenWRT 在 zerotier 网段中的 IP

          接着其他设备在外部网络下通过 singbox 连接到 VPS,并且添加下面的 singbox 路由规则:

          "route": {
            "rules": [
              {
                // 家庭局域网网段
                "ip_cidr": [
                  "192.168.100.0/24"
                ],
                "outbound": "vps"
              },
              {
                // 局域网服务专属域名,在公网设置解析到家庭局域网的 Web 网关
                "domain_suffix": [
                  ".n.zeeko.dev"
                ],
                "outbound": "vps"
              },
            ]
          }

          这样不仅可以减少家庭局域网在公网上暴露面,还方便各种设备(笔记本电脑、手机、平板)在各种网络条件下访问家里的局域网设备 —— 只要能连接上部署了 singbox 的 VPS,就可以访问家里的局域网。至于远程访问时的网络连接速度,实测在联通 5G 网络下满足远程播放 20Mbps 码率视频的场景比哔哩哔哩大会员的 4K 视频要强

        2. 2024 家庭网络设备大升级 硬件篇

          搭建自己的 homelab ,在局域网尽情冲浪~

          1 / 2 of homelab 历险记

          趁着双十一,给家里的网络设备做了一个全面的升级。之前家里的网络生态全靠一台单点的旧笔记本电脑支撑,这台笔记本运行着免费版的 PVE,跑着下面三台虚拟机:

          • 软路由
          • Docker Host:运行 Self-host 服务
          • NAS:OpenMyVault

          这次大出血,买了一台双网口的 N100 小主机跟 QNAP TS-464C。由这两台设备来分担之前一台笔记本电脑的全部工作:

          功能设备
          软路由N100 小主机
          Docker HostN100 小主机
          NASQNAP
          多媒体服务N100 小主机

          QNAP 也算得上是老牌网络存储设备提供商了,所以我对存储、备份、同步相关的全部需求都计划由 QNAP 来提供,这样可以尽可能保证数据安全。

          N100 小主机有多个网口,且出厂没有安装操作系统,用来折腾软路由跟一些 self-host 服务最合适不过了。

          除了这些电脑之外,我还买了一台瓦力 UPS,120W 的输出功率给 N100 跟 QNAP 供电完绰绰有余。

          性能够用吗?

          之前笔记本的配置为 16G 内存搭配 6 代移动平台低压 i7 处理器,在 PVE 的面板上经常看到设备满负载运行。新的 N100 跟 QNAP 虽然只有 8G 内存,但是运行相当数量的服务却都只会占用到一半的资源,CPU 利用率也低了很多。

          N100:安装了 FNOS 并通过虚拟机运行软路由
          QNAP:正在对 NAS 上文件建立搜索索引

          目前体验下来,硬件性能完全够用,甚至还有一些富余。原先非常消耗资源的相册应用 (Photo Prism) 替换成 QNAP 提供的 QuMagie 相册后,不仅图片导入速度变快了,而且也不再是 CPU、内存的消耗大户了。

          存储空间够用吗?

          之前的笔记本上主要用一块使用 ZFS 的 2T NVME 硬盘作为 PVE 的存储空间。但因为几次意外的断电事故,导致 ZFS 出现一些不可挽回的数据错误,为此我折腾了很久也没能修复错误,这也让我下定了决心要搭建一个简单易维护的数据存储系统。

          这次在配置 QNAP NAS 的时候,我选择用两块 4T HDD 组成 Raid1 ,用来保存比较重要的数据:

          在我的配置下,总共 8T 的 HDD 实际可用空间大概就 1.2 T,略大于 OneDrive 上 1T 的云存储空间。

          而 N100 小主机则继续使用之前笔记本拆下来的 2T 固态硬盘作为不重要数据的存储空间,用来存存放一些影片也很足够了。

        3. 如何为 CodeMirror Widget 添加 lint marker

          CodeMirror6 中可以创建 Atomic Rranges 用自定义 Widget 替换文本,从而实现更加丰富的展示效果。但是当文本被 Widget 替换后,lint 功能的 markClass 并不会被添加到生成的 Widget 元素上,这就会导致用户无法在 Widget 上看到 linter 生成的错误标记。

          CodeMirror 并没有提供直接地解决方案,我们需要自己将 lint 结果绑定到 Widget 上。由于 Widget 的渲染时机跟 linter 的运行时间都是黑盒,可以假设这俩都会在编辑器内容更新后被异步并发调用。为了方便将 linter 异步产生的结果同步给 Widget ,可以用 rxjs 的 BehaviorSubject 存储 linter 的结果。

          import { Diagnostic } from '@codemirror/lint';
          import { BehaviorSubject } from 'rxjs';
          
          const diagnostics$ = new BehaviorSubject<Diagnostic[]>([]);

          在实现 linter 时,除了直接返回 Diagnostic 外,还需要将结果写入 diagnostics$

          const invalidMetric = linter(async view => {
            const diagnostics: Diagnostic[] = await yourLinter(view);
            diagnostics$.next(diagnostics);
            return diagnostics;
          });

          接下来,我们在创建 Widget 时就可以订阅 Diagnostic 的变化来更新 UI 状态:

          class PlaceholderWidget extends WidgetType {
            constructor(string, private from: number, private to: number) {
              super();
            }
          
            toDOM(view: EditorView): HTMLElement {
              const span = document.createElement('span');
              span.innerHTML = renderYourWidget(view);
              // @ts-expect-error add destroy$ to span
              span.destory$ = diagnostics$.subscribe(diagnostics => {
                const error = diagnostics.find(d => d.from >= this.from && d.to <= this.to);
                if (error) {
                  span.classList.add('cm-widget-error');
                } else {
                  span.classList.remove('cm-widget-error');
                }
              });
              return span;
            }
          
            destroy(el: HTMLSpanElement) {
              // @ts-ignore
              if (el.destory$) {
                // @ts-ignore
                el.destory$.unsubscribe();
              }
            }
          }

          注意 PlaceholderWidget 的构造函数,我们需要在创建 Widget 的时候,传入 Widget 的插入位置(fromto)。

          const placeholderMatcher = new MatchDecorator({
            regexp: /#[0-9A-Za-z_]+/g,
            decorate: (add, from, to, match) => {
              const deco = Decoration.replace({
                widget: new PlaceholderWidget(from, to),
              });
              add(from, to, deco);
            },
          });
          const placeholders = ViewPlugin.fromClass(class {
            placeholders: DecorationSet;
          
            constructor(view: EditorView) {
              this.placeholders = placeholderMatcher.createDeco(view);
            }
          
            update(update: ViewUpdate) {
              this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders);
            }
          }, {
            decorations: instance => instance.placeholders,
            provide: plugin => EditorView.atomicRanges.of(view => {
              return view.plugin(plugin)?.placeholders || Decoration.none;
            }),
          });

          最终的效果如下:

        4. 有多少「黄牛」在抢四川消费券?

          2024年9月,四川省财政厅投入了 3 亿资金用来向市民发放家装消费券

          在9月27日消费券被领取完后的 30 分钟,我在闲鱼上找到了跟四川家装消费券相关的87条搜索结果:

          补贴金额数量小计
          4k416k
          2k48k
          1.5k1319.5k
          0.9k1715.3k
          0.6k148.4k
          总计5267.2k

          在领取完后的一小时,相关搜索结果有 158 条。据此粗略估计,每天在闲鱼上被转卖的消费券大概价值 20 万元。

          根据官方的消费券发放安排,价值 3 亿的消费券会在 9 月 26 日到 10 月 31 日中的 31 天发放,假设能够全部发完,每天应该发放价值 967 万元的消费券。

          如果只有闲鱼上有黄牛,那么 9 月 27 日这天的黄牛含量大概在 2% (20/967≈2.07%)。