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 (M3) | |
$RANDOM(-1) | Error (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
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.