Logo ryp 的博客

博客

巨大 UI 更新!

avatar ryp 2026-09-26 23:00:26

如题!

本次更新的内容有:

  • 提交详情页面更新!换成了正方形的测试点信息,非常帅!

  • 个人博客首页的更新!改变了页面风格,右侧对齐了火焰图,特别帅!

  • 博客详情页的更新!同样,右边加了个人信息,让页面更丰富,贼帅!

dsv4f 太屌了!

严重安全问题已修复!

avatar ryp 2026-09-24 21:25:06

中秋假期,我拿伟大的 deepseek harness 接入 dsv4f,随后惊喜地发现了 WyOJ 的两个重大 bug,都与“复制题目”相关。

下面是 dsv4f 撰写的 bug 报告。

WyOJ 缺陷报告

  • 审查对象:3b6cabd4(老布局:仓库根即 web 根)
  • 修复补丁:wyoj-security-fixes.patch(22 个文件,已应用到线上)
  • 说明:下面"复现"针对打补丁之前的代码;每条都标了修复位置。本文件只收录已修复的问题。

摘要

No 严重度 类型 位置 状态
1 高 未认证 SQL 注入 + 命令注入 + 反射型 XSS app/controllers/problem_set.php 已修
2 高 judger 上报字段 SQL 注入 app/controllers/judge/submit.php 已修
3 中 存储型 XSS(4 类数据 / 20 处输出) 多处 已修
4 中 CSRF(赞踩 / 私信 / 切换展示勋章) 3 个裸 POST 已修
5 中 HTML::escape() 不转义单引号 app/models/HTML.php:5 已修

1. [高] problem_set.php:未认证 SQL 注入 + 命令注入 + 反射型 XSS

位置:"复制题目"分支(修复前 :39-64)与"迁移题目"分支(修复前 :16-19)。

影响 - 未登录即可盲注数据库(同一个 $pid 同时流向 SQL 和 shell); - 有题目控制权的人可让 web 容器执行任意命令(即 web 主机 RCE); - 附带一处反射型 XSS。

复现(修复前) 1. SQL 注入:POST /problems,copy_problem_submit=submit&copy_problem_list=0 or sleep(3) → 响应延迟约 3 秒(对照 copy_problem_list=1 不延迟)。原因:queryProblemBrief($pid) 把 $pid 直接插进 select * from problems where id = $pid,而它作为权限判断的参数先被求值,因此在 becomeMsgPage() 终止请求之前就已执行。

  1. 命令注入:同一字段填 1; id >/tmp/pwned(需先通过第一轮权限检查,即出题人/超管),$pid 进入 dataCopyProblemData() → exec("cp -r /var/uoj_data/$src …")。

  2. 反射型 XSS:POST /problems,migrate_problem_submit=submit&target_group=<script>alert(1)</script>&start_pid=1&end_pid=1 → "未找到组 …" 原样回显。

  3. 附带 bug:if (!group) 把 group 当常量(求值为真值字符串 'group'),这个"组不存在"守卫从未生效;PHP 8 下直接 fatal。

根因:外部输入未校验就进入 SQL 与 shell 命令;错误消息未转义。

修复 - problem_set.php:52-61:每个 pid 在使用前 validateUInt()(一处同时堵掉 SQL 注入与命令注入,因为同一变量流向两处);:20 转义 $_POST['target_group'];两个 POST 分支加登录门槛。 - app/libs/uoj-data-lib.php:5-41:data*Problem() 系列改用 escapeshellarg() + dataCheckId()(纵深防御,防未来调用方再引入)。


2. [高] judge/submit.php:judger 上报字段 SQL 注入

位置:app/controllers/judge/submit.php(修复前 :32-38、:63-69、:75-77)。

影响:通过 authenticateJudger()(口令比对 judger_info,而仓库预置的是公开占位符 _judger_password_)后,result JSON 里的 status / error 被直接拼进 SQL,score / time / memory 连引号都没有 → 完整 SQL 注入,可改任意表。$_POST['id'] 本身在派发处有 validateUInt,不是注入点。

复现(修复前,需一个有效 judger 账号):POST /judge/submit,带 submit=1&id=1,把 result 的 status 填成 Judged', score=0 where 1=1 -- 即可改变写入语义。

根因:把不可信 JSON 当作 SQL 字面量拼接。

修复:新增 judgeResultFromPost()(:12)统一解码并拒绝非数组;status/error 走 DB::escape()(:48、:50、:84),score/time/memory/success 强制 (int)(列类型本就是 int(11)/tinyint(1),与 judger 端的 int() 一致)。


3. [中] 存储型 XSS:4 类数据、20 处输出未转义

数据 谁能写 修复前的输出点
motto 任何注册用户 index.php:699/730/757、uoj-html-lib.php:1240、views/blog-preview.php:37
组昵称(存于 user_info.email) 组管理员 group_info.php:9,27、user_info.php:16-18、uoj-html-lib.php:432/496、groups_manage.php:87、problem.php:295、problem_set.php:98
题目标题 / 远端题目标题 出题人 getProblemLink/getContestProblemLink、echoGroupProblemList/SizeList、problem.php:220/230、problem_statistics.php:62、rps.php:11、4 处管理页标题
勋章显示名 组管理员 / 超管 echoCertsList 的 $text

复现(修复前):个人设置里把格言设为 <img src=x onerror=alert(1)>(28 字符,validateMotto 只校验长度)→ 打开首页,脚本在所有访客浏览器中执行。组昵称、题目标题同理(写入端分别是"validator 直接 return '';"和"只做 DB::escape")。

根因:输入端只做长度/存在性校验,输出端不转义。

修复:20 处输出统一 HTML::escape()。

注意(易踩):blog 标题不要在输出端转义——它在写入时已被 UOJBlogEditor.php:96 转义,再转一次会双重转义。因此 getBlogLink() 保持原样。这个仓库混用了"存储时转义"和"输出时转义"两种风格。


4. [中] CSRF:三个无 token 的裸 POST

端点 后果 修复
click_zan.php 冒用受害者身份刷赞/踩 :20 加 crsf_defend();js/uoj.js 带 _token
user_msg.php 以受害者名义发私信 :102 加 crsf_defend();页面 JS 带 _token
certs_manage.php 改掉受害者展示的勋章 :36 加 crsf_defend();表单加 HTML::hiddenToken()

复现(修复前):在任意第三方页面放置自动提交的表单,指向 https://<host>/click-zan,字段 id=1&delta=1&type=P;受害者登录状态下访问该页即被记账。

根因:UOJForm 生成的表单内部会校验 token(uoj-form-lib.php:49,277),但这三处是手写表单/AJAX,绕过了它。

修复:端点加 crsf_defend();page-header.php 暴露 uojCsrfToken 给 JS,并 bump js/uoj.js?v=。bump 版本号是必须的——否则浏览器缓存的旧 JS 不带 token,赞踩会全部失效。


5. [中] HTML::escape() 不转义单引号

位置:app/models/HTML.php:5(修复前是裸 htmlspecialchars($str),PHP 7.4 默认 ENT_COMPAT)。

影响:user_info.php:24 把 bgurl 放进单引号 CSS 字符串 url('<…>'),而 bgurl 存储时只做 DB::escape(certs_manage.php:53)→ 一个 ' 就能闭合字符串注入 CSS 声明(视觉劫持、借 url() 外带信息;现代浏览器不会执行 JS)。

复现(修复前):设置背景地址为 x');background:url(//attacker/),查看自己主页的 style 属性。

