A question about how to generate a Discrete Random Number in a functional manner. I want a “next” operator for a random number. There’s plenty of examples of how to generate a sequence of pseudo-random numbers, but I don’t want them all immediately. I don’t think I’m explaining my problem very well. I may have zero or more intermediary actions between new random number requests. I guess you could say that I want a random number randomly
with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;
procedure Example is
package Integer_Random is new Ada.Numerics.Discrete_Random (Integer);
Gen : Integer_Random.Generator;
N : Integer;
begin
Integer_Random.Reset (Gen, 42); -- maybe find a better way to seed. Ada.Calendar.Clock?
N := Integer_Random.Random (Gen);
Ada.Text_IO.Put_Line (N'Image);
end Example;
As long as you have the Gen object visible, you can call Integer_Random.Random (Gen) anytime to get a number.
Thank you for some nice code which generates a sequence of discrete random numbers.
However, I don’t think that this will work as I’m trying to call for a random number from a routine. I gues that is called an “out-of-band” process (??)
I have three actions, only one of which needs a random number, but these actions can occur in any order (eg: A, B, C or A, A, C, B, C) where only action “B” needs a random number.
I’m trying to solve a Rosetta Code Task in Ada using the solution written in Go.
Here’s my code so far (still needs lots of work, even without the Randome Number Generator portion; it doesn’t compile yet, working out the issues):
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Numerics.Discrete_Random;
procedure Two_Bullet_Roulette is
Number_of_Cylinders : constant Positive := 6;
type Revolver_Cylinder_Array is array (1 .. Number_of_Cylinders) of Boolean;
procedure Rshift (Cylinder : in out Revolver_Cylinder_Array) is
Temp : Boolean := Cylinder (Number_of_Cylinders);
begin
for I in reverse 1..(Number_of_Cylinders-1) loop
Cylinder (I+1) := Cylinder (I);
end loop;
Cylinder (1) := Temp;
end Rshift;
procedure Unload (Cylinder : in out Revolver_Cylinder_Array) is
begin
for I in Cylinder'Range loop
Cylinder (I) := False;
end loop;
end Unload;
procedure Load (Cylinder : in out Revolver_Cylinder_Array) is
begin
while (Cylinder (1)) loop
Rshift (Cylinder);
end loop;
Cylinder (1) := True;
Rshift (Cylinder);
end Load;
procedure Spin (Cylinder : in out Revolver_Cylinder_Array) is
Random_Number : Natural := 11; -- need a functional style random number generator (SIGH)
begin
for I in 1..Random_Number loop
Rshift (Cylinder);
end loop;
end Spin;
function Fire (Cylinder : in out Revolver_Cylinder_Array) return Boolean is
Shot : Boolean := Cylinder (1);
begin
Rshift (Cylinder);
return Shot;
end Fire;
function Method (M : Character; Cylinder : in out Revolver_Cylinder_Array) return Natural is
begin
Unload (Cylinder);
case M is
when 'L' => Load (Cylinder);
when 'S' => Spin (Cylinder);
when 'F' => if Fire (Cylinder) then return 1; else return 0; end if;
when others => null;
end case;
return 0;
end Method;
function Display_Action (S : String) return String is
Display_String : Unbounded_String := Null_Unbounded_String;
begin
for I in S'Length loop
case Slice (S, I, I) is
when "L" => Append (Display_String, "load");
when "S" => Append (Display_String, "spin");
when "F" => Append (Display_String, "fire");
end case;
Append (Display_String, ", ");
end loop;
return To_String (Display_String);
end Display_Action;
type Method_String_Array is array (1..4) of String;
Method_Strings : Method_String_Array := ("LSLSFSF", "LSLSFF", "LLSFSF", "LLSFF");
Sum : Natural := 0;
Tests : Positive := 100_000;
PC : Float;
begin
for I in Method_Strings'Range loop
Sum := 0;
for T in 1..Tests'Last loop
Sum := Sum + Method (Method_Strings (I));
end loop;
PC := Float (Sum) * Float (100) / Float (Tests);
Put (Method_Strings (I));
Put (" produces ");
Put (Item => PC, Fore => 2, Aft => 8, Exp => 0);
New_Line;
end loop;
end Two_Bullet_Roulette;
package Natural_Random is new Ada.Numerics.Discrete_Random (Natural);
Gen : Natural_Random.Generator;
procedure Spin (Cylinder : in out Revolver_Cylinder_Array) is
Random_Number : Natural := Natural_Random.Random (Gen);
begin
for I in 1..Random_Number loop
Rshift (Cylinder);
end loop;
end Spin;
I had to fix several other errors to get it to compile, but that does generate numbers as needed for me. I don’t think the result is correct and the execution time can be quite long depending on the random number chosen. Maybe constrain it to mod 128 or something to get a more suitable range.
One of my favorite papers of all time is by Guy Steele et al, and is about a “splittable” random number generator, “SplitMix”. A splittable random number generator is one that can be used deterministically in a multi-threaded environment, without locks, because it can be “split” whenever a new thread is spawned that needs its own sequence of random numbers. I cannot promise you that this is relevant to your problem, but if you want to learn more about random number generators, this paper provides a great background:
If you don’t have access to the ACM digital library, you can probably just do a search for “Guy Steele” and “splittable random number generator” and find the paper or follow-ons to it. A package based on it also made it into the Java standard library, I believe.