CCF-CSP 梯度求解 100分 千位高精+暴力求导

9月份打的了,当时考场上很紧张。一开始的思路确实是想用C++逐步模拟,但是实在是太麻烦了(

后来突然想到能不能直接定义求导呢?之前看过吴恩达的机器学习课程,里面在讲梯度的时候(下图),特意讲了梯度检验法(用数值求解验证解析求解的正确性,经典对拍思路)

下面是更详细的公式说明:

于是尝试了一下默认精度,发现只能通过部分样例。。(精度肯定不够,毕竟mod都是1e9+7了)

突然想到python有一个decimal高精库,于是赶紧使用py构造出字符串表达式(然后可以用exec动态赋值执行,非常方便,py大法好!!!)。由于是一道纯模拟题,输入规模非常小可以完全不需要考考虑时间复杂度。

下面是考场上写的代码:(用时171ms,9.484MB,100%通过)
(可以在这个链接里提交你的代码:计算机软件能力认证考试系统)

from decimal import *

getcontext().prec = 1000  # 设置精度为1000位小数

# 输入n和m
n, m = list(map(eval, input().split()))

# 输入表达式字符串
f_str = input()
items = f_str.split()  # 将表达式字符串按空格拆分为列表

st = []  # 初始化一个空栈
opts = {'+', '-', '*', '/'}  # 运算符集合

i = 0
while i < len(items):
    e = items[i]
    i += 1
    if e not in opts:  # 如果当前元素不是运算符
        st.append(e)  # 将其压入栈中
    else:
        e1 = st.pop()  # 弹出栈顶元素作为第一个操作数
        e2 = st.pop()  # 再弹出栈顶元素作为第二个操作数
        # 判断操作数是否为数字或以正负号开头的字符串,如果是则构造 Decimal 类型的字符串
        if e1[0].isdigit() or e1[0] in ['+', '-']:
            e1 = f"Decimal('{e1}')"
        if e2[0].isdigit() or e2[0] in ['+', '-']:
            e2 = f"Decimal('{e2}')"
        r = f'({e2}{e}{e1})'  # 构造运算表达式
        st.append(r)  # 将结果压入栈中

f = st[0]  # 获取最终的运算表达式


# 1e-901 当做我们的delta
delta = Decimal('0.' + '0' * 900 + '1')  # 定义一个极小的增量
mod = Decimal('1000000007')  # 模值

for x in range(m):
    line = input().split()  # 获取输入的一行数据并按空格分割
    i = eval(line[0])  # 第一个元素是索引值,将其转换为整数
    for y in range(1, len(line)):
        value = eval(line[y])  # 将每个元素转换为相应的数值
        exec(f'x{y}=Decimal({line[y]})')  # 为每个变量赋值为对应的 Decimal 类型的值
    y1 = eval(f)  # 计算运算表达式的值
    value = Decimal(line[i]) - delta  # 获取索引对应的值并减去一个极小的增量
    exec(f'x{i}=Decimal(line[i]) - delta')  # 对索引对应的值赋值为减去增量后的值
    y2 = eval(f)  # 重新计算运算表达式的值
    ans = (y1 - y2) / delta  # 计算差值并除以增量
    # 下面循环最多执行2次,复杂度是O(1)
    while not Decimal('0') <= ans < mod:  # 对结果进行取模操作,使其在 [0, mod) 范围内
        if ans < Decimal('0'):
            ans = ans % mod + mod  # 手动调整到 [0, mod) 区间
        else:
            ans = ans % mod
    print(round(ans, 0))  # 输出结果取整后的值

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值