SRL Archive - OSR Help

Generating Non Recurring Random Numbers


mat_de_b

2011-06-12 16:12:57

I need a procedure/function that can generate a stream of numbers between x and y that are all random. The numbers must also not repeat.

For example I have a 50 long array of records with integer values

I need to assign each array.integer a different but random number between 1-50.

I have got literally no idea how to accomplish this. Any recommendations/help is appreciated.

Thanks in advance, Dom

Sex

2011-06-12 16:17:37

Does it matter how fast or slow it is?

Edit: My idea would be to create an array of all possible numbers, then select a random element from the array, remove it, and repeat.

Kasi

2011-06-12 16:36:12

use RandomRange and a simple function to check weather a number is in the array. erm in theory it get's slower as it goes on but it'll stop eventually.

Edit : kinda like this if i havent made myself clear


Begin
Repeat
K := RandomRange(X, Y);
If Not InArray(Array, K) Then
Begin
AddToArray(Array, K);
Result := True;
End;
Until(Result);
End;

Sabzi

2011-06-12 16:44:23

I was thinking about this a while ago but never worked it out.
My idea was to make a recursive(or not) function that will do this:
So we got the range example 1-10
randomrange and then half the remaining to two and then first random to see which one you go in then randomrange on that and so on, at the end you will get lot's of small arrays which shouldn't take more memory than one big array anyway and it will get slower because there will be more random calls, but this method would eliminate randoming the same number

