I'm sorry to bother you I know this question have been asked quite a lot but never with Ada... I was wondering if there were in the Ada standard library a way to generate a list of unique random numbers (you never pick twice the same number) in O(n) In a way is there an implementation of the Knuth-Fisher-Yates algorithm in Ada?
3 Answers
There's a discussion of implementing the Fisher–Yates
shuffle here. Basically, you need a different range of Discrete_Random
in each iteration, as shown here; Float_Random
is an alternative, as mentioned in A.5.2(50), Note 16. If bias isn't critical, this example may be sufficient.
In any case, shuffling is O(n), but selecting can be O(1).
Addendum: The complexity of creating the set depends on the implementation. For example, Containers.Hashed_Sets, A.18.8(88/2) and Containers.Ordered_Sets, A.18.9(116/2).

- 203,806
- 29
- 246
- 1,045
-
Isnt Dealing/Generating the numbers O(n) also ? after all one needs to check if the number is unique ? (repeated linear search on an array) – NWS Mar 21 '11 at 09:24
-
@NWS: Good point. I used the word _dealing_ carelessly; I meant _selecting_. IIUC, uniqueness is coincident with _creating_ the initial set, with complexity depending on the implementation. – trashgod Mar 21 '11 at 11:23
-
getting back to the Question, if we generate and put in a sorted list, _Before_ we shuffle the numbers might reduce a little :). – NWS Mar 21 '11 at 12:03
-
1@NWS: Fisher–Yates only permutes an existing set. The complexity of creating the set depends on the [implementation](http://www.adaic.org/resources/add_content/standards/05rm/html/RM-A-18-7.html). – trashgod Mar 21 '11 at 12:34
Given that you want: a) Random numbers from 0 to 1000 and b) the numbers are not to repeat according to the link you provided, you could do this rather easily.
Just fill an array with the range of values and perform some number of swaps on randomly chosen elements thereof; this guarantees both requirements are upheld.

- 4,095
- 1
- 17
- 31
-
+1 This is the straightforward approach, but the selection of "some number of swaps" can be a source of bias. – trashgod Mar 20 '11 at 15:26
-
This is true. However you may be able to reduce that a bit by using a RNG with a cycle/range equal to your Array's range (perhaps using MOD) and swapping that index with the 'current' element for all elements. Then the main thing that the bias would come from would be the RNG itself. – Shark8 Mar 24 '11 at 01:51
-
In the [example cited](http://home.roadrunner.com/~jbmatthews/war.html) the [bias](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Implementation_errors) hinges on "selecting `j` from the entire range of valid array indexes on every iteration." Sadly, it's over and above any bias from the PRNG. – trashgod Mar 24 '11 at 03:48
I took the liberty of coding it up. You'll need to With Ada.Numerics.Discrete_Random.
Generic
Low, High : Integer;
Package Initialization is
SubType Element is Integer Range Low..High;
Function Incrementor Return Element;
Type Element_Array is Array(Element) of Element;
Values : Element_Array;
Procedure Print;
End Initialization;
Package Body Initialization is
Count : Element := Element'Last;
Function Incrementor Return Element is
begin
Return Result : Element:= Count do
Null;
Count:= Element'Pred( Result );
Exception
When Constraint_Error => Count:= Element'Last;
End Return;
end Incrementor;
Procedure Swap( Index_1, Index_2 : In Integer ) is
Temp : Constant Element:= Values( Integer(Index_1) );
begin
Values( Integer(Index_1) ):= Values( Integer(Index_2) );
Values( Integer(Index_2) ):= Temp;
end Swap;
Procedure Print is
begin
Put_Line( "Length: " & Values'Length'Img );
Put( "(" );
For Index in Values'First..Integer'Pred(Values'Last) loop
Put( Values(Index)'Img & ',' );
end loop;
Put( Values(Values'Last)'Img );
Put_Line( ")" );
end Print;
Begin
Shuffle:
Declare
Package Random_Element is New
Ada.Numerics.Discrete_Random( Element );
Number : Random_Element.Generator;
Use Random_Element;
Begin
Values:= Element_Array'( Others => Incrementor );
Reset( Number );
For Index in Element'Range loop
Swap( Integer(Index), Integer(Random(Number)) );
end loop;
End Shuffle;
End Initialization;
And you can test it out with something like:
Test:
Declare
Package Q is new
Initialization( Low => 0, High => 1000 );
Begin
Q.Print;
End Test;

- 4,095
- 1
- 17
- 31
-
Becasue it selects from "the entire range of valid array indexes on every iteration," I'm afraid this has the the same [bias](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Implementation_errors). – trashgod Mar 24 '11 at 03:53
-
-
The selection isn't out of range; it just makes some shuffles more likely than others. You have to select from the _remaining_ elements in each iteration. This [article](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#Implementation_errors) finally helped me see how. – trashgod Mar 28 '11 at 02:35