修复:htmlspecialchars($str, ENT_QUOTES, 'UTF-8')。已确认 HTML::escape 的输出没有任何一处落在 <script> 块内,不会破坏 JS。


回归验证清单

  1. php -l 全部改动文件(本机无 php,未过 lint)。
  2. 首页 / 用户主页 / 题库页 / 题目页渲染正常。
  3. 格言填 <img src=x onerror=alert(1)> → 显示字面文本,不弹窗。
  4. 点赞一次 → 计数变化;不带 token 的裸 POST → 返回 This page has expired.
  5. 发私信、切换展示勋章 → 正常。
  6. 复制题目 / 迁移题目(有出题权限)→ 正常。
  7. 提交一次评测 → 结果正常写回(确认 judge/submit.php 的转义没有破坏协议)。

新用户首页!

avatar ryp 2026-09-20 13:18:31

大家可以看到,用户首页有大更新了。

可以看看我的主页。

可以写一篇博客,在 aboutme 界面设置博客 ID,然后就可以在个人首页展示了。

背景图是一样的。复制 URL 就好了。

个人信息的修改,比如个性签名,过几天会写上。

关于 Rating 系统

avatar ryp 2026-09-16 12:57:52

由于 @zxh_qwq 对 Rating 系统提出了疑问,认为他的 Rating 系统更优,我们不妨根据 WyOJ 的历史数据对比一下现有的 Rating 系统(Glicko-2)和他提出的 Perf 等差、向 Perf 移动 $\delta / 5$ 的简单 Rating 系统。 $$ \begin{array}{r|l|r|r|r|r} \text{排名} & \text{用户名} & \text{最终 Rating} & \text{最大回撤} & \text{期望} & \text{方差} \\ \hline 1 & \text{zrl123456} & 2421.10 & 88.69\ (3.77\%) & 2137.73 & 66332.17 \\ 2 & \text{ANDER_} & 2358.09 & 337.53\ (22.50\%) & 1700.67 & 117009.39 \\ 3 & \text{KSCD_} & 2303.37 & 0.00\ (0.00\%) & 1933.70 & 71649.81 \\ 4 & \text{Ame_wiki} & 2273.11 & 223.80\ (12.20\%) & 1903.81 & 70908.29 \\ 5 & \text{aaa} & 2238.02 & 0.00\ (0.00\%) & 1907.84 & 65392.64 \\ 6 & \text{Pigsyy} & 2182.06 & 0.00\ (0.00\%) & 1864.32 & 52585.94 \\ 7 & \text{lzx} & 2152.91 & 119.12\ (5.47\%) & 1920.19 & 56075.78 \\ 8 & \text{wfandy} & 2074.79 & 0.00\ (0.00\%) & 1789.83 & 44398.23 \\ 9 & \text{taccat} & 2061.42 & 307.53\ (14.83\%) & 1828.49 & 25156.47 \\ 10 & \text{wyz} & 2057.55 & 182.63\ (8.23\%) & 1825.61 & 48767.76 \\ 11 & \text{FiraCode} & 1971.40 & 117.74\ (7.85\%) & 1636.94 & 46000.45 \\ 12 & \text{Gejunyi} & 1968.54 & 389.44\ (20.33\%) & 1696.09 & 33460.92 \\ 13 & \text{zxh_qwq} & 1922.19 & 390.29\ (20.43\%) & 1734.50 & 98465.76 \\ 14 & \text{xyh13793601192} & 1907.44 & 0.00\ (0.00\%) & 1716.32 & 21605.07 \\ 15 & \text{_awa_wangjiawen}& 1902.52 & 341.02\ (19.45\%) & 1593.94 & 24096.83 \\ 16 & \text{Dtw} & 1879.18 & 78.66\ (5.10\%) & 1739.44 & 32269.68 \\ 17 & \text{rengpuiyang} & 1866.76 & 238.59\ (13.97\%) & 1619.42 & 18557.34 \\ 18 & \text{Little_Cck} & 1839.22 & 347.64\ (22.85\%) & 1448.36 & 24480.07 \\ 19 & \text{zibenlun} & 1837.13 & 104.57\ (6.97\%) & 1578.87 & 21080.86 \\ 20 & \text{tths01y} & 1796.99 & 43.14\ (2.61\%) & 1639.27 & 11342.89 \\ \end{array} $$

注意到,ANDER_ 超了 KSCD_ 和 aaa,Ame_wiki 超了 aaa。其余的各种问题,我没有心情细细观察。

我们可以非常简单、快速、轻松、自在极意地发现这个 Rating 系统存在许多缺点:

首先,他完全不考虑你对手是谁。只要你打了第一,Perf 就是 max_rating。只要你打了倒数第一,Perf 就是 min_rating。如果为了简单性,直接把对手是谁忘了,那你算的那还叫 Rating 吗。

况且,由于 Perf 的范围固定,导致不可能存在长期的上涨,所有人的 Rating 都趋于同一个范围。这就导致越高水平的人,虽然他理论上应该有更高的 Rating,但因为算法缺陷,他的 Rating 却始终被往下拉。

此外,这个 Rating 系统完全没有波动率,也就是 Glicko-2 中的 Vol,这就导致无法判断某个人最近的状态。Glicko-2 的极大好处之一就是,你可以快速地判断一个人最近是稳定发挥还是爆冷。

其次,水平相近的选手之间波动相当大。两个 Rating 接近、排名相邻的参赛选手,按他的算法,有一个巨大的 Perf 差。这会造成他们两个都长时间上不了分,尽管他们比排名靠后的选手厉害。

@zxh_qwq 指出,“比赛数很少,用大型算法‘不够优秀’,不如用这个。”我想知道,以在比赛少的情况下发挥显著好于常见的 Rating 系统而著称的 Glicko-2,咋就突然不够优秀了呢?况且这个算法有什么证据说明他对比赛少的情况友好?

@zxh_qwq 指出,“太复杂了不好”,这句话实现了时空穿越!因为 Glicko-2 显然完全优于以上这个 Rating 算法,而所谓的“复杂”只存在于我实现这一算法之前。既然我已经实现了这一能够完全偏序他提出的 Rating 算法的优越算法,还存在什么“复杂”可言呢?看来,时空穿越对他来说已经完全不是问题了。

@zxh_qwq 指出,“别再折腾了,就这样吧”。读完这句话,我仿佛陷入了梦境,身边的一切都飘渺起来,因为这句话让我陷入了一种震撼的境地:折腾的人,难道不是我吗?我耗费了好几天的时间,研究了各种 Rating 系统,最后选择并用了一中午实现了 Glicko-2,这些“折腾”,居然可以用一段语法都不正确的代码搪塞过去,“就这样吧”,吾今羽化而登仙矣!

WyOJ 历史比赛数据

avatar ryp 2026-09-15 13:29:14

三列分别是:比赛 ID,用户名,排名。

"1","alt","1"

"1","ryp","2"

"3","lgvc","1"

"3","cxm1024","2"

"3","jxy2012","3"

"3","recall","4"

"3","xxxbt","5"

"3","Un1quAIoid","6"

"3","Dtw","7"

"3","lzx","8"

"3","zibenlun","9"

"3","zxh_qwq","10"

"3","handezheng","10"

"3","contest","10"

"3","star_field","10"

"3","lsxhyyds","10"

"3","danio1029","10"

"3","xuyunao","10"

"4","alt","1"

"5","lzx","1"

"5","taccat","2"

"5","quyuan","3"

"5","xuyunao","4"

