No duplicate random numbers

G

Guest

Hello,

I want to create a quick random generator that will not produce duplicate
random numbers. Here is what I have been able to do so far:

Function RandomTest()
Dim high, low, result, amount, i As Integer

amount = inputbox("How many values do you want generated?", "Random
Generator, Step 1")
low = inputbox("Enter starting range of selection.", "Random Generator, Step
2")
high = inputbox("Enter ending range of selection.", "Random Generator, Step
3")
For i = 1 To amount
Randomize
result = Int((high - low + 1) * Rnd() + 1)
Debug.Print result
Next i
end function

I want the user to define the number of outputs (amount), the boundaries
(high and low). So, 1) how do I make sure that these numbers do not
duplicate and 2) how do I store them so that I can use them later (either in
a table or as a way to refer back to them in code).

Thanks!
Jessica
 
D

Douglas J. Steele

Store them in an array, and check each new value against what's already been
stored.

Function RandomTest() As Variant
Dim booAlreadyUsed As Boolean
Dim high As Integer
Dim low As Integer
Dim result As Integer
Dim amount As Integer
Dim i As Integer
Dim intloop As Integer
Dim intValues() As Integer

amount = InputBox("How many values do you want generated?", _
"Random Generator, Step 1")

ReDim intValues(1 To amount)

low = InputBox("Enter starting range of selection.", _
"Random Generator, Step 2")
high = InputBox("Enter ending range of selection.", _
"Random Generator, Step 3")
i = 1
Do While i <= amount
Randomize
result = Int((high - low + 1) * Rnd() + 1)
For intLoop = 1 To (i - 1)
If intValues(intLoop) = result Then
booAlreadyUsed = True
Exit For
End If
Next intLoop
If booAlreadyUsed = False Then
intValues(i) = result
i = i + 1
End If
Loop

RandomNumbers = intValues

End Function

You'd use this function like:

Dim intLoop As Integer
Dim varArray As Variant

varArray = RandomTest()
For intLoop = LBound(varArray) To UBound(varArray)
Debug.Print varArray(intLoop)
Next intLoop


However, be aware that putting a restriction such as this means, by
definition, that they're not random numbers!

By the way, you may not be aware that

Dim high, low, result, amount, i As Integer

is only defining the variable i as an integer: all others are variants.
 
G

Guest

This might be alot simpler.

Dim a(10) As Integer, cnt As Integer, b As Integer, c As Integer, temp As
Integer
For cnt = 0 To 10 'get these #'s passed to the function and dim the array
a(cnt) = cnt
Next cnt
Randomize
For cnt = 1 To 20 'make this double the # of elements
b = Rnd() * 10
c = Rnd() * 10
temp = a(b)
a(b) = a(c)
a(c) = temp
Next cnt
 
M

Matthias Klaey

Jessica said:
Hello,

I want to create a quick random generator that will not produce duplicate
random numbers. Here is what I have been able to do so far:

Function RandomTest()
Dim high, low, result, amount, i As Integer

amount = inputbox("How many values do you want generated?", "Random
Generator, Step 1")
low = inputbox("Enter starting range of selection.", "Random Generator, Step
2")
high = inputbox("Enter ending range of selection.", "Random Generator, Step
3")
For i = 1 To amount
Randomize
result = Int((high - low + 1) * Rnd() + 1)
Debug.Print result
Next i
end function

I want the user to define the number of outputs (amount), the boundaries
(high and low). So, 1) how do I make sure that these numbers do not
duplicate and 2) how do I store them so that I can use them later (either in
a table or as a way to refer back to them in code).

Thanks!
Jessica

First, you must have amount <= (high - low + 1). If amount is greater
than the number of integers between low and high, there will
neccessarily be duplications.

If the condition is met, this amounts to "Select k out of n Numbers at
random without replacement", where k = amount and n = high - low + 1.

The following code does this:

Public Function Sample(ByVal n As Long, ByVal k As Long) As Variant
' Generate a random sample of k numbers in the range from 1 to N
'(without repetition).
' 1 <= k <= N
' C.T. Fan, M.E. Müller, I Rezucha, JASA (Journal of the
' American Statistical Association) 57 (1962), 387-402
Dim i As Long
Dim aK As Variant
Dim nPos As Long

i = n
nPos = 0
ReDim aK(k) As Long
Do While k > 0
If (k / i) > Rnd Then
nPos = nPos + 1
aK(nPos) = n - i + 1
k = k - 1
End If
i = i - 1
Loop
Sample = aK
End Function

This function returns a variant array. You would use this the
following way.
Notice that this selects numbers at random in the range 1 to n, and
you have to transform them to the desired range

Public Sub Test()

Dim aResult As Variant
Dim i As Integer
Dim low As Integer
Dim high As Integer
Dim amount As Integer

high = 5000
low = 4000
amount = 10

' Use the Randomize statement only once
Randomize
aResult = Sample(high - low + 1, amount)
' Transform to desired range
For i = 1 To amount
aResult(i) = aResult(i) + low - 1
Next

For i = 1 To amount
Debug.Print aResult(i)
Next
End Sub


Also, the resulting randomly selected numbers are output in ascending
order. If you want to "unorder" them, you need a permutation, as
follows:

Public Function RandomPermutation(ByVal n As Long) As Variant
' Generate Random Permutation of numbers 1 to N.

Dim aItem As Variant
Dim i As Long
Dim k As Long
Dim j As Long

' Array aItem mit Zahlen 1 bis N initialisieren
ReDim aItem(1 To n) As Long
For i = 1 To n
aItem(i) = i
Next
' Permute
For i = 1 To n - 1
j = 1 + CLng(Int(Rnd * (n - i + 1)))
k = aItem(i)
aItem(i) = aItem(j)
aItem(j) = k
Next

RandomPermutation = aItem

End Function

Now modify the Test procedure as follows:

Public Sub Test()

Dim aResult As Variant
Dim aIndex As Variant
Dim i As Integer
Dim low As Integer
Dim high As Integer
Dim amount As Integer

high = 5000
low = 4000
amount = 10

' Use the Randomize statement only once
Randomize
aResult = Sample(high - low + 1, amount)
' Transform to desired range
For i = 1 To amount
aResult(i) = aResult(i) + low - 1
Next

aIndex = RandomPermutation(amount)
For i = 1 To amount
Debug.Print aResult(aIndex(i))
Next
End Sub

And you should get the desired result.

HTH
Matthias Kläy
 
G

Guest

Doug,

Thanks for the info. I've put the code in but it keeps freezing Access when
I try to run it. When I hit debug it pops up on the first If statement:
Do While i <= amount
Randomize
result = Int((high - low + 1) * Rnd() + 1)
For intLoop = 1 To (i - 1)
If intValues(intLoop) = result Then
booAlreadyUsed = True
Exit For
End If
Next intLoop
If booAlreadyUsed = False Then
intValues(i) = result
i = i + 1
End If
Loop

Also did you mean to write RandoTest = intValues instead of RandomNumbers =
intValues? Any thoughts on what is going wrong with this?

Thanks

Jessica
 
M

Matthias Klaey

Jessica said:
Doug,

Thanks for the info. I've put the code in but it keeps freezing Access when
I try to run it. When I hit debug it pops up on the first If statement:
Do While i <= amount
Randomize
result = Int((high - low + 1) * Rnd() + 1)
For intLoop = 1 To (i - 1)
If intValues(intLoop) = result Then
booAlreadyUsed = True
Exit For
End If
Next intLoop
If booAlreadyUsed = False Then
intValues(i) = result
i = i + 1
End If
Loop

Also did you mean to write RandoTest = intValues instead of RandomNumbers =
intValues? Any thoughts on what is going wrong with this?

Thanks

Jessica
[...]

The freezing is probably due to the Randomize statement inside the
loop. The Randomizer statement must be executed only once, or the
results will be very non-random.

Instead, use

Randomize
Do While i <= amount
...
Loop

You might also want to try my solution that never generates duplicates
and therefore does not need to reject them.

And yes, assign the intValues array to RandomTest.

HTH
Matthias Kläy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top