力扣竞赛勋章 | 排名分数计算脚本
29082
2021.08.08
2022.08.07
发布于 未知归属地

简介

因为常常看到有人询问Guardian/Knight的勋章怎么得,在力扣官方的帖子中,我们看到如下这句话:竞赛积分 ≥1600 的用户中,根据比例 5%、20%、75% 设定三档段位,段位每周比赛结束后计算一次

也就是说,Guardian是在>=1600分的所有人中,排名前5%的,而Knight是排名前25%;小于1600的人是不参与这个勋章排名计算的。

于是想到通过API之类的东西查询这个数据。然后就发现了这个项目,大佬做出了各种关于竞赛排名的查询的东西,包括每场比赛全球参与人数,你搜索的用户排百分之几之类的。但是我并没有找到类似Guardian多少分查询的东西。但是问题不大,从这个项目中的global_ranking_crawler.py小小的改动一下就好了。

示例

这是我刚刚跑完的结果:
Guardian 2158
Knight 1840

image.png

由于国内的勋章好像是根据国内的人数算的,所以与国外不同 (2021/08/08运行结果):
Guardian 2175 (全国排名627)
Knight 1843 (全国排名3137)
[注意] 这里的数仍然有一点儿小误差,因为第12551人,他的全国排名实际写的比这个数小。可能排行榜有一些奇奇怪怪的人被算进去。暂时没有处理这个小问题。

image.png

代码

新增了全国全球不同的查询!!!
GitHub地址

#!/usr/bin/env python3

import json
import requests

# 力扣目前勋章的配置
RATING = 1600
GUARDIAN = 0.05
KNIGHT = 0.25
# 查询的地址为全国还是全球?很关键
GLOBAL = False
# 二分查找的右端点(可自调)
RIGHT = 3000


class RankingCrawler:
    URL = 'https://leetcode.com/graphql' if GLOBAL else 'https://leetcode.cn/graphql'

    _REQUEST_PAYLOAD_TEMPLATE = {
        "operationName": None,
        "variables": {},
        "query":
r'''{
    localRanking(page: 1) {
        totalUsers
        userPerPage
        rankingNodes {
            currentRating
            currentGlobalRanking
        }
    }
}
''' if not GLOBAL else
r'''{
    globalRanking(page: 1) {
        totalUsers
        userPerPage
        rankingNodes {
            currentRating
            currentGlobalRanking
        }
    }
}
'''
    }

    def fetch_lastest_ranking(self, mode):
        l, r = 1, RIGHT
        retry_cnt = 0
        ansRanking = None
        while l < r:
            cur_page = (l + r + 1) // 2
            try:
                payload = RankingCrawler._REQUEST_PAYLOAD_TEMPLATE.copy()
                payload['query'] = payload['query'].replace('page: 1', 'page: {}'.format(cur_page))
                resp = requests.post(RankingCrawler.URL,
                    headers = {'Content-type': 'application/json'},
                    json = payload).json()

                resp = resp['data']['localRanking'] if not GLOBAL else resp['data']['globalRanking']
                # no more data
                if len(resp['rankingNodes']) == 0:
                    break
                if not mode:
                    top = int(resp['rankingNodes'][0]['currentRating'].split('.')[0])
                    if top < RATING:
                        r = cur_page - 1
                    else:
                        l = cur_page
                        ansRanking = resp['rankingNodes']
                else:
                    top = int(resp['rankingNodes'][0]['currentGlobalRanking'])
                    if top > mode:
                        r = cur_page - 1
                    else:
                        l = cur_page
                        ansRanking = resp['rankingNodes']

                print('The first contest current rating in page {} is {} .'.format(cur_page, resp['rankingNodes'][0]['currentRating']))
                retry_cnt = 0
            except:
                # print(f'Failed to retrieved data of page {cur_page}...retry...{retry_cnt}')
                retry_cnt += 1
        ansRanking = ansRanking[::-1]
        last = None
        if not mode:
            while ansRanking and int(ansRanking[-1]['currentRating'].split('.')[0]) >= 1600:
                last = ansRanking.pop()
        else:
            while ansRanking and int(ansRanking[-1]['currentGlobalRanking']) <= mode:
                last = ansRanking.pop()
        return last


if __name__ == "__main__":
    crawler = RankingCrawler()
    ans = crawler.fetch_lastest_ranking(0)
    n = int(ans['currentGlobalRanking'])
    guardian = crawler.fetch_lastest_ranking(int(GUARDIAN * n))
    knight = crawler.fetch_lastest_ranking(int(KNIGHT * n))
    if not GLOBAL:
        guardian['currentCNRanking'] = guardian['currentGlobalRanking']
        guardian.pop('currentGlobalRanking')
        knight['currentCNRanking'] = knight['currentGlobalRanking']
        knight.pop('currentGlobalRanking')

    print("Done!")
    print()
    print("目前全{}1600分以上的有{}人".format("球" if GLOBAL else "国",n))
    print("根据这个人数,我们得到的Guardian排名及分数信息为:{}".format(guardian))
    print("根据这个人数,我们得到的Knight排名及分数信息为:{}".format(knight))

写在最后

目前实现复杂度为o(logn)

有任何不对的地方或是更快算法的建议,欢迎指出!

评论 (62)