"5","_awa_wangjiawen","5"

"5","Ame_wiki","6"

"5","FXLIR","7"

"5","zhangyining2357","8"

"5","wyz","9"

"5","protractor","10"

"5","WZL1212","11"

"5","Jtwang1","12"

"5","wukuncheng","13"

"5","gctiruct","14"

"5","S08577","15"

"5","Base_ring_tree","16"

"5","wfirstzhang","17"

"5","dongyiyang","18"

"5","tanzicheng","19"

"5","zxh_qwq","20"

"5","lihaobei","21"

"5","yzs","22"

"5","handezheng","23"

"5","zrq2010","24"

"6","lzx","1"

"6","_awa_wangjiawen","2"

"6","gctiruct","3"

"6","zxh_qwq","4"

"6","handezheng","5"

"6","protractor","6"

"6","yzs","7"

"6","FiraCode","8"

"6","xuyunao","9"

"6","tianruihan","10"

"7","rengpuiyang","1"

"7","aaa","2"

"7","KSCD_","3"

"7","gctiruct","4"

"7","Pigsyy","5"

"7","lzx","6"

"7","xuyunao","7"

"7","FiraCode","8"

"7","protractor","9"

"7","quyuan","10"

"7","S08577","11"

"7","cybrex","12"

"7","Dtw","13"

"7","zibenlun","14"

"7","handezheng","15"

"7","tianruihan","16"

"7","YangShangLin","16"

"7","LiuYiPing","16"

"7","ZhouKangYang","16"

"8","zrl123456","1"

"8","zxh_qwq","2"

"8","lihaobei","3"

"8","wfirstzhang","4"

"8","yuanjingxuan","5"

"8","dongyiyang","6"

"8","FXLIR","7"

"8","Jtwang1","8"

"8","keep_shining","9"

"8","liwenhan","10"

"8","WF_wxl","11"

"8","star_field","12"

"8","SaintRelief","13"

"8","xhl_leo","14"

"8","Little_Cck","15"

"8","Base_ring_tree","16"

"8","Gejunyi","17"

"8","taccat","18"

"8","hatred","19"

"8","yzs","20"

"8","ANDER_","21"

"8","hai_kerr","22"

"8","ruiqi2012","23"

"8","Lin_Yi_Rui","24"

"8","wyz","25"

"8","thr_31825","26"

"8","ljm","27"

"8","why110623","28"

"8","woden","29"

"8","_awa_wangjiawen","29"

"8","YangShangLin","29"

"8","LiuYiPing","29"

"8","ZhouKangYang","29"

"10","zrl123456","1"

"10","lzx","2"

"10","KSCD_","3"

"10","Pigsyy","4"

"10","protractor","5"

"10","handezheng","6"

"10","Dtw","7"

"10","zibenlun","8"

"10","quyuan","9"

"10","S08577","10"

"10","cybrex","11"

"10","wyz","12"

"10","xuyunao","13"

"10","tianruihan","14"

"10","taccat","15"

"10","dongyiyang","16"

"10","Jtwang1","17"

"10","star_field","18"

"10","ruiqi2012","19"

"10","wfirstzhang","20"

"10","WF_wxl","21"

"10","lihaobei","22"

"10","Lin_Yi_Rui","23"

"10","yzs","24"

"10","Base_ring_tree","25"

"10","FiraCode","26"

"10","yuanjingxuan","27"

"10","hatred","28"

"10","_awa_wangjiawen","29"

"10","FXLIR","30"

"10","thr_31825","31"

"10","Little_Cck","32"

"10","liwenhan","33"

"10","xhl_leo","34"

"10","qcd_fisher","35"

"10","keep_shining","36"

"10","why110623","37"

"10","ljm","38"

"10","Joe20110905","39"

"10","zhangyining2357","40"

"10","woden","41"

"10","SaintRelief","42"

"10","ANDER_","43"

"10","zhang2010","44"

"10","zxh_qwq","45"

"10","Gejunyi","46"

"10","rengpuiyang","47"

"10","ygbyt","48"

"10","lsxhyyds","48"

"11","aaa","1"

"11","Pigsyy","2"

"11","KSCD_","3"

"11","rengpuiyang","4"

"11","zrl123456","5"

"11","lzx","6"

"11","gctiruct","7"

"11","FiraCode","8"

"11","quyuan","9"

"11","handezheng","10"

"11","Dtw","11"

"11","zibenlun","12"

"11","cybrex","13"

"11","S08577","14"

"11","taccat","15"

"11","Gejunyi","16"

"11","haiker","17"

"11","Jtwang1","18"

"11","lihaobei","19"

"11","star_field","20"

"11","tianruihan","21"

"11","xuyunao","22"

"11","ANDER_","23"

"11","Little_Cck","24"

"11","woden","25"

"11","keep_shining","26"

"11","zhangyining2357","27"

"11","WF_wxl","28"

"11","lijunda","29"

"11","zxh_qwq","30"

"11","dongyiyang","31"

"11","wyz","32"

"11","_awa_wangjiawen","33"

"11","FXLIR","34"

"11","Ame_wiki","35"

"11","SaintRelief","36"

"11","xhl_leo","37"

"11","liwenhan","38"

"11","hatred","39"

"11","ruiqi2012","40"

"11","yzs","41"

"11","OPEC","42"

"11","protractor","43"

"11","Base_ring_tree","44"

"11","why110623","45"

"11","zhang2010","46"

"11","wfirstzhang","47"

"11","yuanjingxuan","48"

"11","CZM","49"

"11","thr_31825","50"

"11","ljm","51"

"11","zyq777","52"

"11","Joe20110905","52"

"12","asd","1"

"14","aaa","1"

"14","KSCD_","2"

"14","lzx","3"

"14","FiraCode","4"

"14","zibenlun","5"

"14","Dtw","6"

"14","Ame_wiki","7"

"14","lihaobei","8"

"14","zxh_qwq","9"

"14","zrl123456","10"

"14","Gejunyi","11"

"14","cybrex","12"

"14","gctiruct","13"

"14","quyuan","14"

"14","S08577","15"

"14","tianruihan","16"

"14","Pigsyy","17"

"14","handezheng","18"

"14","wyz","19"

"14","hatred","20"

"14","keep_shining","21"

"14","_awa_wangjiawen","22"

"14","star_field","23"

"14","SaintRelief","24"

"14","Jtwang1","25"

"14","dongyiyang","26"

"14","why110623","27"

"14","ljm","28"

"14","ruiqi2012","29"

"14","xuyunao","30"

"14","yuanjingxuan","31"

"14","Little_Cck","32"

"14","xhl_leo","33"

"14","OPEC","34"

"14","wukuncheng","35"

"14","liwenhan","36"

"14","zhangyining2357","37"

"14","yzs","38"

"14","lijunda","39"

"14","Lin_Yi_Rui","40"

"14","rengpuiyang","41"

"14","taccat","42"

"14","woden","43"

"14","thr_31825","44"

"14","WF_wxl","45"

"14","haiyixuan","46"

"14","ANDER_","47"

"14","FXLIR","48"

"14","Base_ring_tree","49"

"14","CZM","50"

"14","zibunlen","51"

"14","wfirstzhang","51"

"15","Pigsyy","1"

"15","aaa","2"

"15","zrl123456","3"

"15","FiraCode","4"

"15","Dtw","5"

"15","xuyunao","6"

"15","Gejunyi","7"

"15","handezheng","8"

"15","gctiruct","9"

"15","yzs","10"

"15","lzx","11"

