ptan实战3 || ExperienceSource类

本文介绍PTAN库中ExperienceSource类的使用方法,通过创建简单的环境和智能体,演示了如何利用ExperienceSource和ExperienceSourceFirstLast类进行交互并收集经验。

ptan实战3 || ExperienceSource类

ExperienceSource类返回(s,a,r,done),需传入(env,agent),agent分为DQNAgent、PolicyAgent,agent类需要传入net、selector

ExperienceSourceFirstLast 类返回(s,a,r,s_)

该函数会自动让agent与env互动

  1. 环境的reset()被调用,得到初始状态
  2. agent从返回的状态中选择要执行的动作
  3. 执行step()方法,获得奖励和下一个状态s_
  4. s_传入agent得到下一个动作
  5. 返回状态s转移到s_获得的r和info
  6. 从第三步开始重复,一直到经验源被遍历完

注:steps_count=2 表示智能体会每次迭代2步,返回的 (s,a,r,s_) 中的r 不是立即奖励,而是 r+γr ;s_ 也不是执行一步后的状态,而是执行两步后的状态

import gym
import ptan
from typing import List, Optional, Tuple, Any

class ToyEnv(gym.Env):

    def __init__(self):
        super(ToyEnv, self).__init__()
        self.observation_space = gym.spaces.Discrete(n=5)   # 定义状态空间为discrete类型 范围0~4
        self.action_space = gym.spaces.Discrete(n=3)        # 定义动作空间为discrete类型 范围0~2
        self.step_index = 0

    def reset(self):    # 初始化状态,计数器置零
        self.step_index = 0
        return self.step_index

    def step(self, action):
        is_done = self.step_index == 10     # 计数器到10,游戏结束
        if is_done:     # 游戏结束
            # 返回(s,r,done,info)
            return self.step_index % self.observation_space.n, \
                   0.0, is_done, {}

        # 游戏未结束 返回(s_,r,done,info)
        self.step_index += 1
        return self.step_index % self.observation_space.n, \
               float(action), False, {}

# 无论观察是什么,都返回同一个动作的agent,该动作定义为 int 1
class DullAgent(ptan.agent.BaseAgent):

    def __init__(self, action: int):
        self.action = action

    # Optional[List]=None 表示默认为List类型,且可以为None;Tuple表示元组
    def __call__(self, observations: List[Any], state: Optional[List] = None) \
            -> Tuple[List[int], Optional[List]]:
        return [self.action for _ in observations], state

if __name__ == "__main__":
    env = ToyEnv()
    s = env.reset()
    print("env.reset() -> %s" % s)
    s = env.step(1)
    print("env.step(1) -> %s" % str(s))
    s = env.step(2)
    print("env.step(2) -> %s" % str(s))

    for _ in range(10):
        r = env.step(0)
        print(r)

    agent = DullAgent(action=1)
    print("agent:", agent([1, 2])[0])

    env = ToyEnv()
    agent = DullAgent(action=1)
    # ExperienceSource 返回(s,a,r,done)
    exp_source = ptan.experience.ExperienceSource(env=env, agent=agent, steps_count=2)
    for idx, exp in enumerate(exp_source):
        if idx > 15:                    # 收集15次长度为2的经验条
            break
        print('exp=>',exp)

    exp_source = ptan.experience.ExperienceSource(env=env, agent=agent, steps_count=4)
    print(next(iter(exp_source)))       # 只收集一次长度为4的经验条,当遇到最后一个片段结束时 长度可小于4

    exp_source = ptan.experience.ExperienceSource(env=[ToyEnv(), ToyEnv()], agent=agent, steps_count=4)
    for idx, exp in enumerate(exp_source):
        if idx > 4:
            break
        print(exp)

    print("ExperienceSourceFirstLast")
    # steps_count=3 表示迭代中的三步压缩在一起,返回一个总奖励
    # gamma=0.8 表示第二步的奖励为第一步的0.8倍,第三步的奖励为第二步的0.8倍
    # 但是如果某一步游戏结束,会返回剩余步数的累计奖励,比如第一步就已经结束奖励为0,会返回第二步和第三步的累计奖励非0
    # ExperienceSourceFirstLast 返回(s,a,r,s_)
    exp_source = ptan.experience.ExperienceSourceFirstLast(env, agent, gamma=0.8, steps_count=3)
    for idx, exp in enumerate(exp_source):
        print(exp)
        if idx > 10:
            break
### 问题分析 在 PTA 平台上的“N个数求和”问题中,测试点3通常用于验证程序对大整数运算的处理能力。根据已有信息,如果程序在该测试点失败,主要原因可能包括以下几点: - **数据范围超出 long 型限制**:当多个分数通分累加时,若未及时化简,则中间结果可能会超过 `long` 型的最大表示范围,导致溢出错误[^2]。 - **未正确处理负数符号**:题目明确指出负号只出现在分子前,因此在解析输入或输出格式时若未正确判断负号位置,可能导致计算错误。 - **未正确化简分数**:最终结果要求为最简形式,即分子小于分母且二者互质。若未能正确求最大公约数(GCD),或者未能将分数化简到最简形式,也会导致测试点失败。 - **特殊情况处理不当**:例如,当总和为0时应直接输出0;当分子可以整除分母时应仅输出整数部分。 ### 测试点3的典型输入样例 虽然无法获取PTA平台内部的测试用例,但可以构造一个与测试点3相似的输入来模拟其行为: ``` 3 1/3 -1/6 1/8 ``` 期望输出: ``` 7/24 ``` 这个样例涉及了正负分数相加、通分、化简等操作,是测试程序是否能正确处理分数运算逻辑的理想案例。 ### 建议的调试策略 为了确保通过所有测试点,尤其是测试点3,建议采取如下措施: - 在每次加法后立即进行分数化简,避免中间结果过大; - 使用 `long long` 型代替 `long` 来存储分子和分母,以支持更大的数值范围; - 正确处理负数符号和零的情况; - 输出时注意格式控制,确保符合题意要求。 ### 示例代码(改进版) ```cpp #include <iostream> #include <cstdio> #include <cstdlib> using namespace std; long long gcd(long long a, long long b) { return b == 0 ? a : gcd(b, a % b); } int main() { long long n, numerator = 0, denominator = 1; scanf("%lld", &n); for (int i = 0; i < n; ++i) { long long a, b; scanf("%lld/%lld", &a, &b); // 通分 long long lcm = denominator * b / gcd(denominator, b); numerator = numerator * (lcm / denominator) + a * (lcm / b); denominator = lcm; // 化简 long long g = gcd(abs(numerator), abs(denominator)); numerator /= g; denominator /= g; } if (denominator == 1) printf("%lld\n", numerator); else { long long integer_part = numerator / denominator; long long new_numerator = numerator % denominator; if (new_numerator < 0) new_numerator = -new_numerator; if (integer_part != 0) { printf("%lld ", integer_part); } printf("%lld/%lld\n", new_numerator, denominator); } return 0; } ``` 此版本的代码在每一步都进行了分数化简,从而有效避免了中间结果过大而导致的溢出问题[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值