Real automated code checkers (MOSS, JPlag, Turnitin's code plagiarism module) never compare raw text β a renamed variable or reformatted whitespace would defeat that instantly. Instead the source is first tokenized by grammatical role (keyword, identifier, operator, literal, punctuation) so cosmetic renames vanish, then fingerprinted with the winnowing algorithm (Schleimer, Wilkerson & Aiken, 2003):
1. Slide a window of k consecutive tokens across the stream;
hash each window: h_i = Ξ£ token[i+j] Β· B^j mod M
2. Slide a second window of w consecutive hashes;
keep only the MINIMUM hash in each window
(rightmost on ties) β this token position becomes
a "fingerprint". Adjacent duplicate picks are merged.
3. Similarity(A,B) = |fingerprints(A) β© fingerprints(B)|
βββββββββββββββββββββββββββββββββββ
|fingerprints(A) βͺ fingerprints(B)|
- Renamed vars β identifiers are re-labelled but keep the same token category, so every k-gram hash is unchanged β similarity stays ~100%. This is exactly why naive rename-to-evade attempts fail against real graders.
- Reordered blocks β independent statements are shuffled; k-grams inside a block still match, but the ones spanning a block boundary break β similarity drops but stays substantial.
- Rewritten β a structurally different implementation shares almost no k-gram hashes β low similarity, correctly not flagged.
- Winnowing guarantees at least one fingerprint survives in any window of w tokens, which bounds detection sensitivity β smaller w catches shorter copied fragments but keeps more fingerprints (more storage); larger k reduces accidental hash collisions but can miss very short copied snippets.
This is the same document-fingerprinting idea autograders reuse for test-suite similarity clustering β grouping hundreds of student submissions by structural similarity before a human ever reads them.