VBA excel generate any random number [duplicate] - vba

This question already has answers here:
Generate random number VBA [closed]
(4 answers)
Closed 5 years ago.
I just learning VBA in excel and just want to know how to generate any random number using I believe RND function.

Say we want to randomly pick a number from any number that Excel can represent (positive or negative).
The largest positive value in Excel is 9.99999999999999E+307
Multiply this by Rnd() should generate a random value between 0 and the max
Then randomly pick the sign (+-) of the result:
Sub Randum()
Dim a As Double, b As Double, wf As WorksheetFunction
Set wf = Application.WorksheetFunction
a = 9.99999999999999E+307
Randomize
b = wf.Choose(wf.RandBetween(1, 2), -1, 1) * Rnd() * a
MsgBox b
End Sub

You need to focus on the concept of general numbers in computer science, rather than the more abstract understanding of random. In computer science, random numbers (almost) always start out, as a number between 0 and 1. You can then multiply it, to increase the size of the range and add/subtract numbers to change the offset.
'Example of using Rnd in VBA
Sub RandomPrint()
Dim rand As Double
rand = Rnd * 49 + 1
rand = Round(rand)
Debug.Print "Random integer between 1 and 50: " + CStr(rand)
End Sub
https://www.techonthenet.com/excel/formulas/rnd.php
https://msdn.microsoft.com/en-us/library/f7s023d2(v=vs.90).aspx

Related

How do I generate random numbers from a list of specific numbers that I have?

I want my application to populate random sets of numbers using a list of specific numbers that i chose. For example; I have a set of numbers (1,3,5,9,21,70,56). I want to be able to randomize the order in which these numbers are selected. Is that possible?
If you want to generate a list of 1000 numbers using only those you gave:
Dim r as New Random()
Dim thousand as New List(Of Integer)(1000)
'short way to create an array
Dim onlyFrom = {1,3,5,9,21,70,56}
For i = 1 to 1000
thousand.Add(onlyFrom(r.Next(0, onlyFrom.Length)))
Next i
It repeatedly asks a Random for a random integer between 0 and the array length. Next() may return the lower number, but never the upper number. Documentation
If you want to shuffle those numbers you gave into a random order, easy way to use LINQ:
Dim r as New Random()
Dim onlyFrom = {1,3,5,9,21,70,56}
Dim shuffled = onlyFrom.OrderBy(Function(x) r.Next()).ToArray()
Note: Do not use New Random() in a loop
Randomize()
Dim NumberList= {1,3,5,9,21,70,56}
' Generate random value between 1 and 7, or use NumberList length to make it generic
Dim value As Integer = CInt(Int(( 7 * Rnd()) + 1))
return NumberList(value-1)
* The above code may produce the same value multiple times in a series. so if the requirement is that a different value be produced from the array each time when the code is called seven times, this wouldn't work *
If the requirement is to have a different value from the array each time for the first 7 calls, you may use Shuffle function as laid out here Shuffling an array of strings in vb.net

Randomize seems to miss many possible seeds

In trying to solve this question, I wrote the following in an attempt to implement the Box-Muller transform to generate random normal variables in pure VBA:
Function RandNorm(Optional mean As Double = 0, Optional sd As Double = 1) As Double
Dim s As Double
s = Sqr(-2 * Log(Rnd())) * Cos(6.283185307 * Rnd()) '6.28 etc. is 2*pi
RandNorm = mean + sd * s
End Function
The following somewhat weak test always works, returning a number close to 0:
Sub test1()
Randomize
Dim s As Double
Dim i As Long
For i = 1 To 17000000
s = s + RandNorm()
Next i
Debug.Print s / 17000000
End Sub
On the other hand, the following test never works (because it tries to take the log of 0, which is undefined):
Sub test2()
Randomize
Dim s As Double
Dim i As Long
Debug.Print Rnd() 'just to clock it
For i = 1 To 17000000
s = s + RandNorm()
Next i
Debug.Print s / 17000000
End Sub
The problem is that rnd() returns 0 on average once out of every 2^24 (a bit less than 17,000,000) calls. It is of course easy enough to tweak the definition of RandNorm to avoid the zero (see the linked-to question), but I am still puzzled by the above code. It would make perfect sense to me if each test failed half the time (when the zero is fed into Log()) and worked half the time (when the zero is fed into Cos()). It seems that Randomize avoids at least half of the possible seeds.
Why does Randomize behave this way? Is there a way to seed the random number generator so that all possible states of the random number generator can occur?
On Edit
If I define the following sub:
Sub ReRandomize()
Dim r As Double
Randomize
If Rnd() > 0.5 Then r = Rnd()
End Sub
And modify test1 and test2 above to use ReRandomize instead of Randomize, both of the test subs will fail 50% of the time, so that might answer the part of the question about if there is "a way to seed the random number generator so that all possible states of the random number generator can occur?" It is still mysterious as to why Randomize behaves the way that it does. This is the second time that an Excel VBA question made me realize that Randomize is a weird sub. None of this matters very much for typical use of rnd(), but it does underscore that it is a somewhat low quality random number generator which shouldn't be used for serious statistical work.
I simply modified the Rnd calc to not include 0 or 1. You have to remember that the Rnd Function can produce a number (of type double) in the range of 0 or 1. Therefore, it's chances of having a duplicate number are pretty low.
dbl1stRnd = Rnd()
dblRnd = (0.9999 - 0.0001) * dbl1stRnd + 0.0001
s = Sqr(-2 * Log(dblRnd)) * Cos(6.283185307 * dblRnd) '6.28 etc. is 2*pi
Some example outputs of the regular Rnd() function with Randomize:
3.633606E-02
0.2324036
0.3460443
0.5870923
5.553758E-02
0.2629338
0.2400494
0.1982901
0.5923058
0.7915452
0.4874671
0.2062811
0.5676001
0.1178594
1.932621E-03
0.4326598
0.8291379
I hope this explains some and is what you are looking for.

