More Card Shuffling

Mark Power

Joe Horn's DECK program (V14N1P31) is certainly very neat and efficient. I'm not sure of Ron Cook's suggestion (V14N5P8) that the CEIL command can be removed without affecting the randomness of the output and so the programs presented below include it. My dislike of CEIL's removal is based on the observation that the first item in the deck seems more likely to be the number 1.

When run on my HP48GX rev.P, Joe's original program takes about 0.76s. By converting the program to SYSRPL the execution time can be reduced to 0.48s and the program size reduced from 62.5 bytes to 37.5 bytes. This represents a reduction in both execution time and program size of about 40%.

Note: The programs below use supported entry points only and have been tried on an HP48SX rev.D and HP48GX rev.P, but if you are in doubt, back up your memory first.

DECK (in format ready to be assembled by JAZZ)

:: FIFTYTWO #1+_ONE_DO INDEX@ UNCOERCE DUP %RAN %* %CEIL
COERCE UNROLL LOOP FIFTYTWO {}N ;

If you don't have JAZZ, you can use the following version with ASC->. Make sure that you check the checksum and size!

DECK.ASC (in format ready to be supplied to ASC->)

%%HP: T(3)A(R)F(.);
"D9D2026B46BD37012270FBD81881302CFA2CB9A237FA2AEC81E93304337026B4
695450B213054D7"

Checksum of ASC-> format string: #DACDh 90 bytes.
Checksum of assembled DECK: #7D45h 37.5 bytes.

The basic DECK program can be easily modified to produce a list of N numbers in random order:

DECKN (in JAZZ format)

::
 0LASTOWDOB! CK1NOLASTWD CK&DISPATCH1 real
 ::
  COERCE DUP#0=ITE SETSIZEERR
  DUP1LAMBIND
  #1+_ONE_DO INDEX@ UNCOERCE DUP %RAN %* %CEIL COERCE UNROLL
  LOOP
  1GETABND {}N
 ;
;


DECKN.ASC (for ASC->)

%%HP: T(3)A(R)F(.);
"D9D20D48812BA812BF819FF30D9D20AEC815CE362AC81AC436BD37012270FBD8
1881302CFA2CB9A237FA2AEC81E9330433706B43695450B2130B21303297"

Checksum of ASC-> format string: #C02Bh 130 bytes.
Checksum of assembled DECKN: #7923h 60 bytes.

Error checking is provided to ensure that a real number is on the stack before use and that its value is greater than zero. There is no checking for large numbers.

Negative real numbers are converted to <0d> by COERCE and produce the error "Bad Argument Value" (as does supplying zero as an input). If you supply fractions they are rounded to the nearest integer, so 0.3 DECKN produces "Bad Argument Value" but 0.5 DECKN produces { 1 }. Not supplying a real on the bottom of the stack gives the error "Too Few Arguments" or "Bad Argument Type" depending on whether the stack is empty or contains an object of another type.

The internal binary integer form of the supplied parameter is duplicated and stored in a null named local variable in a new temporary environment by DUP1LAMBIND. At the end of the shuffle loop the variable is recalled and the temporary environment cleared by the use of 1GETABND.

Execution time of 52 DECKN is 0.49s! This is only marginally slower than my original SYSRPL version of DECK and more than made up for by the flexibility of being able to specify the size of the list.