"15","tianruihan","12"

"15","rengpuiyang","13"

"15","zxh_qwq","14"

"15","_awa_wangjiawen","15"

"15","cybrex","16"

"15","ANDER_","17"

"15","protractor","18"

"15","quyuan","19"

"16","rengpuiyang","1"

"16","KSCD_","2"

"16","lzx","3"

"16","zrl123456","4"

"16","zxh_qwq","5"

"16","FiraCode","6"

"16","handezheng","7"

"16","gctiruct","8"

"16","xuyunao","9"

"16","_awa_wangjiawen","10"

"16","Dtw","11"

"16","wyz","12"

"16","cybrex","13"

"16","S08577","14"

"16","protractor","15"

"16","quyuan","16"

"16","tianruihan","17"

"16","__vector__","18"

"16","Quantum","19"

"16","yzs","19"

"16","WF_wxl","19"

"17","zrl123456","1"

"17","taccat","2"

"17","_awa_wangjiawen","3"

"17","wyz","4"

"17","Gejunyi","5"

"17","protractor","6"

"17","ANDER_","7"

"17","star_field","8"

"17","quyuan","9"

"17","keep_shining","10"

"17","Little_Cck","11"

"17","lihaobei","12"

"17","wfirstzhang","13"

"17","tianruihan","14"

"17","WF_wxl","15"

"17","yzs","16"

"17","wangchen101","17"

"17","zxh_qwq","18"

"17","Quantum","19"

"17","cybrex","20"

"18","zrl123456","1"

"18","Gejunyi","2"

"18","ruiqi2012","3"

"18","quyuan","4"

"18","zxh_qwq","5"

"18","S08577","6"

"18","yzs","7"

"18","wyz","8"

"18","taccat","9"

"18","Jtwang1","10"

"18","_awa_wangjiawen","11"

"18","WF_wxl","12"

"18","star_field","13"

"18","keep_shining","14"

"18","wfirstzhang","15"

"18","tianruihan","16"

"18","Little_Cck","17"

"18","ANDER_","18"

"18","liwenhan","19"

"18","xhl_leo","20"

"18","woden","21"

"18","wangchen101","22"

"18","Quantum","23"

"18","protractor","23"

"18","fuyixiang","23"

"18","lihaobei","23"

"18","zhangzexi","23"

"19","KSCD_","1"

"19","aaa","2"

"19","Pigsyy","3"

"19","rengpuiyang","4"

"19","zrl123456","5"

"19","lzx","6"

"19","handezheng","7"

"19","Dtw","8"

"19","quyuan","8"

"19","cybrex","8"

"19","gctiruct","8"

"19","Little_Cck","8"

"19","Quantum","8"

"19","Jtwang1","8"

"19","WF_wxl","8"

"19","star_field","8"

"19","protractor","8"

"19","xhl_leo","8"

"19","__vector__","8"

"20","wfirstzhang","1"

"20","Ame_wiki","2"

"20","taccat","3"

"20","ANDER_","4"

"20","wyz","5"

"20","keep_shining","6"

"20","ruiqi2012","7"

"20","Jtwang1","8"

"20","Little_Cck","9"

"20","star_field","10"

"20","wangchen101","11"

"20","wwwzzzlll","12"

"20","xhl_leo","13"

"20","CZMM","14"

"20","WF_wxl","15"

"20","fuyixiang","16"

"20","___W___Z___L___PVP","17"

"20","OPEC","18"

"20","woden","19"

"20","liwenhan","19"

"22","_awa_wangjiawen","1"

"22","Jtwang1","2"

"22","star_field","3"

"22","wyz","4"

"22","liwenhan","5"

"22","fuyixiang","6"

"22","wangchen101","7"

"22","WF_wxl","8"

"22","quyuan","9"

"22","ANDER_","10"

"22","ruiqi2012","11"

"22","Little_Cck","12"

"22","wfirstzhang","13"

"22","keep_shining","14"

"22","taccat","15"

"22","woden","16"

"22","yzs","17"

"23","protractor","1"

"23","taccat","2"

"23","zxh_qwq","3"

"23","ANDER_","4"

"23","quyuan","5"

"23","wangchen101","6"

"23","ruiqi2012","7"

"23","OPEC","8"

"23","liwenhan","9"

"23","xhl_leo","10"

"23","S08577","11"

"23","tianruihan","12"

"23","xuyunao","13"

"23","wfirstzhang","14"

"23","Jtwang1","15"

"23","fuyixiang","16"

"23","wyz","17"

"23","woden","18"

"23","Quantum","19"

"23","WF_wxl","20"

"23","Little_Cck","21"

"23","cybrex","22"

"23","Ame_wiki","23"

"23","keep_shining","23"

"23","yzs","23"

"23","Base_ring_tree","23"

"23","handezheng","23"

"24","YangShangLin","1"

"25","wyz","1"

"25","yzs","2"

"25","zxh_qwq","3"

"25","Jtwang1","4"

"25","_awa_wangjiawen","5"

"25","taccat","6"

"25","Quantum","7"

"25","handezheng","8"

"25","Dtw","9"

"25","woden","10"

"25","protractor","11"

"25","cybrex","12"

"25","fuyixiang","13"

"25","ruiqi2012","14"

"25","OPEC","15"

"25","WF_wxl","16"

"25","wangchen101","17"

"25","Little_Cck","17"

"25","ANDER_","17"

"25","quyuan","17"

"25","keep_shining","17"

"25","tianruihan","17"

"25","INnoVation__","17"

"27","wfyz57","1"

"27","wfyz32","2"

"27","wfyz154","3"

"27","wfyz38","4"

"27","wfyz34","5"

"27","wfyz6","6"

"27","wfyz21","7"

"27","wfyz33","8"

"27","wfyz46","9"

"27","wfyz58","10"

"27","wfyz45","11"

"27","zibunlen","12"

"27","wfyz140","13"

"27","wfyz28","14"

"27","wfyz64","15"

"27","wfyz60","16"

"27","wfyz65","17"

"27","wfyz41","18"

"27","wfyz37","19"

"27","wfyz44","20"

"27","wfyz113","21"

"27","wfyz79","22"

"27","wfyz84","23"

"27","wfyz51","24"

"27","wfyz43","25"

"27","wfyz13","26"

"27","wfyz77","27"

"27","wfyz16","28"

"27","wfyz54","29"

"27","wfyz97","30"

"27","wfyz42","31"

"27","wfyz103","32"

"27","wfyz94","33"

"27","wfyz87","34"

"27","wfyz68","35"

"27","wfyz86","36"

"27","wfyz55","37"

"27","wfyz151","38"

"27","wfyz69","39"

"27","wfyz76","40"

"27","wfyz128","41"

"27","wfyz85","42"

"27","wfyz4","43"

"27","wfyz106","44"

"27","wfyz62","45"

"27","wfyz150","46"

"27","wfyz75","47"

"27","wfyz117","48"

"27","wfyz100","49"

"27","wfyz1","50"

"27","wfyz24","51"

"27","wfyz66","52"

"27","wfyz27","53"

"27","wfyz162","54"

"27","wfyz25","55"

"27","wfyz47","56"

"27","wfyz26","57"

"27","wfyz96","58"

"27","wfyz136","59"

"27","wfyz30","60"

"27","wfyz40","61"

"27","wfyz7","62"

"27","wfyz166","63"

"27","wfyz50","64"

"27","wfyz131","65"

"27","wfyz116","66"

"27","wfyz3","67"

"27","wfyz155","68"

