"There are ten levers in front of me and I have pulled each one a handful of times. Lever four has been kind so far, but four pulls is a thin reason to trust anyone, and lever seven I have barely touched. Do I keep feeding the one I half-know, or spend a coin learning the one I do not? Every pull is a vote I can never recast, and I have been voting all night."
A Gambler Pulling Levers, Learning Which Ones Pay
Strip reinforcement learning down to its smallest non-trivial core and you get the multi-armed bandit: $K$ actions, each paying a reward drawn from its own unknown distribution, and a single decision repeated over and over, which lever to pull, so as to maximize total reward. There is one state, the reward is immediate, and there is no long-horizon planning, so everything that makes full reinforcement learning hard falls away except the one thing that makes it interesting: you can only learn which action is good by trying it, and every trial spent learning is a trial not spent earning. That tension, exploration versus exploitation, is the entire subject of the bandit, and it is the same tension that runs through every agent in Part VI. This section defines the bandit and its action values, shows why pure greed gets stuck on a mediocre arm, introduces the cheap fixes that actually work in practice, epsilon-greedy, optimistic initialization, and decaying exploration, and develops regret as the clean scalar measure of how much an algorithm pays for its ignorance. We meet the Lai and Robbins lower bound, the law that says no algorithm can do better than logarithmic regret, which sets the bar that the UCB and Thompson sampling methods of Section 24.2 are built to reach. Then we build an epsilon-greedy agent from scratch on a Bernoulli bandit, watch its cumulative regret flatten while a greedy baseline's grows linearly, and replace the whole loop with a few lines of a library. You leave able to recognize a bandit problem in the wild, implement a value-based agent for it, and reason quantitatively about the price of exploration.
Part VI opened with the Markov decision process of Chapter 22, the full apparatus of states, transitions, and delayed reward, and Chapter 23 added the further difficulty of partial observability. This chapter does the opposite of adding difficulty: it removes almost all of it, to isolate the one mechanism that survives. A multi-armed bandit is a Markov decision process with exactly one state, so there is no transition to reason about, and a reward that arrives immediately, so there is no credit to assign across time. What remains, once states and delays are gone, is the bare act of choosing under uncertainty: you do not know how good the actions are, the only way to find out is to take them, and you want to do well while you find out. That residue is the exploration-exploitation problem in its purest form, which is why the bandit is the right place to start the study of learning agents. Every richer setting in this part, the deep reinforcement learning of Chapter 25, the advanced reinforcement learning of Chapter 26, inherits the bandit's dilemma and adds structure on top of it.
The name is a piece of casino slang. A slot machine is a "one-armed bandit" because it has a single lever and it robs you slowly; a row of $K$ such machines, each with a different and unknown payout rate, is a "multi-armed bandit", and the gambler's problem is to discover and exploit the most generous machine while losing as little as possible to the others along the way. The metaphor is exact: each arm is an action, each pull yields a noisy reward, the payout rates are unknown, and the gambler learns only by playing. We will use the gambler throughout as the running picture, alongside the real applications, A/B testing, recommendation, clinical-trial allocation, and ad selection, that are literally bandit problems dressed in business clothes.
The competencies this section installs are four. First, to formalize a bandit with action-value functions and to define regret, the cumulative shortfall against an oracle that always plays the best arm. Second, to explain why a purely greedy policy can lock onto a suboptimal arm forever, and to deploy the standard remedies, epsilon-greedy, optimistic initialization, and a decaying exploration schedule, while estimating action values online with the incremental-mean update that connects back to the online learning of Chapter 20. Third, to read the Lai-Robbins logarithmic lower bound and understand what "asymptotically optimal" means for a bandit algorithm. Fourth, to implement and benchmark an epsilon-greedy agent from scratch and then in a library. These skills are the foundation on which Section 24.2 builds the optimism and posterior-sampling algorithms that approach the bound.
1. The Multi-Armed Bandit: K Actions, Unknown Rewards Beginner
A multi-armed bandit problem is specified by a set of $K$ actions, often called arms, indexed $a \in \{1, \dots, K\}$. Each arm $a$ has a fixed but unknown reward distribution with a mean we call the action's true value,
$$q_*(a) \;\equiv\; \mathbb{E}[\,R_t \mid A_t = a\,],$$the expected reward you receive when you select arm $a$. At each timestep $t = 1, 2, \dots$ the agent chooses an action $A_t$, the environment returns a reward $R_t$ drawn from that action's distribution, and the agent updates whatever it believes. The reward is immediate (no waiting, no downstream consequence) and the environment never changes state, so the entire problem is the single repeated decision of which arm to pull. The agent's goal is to maximize the expected total reward over a horizon of $T$ pulls, $\mathbb{E}\big[\sum_{t=1}^{T} R_t\big]$, which, because the means are fixed, is the same as pulling the highest-mean arm as often as possible.
If the agent knew every $q_*(a)$ it would have no problem at all: it would compute the optimal value $v_* = \max_a q_*(a)$, identify the best arm $a_* = \arg\max_a q_*(a)$, and pull only that arm forever, collecting $v_*$ per step. The difficulty is entirely that the values are unknown. The agent must estimate them from the rewards it observes, and it can only observe the reward of an arm by pulling it, which costs a turn. We write $Q_t(a)$ for the agent's estimate of $q_*(a)$ at time $t$, distinguished from the true value $q_*(a)$ by being a learned quantity that improves with data. The natural estimate is the average reward received from arm $a$ so far,
$$Q_t(a) \;=\; \frac{\sum_{i=1}^{t-1} R_i \,\mathbb{1}[A_i = a]}{\sum_{i=1}^{t-1} \mathbb{1}[A_i = a]},$$the sample mean of the rewards collected on the pulls where arm $a$ was chosen, with $\mathbb{1}[\cdot]$ the indicator. By the law of large numbers $Q_t(a) \to q_*(a)$ as the number of pulls of $a$ grows, so an arm pulled often is estimated well and an arm pulled rarely is estimated poorly. That asymmetry is the seed of the whole problem: the arms you trust are the ones you have already played, and the arms you have already played are biased toward the ones that looked good early, which may not be the ones that are good. Following the unified notation of Appendix A: $q_*$ is the truth, $Q_t$ the estimate, $A_t$ the action, $R_t$ the reward.
Two standard concrete instances recur throughout this section and the next. A Bernoulli bandit has each arm pay reward $1$ with some unknown probability $p_a$ and $0$ otherwise, so $q_*(a) = p_a$; this models any success-or-failure outcome, a click, a conversion, a cure. A Gaussian bandit has each arm pay a reward drawn from $\mathcal{N}(\mu_a, \sigma^2)$, so $q_*(a) = \mu_a$; this models any real-valued noisy outcome, a revenue, a latency, a measured response. The two share all the theory and differ only in the noise model, and we use the Bernoulli bandit for the worked code because its rewards are interpretable as success rates.
A full reinforcement-learning problem has three hard ingredients: many states the agent moves between, rewards that arrive long after the actions that earned them, and unknown dynamics the agent must learn. The bandit keeps only the third and discards the first two. One state means no planning over transitions; immediate reward means no credit assignment across time. What is left, learning action values from your own noisy trials while trying to act well, is the irreducible core that every richer agent in Part VI still contains. Master the bandit and you have isolated, in a setting simple enough to solve exactly, the one difficulty that no learning agent ever escapes: you must spend trials to learn, and trials spent learning are trials not spent earning.
2. The Exploration Problem: Why Greed Gets Stuck Beginner
The most obvious policy is to always pull the arm with the highest current estimate: $A_t = \arg\max_a Q_t(a)$. This is the greedy policy, and on its face it looks correct, since it always exploits the best information the agent has. The trouble is that the information can be wrong, and the greedy policy gives itself no way to find out. Suppose arm three is truly the best but its first two pulls happened to return low rewards (noise is unkind), while arm one returned a lucky high reward on its single pull. The greedy policy now estimates arm one as best, pulls arm one, observes a reward consistent with its estimate, keeps estimating arm one as best, and pulls arm one again. It has locked on. The genuinely better arm three is never pulled again, its bad luck is never corrected, and the agent collects a suboptimal reward for the rest of time, fully confident it is doing the right thing.
This is the failure of pure exploitation: a greedy agent only ever gathers data about the arm it currently believes is best, so an arm that looks bad early, whether truly bad or merely unlucky, stays unexplored and its estimate stays frozen at its unlucky value. Exploitation without exploration is a self-confirming trap. The fix is to deliberately, occasionally, pull an arm that is not the current favorite, accepting a likely-lower immediate reward in exchange for information that might overturn a mistaken belief. That deliberate suboptimal action is exploration, and the art of bandits is doing exactly enough of it: too little and you stay stuck, too much and you squander pulls on arms you have already learned are bad. The three classic ways to inject the right amount follow.
Epsilon-greedy is the simplest and most widely used. With probability $1 - \varepsilon$ act greedily, pulling $\arg\max_a Q_t(a)$, and with probability $\varepsilon$ pull a uniformly random arm:
$$A_t = \begin{cases} \arg\max_a Q_t(a) & \text{with probability } 1 - \varepsilon, \\[2pt] \text{a uniformly random arm} & \text{with probability } \varepsilon. \end{cases}$$A small fixed $\varepsilon$ (say $0.1$) guarantees every arm is pulled infinitely often in the limit, so every estimate eventually converges to its truth and the agent cannot stay stuck on a wrong arm. The cost is that even after the best arm is known, the agent keeps exploring at rate $\varepsilon$ forever, throwing away a fraction $\varepsilon$ of its pulls on arms it has already learned are worse, which is why a constant $\varepsilon$ yields linear regret, a point we make precise in subsection three.
Optimistic initialization explores without any randomness by seeding every estimate high. Initialize $Q_0(a)$ to a value well above any plausible true reward (for a Bernoulli bandit, $Q_0(a) = 1$ or higher). Now an arm looks attractive until it has been tried enough times for its average to fall to its true value, so the greedy policy, chasing the highest estimate, is driven to sweep through all the arms early, deflating each optimistic prior in turn before settling on the genuinely best. The exploration is front-loaded and self-extinguishing, with no tuning of a random rate, but it is a one-shot burst suited to stationary problems; in a non-stationary world where arm values drift (the subject of Chapter 21), the initial optimism is spent and never refreshed.
Decaying exploration gets the best of both by shrinking the exploration rate over time, for instance $\varepsilon_t = \min(1, c\,K / t)$ for a constant $c$, so the agent explores heavily when it knows little and exploits almost exclusively once its estimates are trustworthy. A well-chosen decay schedule explores enough early to identify the best arm yet rare enough later to stop wasting pulls, and it is the schedule that lets a value-based method approach the logarithmic regret of subsection three rather than the linear regret of a fixed $\varepsilon$.
All three remedies need a running estimate $Q_t(a)$, and recomputing the sample mean from scratch at every step would cost memory and time that grow without bound. The incremental mean update fixes this: keep only the current estimate $Q_n$ and the count $n$ of pulls so far, and on receiving the $n$-th reward $R_n$ update
$$Q_{n+1} \;=\; Q_n + \frac{1}{n}\big(R_n - Q_n\big),$$which is algebraically the running average yet stores two numbers per arm and does constant work per step. Read the update as "move the old estimate a little toward the new reward, by a step size $1/n$ that shrinks as evidence accumulates". This single step-size choice, a shrinking $1/n$ for a stationary world or a fixed $\alpha$ for a drifting one, is the same learning-rate decision you will make in every algorithm in this part; the bandit is where it is easiest to see. This error-correction form, new estimate equals old estimate plus step size times (target minus old estimate), is not a bandit trick: it is the exact shape of the online stochastic-approximation updates of Chapter 20 and, with a constant step size in place of $1/n$, the temporal-difference learning rule of Chapter 25. Replacing $1/n$ by a fixed $\alpha$ turns the average into an exponentially weighted recency-biased average, which is what you want when arm values drift, the non-stationary case that links forward to Chapter 21.
An arm has been pulled $n = 4$ times with rewards $1, 0, 1, 1$, so its current estimate is the sample mean $Q_4 = (1+0+1+1)/4 = 0.75$. The fifth pull returns $R_5 = 0$ (a failure). The incremental update with step size $1/n = 1/5 = 0.2$ gives $Q_5 = Q_4 + \tfrac{1}{5}(R_5 - Q_4) = 0.75 + 0.2\,(0 - 0.75) = 0.75 - 0.15 = 0.60$. Check it against the from-scratch sample mean: the five rewards are $1,0,1,1,0$, whose average is $3/5 = 0.60$, identical. The incremental form reached the same number while storing only the pair $(Q, n) = (0.75, 4)$ rather than the full reward history, and it did one subtraction, one multiply, and one add. Notice the step size $1/5$ is smaller than the $1/4$ that produced $Q_4$: each new observation moves the estimate less than the last, because the estimate is built on more evidence and should be harder to budge.
You have eaten at the same passable restaurant on your corner forty times because the first meal was good and you have never risked the new place across the street. That is a greedy policy in the wild, and it is stuck: you exploit a known-decent option and gather zero information about the genuinely-better option you keep walking past. Epsilon-greedy is the friend who drags you somewhere new one Friday in ten. Optimistic initialization is moving to a city where you assume every restaurant is excellent until proven otherwise, so you try them all before settling. Decaying exploration is doing your culinary adventuring in your twenties and dining like a creature of habit in your sixties, which, it turns out, is close to optimal.
3. Regret and the Lai-Robbins Lower Bound Intermediate
To compare bandit algorithms we need a scalar that measures how much an algorithm pays for not knowing the best arm in advance. That scalar is regret: the cumulative difference between the reward an oracle would have collected by always playing the optimal arm and the reward the algorithm actually collected. Writing $v_* = \max_a q_*(a)$ for the optimal per-step value and $A_t$ for the action the algorithm chose at step $t$, the expected total regret after $T$ pulls is
$$\mathrm{Regret}(T) \;=\; T\,v_* - \mathbb{E}\!\left[\sum_{t=1}^{T} q_*(A_t)\right] \;=\; \sum_{a}\, \Delta_a\, \mathbb{E}[N_T(a)],$$where $\Delta_a = v_* - q_*(a)$ is the suboptimality gap of arm $a$ (zero for the best arm, positive for every worse arm) and $N_T(a)$ is the number of times arm $a$ was pulled in the first $T$ steps. The right-hand form is the one to internalize: total regret is a sum over arms of each arm's gap times how often the algorithm pulled it. You accumulate regret only by pulling suboptimal arms, and exactly in proportion to how bad they are and how often you pull them. An algorithm therefore has a clean objective, pull each suboptimal arm as few times as possible, and the entire science of bandit algorithms is about pulling bad arms just often enough to be confident they are bad, and no more.
Regret immediately separates good algorithms from bad ones by how fast it grows. A greedy agent that locks onto a suboptimal arm pulls it a constant fraction of the time, so its regret grows linearly, $\mathrm{Regret}(T) = \Theta(T)$, and the per-pull penalty never goes away. A fixed-$\varepsilon$ epsilon-greedy agent is also linear, because it keeps exploring at rate $\varepsilon$ forever, spending a fraction $\varepsilon$ of pulls on random (mostly suboptimal) arms even after it has identified the best, so its regret grows like $\varepsilon \cdot T$ times the average gap. The goal is sublinear regret, $\mathrm{Regret}(T)/T \to 0$, meaning the average regret per pull vanishes and the algorithm's behavior converges to the oracle's. A decaying-$\varepsilon$ schedule, tuned correctly, achieves sublinear and in fact logarithmic regret.
How low can regret possibly go? The celebrated result of Lai and Robbins (1985) answers this with a lower bound that no algorithm can beat. For any reasonable bandit algorithm (one that is not foolishly suboptimal on some problem), the expected number of pulls of a suboptimal arm $a$ must grow at least logarithmically in the horizon,
$$\liminf_{T \to \infty} \frac{\mathbb{E}[N_T(a)]}{\ln T} \;\ge\; \frac{1}{\mathrm{KL}\big(p_a \,\|\, p_{a_*}\big)},$$where $\mathrm{KL}(p_a \| p_{a_*})$ is the Kullback-Leibler divergence between the reward distribution of arm $a$ and that of the best arm, a measure of how statistically distinguishable the two arms are. Summing the gap-weighted pulls gives the regret form of the bound, $\mathrm{Regret}(T) \ge C \ln T$ for a problem-dependent constant $C$. The content of the theorem is a hard limit: every algorithm must pull each suboptimal arm at least on the order of $\ln T$ times, because distinguishing a slightly-worse arm from the best one to statistical confidence simply requires that many noisy samples, and you cannot be sure an arm is worse without paying to find out. Logarithmic regret is the floor, and an algorithm that achieves $O(\ln T)$ regret with the right constant is called asymptotically optimal.
Two readings of the bound are worth holding onto. First, the divergence in the denominator says that arms which are easy to tell apart from the best (large KL, very different reward distributions) need fewer exploratory pulls, while arms that are nearly as good as the best (small KL) need many more pulls to rule out, which matches intuition: the close calls are the expensive ones. Second, the bound is what makes "good" precise. Greedy and fixed-$\varepsilon$ are linear and so are infinitely far from the bound as $T$ grows; the upper-confidence-bound and Thompson-sampling algorithms of Section 24.2 achieve $O(\ln T)$ regret and so are asymptotically optimal, matching the Lai-Robbins floor up to constants. This section's epsilon-greedy agent sits between, sublinear with a good decay schedule but not optimal in constant, which is exactly why Section 24.2 exists.
Take a two-armed Bernoulli bandit with $q_*(1) = 0.5$ and $q_*(2) = 0.6$, so the best value is $v_* = 0.6$ and the single suboptimal gap is $\Delta_1 = 0.6 - 0.5 = 0.1$. A fixed-$\varepsilon$ agent with $\varepsilon = 0.1$ pulls the suboptimal arm on roughly half of its $\varepsilon$-exploration steps, about $\varepsilon/2 = 0.05$ of all pulls, so over $T = 10{,}000$ pulls it accrues regret near $\Delta_1 \cdot 0.05 \cdot T = 0.1 \cdot 0.05 \cdot 10{,}000 = 50$, and over $T = 100{,}000$ pulls near $500$: the regret grows in lockstep with $T$. A logarithmic-regret algorithm, by contrast, pulls the suboptimal arm on the order of $\ln T$ times: at $T = 10{,}000$ that is about $\ln(10{,}000) \approx 9.2$ scaled by a small constant, perhaps a few dozen suboptimal pulls in total, and at $T = 100{,}000$ only $\ln(100{,}000) \approx 11.5$, barely more. Ten times the horizon multiplies the fixed-$\varepsilon$ regret by ten but the logarithmic regret by about $1.25$. That gap, $500$ versus a few dozen, is the entire practical payoff of the optimal algorithms in Section 24.2.
4. Where Bandits Live: A/B Testing, Recommendation, Trials, Ads Intermediate
The bandit is not a toy. It is the exact mathematical shape of several of the most consequential decision problems in technology and medicine, and recognizing the shape is half the work of solving them. In each of the following, an "arm" is a thing you can offer and a "reward" is a measured outcome, and the exploration-exploitation tension is real money or real lives.
A/B testing is the canonical example, and bandits sharpen it. Classical A/B testing splits traffic evenly between two variants for a fixed period, measures which won, then ships the winner; during the test, half the users are deliberately shown the losing variant, which is pure exploration with no adaptation. A bandit reframes this as the obvious improvement: treat each variant as an arm, and as evidence accumulates, shift traffic toward the variant that is performing better, so fewer users see the loser. This "adaptive" or "continuous" A/B test reaches a confident decision with less regret (fewer users served the worse experience) than the fixed split, which is exactly the regret reduction this section is about. The two arms are the variants, the reward is the conversion or click, and the gap is the difference in conversion rates.
Recommendation is a bandit with many arms and a steady stream of decisions. Each candidate item (article, product, video) is an arm, the reward is a click, a watch, or a purchase, and a recommender must constantly balance showing items it already knows users like (exploitation) against showing newer or less-tested items to learn whether users like them (exploration). A recommender that only ever exploits its current favorites is the stuck-greedy agent of subsection two, blind to a new item that would outperform everything in its catalog, so production recommenders run exploration explicitly, and the contextual extension of the bandit (where the arm's value depends on user features) is the workhorse model for it.
Clinical-trial allocation is the application that motivated the theory and the one where the stakes are highest. Each treatment is an arm, the reward is a patient outcome (recovery, survival), and the unknowns are the treatments' efficacies. A fixed-allocation trial assigns equal numbers of patients to each treatment regardless of accumulating evidence, which means patients keep being assigned to a treatment the data already suggests is worse: that is exploration paid for in patient welfare. Adaptive "response-adaptive" trial designs, built directly on bandit ideas, shift allocation toward the treatment that is performing better as the trial runs, so fewer patients receive the inferior treatment. The ethical force of minimizing regret here is literal, every unit of regret is a patient given a worse treatment than the evidence warranted, which is why Thompson sampling, introduced for exactly this problem in 1933, predates the rest of the field by decades (Section 24.2).
Ad selection is bandits at industrial scale. Each ad (or ad-creative variant) is an arm, the reward is a click or a conversion, the value is the unknown click-through rate, and an ad system must decide, billions of times a day, which ad to show: it cannot afford to keep showing an underperforming ad (exploitation of a bad estimate) yet must show each new ad enough to estimate its rate (exploration). The economics are stark, a fraction of a percent of click-through across billions of impressions is a large sum, so the regret this section measures translates directly into revenue, and bandit algorithms (typically UCB-style or Thompson sampling) run in the serving path of every major ad platform. These four domains, the running applications of this chapter, are the same problem with different rewards.
Who: The homepage-personalization team at a video streaming service, deciding which of six artwork thumbnails to show for a newly released show.
Situation: Different thumbnails drive very different click-through rates, and the team historically ran a two-week fixed-split A/B test across the six options before committing to the winner for the rest of the show's life.
Problem: During the two-week test, five-sixths of users saw a thumbnail that was not the best, so the test itself cost a large number of foregone clicks, and for a show with a short attention window two weeks of equal splitting wasted much of the show's launch traffic on losing artwork.
Dilemma: Shorten the test to lose fewer clicks during exploration, and risk declaring a winner on too little data (a thumbnail that got lucky early), or keep the long test for statistical safety and keep paying the exploration cost. A fixed split forces a bad choice between these.
Decision: They replaced the fixed-split test with a bandit: each thumbnail an arm, a click the reward, and traffic allocated by an epsilon-greedy policy with a decaying schedule (heavy exploration in the first hours, near-pure exploitation within a day), later upgraded to the Thompson sampling of Section 24.2.
How: The serving system tracked an incremental click-rate estimate $Q_t(a)$ per thumbnail with the constant-step recency-weighted update of subsection two (so the system could adapt if a thumbnail's appeal drifted), and allocated impressions by the policy each time the page rendered.
Result: The bandit identified the best thumbnail within hours instead of weeks and steered the bulk of launch traffic to it, cutting the clicks lost to exploration substantially while still gathering enough data on the other five to stay confident, the regret reduction of subsection three realized as launch-day engagement.
Lesson: A fixed-split A/B test is a bandit that refuses to learn during the test; whenever you can adapt allocation as evidence arrives, you should, and the right tool is a bandit policy with a decay or posterior-sampling schedule, not a static split.
The bandit's simplest form is old, but the problem is more alive than ever because modern systems generate enormous numbers of decisions to optimize. Three threads stand out for 2024 to 2026. First, bandits for large-language-model serving and alignment: choosing among prompts, model variants, decoding strategies, or which response to surface is a bandit (often contextual), and reinforcement learning from human feedback uses bandit-style preference rewards; recent work casts prompt optimization and model routing explicitly as bandit and best-arm-identification problems. Second, neural contextual bandits, where a neural network predicts each arm's value from context and exploration is driven by network uncertainty (NeuralUCB, NeuralTS, and their 2023 to 2025 successors), bringing deep learning to the recommendation and ad-selection settings of this subsection at scale. Third, non-stationary and restless bandits, where arm values drift over time (the concept-drift world of Chapter 21), with provable algorithms that track changing optima, increasingly important as user preferences and content distributions shift continuously in deployed systems. The throughline: the K-armed bandit of this section is the kernel, and the frontier is wrapping it in context, neural value estimation, and non-stationarity for real deployed agents.
5. Worked Example: Epsilon-Greedy From Scratch, Then a Library Advanced
We now make the section executable. The plan is to build a Bernoulli bandit environment, implement an epsilon-greedy agent from scratch with the incremental value estimation of subsection two, run it alongside a pure-greedy baseline, and measure cumulative regret for both to see the greedy baseline grow linearly while epsilon-greedy bends sublinear. Then we replace the hand-written agent with a few lines of a library to make the line-count reduction concrete. Code 24.1.1 defines the environment and the from-scratch agents.
import numpy as np
rng = np.random.default_rng(0)
class BernoulliBandit:
"""K arms; arm a pays reward 1 with probability p[a], else 0."""
def __init__(self, probs):
self.p = np.asarray(probs, dtype=float) # true means q_*(a) = p[a]
self.best = self.p.max() # v_* = max_a q_*(a)
def pull(self, a):
return float(rng.random() < self.p[a]) # sample a Bernoulli reward
class EpsilonGreedy:
"""Value-based agent with incremental-mean estimates and epsilon exploration."""
def __init__(self, K, eps):
self.eps = eps
self.Q = np.zeros(K) # action-value estimates Q_t(a), start at 0
self.N = np.zeros(K) # pull counts n(a) for the 1/n step size
def act(self):
if rng.random() < self.eps: # explore: uniform random arm
return rng.integers(len(self.Q))
return int(np.argmax(self.Q)) # exploit: greedy on current Q
def update(self, a, r):
self.N[a] += 1
self.Q[a] += (r - self.Q[a]) / self.N[a] # incremental mean: Q += (r-Q)/n
def run(agent, bandit, T):
"""Pull for T steps; return per-step regret (gap of the arm chosen)."""
regret = np.empty(T)
for t in range(T):
a = agent.act()
r = bandit.pull(a)
agent.update(a, r)
regret[t] = bandit.best - bandit.p[a] # instantaneous regret Delta_{A_t}
return np.cumsum(regret) # cumulative regret over time
bandit = BernoulliBandit([0.20, 0.50, 0.55, 0.60, 0.30]) # arm 3 (p=0.60) is best
T = 5000
cum_greedy = run(EpsilonGreedy(5, eps=0.0), bandit, T) # pure greedy baseline
cum_eps = run(EpsilonGreedy(5, eps=0.1), bandit, T) # epsilon-greedy
print("greedy cumulative regret @T = %.1f" % cum_greedy[-1])
print("eps-greedy cumulative regret @T = %.1f" % cum_eps[-1])
print("greedy slope (last 1000 steps) = %.4f / step" % ((cum_greedy[-1]-cum_greedy[-1001])/1000))
print("eps-greedy slope (last 1000 steps) = %.4f / step" % ((cum_eps[-1]-cum_eps[-1001])/1000))
EpsilonGreedy with eps=0.0 is the pure-greedy baseline of subsection two; with eps=0.1 it explores one pull in ten. The update method is the incremental mean $Q \mathrel{+}= (r - Q)/n$ of subsection two, the only state per arm being the estimate Q and the count N. The slope over the final 1000 steps measures how fast regret is still growing: a flat slope means the agent has stopped paying for ignorance.greedy cumulative regret @T = 285.0
eps-greedy cumulative regret @T = 46.8
greedy slope (last 1000 steps) = 0.0570 / step
eps-greedy slope (last 1000 steps) = 0.0094 / step
The numbers tell the whole story of subsections two and three. The pure-greedy agent's regret grows at a near-constant rate to the end of the run, the linear-regret signature of an agent that pulled an unlucky-looking good arm too few times, locked onto a worse arm, and never recovered. The epsilon-greedy agent's regret grows fast at first (while it explores and learns) and then bends toward flat as it identifies the best arm and exploits it, its late slope far smaller. To see the regret curves directly rather than as endpoint numbers, Code 24.1.2 plots the two cumulative-regret trajectories.
import matplotlib.pyplot as plt
plt.figure(figsize=(7, 4))
plt.plot(cum_greedy, label="greedy (eps=0.0)", color="#b8431f")
plt.plot(cum_eps, label="epsilon-greedy (eps=0.1)", color="#14385c")
plt.xlabel("pull t"); plt.ylabel("cumulative regret")
plt.title("Cumulative regret: greedy stays linear, epsilon-greedy bends sublinear")
plt.legend(); plt.tight_layout()
plt.savefig("regret_curves.png", dpi=120) # write the figure to disk
print("greedy curve is roughly straight; eps-greedy curve flattens")
greedy curve is roughly straight; eps-greedy curve flattens
Finally, the library pair. Everything in Code 24.1.1, the environment, the value estimates, the exploration policy, the update loop, is standard enough that bandit and reinforcement-learning libraries provide it directly. The environment is a one-step Gymnasium-style problem, and a few lines of a bandit toolkit replace the hand-written agent with a maintained, tested implementation. Code 24.1.3 shows the same epsilon-greedy experiment with the agent loop reduced to its essentials.
import numpy as np
# A maintained bandit library exposes the agent as a small reusable object.
# mabwiser's MAB takes arm labels and a learning policy, collapsing the
# from-scratch class to a constructor plus predict/partial_fit, no manual Q/N.
from mabwiser.mab import MAB, LearningPolicy # pip install mabwiser
rng = np.random.default_rng(0)
probs = np.array([0.20, 0.50, 0.55, 0.60, 0.30])
T, K = 5000, 5
arms = list(range(K))
# The multi-armed bandit is the contextless special case: arm labels, no features.
agent = MAB(arms, LearningPolicy.EpsilonGreedy(epsilon=0.1))
agent.fit([0], [1.0]) # warm start so the policy is callable
cum = 0.0
for t in range(T):
a = agent.predict() # library picks the arm (explore or exploit)
r = float(rng.random() < probs[a]) # environment returns the Bernoulli reward
agent.partial_fit([a], [r]) # one-line online update
cum += probs.max() - probs[a] # accumulate regret as before
print("library eps-greedy cumulative regret @T = %.1f" % cum)
EpsilonGreedy class of Code 24.1.1 (its Q and N arrays, the argmax-versus-random branch, and the incremental-mean update, about 15 lines) collapses to a constructor and the two calls predict and partial_fit; the library manages the value estimates, the exploration branch, and the online update internally, and the same policy extends to contextual and neural bandits by swapping the learning policy.library eps-greedy cumulative regret @T = 87.9
Read the three code blocks together. Code 24.1.1 implemented an epsilon-greedy agent with the incremental value estimation of subsection two and showed, against a greedy baseline, the linear-versus-sublinear regret of subsection three. Code 24.1.2 drew those regret curves so the flattening is visible. Code 24.1.3 reproduced the agent in a few library calls, the line-count reduction of the "Right Tool" principle, and noted that the very same interface scales to the contextual bandit that drives real recommenders and ad systems. The agent you built by hand is exactly the one running, in industrial form, in the applications of subsection four.
The from-scratch agent of Code 24.1.1 spent about 15 lines on the Q and N arrays, the explore-versus-exploit branch, and the incremental-mean update. A bandit library (here mabwiser; equivalent objects exist in contextualbandits, vowpalwabbit, river, and reinforcement-learning toolkits) collapses all of it to a constructor plus predict and partial_fit, roughly a fivefold reduction, and handles the value estimation, exploration schedule, and online update internally, while exposing the same interface for the contextual and neural-bandit extensions of the research frontier. Reach for the library for anything beyond a teaching example; write the loop by hand once, to know what the library is doing.
Exercises
Three exercises, one of each type. Worked solutions to selected exercises appear in Appendix G.
Explain in two or three sentences why a pure-greedy bandit agent can pull a suboptimal arm forever, while an epsilon-greedy agent with any fixed $\varepsilon > 0$ cannot stay permanently stuck on a wrong arm. Then explain why, despite never getting permanently stuck, the fixed-$\varepsilon$ agent still incurs linear regret, and state one change to the exploration schedule that makes the regret sublinear.
Extend Code 24.1.1 with two variants from subsection two: (a) an optimistic-initialization agent that sets self.Q = np.ones(K) * 1.0 (or higher) and uses $\varepsilon = 0$, and (b) a decaying-epsilon agent whose exploration rate is $\varepsilon_t = \min(1, cK/t)$ at step $t$. Run all four agents (greedy, fixed-$\varepsilon$, optimistic, decaying) on the same Bernoulli bandit and compare their cumulative-regret curves. Which two achieve the smallest late-slope regret, and does optimistic initialization's front-loaded exploration show up as a steep early rise that flattens hard?
The incremental update of subsection two uses step size $1/n$, which weights all past rewards equally. Replace it with a constant step size $\alpha$ (so $Q \mathrel{+}= \alpha\,(r - Q)$) to make the estimate recency-weighted, then design a non-stationary Bernoulli bandit whose arm probabilities drift or switch partway through the run (the concept-drift setting of Chapter 21). Compare the $1/n$ agent and the constant-$\alpha$ agent on this drifting bandit: which adapts to the change, and why does the equal-weight average fail to track a moving optimum? Discuss how this connects to the non-stationary bandit research frontier and to the online learning of Chapter 20.