M
I
N
I
S
Y
S
©
O
R
G
T
U
T
O
R
I
A
L
S
  M[UMPS] Functions - $R[ANDOM]
Introduced in the 1977 ANSI M[UMPS] language standard.

This function returns a random (integer) value.

(The notation [0..n) means these examples that the result is an integer value between 0 (inclusive) and n (exclusive).)


Reference   Value
$RANDOM(N)   (0, N)
$RANDOM(2)   1 or 0
$RANDOM(2.3)   1 or 0
$RANDOM(1)   0
$RANDOM(0)   Error (interner Link M3)
$RANDOM(-1)   Error (interner Link M3)


A common mistake is to write code like
     ...
     Kill Selected
     For i=1:1:max Do
     . Set ok=0 For  Do  Quit:ok
     . . Set r=$Random(max)+1
     . . Set:'$Data(Selected(r)) ok=r
     . . Quit
     . Set result(i)=ok
     . Set Selected(ok)=1
     . Quit
     ...
    
This code will repeat a random selection until it finds an element that hasn't been selected before, and typically, one doesn't even notice that it may take multiple attempts to find an "not-yet-selected" element until about 90% of the elements have been selected. After that, the time that is spent in the multiple iterations to find a "not-yet-selected" element typically tend to consume noticeable amounts of time.
In short, it does pay to avoid multiple attempts to find a "not-yet-selected" element.

In true keeping with the spirit of a Standards Development Committee, the amount of attention paid to a topic should be in reverse proportion to the importance of the topic.

So... since many people feel that $Random is the least important function in the standardized suite, here goes:

To shuffle a deck of cards:
     Set DeckOne=""
     For i=1:1:52 Set DeckOne=DeckOne_$Char(i)
     Set DeckTwo=""
     For i=52:-1:1 Do
     . Set card=$Random(i)+1
     . Set DeckTwo=DeckTwo_$ASCII(DeckOne,card)
     . Set $Extract(DeckOne,card)=""
     . Quit
     Set suites="Clubs Hearts Clovers Diamonds"
     Set cards="Ace 2 3 4 5 6 7 8 9 10 Jack Queen King"
     For i=1:1:52 Do
     . Set code=$ASCII(DeckTwo,i)-1
     . Set suite=code\13+1,card=code#13+1
     . Write !,$Piece(cards," ",card),
     . Write " of ",$Piece(suites," ",suite)
     . Quit
     Quit
    
When displaying the shuffled deck, internal code numbers 1 through 13 correspond to the suite of Clubs, 14 through 26 the suite of Hearts, 27 through 39 the Clovers, and 40 through 52 the Diamonds.
Within each suite, the first element corresponds to the Ace, and the thirteenth to the King.

Note that one (random) card is removed from DeckOne at each step of the loop, and is added to DeckTwo. Thus, the random selection can never select the same card twice.

Of course, the shuffle can also be accomplished by using one single variable that holds both the original deck of cards, and the cards that still need to be randomized.
The trick is to interchange the first card with a randomly selected one, then the second one, etcetera.
Note that the loop only goes to 51 in this case, because once 51 cards have been selected, the remaining one doesn't need the random generator to be able to find it anymore... (nor does it need to be interchanged with any other card).


     Set Deck=""
     For i=1:1:52 Set Deck=Deck_$Char(i)
     Set n=52 For i=1:1:51 Do
     . Set r=$Random(n)
     . Set n=n-1
     . Set temp=$Extract(Deck,i+r)
     . Set $Extract(Deck,i+r)=$Extract(Deck,i)
     . Set $Extract(Deck,i)=temp
     . Quit
     Quit
    
The above approach works fine as long as the collection of elements has a predictably small size (particularly: less than the number of characters in the character-set).
So, with the 7-bit ASCII set, this will work uip to 127 elements, with the 8-bit ASCII, it will work up to 255 elements, with Unicode, it will work up to 65,535 elements, and with ISO-10646 it would work up to 2**32-1 elements (4,294,967,295), but there currently aren't any known complete implementations of that character-set.

So, for large collections, it will be necessary to rely on MUMPS's strong point: the sparse global variable.

One approach could be to do it like this:


     Kill
     Set max=verymany
     For i=1:1:max Set ^GloVar(i)=0
     For i=max:-1:1 Do
     . Set done=0,t="",target=$Random(i)+1
     . Set count=0 For  Do  Quit:done
     . . Set t=$Order(^GloVar(t))
     . . Set:t'="" count=count+1
     . . If target=count Set done=1 Kill ^GloVar(t)
     . . Quit
     . Write !,"Element number "_(max-i+1)," = ",t
     . Quit
    
Or, making it a tad faster:

     Kill
     Set max=verymany
     For n=1:1:max Set ^GloVar(n)=""
     For i=max:-1:1 Do
     . Set r=$Random(i)+1
     . Set n="" For k=1:1:r Set n=$Order(^GloVar(n))
     . Write !,"Element number "_(max-i+1)," = ",n
     . Kill ^GloVar(n)
     . Quit
    
And, making it a lot faster by removing the need for the $Order altogether:

      Kill
      Set max=verymany
      For n=1:1:max Set ^GloVar(n)=""
      Set n=max For i=1:1:max-1 Do
      . Set r=$Random(n)
      . Set n=n-1
      . Set temp=^GloVar(i+r)
      . Set ^GloVar(i+r)=^GloVar(i)
      . Set ^GloVar(i)=temp
      . Quit
     
Examples with naked references:

$RANDOM(VALUE)

SET ^ABC(1,2)="reset naked indicator"
; Naked indicator is now ^ABC(1,
SET ^(3,4)=$RANDOM(^(5,6))

; 1. fetch ^(5,6) = ^ABC(1,5,6)
; 2. store ^(3,4) = ^ABC(1,5,3,4)
; Naked indicator is now: ^ABC(1,5,3,

This document is © Ed de Moel, 1995-2005.
It is part of a book by Ed de Moel that is published under the title "M[UMPS] by Example" (ISBN 0-918118-42-5).
Printed copies of the book are no longer available.

This document describes the various operators that are defined in the M[UMPS] language standard (ANSI X11.1, ISO 11756).

The information in this document is NOT authoritative and subject to be modified at any moment.
Please consult the appropriate (draft) language standard for an authoritative definition.


In this document, information is included that will appear in future standards.
The MDC cannot guarantee that these 'next' standards will indeed appear.