"27","wfyz104","69"

"27","wfyz49","70"

"27","wfyz112","71"

"27","wfyz125","72"

"27","wfyz121","73"

"27","wfyz105","74"

"27","wfyz119","75"

"27","wfyz132","76"

"27","wfyz63","77"

"27","wfyz91","78"

"27","wfyz70","79"

"27","wfyz167","80"

"27","wfyz80","81"

"27","wfyz92","82"

"27","wfyz61","83"

"27","wfyz22","84"

"27","wfyz18","85"

"27","wfyz138","86"

"27","wfyz81","87"

"27","wfyz110","88"

"27","wfyz53","89"

"27","wfyz109","90"

"27","wfyz118","91"

"27","wfyz144","92"

"27","wfyz135","93"

"27","wfyz39","94"

"27","wfyz5","95"

"27","wfyz93","96"

"27","wfyz139","97"

"27","wfyz11","98"

"27","wfyz108","99"

"27","wfyz78","100"

"27","wfyz89","101"

"27","wfyz2","102"

"27","wfyz161","103"

"27","wfyz141","104"

"27","wfyz19","105"

"27","wfyz129","106"

"27","wfyz160","107"

"27","wfyz17","108"

"27","wfyz157","109"

"27","wfyz111","110"

"27","wfyz133","111"

"27","wfyz168","112"

"27","wfyz74","113"

"27","wfyz82","114"

"27","wfyz143","115"

"27","wfyz107","116"

"27","wfyz145","117"

"27","wfyz142","118"

"27","wfyz130","119"

"27","wfyz146","120"

"27","wfyz126","121"

"27","wfyz123","122"

"27","wfyz90","123"

"27","wfyz98","124"

"27","wfyz102","125"

"27","wfyz120","126"

"27","wfyz67","127"

"27","wfyz35","128"

"27","wfyz153","129"

"27","wfyz83","130"

"27","wfyz114","131"

"27","wfyz122","132"

"27","wfyz95","133"

"27","wfyz156","134"

"27","wfyz59","135"

"27","wfyz124","136"

"27","wfyz72","137"

"27","wfyz48","138"

"27","wfyz115","139"

"27","wfyz31","140"

"27","wfyz127","141"

"27","wfyz149","142"

"27","wfyz164","143"

"27","wfyz73","144"

"27","wfyz134","145"

"27","wfyz159","146"

"27","wfyz8","147"

"27","wfyz148","148"

"27","wfyz29","149"

"27","wfyz99","150"

"27","wfyz137","151"

"27","wfyz101","152"

"27","wfyz152","153"

"27","wfyz71","154"

"27","wfyz147","155"

"27","wfyz165","156"

"27","wfyz52","156"

"27","wfyz158","156"

"27","wfyz88","156"

"27","wfyz163","156"

"27","YangShangLin","156"

"28","alt","1"

"28","Anonymous","2"

"29","Anonymous","1"

"29","zibunlen","2"

"30","Jtwang1","1"

"30","ANDER_","2"

"30","Little_Cck","3"

"30","Gejunyi","4"

"30","fuyixiang","5"

"30","quyuan","6"

"30","ruiqi2012","7"

"30","woden","8"

"30","Lin_Yi_Rui","9"

"30","WF_wxl","10"

"30","wangchen101","11"

"30","wfirstzhang","12"

"30","OPEC","13"

"31","wyz","1"

"31","taccat","2"

"31","wfirstzhang","3"

"31","WF_wxl","4"

"31","wangchen101","5"

"31","Jtwang1","6"

"31","ANDER_","7"

"31","fuyixiang","8"

"31","Little_Cck","9"

"31","zxh_qwq","10"

"31","star_field","11"

"31","Lin_Yi_Rui","12"

"31","yuanjingxuan","13"

"31","lijunda","13"

"31","Ame_wiki","13"

"32","zxh_qwq","1"

"32","ANDER_","2"

"32","taccat","3"

"32","Ame_wiki","4"

"32","wyz","5"

"32","ruiqi2012","6"

"32","quyuan","7"

"32","wfirstzhang","8"

"32","star_field","9"

"32","Little_Cck","10"

"32","woden","11"

"32","WF_wxl","12"

"32","Gejunyi","13"

"32","Jtwang1","14"

"32","Lin_Yi_Rui","15"

"32","xhl_leo","16"

"32","lihaobei","17"

"32","wangchen101","18"

"32","cybrex","18"

"33","zxh_qwq","1"

"33","alt","2"

"33","taccat","3"

"33","wyz","4"

"33","ANDER_","5"

"33","quyuan","6"

"33","FXLIR","7"

"33","___W___Z___L___PVP","8"

"33","WZP","9"

"33","lxc123456","10"

"33","Gejunyi","11"

"33","Jtwang1","12"

"33","Base_ring_tree","13"

"33","WF_wxl","14"

"33","zhaozihan_123456","15"

"33","lihaobei","16"

"33","liwenhan","17"

"33","wangchen101","18"

"33","zhangyining2357","19"

"33","star_field","20"

"33","Lin_Yi_Rui","21"

"33","ruiqi2012","22"

"33","woden","23"

"33","fuyixiang","23"

"33","cybrex","23"

"34","FXLIR","1"

"34","lhj","2"

"34","gaoyunhan","3"

"34","red_wood","4"

"34","ojzbh0011","5"

"34","wfandy","6"

"34","lihaoze2012","7"

"34","zhaozihan_123456","8"

"34","mingjun666","9"

"34","xyx0011","10"

"34","lichengze","11"

"34","liuyian","12"

"34","WEZ","13"

"34","mengjh","14"

"34","songyitong","15"

"34","zhangchao123","16"

"34","FuShulong","17"

"34","hsliujunxi","18"

"34","lyw08128","19"

"34","kyx","20"

"34","XWD_YS","21"

"34","FengQiYunYong","22"

"34","WF_wxl","23"

"34","fuyixiang","23"

"34","Little_Cck","23"

"35","Ame_wiki","1"

"35","zrl123456","2"

"35","ruiqi2012","3"

"35","zxh_qwq","4"

"35","ANDER_","5"

"35","woden","6"

"35","wyz","7"

"35","wfirstzhang","8"

"35","taccat","9"

"35","quyuan","10"

"35","Little_Cck","11"

"35","Gejunyi","12"

"35","fuyixiang","13"

"35","Jtwang1","14"

"35","Lin_Yi_Rui","15"

"35","WF_wxl","16"

"35","star_field","17"

"36","FiraCode","1"

"36","octopus_eats_cat","2"

"36","star_field","3"

"36","Gejunyi","4"

"36","quyuan","5"

"36","Ame_wiki","5"

"36","wyz","5"

"36","WF_wxl","5"

"36","Little_Cck","5"

"36","woden","5"

"36","ruiqi2012","5"

"36","Jtwang1","5"

"36","fuyixiang","5"

"36","ANDER_","5"

"36","Lin_Yi_Rui","5"

"36","zxh_qwq","5"

"36","zrl123456","5"

"38","taccat","1"

"38","Ame_wiki","2"

"38","Jtwang1","3"

"38","Little_Cck","4"

"38","zrl123456","5"

"38","quyuan","6"

"38","S08577","7"

"38","woden","8"

"38","WF_wxl","9"

"38","ruiqi2012","10"

"38","fuyixiang","11"

"38","ANDER_","12"

"38","Lin_Yi_Rui","13"

"39","zrl123456","1"

"39","Gejunyi","2"

"39","Ame_wiki","3"

