Upload
river-wyse
View
217
Download
3
Tags:
Embed Size (px)
Citation preview
Solving Jumble® PuzzlesDictionaries, Hashes and Permutations
Richard A. DeVenezia
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
LASRNO O O
SHACEO O O
HOTGUF O O O
CLATHE O O 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 yymmdd10. answer $char50.;
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.defineKey('word'); dict.defineDone();
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.net/wordlist/agid-4.zip– infl.txt (inflected)
conferee N: confereesconference N: conferencesconference V: conferenced | conferencing | conferencesconferencing N?: conferencings
Part ofSpeech
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 methodOUTPUT(dataset:dataset)
rc = words.output (dataset:’sasuser.agid_dictionary’);
Unjumble
• Data– Four jumbled words
• Permutations– of letter array
• 5! + 5! + 6! + 6!– 1,680 lookups by word– ALLPERM
• N = n1 n2 n3 n4 combinations
Load Dictionary
declare hash dict ();dict.defineKey ('word');dict.defineDone ();
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 = “09OCT2006”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 letter1-letter6• call pokelong (jumble, addrlong (letters[1]))
Letter Array to Word
• length jumble $6
• array letters $1 letter1-letter6• 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 letters1-letters5); else call allperm (i, of letters1-letters6);
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;end;OUTPUT;
c l a t h e* * O O * Oc h a l e tcircled=altwurd=chALeT
WORK.UNJUMBLE
word circled circle _i wurdsnarf sna OOO-- 1 SNArfaches aes O--OO 2 AchESchase cse O--OO 2 ChaSEfought ugt --OO-O 3 foUGhTchalet alt --OO-O 4 chALeT
1211 = 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 ;
A B C D _A _B _C _Dsna aes ugt alt SNArf AchES foUGhT chALeTsna cse ugt alt SNArf ChaSE foUGhT chALeT
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
1 - 5 - 4 - 3 - 2
swap
2 - 5 - 4 - 3 - 1
reverse
2 - 1 - 4 - 3 - 5
2 - 1 - 3 - 4 - 5
i j
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 ;ix1 = i-1; ix2 = j-1; h = indx[ix1]; indx[ix1] = indx[ix2];indx[ix2] = h;
* reverse v[i..n] by swapping;ix1 = i; ix2 = 12;do while (ix1 < ix2); h = indx[ix1]; indx[ix1] = indx[ix2]; indx[ix2] = h;
ix1 + 1; ix2 + (-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]); pool0[ix] = substr (circles[i],j,1); pool [ix] = pool0[ix]; indx [ix] = ix; ix + 1; end;end;
Mapping
* map items that were permuted;
if i > 1 thendo ix = i-1 to dim(pool); pool [ ix ] = pool0 [ indx [ ix ] ] ;end;
return;
a b c
3 1 2
c a b
Dead Ends
• 12 letters
• s n a a e s u g t a l t– No words start with “snaa”
– Can skip remaining sequence of 8! permutations that start with “snaa”
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 12 11 10 9 8 7 6 5
1 2 3 5 4 6 7 8 9 10 11 12
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.defineKey ("length", "count", "prefix");part.defineDone ();
* ... 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
3 5 1 4 2
1 1 . 1 .11
22
32
43
5
3 5 43
22
11
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. DeVeneziaIndependent Consultant
9949 East Steuben RoadRemsen, NY 13438
(315) 831-8802
http://www.devenezia.com/contact.php