Mutating grids with clamping and averaging.
Replace each cell with a function of its surroundings — then enforce a minimum or maximum value.
Clamping forces a value to stay within a range: anything below the floor becomes the floor; anything above the ceiling becomes the ceiling. Averaging replaces a cell with the mean of nearby cells.
The combination — average then clamp — is a basic image-smoothing operation and a classic AP-style 2D problem. The challenge is doing it without reading already-mutated values.
Two operations, one pass.
Compute average from old values; clamp the result; store in a new grid.
Clamping
public static int clamp(int value, int min, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
This idiom is reusable everywhere — image processing, score capping, position limits.
Averaging in-bounds neighbors
public static int neighborAverage(int[][] grid, int r, int c) {
int sum = 0;
int n = 0;
int[] dr = {-1, 1, 0, 0};
int[] dc = {0, 0, -1, 1};
for (int d = 0; d < 4; d++) {
int nr = r + dr[d];
int nc = c + dc[d];
if (nr >= 0 && nr < grid.length
&& nc >= 0 && nc < grid[0].length) {
sum += grid[nr][nc];
n++;
}
}
return sum / n; // integer division
}
Dividing by the actual count n (rather than 4) accounts for corner and edge cells correctly.
Why not mutate in place?
If you write into grid[r][c] while the loop is still running, later neighbor reads pick up the new value. Use a copy as input and the original as the output target — or vice versa.
int[][] result = new int[grid.length][grid[0].length];
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
int avg = neighborAverage(grid, r, c);
result[r][c] = clamp(avg, 0, 255);
}
}
Smoothing vocabulary.
These terms appear in image-processing problems.
- Clamp — restrict a value to a range.
- Floor / ceiling — the minimum and maximum allowed values.
- Average — mean of a set of values.
- Integer division — Java truncates remainders when dividing ints.
- Smoothing — replacing values with neighborhood averages.
- In-place vs result grid — modifying the original vs creating a new array.
How clamping and averaging are tested.
Integer division and in-bounds counts dominate.
- Division by actual neighbor count. Edge cells have fewer neighbors; using a hard-coded 4 gives wrong averages.
- Integer division truncation.
7 / 2is3, not3.5. - Clamp boundary inclusive. Use
<and>(not<=) so the limits themselves are kept. - Read-after-write hazards. Use a result grid when the new value depends on the old neighbors.
Smooth a single cell.
Verify the average and clamp on a small grid.
Problem. Compute the orthogonal neighbor average of cell (1, 1), then clamp it to [0, 100].
{{10, 20, 30},
{40, 200, 60},
{70, 80, 90}}
Step 1. Neighbors of (1, 1): up = 20, down = 80, left = 40, right = 60. Sum = 200. Count = 4. Average = 50.
Step 2. Clamp 50 to [0, 100]: 50 is already in range, so result = 50.
Answer. Cell (1, 1) becomes 50 in the result grid (the original 200 is preserved as input).
What if the average were 150? Clamp would return 100, the ceiling. The clamp catches out-of-range outputs.
Clamping and averaging errors.
Most come from integer math or in-place hazards.
- Dividing by 4 always. Edge cells have fewer in-bounds neighbors.
- Forgetting to count actual neighbors. The counter
nmust match what was summed. - Mutating in place. Causes new averages to read partly-updated neighbors.
- Off-by-one clamp. Using
<=instead of<excludes the boundary itself. - Dividing by zero in a 1x1 grid — neighbor count would be 0.
Smoothing recipe.
The three sub-steps and their gotchas.
Clamp & Average Reference
AP Quick Reference| Step | Operation | Gotcha |
|---|---|---|
| Count valid neighbors | Bounds-check each direction | Increment a counter inside the check. |
| Sum values | Accumulate grid[nr][nc] |
Inside the same check. |
| Average | sum / n |
Integer division truncates. |
| Clamp | Restrict to [min, max] |
Boundary values are kept as-is. |
| Store | Write to a new grid | Never write into the input. |
How to master clamping and averaging.
Build a clamp helper and a neighbor-average helper, then compose.
Use the three practice tools below this lesson in order:
- MCQ Practice — for each cell, count the actual in-bounds neighbors before computing the average.
- Java Lab — write clamp and neighborAverage as separate methods. Combine them in a third method.
- FRQ Practice — for smoothing or capping problems, always build a result grid first; never mutate in place.
Practice — attempt these now.
AP-style assessments aligned to this lesson. Time them.