"39","quyuan","4"

"39","Jtwang1","5"

"39","WF_wxl","6"

"39","ruiqi2012","7"

"39","ANDER_","8"

"39","woden","9"

"39","Lin_Yi_Rui","10"

"39","Little_Cck","11"

"39","S08577","12"

"39","fuyixiang","13"

"39","taccat","14"

"39","wfirstzhang","15"

"40","zrl123456","1"

"40","wfirstzhang","2"

"40","quyuan","3"

"40","taccat","4"

"40","Lin_Yi_Rui","5"

"40","fuyixiang","6"

"40","Little_Cck","7"

"40","ruiqi2012","8"

"41","ANDER_","1"

"41","WF_wxl","2"

"41","Lin_Yi_Rui","3"

"41","wfirstzhang","4"

"41","fuyixiang","5"

"41","ruiqi2012","6"

"41","woden","7"

"41","Jtwang1","8"

"41","Little_Cck","9"

"41","quyuan","10"

"41","Gejunyi","11"

"42","zrl123456","1"

"42","zxh_qwq","2"

"42","Ame_wiki","3"

"42","wyz","4"

"42","Little_Cck","5"

"42","Gejunyi","6"

"42","taccat","7"

"42","ANDER_","8"

"42","ruiqi2012","9"

"42","quyuan","10"

"42","Jtwang1","11"

"42","S08577","12"

"42","woden","13"

"42","wfirstzhang","14"

"42","WF_wxl","15"

"42","fuyixiang","16"

"43","xyh13793601192","1"

"43","tths01y","2"

"43","liuruixvan","3"

"43","weiyubo","4"

"43","cybrex","5"

"43","userdbx","6"

"43","yile2014","7"

"43","abcd123456","8"

"43","lbz___","9"

"43","jfy2015","10"

"43","xiaorui13","11"

"44","wfandy","1"

"44","lbz___","2"

"44","weiyubo","3"

"44","xyh13793601192","4"

"44","Zhr_002033","5"

"44","userdbx","6"

"44","liuruixvan","7"

"44","tths01y","8"

"44","jinkuner","9"

"44","yile2014","10"

"44","abcd123456","11"

"44","python2","12"

"44","jfy2015","13"

"44","lvfufu","14"

"45","Ame_wiki","1"

"45","ANDER_","2"

"45","quyuan","3"

"45","ruiqi2012","4"

"45","Jtwang1","5"

"45","woden","6"

"45","Gejunyi","7"

"45","zxh_qwq","8"

"45","wyz","9"

"45","Lin_Yi_Rui","10"

"45","wfirstzhang","11"

"45","Base_ring_tree","12"

"45","lihaobei","13"

"46","zxh_qwq","1"

"46","ANDER_","2"

"46","wfirstzhang","3"

"46","Gejunyi","4"

"46","taccat","5"

"46","Little_Cck","6"

"46","Jtwang1","7"

"46","wyz","8"

"46","woden","9"

"46","ruiqi2012","10"

"46","star_field","11"

"46","fuyixiang","12"

"46","___W___Z___L___PVP","13"

"46","lvxinyu001","14"

"46","Fisher_OvO","15"

"46","rain_x3","16"

"46","lxc123456","17"

"46","hatred","18"

"46","FXLIR","19"

"46","lihaobei","20"

"46","Lin_Yi_Rui","21"

"46","Alan5","22"

"46","WF_wxl","23"

"46","liwenhan","24"

"46","Base_ring_tree","25"

"46","xvyingxiao","26"

"47","wfandy","1"

"47","Zhr_002033","2"

"47","ojzbh0011","3"

"47","mengjh","4"

"47","zm0402","5"

"47","God_nodog_lhj","6"

"47","lihaoze2012","7"

"47","lvfufu","8"

"47","lichengze","9"

"47","gaoyunhan","10"

"47","ABCDEFG","11"

"47","wenhualjx","12"

"47","zhangchao","13"

"47","WEZ12345","14"

"47","JohnSmith","15"

"48","wyz","1"

"48","ANDER_","2"

"48","Gejunyi","3"

"48","wfirstzhang","4"

"48","zxh_qwq","5"

"48","woden","6"

"48","Jtwang1","7"

"48","Ame_wiki","8"

"48","xhl_leo","9"

"48","FXLIR","10"

"48","WF_wxl","11"

"48","tanzicheng","12"

"48","ruiqi2012","13"

"48","Lin_Yi_Rui","14"

"48","quyuan","15"

"48","___W___Z___L___PVP","16"

"48","Little_Cck","17"

"48","taccat","18"

"48","lihaobei","19"

"48","xvyingxiao","20"

"48","shuxuelianxice","21"

"48","Base_ring_tree","22"

"48","keep_shining","23"

"48","zhangyining2357","24"

"48","rain_x3","25"

"48","zhaozihan_123456","26"

"48","hatred","27"

"48","hai_ker","28"

"48","lxc123456","29"

"48","lvxinyu001","30"

"48","liuxiaolei","31"

"48","zyq777","32"

"48","fuyixiang","33"

"49","wyz","1"

"49","ANDER_","2"

"49","Gejunyi","3"

"49","taccat","4"

"49","Ame_wiki","5"

"49","woden","6"

"49","Little_Cck","7"

"49","Jtwang1","8"

"49","ruiqi2012","9"

"49","WF_wxl","10"

"49","keep_shining","11"

"49","wfirstzhang","12"

"49","fuyixiang","13"

"49","shuxuelianxice","14"

"49","Lin_Yi_Rui","15"

"49","xhl_leo","16"

"49","lihaobei","17"

"49","quyuan","18"

"49","zxh_qwq","19"

"50","tths01y","1"

"50","xyh13793601192","2"

"50","yile2014","3"

"50","Chansonnier","4"

"50","lbz___","5"

"50","jinkuner","6"

"50","python2","7"

"50","userdbx","8"

"50","majingyi","9"

"50","henaiyi","10"

"50","MLK_NS","11"

"50","xyx0011","12"

"50","jfy2015","13"

"50","yulinjiang","14"

"50","luyunqing123","15"

"50","WXY","16"

"50","Zsz141208","17"

"50","mushuaiyu","18"

"50","lirunyang","19"

"50","chenjia2013","20"

"50","xiaorui13","21"

"50","zmy6658","22"

"50","lujiadashao","23"

"50","dongxiawen","24"

"50","fyc_6","25"

"50","trues","26"

"50","kunqi2014","27"

"50","WXQyxl88","28"

"51","taccat","1"

"51","wyz","2"

"51","star_field","3"

"51","ANDER_","4"

"51","Jtwang1","5"

"51","Little_Cck","6"

"51","zxh_qwq","7"

"51","FXLIR","8"

"51","woden","9"

"51","___W___Z___L___PVP","10"

"51","Base_ring_tree","11"

"51","tanzicheng","12"

"51","rain_x3","13"

"51","lvxinyu001","14"

"51","WF_wxl","15"

"51","hatred","16"

"51","lihaobei","17"

"51","wfirstzhang","18"

"51","fuyixiang","19"

"51","xhl_leo","20"

"51","ruiqi2012","21"

"51","Lin_Yi_Rui","22"

"51","quyuan","23"

"52","wfandy","1"

"52","zyq777","2"

"52","zhaozihan_123456","3"

"52","qcd_fisher","4"

"52","zangjh666","5"

"52","Alan5","6"

"52","Zhr_002033","7"

"52","lvfufu","8"

"52","JohnSmith","9"

