Part 3 · Constraint Randomization · Intermediate
Array Constraint Cookbook
Six classic interview problems with complete solutions: sorted array, exactly-K matches, no adjacent equal, one-hot array, alternating pattern, bounded sum.
How to use this cookbook
Every problem below is a real interview question. Each solution composes the same three building blocks from the previous lessons: a size bound , foreach element/relation constraints (with boundary guards), and aggregate equations (often the sum-of-booleans counting idiom). Before writing any constraint, run the two feasibility checks: does the value space have enough room (pigeonhole), and can the element bounds reach the aggregate target (min/max arithmetic)? Interviewers award as much credit for stating these checks as for the constraint itself.
COOKBOOK DECISION GUIDE
"ordered / sorted" -> foreach + guarded neighbor relation
"exactly K / at most K" -> sum() with (int'(condition)) == K
"all different" -> unique { arr } (check pigeonhole!)
"no two adjacent ..." -> foreach + guarded i / i-1 relation
"one-hot / one special" -> counting sum == 1 (+ rand index variant)
"total/budget/checksum" -> sum() with (int'(item)) == N
(check: size*min <= N <= size*max)Problems 1-3: sorted, exactly-K, no adjacent equal
1. Sorted (non-decreasing) array
class p1_sorted;
rand bit [7:0] a[];
constraint c {
a.size() inside {[5:10]};
foreach (a[i]) if (i > 0) a[i] >= a[i-1]; // non-decreasing
// strictly increasing variant: a[i] > a[i-1]
// feasibility: needs >= size distinct values in range
}
endclassNon-decreasing (>=) is always feasible. Strict (>) adds the pigeonhole requirement — say this out loud in an interview.
2. Exactly K elements equal to V (sum-of-booleans idiom)
class p2_exactly_k;
rand bit [7:0] a[10];
parameter bit [7:0] V = 8'h5A;
parameter int K = 3;
constraint c {
a.sum() with (int'(item == V)) == K;
// optional: non-matching elements stay away from near-misses
foreach (a[i]) (a[i] != V) -> !(a[i] inside {V-1, V+1});
}
endclassThe comparison item == V is 1 or 0 per element; the widened sum counts matches. Variants: <= K for "at most", >= 1 for "at least one".
3. No two adjacent elements equal
class p3_no_adjacent;
rand bit [3:0] a[];
constraint c {
a.size() inside {[6:12]};
foreach (a[i]) if (i > 0) a[i] != a[i-1];
}
endclass
// Feasible whenever the value space has >= 2 values:
// each element only needs to avoid ONE neighbor value.Problems 4-6: one-hot, alternating, bounded sum
4. One-hot array (exactly one element nonzero)
class p4_onehot;
rand bit [7:0] a[8];
rand int unsigned hot_idx;
// Style A: pure counting — position falls where it may
constraint c_count { a.sum() with (int'(item != 0)) == 1; }
// Style B: rand index — when the test wants to KNOW the position
constraint c_idx {
hot_idx < 8;
foreach (a[i]) (i == hot_idx) ? (a[i] != 0) : (a[i] == 0);
}
// Use ONE style; both together are consistent but redundant.
endclassStyle B is the senior answer when follow-up constraints need the position ("the hot element is in the upper half": add hot_idx >= 4).
5. Alternating pattern (high/low or parity)
class p5_alternating;
rand bit [7:0] a[];
rand bit start_high; // randomize which phase comes first
constraint c {
a.size() inside {[4:10]};
foreach (a[i])
if (i % 2 == int'(start_high))
a[i] >= 8'h80; // "high" slots
else
a[i] < 8'h80; // "low" slots
}
endclass
// Pure parity-of-index version: a[i][0] == (i % 2) forces
// alternating odd/even VALUES instead of high/low bands.6. Sum in a range with bounded elements (feasibility math)
class p6_budget;
rand bit [7:0] a[];
constraint c {
a.size() inside {[4:8]};
foreach (a[i]) a[i] inside {[10:50]};
a.sum() with (int'(item)) inside {[120:200]};
}
endclass
// Feasibility check BEFORE trusting this:
// size=4: total range [4*10 : 4*50] = [40:200] -> overlaps [120:200] OK
// size=8: total range [8*10 : 8*50] = [80:400] -> overlaps OK
// If the target range missed every size's reachable window,
// randomize() would return 0 with no error message.Walking the min/max arithmetic for each candidate size is exactly the verbal reasoning interviewers want to hear — it proves you can predict solver failures instead of debugging them.
Interview angle
How to perform on a live array puzzle
Restate the problem as constraints out loud: size bound, element bounds, relations, aggregates.
Run feasibility checks verbally: pigeonhole for uniqueness/strict order, min-max window for sums.
Write the guarded foreach FIRST if relations exist — boundary guards are the most-watched detail.
Reach for sum() with (int'(cond)) for any counting requirement — name it as the counting idiom.
Mention the int'() cast on every sum over narrow elements, even if totals look small today.
Offer a post_randomize() check or a $display loop to validate — shows verification mindset.
If you blank on syntax, describing the expanded constraint set ("I need a[i] != a[i-1] for every i from 1 to size-1") earns most of the credit — interviewers test the solution-space model more than keyword recall.
Key takeaways
Every classic array puzzle decomposes into size bound + guarded foreach relations + aggregate equations.
The counting idiom sum() with (int'(cond)) == K handles exactly/at-most/at-least in one form.
Always verbalize feasibility: pigeonhole for distinct/strict-order, min-max window for sum targets.
Prefer a rand index variable over pure counting when later constraints need to reference the special position.
Validate generated arrays procedurally after randomize — constraints and intent can disagree silently.
Common pitfalls
Unguarded a[i-1]/a[i+1] at boundaries — the most common live-coding error in this topic.
Sum targets outside the reachable [size*min : size*max] window — silent randomize() failure.
Missing int'() cast on sums of narrow elements — mod-2^width wraparound corrupts the equation.
Mixing two redundant styles (counting AND indexed one-hot) without realizing one suffices — signals shaky understanding.
Forgetting that strictly-increasing + narrow element type + large size is pigeonhole-unsatisfiable.