Ready to begin
No records have been processed. Start with Next step.

Follow the dictionary as its keys and values change. Predict each lookup or update before revealing the next step.
Ready
No records have been processed. Start with Next step.
Predict the next key, whether it already exists, and the value that will be stored.
seen = {}
foreach A in scores {
seen[A.CardNo] = True
foreach B in scores {
if (not isKey(seen,B.CardNo)) {
if (A.Mathematics == B.Mathematics) {
# record [A.CardNo,B.CardNo]
}
}
}
}Mark the OUTER card before checking inner cards. The mark excludes the card itself and all earlier outer cards. A later inner card remains unmarked until it becomes an outer card. Marking every inner candidate would wrongly remove future pairs. Start a fresh seen dictionary for a new task.
For three cards A,B,C, the accepted comparisons are A–B, A–C and B–C. A–A and reversed B–A are skipped. For 30 students there are 435 accepted mark comparisons. This particular two-full-loop algorithm still performs 900 membership checks. It organizes pairs but does not remove the nested loops.
Why mark A before the inner loop?
To skip A itself and every previously processed outer card.
Should we mark B just because it was an inner candidate?
No. That would wrongly prevent later comparisons involving B.
Does a seen dictionary make this pair algorithm one-pass linear time?
No. It still scans all inner cards for each outer card; the accepted unordered comparisons number n(n−1)/2.