"52","mengjh","10"

"52","God_nodog_lhj","11"

"52","ojzbh0011","12"

"52","zm0402","13"

"52","lichengze","14"

"52","mingjun123","15"

"52","ABCDEFG","16"

"52","gaoyunhan","17"

"52","whljx_666","18"

"52","songyitong123","19"

"52","WEZ11111","20"

"53","quyuan","1"

"53","wfirstzhang","1"

"53","ANDER_","1"

"53","ruiqi2012","1"

"53","WF_wxl","1"

"53","___W___Z___L___PVP","1"

"53","Jtwang1","1"

"53","taccat","1"

"53","wyz","1"

"53","rain_x3","1"

"53","Base_ring_tree","1"

"53","hai_ker","1"

"53","Fisher_OvO","1"

"54","taccat","1"

"54","ANDER_","2"

"54","quyuan","3"

"54","Little_Cck","4"

"54","woden","5"

"54","Jtwang1","6"

"54","ruiqi2012","7"

"54","zxh_qwq","8"

"54","WF_wxl","9"

"54","wyz","10"

"54","wfirstzhang","11"

"54","Lin_Yi_Rui","12"

"54","star_field","13"

"54","fuyixiang","14"

"54","lihaobei","15"

"55","ANDER_","1"

"55","Ame_wiki","2"

"55","star_field","3"

"55","zxh_qwq","4"

"55","wyz","5"

"55","Little_Cck","6"

"55","Gejunyi","7"

"55","taccat","8"

"55","Lin_Yi_Rui","9"

"55","woden","10"

"55","fuyixiang","11"

"55","WF_wxl","12"

"55","Jtwang1","13"

"55","lihaobei","14"

"55","wfirstzhang","15"

"55","lyw08128","16"

"56","YangShangLin","1"

"56","2yp","2"

书单

avatar ryp 2026-09-11 13:33:37

Introduction to Mathematical Statistics

Forecasting: Principles and Practice

更新的新 Rating 系统!

avatar ryp 2026-09-05 13:09:34

如题,我们更新到了更先进的 Glicko-2 Rating 系统!

目测现在大家的 Rating 都很合理。

同时,Glicko-2 给我们带来了除了 Rating 外的两个参数:RD 和 Vol。

RD 可以理解为方差,一个人的 Perf 有 95% 的可能在 [Rating - 2RD, Rating + 2RD] 内。

Vol 表示这个人最近表现的波动程度。Vol 大的人,最近表现的特别好,或者特别烂。

我们还添加了一个 Rating 分布图像,在每个人的首页,Rating 曲线下面可以看到。非常有趣。

另外,格言已经回滚。但由于备份文件损坏,回滚到的是 7 月 4 日下午 14:00 的版本。

论文地址:Glicko-2 PDF

代码:

function g2_g($x) {
        return 1 / sqrt(1 + 3 * $x * $x / M_PI / M_PI);
}

function g2_E($u, $uj, $pj) {
        return 1 / (1 + exp(-g2_g($pj) * ($u - $uj)));
}

function g2_f($x, $delta, $phi, $v, $a, $tau) {
        return (exp($x) * ($delta * $delta - $phi * $phi - $v - exp($x))
                / (2 * pow($phi * $phi + $v + exp($x), 2)))
                - ($x - $a) / $tau / $tau;
}

function calcRatingGlicko2($standings, &$new_rating, &$new_rd, &$new_vol) {
        $TAU = 0.6;

        $n = count($standings);
        $rating = array();
        $rd = array();
        $vol = array();
        for ($i = 0; $i < $n; ++$i) {
                $rating[$i] = ($standings[$i][2][1] - 1500) / 173.7178;
                $rd[$i] = $standings[$i][2][2] / 173.7178;
                $vol[$i] = ($standings[$i][2][3] ?? 0.06);
                if ($vol[$i] <= 0) $vol[$i] = 0.06;
        }

        if ($n == 1) {
                $new_rating[0] = $rating[0];
                $new_rd[0] = $rd[0];
                $new_vol[0] = $vol[0];
        }

        for ($i = 0; $i < $n; $i++) {
                $delta = 0;
                $v = 0;

                for ($j = 0; $j < $n; $j++) if ($i != $j) {
                        $s = 0;
                        if ($standings[$i][3] > $standings[$j][3])
                                $s = 0;
                        else if ($standings[$i][3] == $standings[$j][3])
                                $s = 0.5;
                        else
                                $s = 1;
                        $e = g2_E($rating[$i], $rating[$j], $rd[$j]);
                        $v += pow(g2_g($rd[$j]), 2) * $e * (1 - $e);
                        $delta += g2_g($rd[$j]) * ($s - $e);
                }

                if ($v < 1e-20) $v = 1e-20;
                $v = 1 / $v;
                $delta *= $v;

                if ($delta > 10) $delta = 10;
                if ($delta < -10) $delta = -10;

                $a = 2 * log($vol[$i]);

                $A = $a;
                if ($delta * $delta > $rd[$i] * $rd[$i] + $v)
                        $B = log($delta * $delta - $rd[$i] * $rd[$i] - $v);
                else {
                        for ($k = 1; $k < 100 && g2_f($a - $k * $TAU, $delta, $rd[$i], $v, $a, $TAU) < 0; ++$k)
                                ;
                        $B = $a - $k * $TAU;
                }

                $fa = g2_f($A, $delta, $rd[$i], $v, $a, $TAU);
                $fb = g2_f($B, $delta, $rd[$i], $v, $a, $TAU);
                while ($fa * $fb > 0) {
                        $B -= $TAU;
                        $fb = g2_f($B, $delta, $rd[$i], $v, $a, $TAU);
                }

                $k = 0;
                while (abs($B - $A) > 1e-6 && $k < 100) {
                        if (!is_finite($fa) || !is_finite($fb)) break;
                        if (abs($fa - $fb) < 1e-15) break;
                        $C = $A + ($A - $B) * $fa / ($fb - $fa);
                        $fc = g2_f($C, $delta, $rd[$i], $v, $a, $TAU);
                        if ($fc * $fb <= 0) {
                                $A = $B;
                                $fa = $fb;
                        }
                        else
                                $fa = $fa / 2;
                        $B = $C;
                        $fb = $fc;
                        ++$k;
                }

                $new_vol[$i] = exp(($A + $B) / 4);
                $new_rd[$i] = sqrt($rd[$i] * $rd[$i] + $new_vol[$i] * $new_vol[$i]);
                $new_rd[$i] = 1 / sqrt(1 / $v + 1 / $new_rd[$i] / $new_rd[$i]);
                $new_rating[$i] = $rating[$i] + $delta / $v * $new_rd[$i] * $new_rd[$i];
                $new_rating[$i] = $new_rating[$i] * 173.7178 + 1500;
                $new_rd[$i] *= 173.7178;
        }
}

原帖:

WyOJ 现在切换到了新的 和 Codeforces 算法相同的 Rating 系统。

具体算法:

写的很着急,如果有问题可以跟帖。

同时,所有 Rating 已经重新计算。现在的 Rating 应该是非常合理的。

新的 Rating 计算函数,采用 Codeforces 算法。

具体算法是:

设参赛前每人的 Rating 是 $r_i$, 比赛的最终排名是 $R_i$,那么

首先承认:在先前 Rating 的基础上,i 战胜 j 的概率是 $P_ij = \dfrac{1}{1 + 10^{\dfrac{r_j - r_i}{400}}}$

立即得到 i 的期望排名 $E_i = \sum_{j \ne i} P_{ji} + 1$

