Congratulations to Ian Taylor. He is the Fastest Codeslinger for 2008.
Ian took around 30 minutes to craft his solution in Ruby and post it to the local contest server.
A statement of the problem is here: http://www.gatorlug.org/node/199 [1]
Behold the winning entry:
# Ian Taylor
#
# Gatorlug 2008 Codeslinger Shootout Submission
#
# answer 358926471
$limit = 100000
def perm(a,b=[])
if a.empty?
$limit -= 1
if $limit == 0
puts b.to_s
exit
end
else
a.each do |e|
perm(a-[e],b+[e])
end
end
end
a = (1..9).to_a
perm(a)
Jim Wilson submitted this gem in Python:
# Jim Wilson's Python solution to the 2008 Codeslinger competition. This is # essentially a translation of Ian's winner with a little annotation to # make it comprehensible to my feeble brane. # # I stared at this thing until I thought my head would explode. I was # trying to think of Ian's code as a bottom-up approach; after I realized # it was obviously more top-down, it suddenly became crystal clear. # # NOTE for Rubyheads: Python "lists" are apparently Ruby "arrays". count = 0 # Permutation counter def permute(head, tail): # completed prefix, ordered set of leftovers global count # Static count for recursive routine if tail: # If still more items to tack onto end, for item in tail: # Loop through items (largest first): newhead = head+[item] # new prefix (a *separate* list!) newtail = list(tail) # new suffix (includes spurious "item") newtail.remove(item) # remove "item", earler appended to prefix permute(newhead, newtail) # handle (now-reduced) tail permutations else: # If no more items to add, count += 1 # count this valid permutation if count == 100000: # If 100,000th valid permutation, print head # print the digits (as a list) raise SystemExit # No need to go further. # Ian's solution is O(N!), where N is the number of items (digits) in the list. # Here's a (supposedly) O(N*N) solution. This approach was suggested to me by # Robert Munyen who was wasn't competing, but was solving it on a napkin. # # Of course, I'm too lazy to code it myself; this version was lifted almost # verbatim from: # # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/126037 def NPerms(L): """ Returns factorial(len( )), the number of ways
can be permuted" """ return reduce(lambda x, y: x*y, range(1, len(L)+1), 1) def PermN (L, N): """ Returns the Nth permutation of list L. NOTE: L is devoured. """ N -= 1 # 1st, 2nd, 3rd, ... instead of 0th, 1st, 2nd, ... R = [] # Result P = NPerms(L) # Number of possible permutations N %= P while L: # Until list is devoured: P /= len(L) # Number possible permutation remaining D, N = N//P, N%P # L[D] is next digit of result R.append(L.pop(D)) # Remove digit from L, append it to R return R # We must call the O(N) function first since the O(N!) version exits when it # reaches the target permutation. On my machine, the first answer comes # immediately; the second arrives after a substantial pause. print PermN([1,2,3,4,5,6,7,8,9], 100000) permute([], [1,2,3,4,5,6,7,8,9])
Robert Munyer was trying to work things out on a napkin. He sent this in:
; For the person who wanted the algorithm that I was using (mentally) to get
; the answer using a napkin and calculator: it's below. On a computer, the
; speed is too fast to measure, because it gets the answer with only eight
; multiplications and eight divisions.
;
; To make it slow enough to measure, I had it calculate the googolth
; permutation, instead of just the hundred thousandth permutation, and I ran
; it on a 166 MHz pentium. It got that answer in 0.02 seconds.
(defun shoot (num-cards index)
(labels ((main-loop (index factorials in out) ; in & out are lists of cards
(cond ((zerop index) (revappend out in))
(t (multiple-value-bind (quotient remainder)
(truncate index (first factorials))
(let ((card (nth quotient in)))
(main-loop remainder (rest factorials)
(remove card in) (cons card out))))))))
; the loop below just builds a list of cards and a list of factorials
(do ((n 2 (+ 1 n))
(fact 1 (* n fact))
(cards '(1) (cons n cards))
(facts nil (cons fact facts)))
((> n num-cards)
(main-loop (- index 1) facts (reverse cards) nil)))))
; > (shoot 9 100000)
; (3 5 8 9 2 6 4 7 1)
;
; > (shoot 70 (expt 10 100))
; (59 31 14 44 33 24 51 1 29 9 17 12 43 61 69 70 53 21 65 36 68 30 41 19 66 34
; 50 7 26 6 32 10 60 20 55 42 4 25 67 64 2 57 39 27 52 48 38 15 54 45 58 18 23
; 37 3 22 13 16 46 40 56 11 62 63 49 8 35 28 47 5)
Cain Norris makes a pitch for Haskell with this:
While I didn't make it out for the contest, I'll go ahead and shill for Haskell by submitting a solution. I've written it out twice, one in good style, once in three lines. Same hopefully smart algorithm, and basically the same code, though. Both print "358926471"-- hopefully correct! Cain long pretty version ------------------- import Data.List main :: IO () main = putStrLn $ lexpermn "123456789" 99999 fact :: Int -> Int fact = product . enumFromTo 1 lexpermn :: (Eq a) => [a] -> Int -> [a] lexpermn [] _ = [] lexpermn xs n = x : lexpermn (delete x xs) (mod n m) where m = fact $ length xs - 1 i = div n m x = xs!!i 3 78col lines version --------------------- import Data.List; main = putStrLn $ lexpermn "123456789" 99999 lexpermn xs n = if xs == [] then [] else x : lexpermn (delete x xs) (mod n m) where m = product . enumFromTo 1 $ length xs - 1; i = div n m; x = xs!!i
Edward Allcut shows us that Bash can get the job done too:
#!/bin/bash
shopt -s extglob nullglob
declare -i TARGET=100000
declare -i FOUND=0
ITEMS="1 2 3 4 5 6 7 8 9"
function remove()
{
echo "${2//$1?( )}"
}
function candidate()
{
FOUND=$(( FOUND + 1 ))
#echo "Found $FOUND"
if [[ $FOUND -eq $TARGET ]]; then
echo "$1"
exit
fi
}
function gen()
{
if [[ $1 == *( ) ]]; then
candidate "$2"
return
fi
for i in $1; do
gen "$(remove $i "$1")" "$2$i"
done
}
# prints 358926471
gen "$ITEMS" ""
Ian Taylor doesn't want C to be left out:
// Ian Taylor// // Adapted from the algorithm from: // // The Art of Computer Programming // Volume 4: Generating All Tuples and Permutations // 7.2.1.2 #include #include void swap(char *a, char *b) { int temp = *a; *a = *b; *b = temp; } int main() { char a[] = "0123456789"; int n = strlen(a)-1; int i,j,k,l; for (i=0; i<=100000; i++) { // puts(a+1); for (j=n-1; a[j] >= a[j+1]; j--); for (l=n; a[j] >= a[l]; l--); swap(&a[j], &a[l]); for (k=j+1,l=n; k < l; k++,l--) swap(&a[k],&a[l]); } puts(a+1); return 0; }