comp.lang.ada
 help / color / mirror / Atom feed
From: "Robert I. Eachus" <rieachus@attbi.com>
Subject: Re: Ada2005 random
Date: Sun, 13 Apr 2003 14:56:03 GMT
Date: 2003-04-13T14:56:03+00:00	[thread overview]
Message-ID: <3E997A99.3060506@attbi.com> (raw)
In-Reply-To: dstanbro-883BF5.20214204042003@mec2.bigpond.net.au

Dale Stanbrough wrote:
> Randy Brukardt wrote:
> 
> 
>>I needed that for a 'choosing' algorithm, say drawing balls one by one
>>out of a bowl. If you have 8 balls originally, you're choosing 1 out of
>>7, then 1 out of 6, etc. If you try to use 1 out of 8 the whole time and
>>discarding the useless ones, the second last draw can take a very long
>>time (only 2 out of 8 choices are meaningful). I'm pretty sure that this
>>is a valid technique; certainly the underlying float generator is still
>>random (at least as much as it ever was!), and the use of that result is
>>unbiased.
>>
>>While it would be nice if Ada supported this directly, it's easy enough
>>to write it yourself.

If you need a permutation, the best way to do it is to generate a set of 
random (float) values, associate them with the data to be permuted and 
the sort on this key.  I have seen a lot of code that attempts to 
permute a list by going through the list and exchanging elements.  For 
lists of length greater than two this can easily be shown to be biased.

For example there are 3! = 6 possible permutions of a list of three 
elements, the usual version of the swapping algorithm has 3^3 = 27 
possible outcomes, another (worse) variation has 2^3 = 8 different 
outcomes.  In either case the number of outcomes is not evenly divisible 
by the number of different permutations.

Even if you do this, there is another potential problem.  For a set of 
size N, there are N! possible permutations.  For example for a deck of 
cards, there are 52! possible permutations.  If you are generating 
bridge hands, there are 52!/(13!)^4 different possible deals.  Now if 
you are taking the (pseudo-)random sequence from a single generator, 
there will be at most as many different possible hands as there are 
possible starting seeds for the generator.  For the generator that I 
wrote, which AFAIK is still used by GNAT, if you use the time based 
version of Reset, there are more than 2^32 possible starting values, but 
of course, they won't all occur in a particular year.

If you really, really care about making it possible to generate a large 
fraction of all possible permutations, the way to do it is to take 
several generators with seeds generated based on different random data, 
then shuffle the deck more than once.

This program should serve as an example of how to do this.  (Note, I 
don't claim this is "right" since I don't use two generators, and 
shuffle twice.)

with Ada.Numerics.Float_Random;
package Statistics is

    subtype Generator is Ada.Numerics.Float_Random.Generator;

    type Direction is (Ascending, Descending);

    generic
      type Element_Type is private;
      type Index_Type is (<>);
      type List_Type is array (Index_Type range <>) of Element_Type;
      with function "<" (L, R : Element_Type) return Boolean is <>;
    procedure Sorting (List : in out List_Type);
    --  General-purpose sorting procedure

    generic
      type Element_Type is private;
      type Index_Type is (<>);
      type List_Type is array (Index_Type range <>) of Element_Type;
    procedure Shuffling (List : in out List_Type;
                         Gen  : in     Generator);
    --  Permute the contents of List randomly

end Statistics;
------------------------------------------------------------------------
with Ada.Numerics.Float_Random;
package body Statistics is

    package NFR renames Ada.Numerics.Float_Random;

