Solving Jumble Puzzles Dictionaries Hashes and Permutations Richard
Solving Jumble® Puzzles Dictionaries, Hashes and Permutations Richard A. De. Venezia
Jumble® Puzzles • Four scrambled words – Two five letter words – Two six letter words • Marked letters of unscrambled words – Scramble of an answer to a cartoon hint
Sample Puzzle LASRN OOO SHACE O OO HOTGUF OO O CLATHE OO O Hint: You are here. A:
Puzzle Representation • Scrambled words – letter pool • Unscrambled key – Circled positions to answer pool • Answer key – Dashes
Problem Solving • Input • Unjumble – Permutation, Lookup • Letter Pool • Find Answer – Permutation, Lookup • Output Results
Puzzle Data • One puzzle per date datalines; 2006 -10 -09 ------fasrn OOO-shace O--OO hotguf --OO-O clathe --OO-O run;
Input data jumbles (keep=date jumble circle) answers (keep=date answer) ; input date yymmdd 10. answer $char 50. ; output answers; do i = 1 to 4; input jumble: $6. circle: $6. ; output jumbles; end;
ALLPERM • Permutations – interchange • “Because each permutation is generated from the previous permutation by a single interchange, the algorithm is very efficient. ” SAS Help
ALLPERM data allperms; array p[5] (1: 5); do i = 1 to FACT(5); call ALLPERM(i, of p[*]); output; end; run;
Lookup • DATA Step – SET KEY=index – Custom format – Hash object • Hash – Key – Data
Hash object declare HASH dict (); dict. define. Key('word'); dict. define. Done(); word='SESUG'; dict. add(); word='SUGI'; found = (dict. check()=0); put found=; --found=0
Dictionary Data • http: //wordlist. sourceforge. net/ – Word lists • http: //prdownloads. sourceforge. ne t/wordlist/agid-4. zip – infl. txt (inflected) Part of Speech conferee N: conferees conference N: conferences conference V: conferenced | conferencing | conferences conferencing N? : conferencings
Dictionary Input infile INFL dlm=' , |' missover end=end; input word pos @; do until (word=''); word = compress (word, '~<!? '); if not indexc (word, '123456790. {}') then words. replace(); input word @; end; input;
Dictionary Output • Hash method OUTPUT(dataset: dataset) rc = words. output (dataset: ’sasuser. agid_dictionary’);
Unjumble • Data – Four jumbled words • Permutations – of letter array • 5! + 6! – 1, 680 lookups by word – ALLPERM • N = n 1 n 2 n 3 n 4 combinations
Load Dictionary declare hash dict (); dict. define. Key ('word'); dict. define. Done (); length word $6; do until (end_dict); set &dictionary (where=(length(word) in (5, 6))) end=end_dict ; word = lowcase(word); dict. replace(); end;
Data do until (end_jumble); set jumbles end=end_jumble; where date = “ 09 OCT 2006”D; _i + 1; jumble = lowcase(jumble); link allperm; end;
ALLPERM section • Jumbled Word to Letter Array • For each permutation of Array – Array to Word – If Word in Dictionary • Determine circled letters • OUTPUT
Word to Letter Array • length jumble $6 • array letters $1 letter 1 -letter 6 • call pokelong (jumble, addrlong (letters[1]))
Letter Array to Word • length jumble $6 • array letters $1 letter 1 -letter 6 • jumble = peekclong (addrlong (letters[1])), 6)
Check Each Permutation L = length (jumble); call pokelong(jumble, addrlong(letters[1])); do i = 1 to fact (L); if L = 5 then call allperm (i, of letters 1 -letters 5); else call allperm (i, of letters 1 -letters 6); word = peekclong (addrlong (letters(1)), L); if (word ne jumble) and dict. check () = 0 then. . . end;
Circled Letters k = 1; circled = ' '; do j = 1 to length (circle); if substr(circle, j, 1) = 'O' then do; substr(circled, k, 1) = substr(word, j, 1); substr(wurd, j, 1) = upcase(substr(wurd, j, 1)); k + 1; end; OUTPUT; c l a t h e * * O O * O c h a l e t circled=alt wurd=ch. ALe. T
WORK. UNJUMBLE 1 2 1 1 = 2 combinations
WORK. POOL create table pool as select a. circled as A, b. circled as B , c. circled as C, d. circled as D , a. wurd as _A, b. wurd as _B , c. wurd as _C, d. wurd as _D from unjumble as a, unjumble as b , unjumble as c, unjumble as d where a. _i = 1 and b. _i = 2 and c. _i = 3 and d. _i = 4 ;
Finding the Answer • Permute pool – ALLPERM ? – 12 letter pool = 479, 001, 600 perms • Lexicographic ordering – Permutation f precedes a permutation g in the lexicographic (alphabetic) order iff for the minimum value of k such that f(k) g(k), we have f(k) < g(k).
Lexico-what? • Ordered progression – Avoid unnecessary checks • Example – iterator arrives at 1 -3 -2 -4 -5 – dictionary says no 1 -3’s – advance to nextperm 1 -4 -x-x-x
Next Perm • From right – find i where f ( i-1 ) < f ( i ) • From i+1 – find j where f ( j ) < f ( i-1 ) • Swap – f ( i-1 ) and f ( j - 1 ) • Reverse – from i to end i j 1 -5 -4 -3 -2 swap 2 -5 -4 -3 -1 reverse 2 -1 -4 -3 -5 2 -1 -3 -4 -5
NEXTPERM next_perm: i = 12; do while (i > 1); if (indx[i-1] <= indx[i]) then leave; i + (-1); end; if i = 1 then return; j = i + 1; do while (j <= 12); if (indx[i-1] >= indx[j]) then leave; j + 1; end;
NEXTPERM * swap ; ix 1 = i-1; ix 2 = j-1; h = indx[ix 1]; indx[ix 1] = indx[ix 2]; indx[ix 2] = h; * reverse v[i. . n] by swapping; ix 1 = i; ix 2 = 12; do while (ix 1 < ix 2); h = indx[ix 1]; indx[ix 1] = indx[ix 2]; indx[ix 2] = h; ix 1 + 1; ix 2 + (-1); end;
Letter Pool • POOL_0 – Original letters • POOL – Letters ordered according to current permutation • Mapping – Pool_0 to Pool
Filling the Pool array circles [4] $6 a b c d; ix = 1; do i = 1 to dim(circles); do j = 1 to length (circles[i]); pool 0[ix] = substr (circles[i], j, 1); pool [ix] = pool 0[ix]; indx [ix] = ix; ix + 1; end;
Mapping * map items that were permuted; if i > 1 then do ix = i-1 to dim(pool); pool [ ix ] = pool 0 [ indx [ ix ] ] ; end; return;
Dead Ends • 12 letters • snaaesugtalt – No words start with “snaa” – Can skip remaining sequence of 8! permutations that start with “snaa”
Short Cut • • Word not in dictionary Find shortest prefix that also isn’t Sort after prefix Compute next permutation
Prefix Dictionary * hash for dictionary of word prefixes; declare hash part (); part. define. Key ("length", "count", "prefix"); part. define. Done (); *. . . For each word added to dictionary. . . ; * add word prefixes to word prefix dictionary; length = length(word); do count = 2 to length-1; prefix = substr(word, 1, count); if part. check() eq 0 then continue; part. add(); end;
Word Tests word=peekclong(addrlong(pool(p)), length); if (dict. check() ne 0) then do; * word not found, find smallest prefix * not in prefix dictionary; do count = 2 to length-1; prefix = substr(word, 1, count); if part. check() ne 0 then leave; end;
Last Perm of Tail • Sort the mapping indices – Descending order • Method – Index Testing – Not Quicksort
Tail Sort array map[12]; call missing (of map[*]); LEFT = p + count; RIGHT = 12; do ix = LEFT to RIGHT; map [ indx [ ix ] ] = 1; end; j = 12; do ix = 1 to 12 while (j >= LEFT); if not map[ ix ] > 0 then continue; indx [ j ] = ix; j = j - 1; end;
Word Found • Two cases – Last word of answer • Output • Proceed using next perm – Not last word • Continue using same perm
Successes if (q = 2) then do; if (soln. check() ne 0) then do; soln. add(); put 'NOTE: ' words[*]; end; words[q] = ' '; end; else do; * advance p to next word place; q + 1; p + length; length = wordlens[q]; CONTINUE; * return to top of loop; end;
Fallback • Word construction – starts at position p • Permutation – altered indices prior to p • search space exhausted • Response – reduce p to prior start points
Backing Up do while ((i <= p) and (i > 1)) ; q = q - 1; words[q]=''; length = wordlens[q]; p = p - length; end;
Answer is: SESUG ATLANTA Paper, Slides, and Code available at http: //www. devenezia. com/papers
About the Author Richard A. De. Venezia Independent Consultant 9949 East Steuben Road Remsen, NY 13438 (315) 831 -8802 http: //www. devenezia. com/contact. php
- Slides: 44