Wang Zhiyu's Blog - Wang Zhiyu
type: channel
link: https://blog.imwangzhiyu.xyz/index.php/author/1/
description: Wang Zhiyu
items: 关于独立开发并维护一个中小型软件项目的一些总结
items: 为 Python 设计的状态机库 Transitions 开发文档 (中文翻译)
items: 《学习实践中重复间隔的优化》部分翻译与简易应用
items: 关于电子邮件的"来源伪造"攻击分析
items: 搭建 Docker Hub 镜像所需配置
items: Dijkstra 和 Floyd 算法的 C++ 实现 供演示使用
items: 关于 FileZilla 链接 FTP 超时无法列出目录的解决方案
items: 如何通过命令行调整 16.04 旧版架构 Ubuntu Touch 系统的音量
items: 自动续期三丰云免费服务器教程
items: 记 vector 容器初始化长度为0导致的段错误ub

关于独立开发并维护一个中小型软件项目的一些总结
type: item
date: 2026-01-17T00:13:00+08:00
description: 最近统计了一下"潜进"记忆软件项目的最新代码, 算上注释和必要的空行(经过了 black 格式化), 合计已经超过了9000行. 在断断续续改进这个项目的六个月里, 产生一些经验, 现在做一点总结. ➜ HeurAMS git:(refactor/v0.5.0) ✗ date 2026年 01月 17日 星期六 00:07:43 CST ➜ HeurAMS git:(refactor/v0.5.0) ✗ cloc . --exclude-list-file=.gitignore 2200 text files. 1913 unique files. 2283 files ignored. github.com/AlDanial/cloc v 2.06 T=0.78 s (125.9 files/s, 12563.8 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Python 79 1520 1395 5490 Markdown 8 62 0 338 TOML 8 40 12 146 Jupyter Notebook 1 0 684 80 Text 1 0 0 8 JSON 1 0 0 1 ------------------------------------------------------------------------------- SUM: 98 1622 2091 6063 ------------------------------------------------------------------------------- ➜ HeurAMS git:(refactor/v0.5.0) ✗

为 Python 设计的状态机库 Transitions 开发文档 (中文翻译)
type: item
date: 2026-01-02T02:49:00+08:00
description: Transitions 是一个轻量级的、面向对象的 Python 状态机实现，包含许多扩展功能。兼容 Python 2.7+ 和 3.0+。 安装 pip install transitions 快速入门 俗话说，一个好的例子抵得上 100 页 API 文档、一百万个指令或一千句话。 好吧，说这话的人可能有点夸张... 但不管怎样，这里有一个例子： from transitions import Machine import random class 嗜睡超级英雄(object): # 定义一些状态。大多数时候，嗜睡超级英雄就像普通人一样。 # 除了... states = ['睡觉中', '闲逛中', '饥饿中', '满身汗', '拯救世界'] def __init__(self, name): # 在我的地盘上不允许有无名的超级英雄！每个嗜睡超级英雄都必须有名字。 # 任何名字都行。瞌睡侠、梦游女。你懂的。 self.name = name # 今天我们救了啥？ self.kittens_rescued = 0 # 初始化状态机 self.machine = Machine(model=self, states=嗜睡超级英雄.states, initial='睡觉中') # 添加一些转换。我们也可以像上面定义状态那样，用一个字典的静态列表来定义它们， # 然后将这个列表作为 transitions= 参数传给 Machine 的初始化器。 # 某个时刻，每个超级英雄都必须起床亮相。 self.machine.add_transition(trigger='唤醒', source='睡觉中', dest='闲逛中') # 超级英雄需要保持身材。 self.machine.add_transition('锻炼', '闲逛中', '饥饿中') # 消耗的卡路里不会自己补充！ self.machine.add_transition('吃', '饥饿中', '闲逛中') # 超级英雄随时待命。永远待命。但他们并不总是穿着适合工作的服装。 self.machine.add_transition('求救信号', '*', '拯救世界', before='换上超级秘密战衣') # 他们下班时，浑身是汗，脏兮兮的。但在做其他事情之前， # 他们必须仔细记录他们最新的冒险经历。因为法律部门这么说。 self.machine.add_transition('完成任务', '拯救世界', '满身汗', after='更新日志') # 汗水是一种可以通过水解决的状况。 # 除非你度过了特别漫长的一天，那样的话... 就该睡觉了！ self.machine.add_transition('清洁', '满身汗', '睡觉中', conditions=['筋疲力尽了吗']) self.machine.add_transition('清洁', '满身汗', '闲逛中') # 我们的嗜睡超级英雄几乎可以在任何时间睡着。 self.machine.add_transition('小睡', '*', '睡觉中') def 更新日志(self): """ 亲爱的日记，今天我救了胡须先生。又一次。 """ self.kittens_rescued += 1 @property def 筋疲力尽了吗(self): """ 基本上就是抛硬币。 """ return random.random() < 0.5 def 换上超级秘密战衣(self): print("帅吧？") 好了，现在你已经把一个状态机烘焙进了 嗜睡超级英雄。让我们带他/她/它出去遛遛... >>> 蝙蝠侠 = 嗜睡超级英雄("蝙蝠侠") >>> 蝙蝠侠.state '睡觉中' >>> 蝙蝠侠.唤醒() >>> 蝙蝠侠.state '闲逛中' >>> 蝙蝠侠.小睡() >>> 蝙蝠侠.state '睡觉中' >>> 蝙蝠侠.清洁() MachineError: "无法从状态 '睡觉中' 触发事件 '清洁'！" >>> 蝙蝠侠.唤醒() >>> 蝙蝠侠.锻炼() >>> 蝙蝠侠.state '饥饿中' # 蝙蝠侠还没干任何有用的事... >>> 蝙蝠侠.kittens_rescued 0 # 我们现在为您直播一场可怕的小猫被困事件现场... >>> 蝙蝠侠.求救信号() '帅吧？' >>> 蝙蝠侠.state '拯救世界' # 回基地。 >>> 蝙蝠侠.完成任务() >>> 蝙蝠侠.state '满身汗' >>> 蝙蝠侠.清洁() >>> 蝙蝠侠.state '睡觉中' # 太累了，澡都不洗了！ # 又是高效的一天，阿尔弗雷德。 >>> 蝙蝠侠.kittens_rescued 1 虽然我们无法读懂真正蝙蝠侠…

《学习实践中重复间隔的优化》部分翻译与简易应用
type: item
date: 2025-07-03T21:14:00+08:00
description: 本文针对知识保留水平为 95% 的配对联想学习，确定了一个计算重复间隔的通用公式。 据称，该公式可广泛应用于各种学科的学习实践中，且不受个体学习者能力的限制。 翻译对原文的表述做了一些改动 关于此文章 此文章所使用的论文的原标题是: Optimization of repetition spacing in the practice of learning 翻译对原文的表述做了一些改动 原始 PDF 文档在 此处 可以下载 (镜像) 论文原始作者是 Piotr A. Woiniak 和 Edward J. Gorzelanczyk (来自波兹南医学院组织胚胎学系) 这篇文章的相关原理可被运用到辅助记忆软件中. 此文章虽未提及, 但与 SM-2 算法密切相关. 作为主流记忆软件的理论基石之一, 此文章或许可作为改进变体的理论依据. 摘要 本文针对知识保留水平为 95% 的配对联想学习，确定了一个计算重复间隔的通用公式。 据称，该公式可广泛应用于各种学科的学习实践中，且不受个体学习者能力的限制。 研究思路 许多出版物都广泛讨论了不同重复间隔对学习效果的影响。特别是，间隔效应被认为是一种普遍现象。间隔效应是指，如果重复的间隔是分散/稀疏的，而不是集中重复，那么学习任务的表现会更好。因此，有观点提出，学习中使用的最佳重复间隔是最长的、但不会导致遗忘的间隔。然而，遗忘具有随机性，因此无法预测它何时会在特定情况下发生。所以，尽管进行了多次尝试，但很少有研究提出可在学习实践中使用的重复间隔。 在本研究中，我们采用了一种随机方法。我们通过将最佳间隔定义为导致少量（预先确定）学习材料被遗忘的间隔来计算最佳重复间隔。我们使用了一个计算机程序来监控学习过程，确保在重复时只有 5% 的待记忆项目未被记住。该程序采用了一种优化算法，当遗忘项目的比例低于或高于所需 5% 的水平时，它会自动延长或缩短重复间隔。 此外，在学习过程中，待记忆项目根据受试者的表现自动分为不同的难度类别。对于每个难度类别，我们都应用了不同的重复间隔。 实验过程 参与实验的受试者是波兹南工业大学的 7 名计算机科学系本科生，他们是无偿志愿者。在 18 个月的时间里，他们总共记忆和重复了超过 35000 个他们选择的项目（这些项目是波兰语-英语单词对）。这些记忆的项目是受试者在实验前不认识的。受试者在 2 个月内使用下述算法分批掌握了所有材料，并在接下来的 16 个月内继续重复。 算法中的每次重复过程如下： 呈现问题 受试者尝试回答 将回答与正确答案进行比较 受试者进行 0-5 分的自我评估（0 分 - 非常差，5 分 - 非常好） 用于确定最佳重复间隔的函数（最佳间隔函数）以表格形式的最佳因子矩阵表示. (1) 式: $$ I(EF, 1) = OF(EF, 1) $$ (2) 式: $$ I(EF, R) = I(EF, R - 1) * OF(EF, R) $$ 公式中: $ EF $ - 简单度因子，用于反映该项目被记忆的难易程度 $ I(EF,R) $ - 间隔 (单位: 天)，对于简单度因子确定为 EF 的项目，在第 R 次重复之前的间隔 $ OF(EF, R) $ - 最优间隔函数 为了更好地拟合最优间隔因子，我们使用了一种优化算法来修改矩阵 OF 的初始值，主要判据是稳定的知识保留率达到95%。 使用了两类不同的OF矩阵： 单一值矩阵：所有条目初始化为相同的值，即对于所有的EF和R，OF(EF,R) := 1.5。 预定值矩阵：OF条目根据不同的难度类别（Woiniak和Biedalak 1992）而变化：对于所有的EF，OF(EF, 1) := 5；对于所有的EF和R，OF(EF,R) := EF；其中EF的选择范围是1.3, 1.4, 1.5, . . . , 3.2（参见表一）。 预定矩阵OF旨在确保OF条目更快地收敛到其最优值，而单一值矩阵OF则用于验证优化算法的有效性。15%的项目根据单一值OF矩阵进行优化，85%的项目则使用预定OF矩阵进行安排。 使用了以下学习算法（请注意，重复可以无限期进行，并且OF矩阵的实验值可以随时获取，越晚越好）： 记忆一个新项目i：以自定进度的方式学习问题和答案之间的关联。 设置初始EF值：EFi := 2.5（设置对应于项目i的EF因子的初始值）。 设置重复次数：Ri := 1（将重复次数设置为1）。 确定首次重复日期：使用OF矩阵根据公式（1）确定首次重复的日期。 进行重复：在预定的重复日期，通过回答相关问题来重复该项目，并以0-5的等级评估回答的质量（表现良好则等级高）。 继续学习或停止：如果学习者希望继续学习，则Ri := Ri + 1；否则，停止。 调整EFi值：如果质量低于4，则降低EFi值；否则，增加。学习中出现问…

关于电子邮件的"来源伪造"攻击分析
type: item
date: 2025-05-10T03:04:00+08:00
description: 起因是收到了一封来自"自己"的难绷诈骗邮件 以下是原文: (迫 真 演 技) Hello pervert, I've sent thіs message from your Microsoft account. I want to іnform you about a very bad sіtuatіon for you. However, you can benefіt from іt, іf you wіll act wіsely. Have you heard of Pegasus? Thіs іs a spyware program that іnstalls on computers and smartphones and allows hackers to monіtor the actіvіty of devіce owners. It provіdes access to your webcam, messengers, emaіls, call records, etc. It works well on Androіd, іOS, macOS and Wіndows. I guess, you already fіgured out where I’m gettіng at. It’s been a few months sіnce I іnstalled іt on all your devісes because you were not quіte choosy about what lіnks to clіck on the іnternet. Durіng thіs perіod, I’ve learned about all aspects of your prіvate lіfe, but one іs of specіal sіgnіfіcance to me. I’ve recorded many vіdeos of you jerkіng off to hіghly controversіal рorn vіdeos. Gіven that the “questіonable” genre іs almost always the same, I can conclude that you have sіck рerversіon. I doubt you’d want your frіends, famіly and co-workers to know about іt. However, I can do іt іn a few clіcks. Every number іn your contact Iіst wіll suddenly receіve these vіdeos – on WhatsApp, on Telegram, on Instagram, on Facebook, on emaіl – everywhere. It іs goіng to be a tsunamі that wіll sweep away everythіng іn іts path, and fіrst of all, your former lіfe. Don’t thіnk of yourself as an іnnocent vіctіm. No one knows where your рerversіon mіght lead іn the future, so consіder thіs a kіnd of deserved рunіshment to stop you. I’m some kіnd of God who sees everythіng. However, don’t panіc. As we know, God іs mercіful and forgіvіng, and so do I. But my merсy іs not free. Transfer 1700$ to my Lіtecoіn (LTC) wallet: ltc1quvupuw5hz3pr9aguqtevhd342azs6gz2c04zl2 Once I receіve confіrmatіon of the transactіon, I wіll рermanently delete all vіdeos compromіsіng you, unіnstall Pegasus from all of your devіces, and dіsappear from your lіfe. You can be sure – my benefіt іs only money. Otherw…

搭建 Docker Hub 镜像所需配置
type: item
date: 2024-08-28T02:37:00+08:00
description: 如题 服务器部署 使用 Docker Compose 部署: #version: '3' # 旧版兼容 services: registry: image: registry:2 ports: - "5000:5000" # 可以把前面的 5000 换成想要的端口 environment: REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io # 上游 REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: inmemory # 内存缓存 volumes: - ./data:/var/lib/registry 然后可以用 Nginx 反向代理端口, 添加证书到 HTTPS 就行了. 本地镜像站配置 在客户端上 echo '{"registry-mirrors": ["https://镜像站域名"]}' | sudo tee /etc/docker/daemon.json > /dev/null

Dijkstra 和 Floyd 算法的 C++ 实现 供演示使用
type: item
date: 2024-08-28T01:58:00+08:00
description: 如题, 有 Dijkstra 和 Floyd 算法 /* * @Author: wangzhiyu * @Date: 2023-09-03 02:29:26 * @Last Modified by: wangzhiyu * @Last Modified time: 2024-07-30 22:47:07 */ #include <cmath> #include <cstring> #include <time.h> #include <iostream> #include <vector> #include <unistd.h> // for linux // #include <windows.h> // for windows using namespace std; using ll = long long; constexpr int LIM1 = 205; // 预分配阙值 constexpr int INF = 0x3f3f3f3f; // 近似无限大 /// @brief 图论相关 namespace graph { int n; /// @brief 节点数据结构 struct Node { int nxt; int w; }; vector<Node> edges[LIM1]; /// @brief 增加一条边 /// @param from /// @param to /// @param w 权值 void addedge(int from, int to, int w) { edges[from].push_back({to, w}); } /// @brief 删除某边 /// @param from /// @param to void deledge(int from, int to) { // delete an edge for (auto i = edges[from].begin(); i != edges[from].end();) { if ((*i).nxt == to) { i = edges[from].erase(i); } else { i++; } } } /// @brief 查询某边的权值 /// @param from /// @param to /// @return 权值 int queryedge(int from, int to) { // to query an edge's weight if (from == to) return 0; for (auto i = edges[from].begin(); i != edges[from].end(); i++) { if ((*i).nxt == to) { return (*i).w; } } return INF; } /* void dfs_launcher(){ for(int i = 1; i <= n; i++){ dfs(i); } } void dfs(int begin, ){ cerr << "Access: " << begin; dfs(edges[begin].nxt); } */ /// @brief Floyd 最短路算法 /// @param spec 待展示的点标号 void sp_f(int spec) { // Floyd algorithm int sp[LIM1][LIM1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { sp[i][j] = queryedge(i, j); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { for (int k = 1; k <= n; k++) { sp[i][j] = min(sp[i][j], sp[i][k] + sp[k][j]); } } } for (int i = 1; i <= n; i++) { string dist = to_string(sp[spec][i]); if (dist == to_string(INF)) dist = "∞"; // unable to reach cerr << spec << " -[" << dist << "]-> " << i << " (floyd)" << endl; } } /// @brief 输出某点详情 /// @param idx 特定点索引 void print(int idx) { // to print a particular vertex's detail informatio…

关于 FileZilla 链接 FTP 超时无法列出目录的解决方案
type: item
date: 2024-08-16T01:50:00+08:00
description: 如题 不要使用快速连接 在站点管理器里把模式切换到主动 OK

如何通过命令行调整 16.04 旧版架构 Ubuntu Touch 系统的音量
type: item
date: 2024-08-06T17:14:00+08:00
description: 如题, 测试机是装载 Ubuntu Touch 16.04 的 MIX 3 不要用 amixer 和 alsamixer, 会出错: phablet@ubuntu-phablet:~$ amixer amixer: Mixer default load error: No such device phablet@ubuntu-phablet:~$ alsamixer cannot load mixer controls: No such device 使用 pactl: pactl set-sink-volume 0 20% # 调到 20 % pactl set-sink-volume 0 +20% # 增加 20 % pactl set-sink-volume 0 -20% # 减少 20 %

自动续期三丰云免费服务器教程
type: item
date: 2023-07-21T13:58:00+08:00
description: 自费购买云服务器绝对是一种经济负担, 使用免费服务器资源无不是一种很有吸引力的选择, 问题在于免费的服务器资源一般都要续期 所以我写了个脚本来自动白嫖免费服务器... 阅读之前 请确保你有这些东西: 一个手机号 1 到 3 元钱 (用于实名认证) 对数据随时丢失 & 服务不稳定的心理准备 不能搭建 http(s) 网站的心理准备 (除非你有一个已经备案的域名, 三丰云的免备案简直没法用, 但是 tcp 连接不受限制) 基本的 Linux 服务器管理技能 第一步：注册并登录三丰云 此过程略 记得设置管理员密码 系统选择 Ubuntu 16.04(这是三丰云提供的最新系统版本, 凑合用罢). 不要选 Windows, 太耗资源了. 此时你已经可以通过 ssh 管理服务器了, 关于管理服务器, 本文不再赘述 另外第一次续期时间点设置很反人类, 先手动过一下, 后面就可以自动化了 第二步：使用自动续期脚本 先说明一下, 这个脚本虽然理论上可以在你的免费服务器运行, 但是不建议这么做 首先, 因为系统版本低的缘故, 你要手动编译安装 Python 3.7.5, 因为系统自带的最高版本是 Python 3.5, 而且就算你搞好了 Python 环境, 你还要处理 Geckodriver 和老旧火狐浏览器版本一类的破事, 是真的麻烦. 其次, 就算你手动或者用 docker 啥的配好了所有环境, 开心跑脚本提交续期的时候, 你会发现你的续期图片的 IP 地址是北京什么公司, 过不了审核的 个人建议可以在自己的电脑/手机/家庭服务器上整个 Linux 环境的计划任务运行脚本 (理论上 Windows 也可以运行, 但是我没做测试) 如果实在没有设备, 还可以借助 Vercel 的部署脚本在你的免费服务器上整个计划 git commit 运行, 但是这需要你的配置 第三步：部署脚本并运行 从软件源安装 FireFox Python 3.7 或更高 (记得安装 python3-dev python3-pip) (debian 上貌似只有 firefox-esr, 也能用) 如果你要搞计划任务还要安装 cron, 本文不再赘述 使用 pip3 安装依赖包: selenium 创建一个目录, 按文件名放置脚本文件 # run.sh rm -rf result.png python3 webposter.py && python3 imgeditor.py && python3 webuploader.py rm -rf scrst.png rm -rf upload.png rm -rf geckodriver.log # imgeditor.py from PIL import Image def crop_image(input_path, output_path, left, upper, right, lower): try: image = Image.open(input_path) cropped_image = image.crop((left, upper, right, lower)) cropped_image.save(output_path) print("[LOG] 成功生成上传图片") except FileNotFoundError: print("[ERROR] 找不到输入图片") except Exception as e: print("[ERROR] 裁剪图片时出错", str(e)) print("+-- IMGEDITOR --+") print("执行任务: 编辑图片文件") print("任务目标: 生成上传图片") input_path = "scrst.png" output_path = "upload.png" left = 180 upper = 13 right = 1131 lower = 309 crop_image(input_path, output_path, left, upper, right, lower) print("[LOG] 成功生成上传图片") # webposter.py import random from time import sleep from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.firefox.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui impo…

记 vector 容器初始化长度为0导致的段错误ub
type: item
date: 2023-07-12T15:23:00+08:00
description: 初始化 std::vector 时, 有时这么写: std::vector vec(length); 编译可正常通过, 然而当把 length 设置为 0 时, 程序发生段错误, 并直接崩溃 因此当 length 为变量时, 应作特判, 避免测试点 RE. 例如 USACO1.3 混合牛奶 Mixing Milk 的测试点 #2. 如果不想特判, 还有一个解决办法: 使用 resize 方法替代初始化长度 #include <bits/stdc++.h> using namespace std; int main(){ int n; cin >> n; vector<int> vec; vec.resize(n); for(int &i:vec) cout << i << endl; return 0; } 如果对这段代码输入 0, 程序不会输出任何值, 立即退出, 这也说明了 STL 方法一般是安全的. 初始化一个数组长度为 0 似乎并不会出问题, 估计指向的是nullptr罢...