# 写在前面
目前 P-Rating 分值的主要问题在于,系统题库中难度分的设置并不是特别完善,尤其是旧题目被隐藏后很久没有启用过,现阶段难以估计其实际难度。这可能需要一定的时间来针对题库难度标级进行完善。
因此,目前计算得到的 P-Rating 分值仅供新同学参考。旧账号的分值会慢慢更新。
**同时也希望各位解题数达到 250 题的同学,平时多刷刷 Codeforces 的题目,对其题目的难度标级有一定的认识,并帮助我们完善系统题库中开放的题目难度。符合条件的同学可以在题库的题目查看页面当中,在右侧菜单找到“设置”栏,为题目配置难度分以及算法标签。**
对于洛谷和 AtCoder 上出现过的题目,难度分给定标准可以参考 [这篇博客](/blog/private/201931990228/p/39/view)。特别是洛谷上出现的题目,可以参考洛谷的标级以及博客内的分段表来判定难度分。
对于 Codeforces 的题目,直接取网站上的评分即可。
---
# P-Rating 第三版计算方法
这一版是第二版的迭代,主要将大量浮点运算的部分舍去,并使用 $<\tt{总分值}, \tt{题数限制}>$ 这样的数对来替代原本的增量辅助确定每一分段总分的方法。
此外,针对超出某一分段后的得分情况,从原本的类线性相关改为指数相关,过题数越多能够增加的分数会越少。
未超出的部分得分仍然与题数线性相关。
因此,现在这一方法针对单一分段而言,过题数(X 轴)与得分(Y 轴)的大致变化关系可见下图(这不代表实际的计算结果,仅表示变化趋势):
![1732827671809.png](/userfiles/blog-images/f1036101-5f09-4626-9e1b-fcaaf3e30c22.png)
计算方法(第三版)主要配置如下:
```java
private static final int[][] pRatingLimits = {
// 600 ~ 900
{120, 60},
{-1, -1},
{150, 50},
{180, 50},
// 1000 ~ 1200
{200, 45},
{210, 42},
{225, 40},
// 1300 ~ 1600
{233, 37},
{245, 35},
{250, 32},
{258, 30},
// 1700 ~ 2000
{263, 28},
{268, 26},
{271, 24},
{271, 22},
// 2100 ~ 2500
{271, 20},
{266, 18},
{259, 16},
{265, 15},
{289, 15},
// 2600 ~ 3000
{290, 14},
{290, 13},
{290, 12},
{290, 11},
{290, 10},
// 3100 ~ 3500
{320, 10},
{350, 10},
{380, 10},
{410, 10},
{440, 10}
};
private static final double pRatingStage2Increment = 0.6;
private static final double pRatingStage2Power = 0.73;
private int calculateProblemRating(Map<Integer, Integer> map) {
double rating = 0;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int score = entry.getKey(), count = entry.getValue();
if (score < 600 || score > 3500)
continue;
int id = score / 100 - 6;
double single = 1.0 * pRatingLimits[id][0] / pRatingLimits[id][1];
// stage 1
rating += Math.min(count, pRatingLimits[id][1]) * single;
// stage 2
rating += (Math.pow(Math.max(count - pRatingLimits[id][1], 0) + pRatingStage2Increment, pRatingStage2Power) - pRatingStage2Increment) * single;
}
return (int) rating;
}
```
---
# P-Rating 第二版计算方法(已废弃)
设置 P-Rating 的目的是衡量每位用户的做题情况,这个数值会随着总过题数的增加而增加。
但如果某一分段的题目做得特别多,在题数达到该分段题数预设值后,Rating 分的增加速度将会降低。
做难度较高的题目对 Rating 值的提升幅度会比难度低的题目更大。
目前设计的计算方法(第二版)主要配置如下:
```java
private static final int[] pRatingScoreAmountLimits = {
60, -1, // 600 ~ 700
55, 50, // 800 ~ 900
45, 42, 40, // 1000 ~ 1200
38, 36, 34, 32, // 1300 ~ 1600
30, 28, 26, 24, // 1700 ~ 2000
22, 21, 20, 19, 18, // 2100 ~ 2500
17, 16, 15, 14, 13, 12, 11, 10, 10, 10 // 2600 ~ 3500
};
private static final int pRatingScoreIncrementA = 1800;
private static final int pRatingScoreIncrementB = -400;
private static final double pRatingReductionRateBeforeExceedLimit = 0.6;
private static final double pRatingReductionRateAfterExceedLimit = 0.03;
private static final double pRatingFinalScoreScalingFactor = 0.1058;
private int calculateProblemRating(Map<Integer, Integer> map) {
double rating = 0;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int score = entry.getKey(), count = entry.getValue();
if (score < 600 || score > 3500)
continue;
int t = pRatingScoreAmountLimits[score / 100 - 6];
rating += pRatingReductionRateBeforeExceedLimit * ((double) Math.min(t, count) / t) * (score + pRatingScoreIncrementA);
rating += pRatingReductionRateAfterExceedLimit * Math.max(0, count - t) * (score + pRatingScoreIncrementB);
}
return (int) (rating * pRatingFinalScoreScalingFactor);
}
```
- `pRatingScoreAmountLimits` 数组表示每一分段的题数预设值。当用户该分段的题数未到达此预设值时,采用第一种方法计算分值;当用户题数超过了此预设值,则多出来的题目数量会采用第二种方法计算额外的分值。
- `pRatingScoreIncrementA` 表示第一种计算方法对于题目分值的增量。由于低分题每道得分也受限于题数预设值,因此该属性可用于缩小低分题与高分题的最大分值差别。
- `pRatingScoreIncrementB` 表示第二种计算方法对于题目分值的增量。第二种计算方法并不会受到题数预设值的影响,完全按照题目分与过题数的乘积再乘上某一浮点系数进行计算,因此此增量用于稍微扩大一些低分题与高分题的分值差别。
- `pRatingReductionRateBeforeExceedLimit` 表示第一种计算方法算入最终分值所乘上的系数。
- `pRatingReductionRateAfterExceedLimit` 表示第二种计算方法算入最终分值所乘上的系数。由于第二种计算方法并不会受到题数预设值的影响,因此目前暂定此系数为第一种方法系数的 $\dfrac 1 {20}$。
- `pRatingFinalScoreScalingFactor` 表示最终分值的放缩比例,用于控制分数最值(后面会将其结合进上面的两个系数当中)。
---
经过测算,目前每道题通过后对 Rating 的影响可见下表:
| 题目分 | 方法一 | 方法二 | 比例 |
| ------ | -------- | ------- | ------- |
| $600$ | $2.539$ | $0.635$ | $0.250$ |
| $800$ | $3.001$ | $1.270$ | $0.423$ |
| $900$ | $3.428$ | $1.587$ | $0.463$ |
| $1000$ | $3.950$ | $1.904$ | $0.482$ |
| $1100$ | $4.383$ | $2.222$ | $0.507$ |
| $1200$ | $4.761$ | $2.539$ | $0.533$ |
| $1300$ | $5.179$ | $2.857$ | $0.552$ |
| $1400$ | $5.643$ | $3.174$ | $0.562$ |
| $1500$ | $6.161$ | $3.491$ | $0.567$ |
| $1600$ | $6.745$ | $3.809$ | $0.565$ |
| $1700$ | $7.406$ | $4.126$ | $0.557$ |
| $1800$ | $8.162$ | $4.444$ | $0.544$ |
| $1900$ | $9.034$ | $4.761$ | $0.527$ |
| $2000$ | $10.051$ | $5.078$ | $0.505$ |
| $2100$ | $11.253$ | $5.396$ | $0.479$ |
| $2200$ | $12.091$ | $5.713$ | $0.472$ |
| $2300$ | $13.013$ | $6.031$ | $0.463$ |
| $2400$ | $14.032$ | $6.348$ | $0.452$ |
| $2500$ | $15.165$ | $6.665$ | $0.440$ |
| $2600$ | $16.430$ | $6.983$ | $0.425$ |
| $2700$ | $17.854$ | $7.300$ | $0.409$ |
| $2800$ | $19.467$ | $7.618$ | $0.391$ |
| $2900$ | $21.311$ | $7.935$ | $0.372$ |
| $3000$ | $23.439$ | $8.252$ | $0.352$ |
| $3100$ | $25.921$ | $8.570$ | $0.331$ |
| $3200$ | $28.855$ | $8.887$ | $0.308$ |
| $3300$ | $32.375$ | $9.205$ | $0.284$ |
| $3400$ | $33.010$ | $9.522$ | $0.288$ |
| $3500$ | $33.644$ | $9.839$ | $0.292$ |
后面可能会将第二种计算方法从目前的正比例函数改为指数函数相关。
绘图得关系如下:
![1732637222100.png](/userfiles/blog-images/7ed77a0e-0f39-43cb-b619-09dda7877d6e.png)