--  generic
--    type Element_Type is private;
--    type Index_Type is (<>);
--    type List_Type is array (Index_Type range <>) of Element_Type;
--    with function "<" (L, R : Element_Type) return Boolean is <>;
    procedure Sorting (List : in out List_Type) is
       --  Quicksort
       Part  : Element_Type;
       First : Index_Type;
       Last  : Index_Type;
    begin
      --  Don't break on short lists;
       if List'Length <= 1
       then return;
       elsif List'Length = 2 then
          if List(List'Last) < List(List'First)
          then
             Part := List(List'First);
             List(List'First) := List(List'Last);
             List(List'Last) := Part;
          end if;
          return;
       end if;

       --  now it is safe to assume that List'First and List'Last
       --  are in range.

       First := List'First;
       Last  := List'Last;

       -- choose partition value
       Part := List(First);

       -- partition list
    Main_Loop:
       loop
          if Part < List(Last) then
             Last := Index_Type'PRED(Last);
             exit Main_Loop when Last=First;
          else
             List(First) := List(Last);
             First := Index_Type'SUCC(First);
             exit Main_Loop when Last=First;
             while List(First) < Part loop
                First := Index_Type'SUCC(First);
                exit Main_Loop when Last=First;
             end loop;
             List(Last) := List(First);
             Last := Index_Type'PRED(Last);
             exit Main_Loop when Last=First;
          end if;
       end loop Main_Loop;

       List(First) := Part; -- at this point First = Last.
       Sorting(List(List'First..Index_Type'PRED(Last)));
       Sorting(List(Index_Type'SUCC(Last)..List'Last));
    end Sorting;


    --  generic
    --   type Element_Type is private;
    --   type Index_Type is (<>);
    --   type List_Type is array (Index_Type range <>) of Element_Type;
    procedure Shuffling (List : in out List_Type;
                         Gen  : in     Generator) is
       List_Copy : constant List_Type := List;

       type Rec is
          record
             Index : Index_Type;
             Rand  : Float;
          end record;

       type Rec_Array is array (Index_Type range <>) of Rec;

       Temp : Rec_Array(List'Range);

       function "<" (L, R: Rec) return Boolean is
       begin return L.Rand < R.Rand; end "<";

       procedure Sort is new Sorting(Rec, Index_Type, Rec_Array, "<");

     begin
        for I in Temp'Range loop
           Temp(I) := (I, NFR.Random(Gen));
        end loop;

        Sort(Temp);  --  Sort based on value of random number

        --  Now rearrange List based on the order of indices in Temp
        for I in List'Range loop
           List(I) := List_Copy(Temp(I).Index);
        end loop;
     end Shuffling;

end Statistics;
---------------------------------------------------------------------------
with Ada.Numerics.Float_Random;
with Statistics;
with Text_IO; use Text_IO;
procedure Bridge_Hands is
-- a procedure to produce random (but sorted) bridge hands for duplicate.

   package NFR        renames Ada.Numerics.Float_Random;

   Gen: NFR.Generator;
   function Rand(G: in NFR.Generator := Gen) return Float renames 
NFR.Random;

   type Direction is (North, East, South, West);
   type Suits is (Spades, Hearts, Diamonds, Clubs);
   type Ranks is (Ace, King, Queen, Jack, Ten, Nine, Eight, Seven,
                 Six, Five, Four, Three, Deuce);

   Display_Suit: constant array(Suits) of Character := "SHDC";
   Display_Rank: constant array(Ranks) of Character := "AKQJT98765432";

   subtype Dealer_Name is String(1..5);
   Display_Dealers: constant array(0..3) of Dealer_Name :=
      ("North", "East ", "South", "West ");
   subtype Vulnerability is String(1..11);
   Display_Vulnerability: constant array(0..3) of Vulnerability :=
      ("None       ","North/South","East/West  ","Both       ");
   package Int_IO is new Text_IO.Integer_IO(Integer);
   Number_of_Hands: Integer;

   type Card is record
     Suit: Suits;
     Rank: Ranks;
   end record;

   type Card_Array is array (Natural range <>) of Card;

   Deck: Card_Array(0..51);

   subtype Hand is Card_Array(0..12);
   Hands: array(Direction) of Hand;


   function By_Rank(L,R: Card) return Boolean is
   begin return L.Rank < R.Rank; end By_Rank;
   pragma INLINE(By_Rank);

   procedure Sort_Hand is
     new Statistics.Sorting(Card, Natural, Card_Array, By_Rank);

   procedure Shuffle is
     new Statistics.Shuffling(Card, Natural, Card_Array);

   procedure Deal is
   begin
     for I in Hand'Range loop
       for J in Hands'Range loop
         Hands(J)(I) := Deck(I*4 + Direction'POS(J));
       end loop;
     end loop;
     for I in Hands'Range loop
       Sort_Hand(Hands(I));
       -- sort cards by rank, but not by suit.
     end loop;
   end Deal;

   function Cards(D: in Direction; S: in Suits) return String is
     Temp: String(1..26) := (others => ' ');
     Index: Natural := 3;
   begin
     Temp(1) := Display_Suit(S);
     for I in Hand'Range loop
       if Hands(D)(I).Suit = S
       then
         Temp(Index) := Display_Rank(Hands(D)(I).Rank);
         Index := Index + 2;
       end if;
     end loop;
     if Index = 3 then Temp(3..5) := "---"; end if;
     return Temp;
   end Cards;

begin

   -- Use time-dependant random numbers:

   NFR.Reset(Gen);

   -- Initialize deck.

   for I in Deck'Range loop
     Deck(I) := (Suits'VAL(I/13), Ranks'VAL(I mod 13));
   end loop;

   Text_IO.Put_Line("Enter number of hands, default is 32:");

   begin
     Int_IO.Get(Number_of_Hands);
   exception when others =>
     Number_of_Hands := 32;
   end;

   for I in 0..Number_of_Hands - 1 loop
     Text_IO.New_Page;
     Text_IO.New_Line(3);
     Text_IO.Put_Line("                                Board:        "
         & Integer'IMAGE(I+1));
     Text_IO.Put_Line("                                Dealer:        "
       & Display_Dealers(I mod 4));
     Text_IO.Put_Line("                                Vulnerability: "
       & Display_Vulnerability((I + I/4) mod 4));
     New_Line(3);

     Shuffle(Deck, Gen);

     Deal;

     Text_IO.Put_Line("                                NORTH");
     Text_IO.New_Line;
     Text_IO.Put_Line("                              " &
                      Cards(North,Spades));
     Text_IO.Put_Line("                              " &
                      Cards(North,Hearts));
     Text_IO.Put_Line("                              " &
                      Cards(North,Diamonds));
     Text_IO.Put_Line("                              " &
                      Cards(North,Clubs));
     New_Line(2);
     Text_IO.Put_Line("       WEST                                  " &
                      "          EAST");
     Text_IO.New_Line;
     Text_IO.Put_Line("     " & Cards(West,Spades) &
                      "                      " & Cards(East,Spades));
     Text_IO.Put_Line("     " & Cards(West,Hearts) &
                      "                      " & Cards(East,Hearts));
     Text_IO.Put_Line("     " & Cards(West,Diamonds) &
                      "                      " & Cards(East,Diamonds));
     Text_IO.Put_Line("     " & Cards(West,Clubs) &
                      "                      " & Cards(East,Clubs));
     New_Line(2);
     Text_IO.Put_Line("                                SOUTH");
     Text_IO.New_Line;
     Text_IO.Put_Line("                              " &
                      Cards(South,Spades));
     Text_IO.Put_Line("                              " &
                      Cards(South,Hearts));
     Text_IO.Put_Line("                              " &
                      Cards(South,Diamonds));
     Text_IO.Put_Line("                              " &
                      Cards(South,Clubs));
   end loop;

end Bridge_Hands;




  parent reply	other threads:[~2003-04-13 14:56 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-04-03 12:27 Ada2005 random David C. Hoos, Sr.
2003-04-03 12:39 ` Peter Hermann
2003-04-03 22:10   ` Randy Brukardt
2003-04-04  7:43     ` Peter Hermann
2003-04-04 10:21     ` Dale Stanbrough
2003-04-04 12:11       ` Stuart Palin
2003-04-04 14:25       ` Lutz Donnerhacke
2003-04-04 21:51         ` Dale Stanbrough
2003-04-05 18:34           ` Samuel Tardieu
2003-04-05  6:46       ` Martin Krischik
2003-04-07 21:07         ` Randy Brukardt
2003-04-13 14:56       ` Robert I. Eachus [this message]
2003-04-13 21:58         ` Mark Biggar
2003-04-04 12:20     ` Stuart Palin
2003-04-07  7:26       ` Jean-Etienne Doucet
2003-04-07  8:09         ` Lutz Donnerhacke
2003-04-04  8:27   ` 
2003-04-04 12:14     ` Peter Hermann
2003-04-04 14:26       ` Lutz Donnerhacke
     [not found]     ` <6lk1m-lm3.ln1@beastie.ix.netcom.com>
2003-04-05  7:29       ` Pascal Obry
2003-04-05  9:00         ` tmoran
2003-04-05 17:02           ` Pascal Obry
  -- strict thread matches above, loose matches on Subject: below --
2003-04-02 13:27 Peter Hermann
2003-04-02 13:44 ` Lutz Donnerhacke
2003-04-03  9:56   ` Peter Hermann
2003-04-03 10:13     ` Lutz Donnerhacke
2003-04-04  3:50 ` Steve
2003-04-04 14:30   ` Lutz Donnerhacke
2003-04-05  3:02     ` Steve
2003-04-04  4:33 ` Christoph Grein
2003-04-06 15:44   ` 
2003-04-05  0:14 ` Jeffrey Carter
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox