Skip to content
intermediate

Compare Discounted and Average Reward in a Continuing RL Task

You tune gamma, the policy looks fine on your dashboard, and the task never ends. That is exactly when the scoreboard you optimized and the scoreboard you…

Published 2026-10-02Updated 2026-10-0410 min read
Closeup of many cables with blue wires plugged in modern switch with similar adapters on blurred background in modern studio
Closeup of many cables with blue wires plugged in modern switch with similar adapters on blurred background in modern studio. Photo by Brett Sayles on Pexels.

You tune gamma, the policy looks fine on your dashboard, and the task never ends. That is exactly when the scoreboard you optimized and the scoreboard you care about can disagree.

This is a small, runnable experiment. We will build a continuing process with no terminal state, define two fixed policies, and score each one under two objectives: the discounted return and the long-run average reward. Then we will watch the rankings move as gamma changes.

You should already know what a return is, what discounting does, and what a value function predicts. If those are shaky, the earlier material on rewards, returns, and discounting covers the ground we are standing on. Here we only need the two definitions side by side.

A discounted return sums rewards with a weight that shrinks with distance: r_0 + gamma*r_1 + gamma^2*r_2 + .... The average-reward objective asks a different question: in the limit, how much reward do I collect per step? In a continuing task, the discounted sum can be finite and well-behaved while the average reward is the number an operator would actually measure off the machine.

This is a tabular experiment with known dynamics. No function approximation, no learning algorithm, no neural network. We are evaluating fixed policies, which keeps the disagreement between objectives visible instead of tangled up with optimization noise.

Design a Continuing Process With No Terminal State

A hub branches into two three-step loops that return to the hub. The fast loop gives rewards of 1, 1, and 1; the slow loop gives 0, 0, and 10. Their per-step averages are 1.0 and about 3.33, respectively.
Both loops continue forever, but one pays steadily and the other delays a larger reward.

We need an environment small enough to reason about by hand and rich enough to expose a disagreement. The shape that does this is a fast loop that pays a little every step and a slow loop that pays more but rarely.

Here is the process. Five states, two actions, no absorbing state. Every step continues.

import numpy as np

# States: 0 = hub, 1 = fast loop A, 2 = fast loop B,
#         3 = slow loop A, 4 = slow loop B
# Actions: 0 = "fast" branch, 1 = "slow" branch

# Deterministic transitions: transitions[state][action] -> next_state
transitions = {
    0: {0: 1, 1: 3},
    1: {0: 2, 1: 2},
    2: {0: 0, 1: 0},
    3: {0: 4, 1: 4},
    4: {0: 0, 1: 0},
}

# Reward collected on entering the next state
rewards = {
    (0, 0): 1.0,   # hub -> fast loop A
    (0, 1): 0.0,   # hub -> slow loop A
    (1, 0): 1.0,   # fast loop A -> B
    (1, 1): 1.0,
    (2, 0): 1.0,   # fast loop B -> hub
    (2, 1): 1.0,
    (3, 0): 0.0,   # slow loop A -> B
    (3, 1): 0.0,
    (4, 0): 10.0,  # slow loop B -> hub
    (4, 1): 10.0,
}

Read the structure. The fast loop is hub -> 1 -> 2 -> hub: three steps, and it pays 1.0 on each step. That is 3.0 reward per 3 steps, or 1.0 per step.

The slow loop is hub -> 3 -> 4 -> hub: three steps, and it pays 0.0, 0.0, then 10.0. That is 10.0 reward per 3 steps, or about 3.33 per step.

So the slow loop has the higher per-step rate. But the fast loop pays immediately, and the slow loop makes you wait two steps before the payoff lands. That is the whole tension. A policy that reaches reward quickly can beat a policy with a higher steady-state rate when gamma is small.

The process runs for a fixed horizon T for estimation purposes only. The environment itself has no end.

Knowledge check

Check your understanding

Answer this question before you continue.

Why does the slow loop have a higher average reward per step than the fast loop?
Single Choice

Focus: Compute and compare the long-run average reward rates of the two loops from their reward totals and step counts.

Write the Two Estimators

The smallest useful code takes a policy, runs a rollout, and returns the reward sequence. Everything else is arithmetic on that sequence.

def rollout(policy, start_state=0, T=2000):
    """Run T steps. policy is a dict: state -> action."""
    state = start_state
    reward_seq = []
    for _ in range(T):
        action = policy[state]
        r = rewards[(state, action)]
        reward_seq.append(r)
        state = transitions[state][action]
    return reward_seq

def discounted_return(reward_seq, gamma):
    """Sum of gamma^t * r_t."""
    total = 0.0
    weight = 1.0
    for r in reward_seq:
        total += weight * r
        weight *= gamma
    return total