显然,真正的排名和期望排名之间的差距,就是 rating delta 的来源。

我们取 $S_i = \sqrt{E_i R_i}$,二分出一个 u 使得计算出的 $S_i = E_i$,rating 应当向 u 的方向变化。

取 $\delta = \dfrac{u + r_i}{2}$,随后我们对 $\delta$ 进行微调。

第一次微调:取 $d = \dfrac{-1 - \sum_{i} \delta_i}{n}$,将所有人的 \delta 加上 d。

第二次微调:取排名前 $s = \min{n, 4\sqrt n}$ 高的人,让这些人的 rating 变化量之和为零,得到 d。

随后再将所有人的 $\delta$ 加上 d,得到最终的 rating。

/*
 * 新的 Rating 计算函数,采用 Codeforces 算法。
 * 具体算法是:
 * 设参赛前每人的 Rating 是 r_i, 比赛的最终排名是 R_i,那么
 * 首先承认:在先前 Rating 的基础上,i 战胜 j 的概率是 P_ij = \dfrac{1}{1 + 10^{\dfrac{r_j - r_i}{400}}}
 * 立即得到 i 的期望排名 E_i = \sum_{j \ne i} P_ji + 1
 * 显然,真正的排名和期望排名之间的差距,就是 rating delta 的来源。
 * 我们取 S_i = \sqrt{E_i R_i},二分出一个 u 使得计算出的 S_i = E_i,rating 应当向 u 的方向变化。
 * 取 \delta = \dfrac{u + r_i}{2},随后我们对 \delta 进行微调。
 * 第一次微调:取 d = \dfrac{-1 - \sum_{i} \delta_i}{n},将所有人的 \delta 加上 d。
 * 第二次微调:取排名前 s = \min{n, 4\sqrt n} 高的人,让这些人的 rating 变化量之和为零,得到 d。
 * 随后再将所有人的 \delta 加上 d,得到最终的 rating。
 * */
function calcRatingX($standings) {
        $n = count($standings);
        $rating = array();
        $delta = array();
        $ds = 0;
        for ($i = 0; $i < $n; ++$i)
                $rating[$i] = $standings[$i][2][1];
        for ($i = 0; $i < $n; ++$i) {
                $l = 0;
                $r = 4000;
                while ($l <= $r) {
                        $mid = ($l + $r) / 2;
                        $e = 1;
                        for ($j = 0; $j < $n; ++$j)
                                if ($i != $j)
                                        $e += 1 / (1 + pow(10, ($mid - $rating[$j]) / 400));
                        $s = sqrt($e * ($i + 1));
                        if ($e <= $s)
                                $r = $mid - 1;
                        else
                                $l = $mid + 1;
                }
                $delta[$i] = (($l + $r) / 2 - $rating[$i]) / 2;
                $ds += $delta[$i];
        }
        $ds = (-1 - $ds) / $n;
        for ($i = 0; $i < $n; ++$i)
                $delta[$i] += $ds;
        $s = min($n, 4 * sqrt($n));
        $ds = 0;
        for ($i = 0; $i < $s; ++$i)
                $ds += $delta[$i];
        $ds = -$ds / $s;
        for ($i = 0; $i < $s; ++$i)
                $delta[$i] += $ds;
        $R = array();
        for ($i = 0; $i < $n; ++$i)
                $R[$i] = ceil($rating[$i] + $delta[$i]);
        return $R;
}

关于比赛

avatar ryp 2026-09-04 12:50:08
  • 已经可以在比赛开始后报名了。
  • Rating 曲线已经修复
  • 20260820CSP-S 专题模拟赛2 的错误已经修复

勋章酷炫更新!

avatar ryp 2026-09-02 12:26:41

显然,我的勋章是现在全站最帅的,没有之一。

如果没有刷新的话,可以按 ctrl-shift-R 强制刷新一次。

我们添加了三种特别勋章:Pro、Max、Ultra。我现在是 Ultra。

并且,现在,组管理员可以给每个组成员颁发勋章了!在“组管理”界面,可以找到入口。

组管理员添加的勋章,只能是普通颜色,不能是 Pro、Max、Ultra 这种炫酷颜色。后三者只能由管理员颁发。

如果想要勋章,可以在本帖子下跟帖!

但是十分尴尬的是,更新勋章的时候不小心把所有人的格言都设成了 deepblue。。回滚数据库有点麻烦。大家自己重新更新吧。

-- upd 20:43

增加了 Pro Max,本来打算垄断的,结果被抢注了,不值钱了。Pro Max 不给注册!!!

欢迎大家上传自己的题目以及博客系统说明

avatar ryp 2026-08-27 18:17:10

WyOJ 小资历可能不知道,OJ 的普通用户也是可以上传自己的题目的。

具体方法是:

  • 点击右上角的用户名,打开“组管理”菜单。

  • 在“我的组”中,每个普通用户可以创建一个独属于自己的组,但仅能创建一个。

  • 创建完毕后,点击“管理”,进入组管理界面。

可以在左侧进行“编辑昵称”、“成员管理”、“题目管理”、“空间管理”。

WyOJ 组的管理很简单:每个组有管理员(也就是超级用户)和普通成员,管理员可以创建新题,而普通成员不能。

怎么上传自己的题目

创建题目的方法:在“题库”界面中,点入“团队 group_XXX 题目”,就可以进入组题目的列表。页面最下方点击“添加新题”即可。

创建完题目后,点进去并进入管理界面。在“编辑”标签页可以编辑题面,“数据”界面可以上传数据。

WyOJ 的题目数据配置需要一个 problem.conf 配置文件。这个配置文件,可以通过运行 https://ryp.wyoj.org.cn/post/29 中的 Python 脚本来创建。在数据目录下,只需要输入时间和空间限制,就可以得到一个 problem.conf 文件。然后将所有数据包括这个文件打包成 ZIP 压缩包,再点击“数据”中的上传数据。

如果你不想自己创建 problem.conf,也有一种更直观但是可能存在一些小问题的方法:首先按照同样的方法上传完数据,然后在“数据”标签页点击“试题配置”。对比函数一般填“单行字符串序列(wcmp)”即可。额外数据点数和样例数量,都填写 0。

数据上传完后,一定要点击“校验配置并同步”,WyOJ 才会真正配置你的题目。

还有一些小问题

组中题目默认是私密的,也就是只有组中的人能看见。管理员可以将其改为“公开”,于是所有人都可以看到这道题,不管是否在组中。

当然,WyOJ 管理员拥有组的全部权限,即使你并没有把我加进组中。

题目管理中,你可以看到已经创建的题目。

空间管理中,你可以看到所有题目空间的总和。这个空间指的是题目数据压缩包的空间之和。

每个组创建题目的数量是没有限制的,但是空间有限。超出了空间就无法再创建新的题目,只能选择删除旧题或者和管理员申请扩容。一般管理员是会同意的。

每个组默认的空间配额是 500MB,解压后大概能到数个 G。对于几乎所有人,这是足够的。整个 WyOJ 现在也只有 10G 的题目数据压缩包。

我们非常欢迎大家上传自己的题目,并相互分享,交流做法。

大家可以将做法发到博客系统上。

当前,必须联系管理员手动开通后才能使用博客系统。具体联系方法很简单,直接在 WyOJ 激活群中 @ryp 联系处理即可。

或者也可以私聊加 ryp:QQ 2681243014。

ryp Avatar
我拒绝零假设
活跃度 近 53 周
查看全年