Finding max value of a loop with VBA

I am trying to find max value of a loop. First, I have two random arrays and I want to find these two arrays correlation coefficient. Then, I want to calculate it multiple times as much as "I3" cell. After that, I want to write a code which finds max correlation coefficient from this calculation. I wrote the code below but it didn't work.
Sub Macro1()
Dim i As Long
For i = 1 To Range("I3")
Calculate
Next
DMax = Application.WorksheetFunction.Max("A2")
Range("I4").Value = DMax
End Sub
Any help is appreciated.
Your Max-Function needs a proper argument. Just typing "A2" doesn't work in VBA. Try:
DMax = Application.WorksheetFunction.Max(Range("A2"))
This will give you the max-Value of the Array A2. But keep in mind that the max-Value of a range consisting of a single cell is always the cell value.
If you want to calculate the maximum value of all iterations, you should use the max-function in each iteration (inside for-loop) and store it's value. In each following iteration you should then overwrite the max-Value if your new max value is larger than the old one. Just like this:
Sub Macro1()
Dim i As Long
DMax = 0
For i = 1 To Range("I3")
Calculate
DMax2 = Application.WorksheetFunction.Max(Range(...))
If DMax2 > DMax Then DMax = DMax2
Next i
Range("I4").Value = DMax
This will give you the max-Value of Range(...) of all iterations.
I barely understand your code, but the solution will be nasted loop. Suppose you have two sets of numbers: A2 (Cells(2, 1)) through I2 (Cells(2, 7)) and A3 (Cells(3, 1)) through I3 (Cells(3, 7)). You want to calculate partial correlation and find what was the maximum value of it.
For i = 1 To 7
For j = 1 To i
'Calculate the correlation
Next j
'here you have partial coefficient and you can compare it,
'if it is greater than previous one then save it and store
Next i
For i = 1 To Range("I3").value 'to calculate from 1 to the value in that cell
what i would recommend for your question.
For i = 1 To 10 ' loop 10 times
For j = 1 To i ' here it will allow you to check your stuff multiple times before moving on to the next value
arr1(i) = arr2(j) ' see if your array match
Next j
Next i

VBA: Return 2nd smallest number from list

Using: Excel 2010, VBA
Goal: Return second smallest number (whether it's an integer or real number with decimals) from a list
Example Dataset (I'd want it to return 10.123):
11
9
26.0
37.123
45
10.123
Problem: I came across several threads that explain how to do this, but it seems like everything only pertains to integers. I need this to work for a list of values that are integers and real numbers (e.g., 1 and 1.2345).
What I've Tried (but I think this only works for integers):
For dblX = LBound(arrFwdTimes) To UBound(arrFwdTimes) - 1
For dblY = dblX + 1 To UBound(arrFwdTimes)
If arrFwdTimes(dblX) > arrFwdTimes(dblY) Then
strT = arrFwdTimes(dblY)
arrFwdTimes(dblY) = arrFwdTimes(dblX)
arrFwdTimes(dblX) = strT
End If
Next dblY
Next dblX
You can use the following to achieve what you want. This is the same as using the SMALL formula function
ans = WorksheetFunction.Small(YourRange,2)

Excel VBA Powerful Random Number Generator

I'll try and keep this as basic and to the point as possible.
Basically, I have weights/probabilities associated with a certain range of numbers. For example :
0: 10%
1: 50%
2: 15%
3: 25%
This then translates into cumulative probabilities :
0: 10%
1: 60%
2: 75%
3: 100%
Using a uniform RNG in VBA, the program generates numbers between 0 and 1, or whatever inferior limit it is set to. Using the same values as the previous example, but only generating numbers greater than 60% (and <= 100%), this results in numbers between 0.6 - 1.0.
This is where I'm stuck. I need to convert these random numbers very efficiently into their "corresponding values".
All of it is stored in VBA variables and needless to say, I don't want to have to write a Select Case for every situation since they're actually 120 different variables and weights.
As of right now, this is what I have to generate those numbers:
RandomNumber = LowerLimit + Rnd() * (1 - LowerLimit)
Thanks is advance for all your help! If I missed a post that was discussing this particular issue please feel free to refer me to it but I really didn't find anything relating to corresponding random numbers.
Place the following function into a public module. You would call it like so mynumber = WeightedRnd(Array(0, 1, 2, 3), Array(0.1, 0.5, 0.15, 0.25)).
Public Function WeightedRnd(values As Variant, weights As Variant) As Double
'First, calculate the cumulative weights
Dim cumulativeWeight As Double
For i = 0 To UBound(weights)
weights(i) = weights(i) + cumulativeWeight
cumulativeWeight = weights(i)
Next
'Next, generate our random number
Dim randomNumber As Double
randomNumber = Rnd()
'Finally, figure out which "bucket" it falls into
For i = 0 To UBound(weights)
If randomNumber <= weights(i) Then
WeightedRnd = values(i)
Exit Function
End If
Next
End Function