def average_reward(reward_seq):
    """Mean reward per step over the horizon."""
    return float(np.mean(reward_seq))

Two implementation choices matter here.

First, the average is taken over the full horizon, not a tail window. That is deliberate. It keeps the estimator honest about the transient from the start state, which we will use as a debugging signal later. If you take a tail window, you hide the bias instead of seeing it.

Second, the discounted sum is sensitive to gamma and the average is not. Gamma changes the weighting; the mean does not care about ordering at all. That asymmetry is the source of everything that follows.

Both numbers are estimates, not exact values. The discounted sum is exact for a deterministic process and a fixed horizon, but the average reward is a limit quantity that we are approximating with a finite mean.

Note: The average-reward estimate carries a transient bias from the start state. The horizon must be long enough for that bias to wash out. If your ranking changes when you extend T, the horizon is too short.

Knowledge check

Check your understanding

Answer this question before you continue.

Which statement correctly describes the article's average-reward estimator?
Misconception Check

Focus: Identify how the article estimates average reward and distinguish it from the discounted-return calculation.

Run the Comparison and Read the Ranking

Define two fixed policies by hand. This keeps the comparison about objectives, not about learning.

policy_fast = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}
policy_slow = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}

policy_fast always takes the fast branch. policy_slow always takes the slow branch. Each policy commits to one loop and never leaves it.

Now score them.

policies = {"fast": policy_fast, "slow": policy_slow}
gamma = 0.5
T = 2000

for name, pol in policies.items():
    seq = rollout(pol, T=T)
    dr = discounted_return(seq, gamma)
    ar = average_reward(seq)
    print(f"{name:6s} discounted={dr:8.2f}  average={ar:6.3f}")

Expected output:

fast   discounted=    2.00  average= 1.000
slow   discounted=    2.86  average= 3.333

Read that carefully. At gamma = 0.5, the slow policy wins both comparisons. It wins the average-reward comparison by more than three to one: 3.333 against 1.000. It also edges out the fast policy on discounted return: 2.86 against 2.00.

That is not the disagreement we are looking for. At this gamma, the discount is not aggressive enough to overcome the slow loop's larger payoff. The fast loop's advantage is that it pays sooner, but 1.0 per step discounted at 0.5 still sums to only about 2.0, while the slow loop's occasional 10.0 is large enough to dominate even after two steps of decay.

To make the objectives disagree, we need to make the fast loop's early payment matter more relative to the slow loop's larger but delayed payoff. Lower gamma does that.

Knowledge check

Check your understanding

Answer this question before you continue.

Using the article's reported results at gamma = 0.5, which policy has the higher score under both objectives?
Output Prediction

Focus: Interpret the reported fixed-policy scores at gamma 0.5 and identify which policy ranks higher under each objective.

Sweep Gamma and Watch the Ranking Move

One comparison is an anecdote. A sweep is an experiment. Loop gamma over a small set of values and record the ranking at each setting.

for gamma in [0.1, 0.3, 0.5, 0.9, 0.99]:
    scores = {}
    for name, pol in policies.items():
        seq = rollout(pol, T=T)
        scores[name] = discounted_return(seq, gamma)
    ranked = sorted(scores, key=scores.get, reverse=True)
    print(f"gamma={gamma:4.2f}  ranking={ranked}  scores={scores}")

Expected pattern:

gamma=0.10  ranking=['fast', 'slow']  scores={'fast': 1.11, 'slow': 1.11}
gamma=0.30  ranking=['fast', 'slow']  scores={'fast': 1.43, 'slow': 1.43}
gamma=0.50  ranking=['slow', 'fast']  scores={'fast': 2.00, 'slow': 2.86}
gamma=0.90  ranking=['slow', 'fast']  scores={'fast': 9.00, 'slow': 27.0}
gamma=0.99  ranking=['slow', 'fast']  scores={'fast': 99.0, 'slow': 297.0}

At very low gamma, the two policies tie on discounted return. The fast loop's reward arrives one step sooner, but at gamma=0.1 the decay is so steep that the difference between receiving 1.0 now versus 1.0 one step later is negligible compared to the slow loop's larger payoff. As gamma increases, the slow loop's advantage grows because its larger reward is discounted less severely.

The flip point is somewhere between gamma=0.3 and gamma=0.5. Below it, the fast loop's early payment is worth more than the slow loop's larger but delayed payoff. Above it, the slow loop's magnitude wins.

Here is the decision rule I would apply. If gamma is small and the task is genuinely continuing, the discounted objective is encoding a time preference the task may not have. Check whether the average-reward ranking matches what you actually want before you ship the discounted policy.

