FRQ
› csapa
Feeder: 2024 FRQ 1
A step-by-step solution to the 2024 AP CSA FRQ 1 (Feeder), covering weighted random simulation and using a helper method to count successful trials in Java.
A bird feeder that occasionally gets raided by a bear sits behind this AP Computer Science A free-response question — you simulate a single day of eating first, then run that simulation across many days and count how often there was actually food to find.
What This FRQ Tests
- AP CSA units: Unit 3 (Boolean Expressions and if Statements), Unit 4 (Iteration), and Unit 5 (Writing Classes/methods)
- Core skill: using
Math.random()to model two different kinds of randomness in the same method — a rare "abnormal" event, and a normal, evenly-distributed amount - Secondary skill: calling a helper method inside a loop and using its side effect (rather than its return value) to decide what to count
- Official category: "Methods and Control Structures" — always FRQ 1 on the AP CSA exam
The Setup
Feederholds:private int currentFood— grams of food currently in the feeder, never negative
- You're asked to write two methods:
void simulateOneDay(int numBirds)— simulates one day of eating and updatescurrentFoodint simulateManyDays(int numBirds, int numDays)— runssimulateOneDayfornumDaysdays and counts how many of them found food
Part (a): Writing simulateOneDay(int numBirds)
The Rule, Broken Down
- 5% of the time (abnormal conditions): a bear empties the feeder completely —
currentFoodbecomes0. - 95% of the time (normal conditions): every bird eats the same random amount, somewhere from 10 to 50 grams inclusive, with every whole-gram value equally likely.
- The total eaten is
numBirdstimes that per-bird amount. - If that total is more than what's currently in the feeder, the birds simply empty it (
currentFoodbecomes0) rather than going negative. - Otherwise, subtract the total from
currentFoodas normal.
Step-by-Step Approach
- Roll a single random check for the 5% bear scenario:
Math.random() < 0.05. - If that's true, set
currentFoodto0and stop — nothing else needs to happen this day. - Otherwise, generate one random per-bird amount from 10 to 50 inclusive (41 possible whole values).
- Multiply that by
numBirdsto get the total eaten. - Compare the total to
currentFood: if it's more, setcurrentFoodto0; if not, subtract it.
The Code
public void simulateOneDay(int numBirds)
{
if (Math.random() < 0.05)
{
currentFood = 0;
}
else
{
int perBird = (int) (Math.random() * 41) + 10;
int totalEaten = perBird * numBirds;
if (totalEaten > currentFood)
{
currentFood = 0;
}
else
{
currentFood = currentFood - totalEaten;
}
}
}
Why Each Piece Matters
Math.random() < 0.05— sinceMath.random()is uniform over[0.0, 1.0), the sub-range[0.0, 0.05)covers exactly 5% of that interval, giving the bear scenario exactly a 5% chance.(int) (Math.random() * 41) + 10—Math.random() * 41produces a value in[0.0, 41.0); truncating gives a whole number from0to40(41 possibilities); adding10shifts that range to10through50, matching "10 to 50 grams inclusive" exactly.- One random amount shared by every bird — the problem says each bird eats the same amount that day, so
perBirdis generated once and multiplied bynumBirds, not generated separately per bird. totalEaten > currentFood, not>=— if the total eaten exactly equals what's left, the feeder ends the day at exactly0either way; the>vs>=distinction only matters for correctly landing on0instead of going negative when the total exceeds what's available.
Common Mistakes to Avoid
- Checking the 5% condition with the wrong comparison direction (e.g.
Math.random() > 0.05gives the bear a 95% chance instead of 5%). - Generating a new random amount per bird instead of one shared amount for the whole day — the problem is explicit that every bird eats the same amount on a given day.
- Using the wrong random range, e.g.
(int) (Math.random() * 50) + 10, which would allow values up to59instead of stopping at50. - Letting
currentFoodgo negative by subtractingtotalEatenunconditionally instead of checking againstcurrentFoodfirst.
Part (b): Writing simulateManyDays(int numBirds, int numDays)
The Rule, Broken Down
- Simulate
numDaysdays in a row usingsimulateOneDay. - A day counts as one where "food was found" if there was food in the feeder before that day's simulation ran.
- Return how many of the
numDaysdays met that condition.
Step-by-Step Approach
- Track a running count, starting at
0. - Loop
numDaystimes. - Each iteration, check
currentFoodbefore callingsimulateOneDay— if it's greater than0, increment the count. - Call
simulateOneDay(numBirds)to advance to the next day. - After the loop, return the count.
The Code
public int simulateManyDays(int numBirds, int numDays)
{
int count = 0;
for (int day = 0; day < numDays; day++)
{
if (currentFood > 0)
{
count++;
}
simulateOneDay(numBirds);
}
return count;
}
Why Each Piece Matters
- Checking
currentFoodbefore callingsimulateOneDay— once the feeder is empty, no later call can "find" food that isn't there, so the only meaningful moment to check is right before that day's simulation runs. - Calling
simulateOneDayonce per iteration, unconditionally, after the check — the day still happens (and food amounts still change) regardless of whether it counted, sincesimulateOneDayis what actually advances the simulation. - Not checking
currentFoodagain after the call — the problem only cares whether food was available going into the day, not what's left afterward.
Tracing the Example
Using the second example from the question — currentFood starts at 250, simulateManyDays(10, 5):
| Day | currentFood before |
Counted? | currentFood after |
|---|---|---|---|
| 1 | 250 | yes | 150 |
| 2 | 150 | yes | 0 |
| 3 | 0 | no | 0 |
| 4 | 0 | no | 0 |
| 5 | 0 | no | 0 |
Final count: 2 — matches the question exactly, and once currentFood hits 0 on day 2, every remaining day correctly stops counting even though simulateOneDay still runs on each of them.
Common Mistakes to Avoid
- Checking
currentFoodafter callingsimulateOneDayinstead of before — this would incorrectly count a day where the feeder started at0and stayed at0, or miss counting the very last day the feeder still had food before being emptied. - Not calling
simulateOneDayat all whencurrentFoodis already0. The day still needs to run (in case a future part of a larger simulation depended on it), even though it won't count. - Off-by-one on the loop, running
numDays + 1ornumDays - 1iterations instead of exactlynumDays.
Key Takeaways
- Modeling "two different random outcomes with different probabilities" is a nested pattern: check the rare case first with its own probability, and only compute the common case's details in the
elsebranch. - "Did something happen on this step" almost always needs to be checked based on the state before the step runs, not after.
- Calling a helper method inside a loop and using its side effect (the state it changes) rather than a return value is a common pattern once a method's job is to update, not compute.