example: randomrange(1, 10) -> 7
two array now: 1-6; 8-10
random(1) -> 0 -> randomrange(1-6) -> 4
arrays: 1-3; 5,6;;; 8-10 in a tree formation(or whatever it's called precisely in english)
then when the first random(1) is 0 you would do a random(1) again

Yago

2011-06-12 16:47:02

From: Sex
Does it matter how fast or slow it is?

Edit: My idea would be to create an array of all possible numbers, then select a random element from the array, remove it, and repeat.


I like this one…

Nava2

2011-06-12 16:50:56

From: Sabzi
I was thinking about this a while ago but never worked it out.
My idea was to make a recursive(or not) function that will do this:
So we got the range example 1-10
randomrange and then half the remaining to two and then first random to see which one you go in then randomrange on that and so on, at the end you will get lot's of small arrays which shouldn't take more memory than one big array anyway and it will get slower because there will be more random calls, but this method would eliminate randoming the same number

example: randomrange(1, 10) -> 7
two array now: 1-6; 8-10
random(1) -> 0 -> randomrange(1-6) -> 4
arrays: 1-3; 5,6;;; 8-10 in a tree formation(or whatever it's called precisely in english)
then when the first random(1) is 0 you would do a random(1) again


That would be a *very* elegant solution to the problem. Much like the quick sort algorithm. :)

Dgby714

2011-06-12 16:57:08

function RandIntArr(L: integer): TIntegerArray;
var
I, J, H: integer;
IntArr: TIntegerArray;
begin
SetLength(IntArr, L);
for I := 0 to L - 1 do
IntArr[I] := I;

while (Length(IntArr) > 0) do
begin
SetLength(Result, Length(Result) + 1);
J := Random(High(IntArr) + 1);
Result[High(Result)] := IntArr[J];

H := High(IntArr);
for I := J + 1 to H do
IntArr[I - 1] := IntArr[I];
SetLength(IntArr, Length(IntArr) - 1);
end;
end;
function RandIntArr(L: integer): TIntegerArray;
var
I, J, H, C: integer;
RArr: TPointArray;
begin
SetLength(RArr, 1);
RArr[0] := Point(0, L - 1);

SetLength(Result, L);
C := 0;

repeat
WriteLn(RArr);
if (Length(RArr) = 0) then
Break;

J := Random(Length(RArr));
Result[C] := RandomRange(RArr[J].X, RArr[J].Y);

if (not ((Result[C] = RArr[J].X) or (Result[C] = RArr[J].Y))) then
begin
SetLength(RArr, Length(RArr) + 1);
RArr[High(RArr)] := Point(Result[C] + 1, RArr[J].Y);
RArr[J].Y := Result[C] - 1;
end else
begin
if ((Result[C] = RArr[J].X) and (Result[C] = RArr[J].Y)) then
begin
H := High(RArr);
for I := J + 1 to H do
RArr[I - 1] := RArr[I];
SetLength(RArr, Length(RArr) - 1);

Inc(C);
Continue;
end;

if (Result[C] = RArr[J].X) then
Inc(RArr[J].X);

if (Result[C] = RArr[J].Y) then
Dec(RArr[J].Y);
end;

Inc(C);
until (False);
end;
First one is mat_de_b's idea.
Second one is Sabzi's idea.

Yago

2011-06-12 17:04:43


{$i Srl\Srl.Scar}

var
NumbArray, FinalArray: TIntegerArray;
I, A: Integer;
begin
setlength(NumbArray, 49);
setlength(FinalArray, 49);
for i := 0 to high(numbarray)do
numbarray[i] := i;

for i := 0 to High(FinalArray) do
begin
A := Random(High(NumbArray));
FinalArray[i] := NumbArray[A];
DeleteValueInIntArray(NumbArray, A);
end;

for i := 0 to High(FinalArray) do
Writeln(ToStr(FinalArray[I]));
end.

Practically Sex's Idea.

I tested it.

Crap DG beat me to it i think.

mat_de_b

2011-06-12 17:06:59

PureBlood, I didn't even know that procedure/function existed I will try that out

Sabzi, I definitely though about that, but I could never write something that complex. I cant even comprehend how I would go about doing it let alone doing it.

Sex,Speed is not a factor as long as we aren't talking minutes. Which I don't think we are. I kind of understand your idea but I'm not sure how to go about changing/choosing numbers from the array? For example if say the first number i pick from the array is 42, how do I stop 42 still being a possibility. If i remove that slot of the array, then when happens when it gets chosen? a result will return false and it will break? or if i move the item above into '42's slot but i don't know how to do that.and it would be very long winded each time a number is picked (which i think is what you were talking about.

I will look over all of these and come back with a reply some time tomorrow (i have a geog GCSE tomorrow at 9:00 am)


EDIT: DeleteValueInIntArray(NumbArray, A); That is the PERFECT procedure.

Problem:
Procedure ShuffleCards;
var
NumbArray : TIntegerArray;
I, A: Integer;
begin
setlength(NumbArray, 51);
for i := 0 to high(numbarray)do
numbarray[i] := i;

for i := 0 to High(Card) do
begin
A := Random(High(NumbArray));
Card[i].Position := NumbArray[A];
DeleteValueInIntArray(NumbArray, A);
end;
End;

Each cards position is now reading 0 every time.

This is the setup of the variables/arrays

type
TCard = record
Suite: string ;
Number: Integer ;
Position: Integer ;
end;

var
Card: array of TCard;

If someone can help with a solution i would be extremely grateful as i'm a little to tired to figure it out now.

Yago

2011-06-12 17:08:51

From: mat_de_b


I will look over all of these and come back with a reply some time tomorrow (i have a geog GCSE tomorrow at 9:00 am


I just posted mines… Make sure you see it so I feel important :p

Dgby714

2011-06-12 17:13:31

Also made the more elegant version Sabzi mentioned.

Sabzi

2011-06-12 17:15:17

@nava: That's good :p, I guess.

@mat_de_b: being summer I may write it, not that hard, but knowing myself and how lazy I am, pffff …

EDIT: Oh never mind … ^

Dgby714

2011-06-12 17:16:34

From: Sabzi
@nava: That's good :p, I guess.

@mat_de_b: being summer I may write it, not that hard, but knowing myself and how lazy I am, pffff …

EDIT: Oh never mind … ^


=P Ninja'd you I guess.

mat_de_b

2011-06-12 17:19:00

Will check sabzi's tomorrow.

I might as well give you the whole script so you guys can mess around with your own card decks ;)

{$i Srl\Srl.Scar}

type
TCard = record
Suite: string ;
Number: Integer ;
Position: Integer ;
end;

var
Card: array of TCard;


procedure SetCardArrays;
var
I: Integer;
begin
SetLength(Card, 52);

for I := 0 to 12 do
begin
Card[I].Suite := 'Heart' ;
Card[I].Number := I+1 ;
end;

for I := 13 to 25 do
begin
Card[I].Suite := 'Diamond' ;
Card[I].Number := I-12 ;
end;

for I := 26 to 38 do
begin
Card[I].Suite := 'Club' ;
Card[I].Number := I-25 ;
end;

for I := 39 to 51 do
begin
Card[I].Suite := 'Spade' ;
Card[I].Number := I-38 ;
end;
end;

{Procedure ShuffleCards;
var
NumbArray : TIntegerArray;
I, A: Integer;
begin
setlength(NumbArray, 51);
for i := 0 to high(numbarray)do
numbarray[i] := i;

for i := 0 to High(Card) do
begin
A := Random(High(NumbArray));
Card[i].Position := NumbArray[A];
DeleteValueInIntArray(NumbArray, A);
end;
End;}


Procedure WriteCards;
var
I: Integer;
Begin
for I := 0 to 51 do
Begin
writeLn(' Card Suite - '+Card[I].Suite);
If Card[I].Number = 1 Then writeLn(' Card Number - Ace');
If Card[I].Number = 11 Then writeLn(' Card Number - Jack');
If Card[I].Number = 12 Then writeLn(' Card Number - Queen');
If Card[I].Number = 13 Then writeLn(' Card Number - King');
If ((Card[I].Number > 1) And (Card[I].Number < 11)) Then writeLn(' Card Number - '+IntToStr(Card[I].Number));
writeLn(' Card Position - '+IntToStr(Card[I].Position));
writeLn('======================');
End;
End;

Begin
SetCardArrays;
//ShuffleCards;
WriteCards;
End.


I'm getting an error if anyone can figure out why.

I shall explain what this is for tomorrow. have fun :D

Sabzi

2011-06-12 17:19:20

From: Dgby714
=P Ninja'd you I guess.


O yeah, you did. I am still happy tho. Now I don't have to do it :D.

Dgby714

2011-06-12 17:34:47

type
TSuit = (Spade, Heart, Club, Diamond);
TNumber = (Card2, Card3, Card4, Card5, Card6,
Card7, Card8, Card9, Card10, Jack, Queen, King, Ace);

TCard = record
Suit: TSuit;
Number: TNumber;
end;

TDeck = array[0..51] of TCard;

var
Deck: TDeck;

procedure ResetDeck;
var
I, J, K: Integer;
Suit: TSuit;
Number: TNumber;
begin
I := 0;
for J := 0 to 3 do
for K := 0 to 12 do
begin
Deck[I].Suit := TSuit(J);
Deck[I].Number := TNumber(K);
Inc(I);
end;
end;

function RandIntArr(L: integer): TIntegerArray;
var
I, J, H, C: integer;
RArr: TPointArray;
begin
SetLength(RArr, 1);
RArr[0] := Point(0, L - 1);

SetLength(Result, L);
C := 0;

while (Length(RArr) > 0) do
begin
J := Random(Length(RArr));
Result[C] := RandomRange(RArr[J].X, RArr[J].Y);

if (not ((Result[C] = RArr[J].X) or (Result[C] = RArr[J].Y))) then
begin
SetLength(RArr, Length(RArr) + 1);
RArr[High(RArr)] := Point(Result[C] + 1, RArr[J].Y);
RArr[J].Y := Result[C] - 1;
end else
begin
if ((Result[C] = RArr[J].X) and (Result[C] = RArr[J].Y)) then
begin
H := High(RArr);
for I := J + 1 to H do
RArr[I - 1] := RArr[I];
SetLength(RArr, Length(RArr) - 1);

Inc(C);
Continue;
end;

if (Result[C] = RArr[J].X) then
Inc(RArr[J].X);

if (Result[C] = RArr[J].Y) then
Dec(RArr[J].Y);
end;

Inc(C);
end;
end;

procedure Shuffle;
var
I: integer;
Temp: TIntegerArray;
TempDeck: TDeck;
begin
Temp := RandIntArr(52);
for I := 0 to 51 do
TempDeck[I] := Deck[Temp[I]];
Deck := TempDeck;
end;

function SuitToStr(Suit: TSuit): string;
begin
case Suit of
Spade: Result := 'Spade';
Heart: Result := 'Heart';
Club: Result := 'Club';
Diamond: Result := 'Diamond';
end;
end;

function NumToStr(Num: TNumber): string;
begin
case Num of
Jack: Result := 'Jack';
Queen: Result := 'Queen';
King: Result := 'King';
Ace: Result := 'Ace';
else
Result := IntToStr(Integer(Num) + 2);
end;
end;

procedure DebugDeck;
var
I: integer;
begin
WriteLn('Deck - ');
for I := 0 to 51 do
WriteLn(' Suit: ' + SuitToStr(Deck[I].Suit) + ', Number: ' + NumToStr(Deck[I].Number));
WriteLn('');
end;

begin
ResetDeck;
DebugDeck;
Shuffle;
DebugDeck;
end.

Sex

2011-06-12 18:07:57

From: mat_de_b
[…] Sex,Speed is not a factor as long as we aren't talking minutes. Which I don't think we are. I kind of understand your idea but I'm not sure how to go about changing/choosing numbers from the array? For example if say the first number i pick from the array is 42, how do I stop 42 still being a possibility. If i remove that slot of the array, then when happens when it gets chosen? a result will return false and it will break? or if i move the item above into '42's slot but i don't know how to do that.and it would be very long winded each time a number is picked (which i think is what you were talking about.
Yeah, then speed is fine. I'm talking about having an array of every possible number (i.e all numbers from rangeX to rangeY). Then another array for the result. Pick a random index from the array and then remove the index and repeat. There will be no way to pick 42 again since it does not exist in the array. Another way would also be to just randomize the first array.

Dgby714

2011-06-12 18:16:47

From: Sex
Yeah, then speed is fine. I'm talking about having an array of every possible number (i.e all numbers from rangeX to rangeY). Then another array for the result. Pick a random index from the array and then remove the index and repeat. There will be no way to pick 42 again since it does not exist in the array. Another way would also be to just randomize the first array.


That works just like the first one I wrote. =)

Yago

2011-06-12 18:46:31

From: Sex
Yeah, then speed is fine. I'm talking about having an array of every possible number (i.e all numbers from rangeX to rangeY). Then another array for the result. Pick a random index from the array and then remove the index and repeat. There will be no way to pick 42 again since it does not exist in the array. Another way would also be to just randomize the first array.


Check out the one I made. Same principle.

MylesMadness

2011-06-12 21:29:09

program New;
var
Deck: TIntegerArray;

var
Size, Randomization, i: Integer;
begin
Size := 50;
Randomization := 500;
SetArrayLength(Deck, Size);
for i := 1 to Size do
Deck[i - 1] := i;
for i := 1 to Randomization do
Swap(Deck[Random(Size)], Deck[Random(Size)]);
for i := 0 to High(Deck) do
WriteLn(Deck[i]);
end.

Yago

2011-06-12 21:40:57

From: MylesMadness
program New;
var
Deck: TIntegerArray;

var
Size, Randomization, i: Integer;
begin
Size := 50;
Randomization := 500;
SetArrayLength(Deck, Size);
for i := 1 to Size do
Deck[i - 1] := i;
for i := 1 to Randomization do
Swap(Deck[Random(Size)], Deck[Random(Size)]);
for i := 0 to High(Deck) do
WriteLn(Deck[i]);
end.


Wow by far the simplest

Dgby714

2011-06-12 22:45:46

From: Yago
Wow by far the simplest


Simple is not always better, with his example, there is a chance some cards will not move.

Yago

2011-06-12 22:49:48

From: Dgby714
Simple is not always better, with his example, there is a chance some cards will not move.


I saw that too.
Which is good because it should be random.

mat_de_b

2011-06-13 10:19:56

Don't worry for now

masterBB

2011-06-13 14:34:07

From: Yago
I saw that too.
Which is good because it should be random.


No it isn't. Certain numbers will be more common as others. Though at higher numbers of randomization the spread becomes more equal.

Wouldn't this solve it:
program New;
var
Deck: TIntegerArray;

var
Size, i: Integer;
begin
Size := 50;
SetArrayLength(Deck, Size);
for i := 1 to Size do
Deck[i - 1] := i;
for i := 1 to Size do
Swap(Deck[i-1], Deck[Random(Size)]);
for i := 0 to High(Deck) do
WriteLn(Deck[i]);
end.

mat_de_b

2011-06-13 14:59:32

This is what i'm using and it works perfectly..
Procedure ShuffleCards;
var
i,A: Integer;
begin
SetLength(RandomDeck, 53);

for i := 0 to high(RandomDeck)do
RandomDeck[i] := i;

for i := 0 to High(Card) do
begin
A := Random(high(RandomDeck));
Card[i].Position := RandomDeck[A];
DeleteValueInIntArray(RandomDeck, A);
End;
end;

BazzBarrett

2011-06-13 15:58:31

from my blackjack script :P

procedure suffledeck;
var
TDeck: Array [1..52] of TCard;
I, T: Integer;
Done: Boolean;
begin
for I := 1 to 52 do
begin
done := false;
repeat
T := (Random(52)+1);
// writeln(T);
if TDeck[T].Number = 0 then
begin
// writeln(I);
TDeck[T] := Deck[I];
done := true;
end;
until done;
end;
for I := 1 to 52 do Deck[I] := TDeck[I];
end;