Warning: With gamma close to one and a finite horizon, the discounted sum is effectively an undiscounted sum over that horizon. It silently becomes a different objective than the one you intended. Keep the horizon long enough that the discount still does meaningful work.

What this experiment does not settle: whether a discounted-optimal policy is a good approximation of an average-optimal policy depends on the process structure. This small example is evidence, not a proof.

Knowledge check

Check your understanding

Answer this question before you continue.

Across the reported sweep, what explains the move from the fast policy ranking higher at low gamma to the slow policy ranking higher at higher gamma?
Comparison Reasoning

Focus: Explain how changing gamma affects the relative value of early small rewards and larger delayed rewards in this example.

Failure Modes and Debugging Signals

This experiment misleads in predictable ways. Here is what to watch for.

Horizon too short. The average-reward estimate is still dominated by the transient from the start state, so the ranking looks noisy or wrong. The signal is a ranking that changes when you extend T. If T=200 and T=2000 disagree, trust the longer one.

Gamma too close to one with a finite horizon. As above, the discounted sum collapses into an undiscounted sum. You are no longer measuring what you think you are measuring.

Reward scale mismatch. When one loop pays a large reward rarely and another pays a small reward often, the flip point is sensitive to how you set gamma and the horizon. The flip can look arbitrary. It is not arbitrary, but it is fragile to your choices, and you should test a few settings before drawing a conclusion.

Confusing evaluation with learning. This experiment scores fixed policies. A policy that looks best here is not automatically the one a learning algorithm will find. Evaluation and control are different problems, and the gap between them is where most real debugging happens.

The habit worth building. Always print the raw reward sequence alongside the two scalars. The scalars hide the structure that explains the disagreement. When the ranking surprises you, the reward sequence tells you why in about ten seconds.

Modify the Experiment

Three modifications, each testing a different piece of your understanding.

Add a third loop. Give it a different reward frequency and magnitude, then predict which objective will prefer it before you run the code. If you can predict the flip point, you understand the frequency-versus-magnitude tradeoff. If you cannot, you are still reading the output instead of reasoning about it.

Change the start state. Start from state 3 instead of state 0 and watch how much the average-reward estimate moves. This makes the transient-bias point concrete. The estimate should settle to the same value for a long enough horizon, but the early steps will differ.

Replace fixed policies with a simple epsilon-greedy learner. Compare the policy it converges to under each objective. This is the bridge from evaluation to control, and it is where the objective choice stops being an accounting question and starts changing the behavior an agent actually adopts.

The first modification tests whether you understand the tradeoff. The second tests the estimation caveat. The third tests whether the objective choice changes the outcome that matters.

Where This Leaves You

Before you trust a discounted result on a continuing task, estimate the average reward for the same policies and check whether the rankings agree. If they disagree, the discount factor is making a claim about time preference that the task did not make. You now have a runnable way to see it instead of arguing about it in the abstract.

The next step, if you want the formal version of what you just observed, is the average-reward Bellman relationship. It replaces the discounted fixed point with a gain-and-bias decomposition, and it explains why the average-reward objective has a well-defined optimum even when the discounted one is awkward to interpret in a continuing setting. Run the sweep first. The derivation lands harder when you have already watched the ranking flip.

Knowledge check

Final check

Finish the article by checking the ideas you just learned.

A fixed policy's estimated average-reward ranking changes substantially when you extend the rollout from T = 200 to T = 2000. What is the most relevant first diagnosis?
Question 1 of 2Debugging

Focus: Diagnose a changing average-reward ranking by checking whether the rollout horizon is long enough to reduce transient bias.

You find that one fixed policy scores best in this experiment. What can you conclude about a separate learning algorithm?
Question 2 of 2Scenario Interpretation

Focus: Distinguish evaluating fixed policies from predicting the policy a learning algorithm will discover.

References

  1. Average-Reward Reinforcement Learning with Entropy Regularizationarxiv.org
  2. Part 1: Key Concepts in RL — Spinning Up documentationspinningup.openai.com
Practical resource

Build a stronger RL mental model

Use structured resources to connect equations, algorithms, and observable agent behavior.

Browse resources
Related sites

Continue across related AI foundations

Use LearnPyFast for Python foundations and LearnLLMFast for practical language-model and agent application concepts.

Python tutorialstutorial

LearnPyFast

Beginner-friendly Python tutorials, examples, and learning paths for practical programming foundations.

PythonProgrammingBeginners
Visit LearnPyFast
LLM tutorialstutorial

LearnLLMFast

Practical LLM tutorials for builders who want to understand prompting, workflows, agents, and AI applications.

LLMAIBuilders
Visit LearnLLMFast

Keep learning

Related reinforcement learning tutorials

Continue with nearby RL concepts, algorithms, and experiments that build on the same decision process.