I made a randomization function for my regen and it just makes all the things that respawn in a diagonal line across the form... the code is this:
Public Function RandomNumber(ByVal MaxNumber As Integer, _
Optional ByVal MinNumber As Integer = 0) As Integer
'initialize random number generator
Dim r As New Random(System.DateTime.Now.Millisecond)
'if passed incorrect arguments, swap them
'can also throw exception,return 0
If MinNumber > MaxNumber Then
Dim t As Integer = MinNumber
MinNumber = MaxNumber
MaxNumber = t
End If
Return r.Next(MinNumber, MaxNumber)
End Function
and the code for 1 regen is this:
'regen coins
z = coin1
z.Location = zloc
z.Hide()
zloc = New Point(RandomNumber(playspace.Width), RandomNumber(playspace.Height))
If zloc.Y > 595 Then
zloc = New Point(RandomNumber(playspace.Width), RandomNumber(playspace.Height))
End If
z.Location = zloc
z.Show()
I do not know why it just makes a diagonal line but help would be VERY appreciated!
Your question is a bit vague, but I'm going to make a suggestion. Initialize your random number generator outside the RandomNumber function. Otherwise you create a new instance every time you make a call:
'initialize random number generator outside the function
Public _r As New Random(System.DateTime.Now.Millisecond)
Public Function RandomNumber(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
' ensure min is less than max
If MinNumber > MaxNumber Then
Return _r.Next(MaxNumber, MinNumber)
Else
Return _r.Next(MinNumber, MaxNumber)
End If
End Function
Your Random object, r, has function-level scope, which means that you re-initialize it upon each entry to the RandomNumber function.
A lot of people make this mistake and get the same "random" value each time*. You do get a slightly different number each time because, instead of using the default constructor, you're re-initializing it with a semi-random seed (the current millisecond count from the clock). Either way, the call to r.Next() is still not doing what you want it to do.
To fix this, ensuring that it's the same Random object that is used on each call to the function, hoist the declaration of r up a scope level (e.g., to the containing class), or mark it Static at function-level.
That will probably help enough to make you happy. If not, you're in for more education than you were probably hoping. The topic of randomness is a big philosophical one. There is no better place to start than the Wikipedia article on the subject, or perhaps this famous Stack Overflow question.
* Which is, of course, entirely allowed within the definition of "random". It's just not what people are wanting.
Related
I am trying to create a program that is part of Monte Carlo's Integration.
What I have managed to do is create a loop that generates x and y values a specific number of times that have been requested by the user.
Dim ninput As String
ninput = Val(Console.ReadLine())
Dim xvalue As Decimal
Dim yvalue As Decimal
Dim r As New Random()
Dim index As Integer = 0
Do
index +=1
xvalue = r.NextDouble()
yvalue = r.NextDouble()
Loop Until index = ninput
The problem I am having is that I need to generate 'pairs' of x and y values between 0 - 1. So if the user input 1, then it'd be one pair of x and y. If they input 30, then I'd need to generate 30 pairs of x and y values. In my case, I just repeat the random function n amount of times.
I don't know how to generate these pairs of x and y values depending on what the user has inputted for n.
Any help is greatly appreciated, cheers.
It looks like it would be convenient to have those X, Y pairs in the same entity, so you could create a class for them with members that store the values.
While you're creating a class, you can add methods to it to perform operations on the data, for example you might want an easy way to make a new instance of the class, or get some string representation of it.
To store a lot of instances of something, you could use an array, but a List is often more convenient as it does not need its size (capacity) to be declared in advance: you just add a new item to it and the framework takes care of the storage for you.
So you could have something like:
Option Strict On
Module Module1
Public Class PointDec
Property X As Decimal
Property Y As Decimal
Public Sub New(x As Double, y As Double)
Me.X = Convert.ToDecimal(x)
Me.Y = Convert.ToDecimal(y)
End Sub
Public Overrides Function ToString() As String
Return $"({X}, {Y})"
End Function
End Class
Sub Main()
Dim nInput As Integer
Console.Write("Enter number of points to generate: ")
nInput = Integer.Parse(Console.ReadLine())
Dim rand As New Random()
Dim points As New List(Of PointDec)
For i = 1 To nInput
points.Add(New PointDec(rand.NextDouble, rand.NextDouble))
Next
Console.WriteLine("The first point has a y-value of " & points(0).Y)
Console.WriteLine(String.Join(vbCrLf, points))
Console.ReadLine()
End Sub
End Module
Sample output:
Enter number of points to generate: 3
The first point has a y-value of 0.243760395908617
(0.390420139483372, 0.243760395908617)
(0.0321734459289226, 0.175302619661811)
(0.336522278066968, 0.0186830754478849)
It is a good idea to set Option Strict On as the default for new projects: it helps a lot because then Visual Studio can point out several programming mistakes for you.
I used the Decimal type in PointDec as I assumed you actually needed it instead of Double. The Double type is faster for calculations, and the program might be doing a lot of those with a Monte Carlo simulation.
The Val() function should usually be avoided, as it can accept some things that you'd wouldn't at first glance consider to be a number.
I generally use VBA but have been reading up on programming techniques in The C# Programming Yellow Book which, obviously, is more specific to C#. Anyway, it mentions a technique of passing parameters using the Out keyword.
I already know that VBA supports byVal and byRef and am fairly certain there is no direct equivalent for Out. Passing parameters using Out is subtly different to passing parameters by Ref.
This Answer https://stackoverflow.com/a/388781/3451115 seems to give a good explanation of the difference between Out & Ref.
The Ref modifier means that:
The value is already set and
The method can read and modify it.
The Out modifier means that:
The Value isn't set and can't be read by the method until it is set.
The method must set it before returning.
In the code base that I've inherited there are several places where values are assigned to variables using methods that accept parameters byRef. It seems to me that while passing byRef does the job, passing by Out would be safer... So (and here is the question) is there a way of safely / reliably replicating Out in VBA?
In my first iteration (original question) I imagined that the code would have a pattern like:
Sub byOutExample(byRef foo As String)
' Check before running code:
' foo must = vbNullString
If foo <> vbNullString then Err.Raise(someError)
' Do Something to assign foo
foo = someString
' Check before exiting:
' foo must <> vbNullString
If foo = vbNullString then Err.Raise(someError)
End Sub
Other considerations: is it worth doing, is there a better way, what could go wrong?
Edit: I noticed in the comments for the above definition of Ref vs Out that the passed parameter need not be null, nothing, empty etc. it can be preassigned - the main criteria seems that it is re-assigned.
In light of #ThunderFrame's answer below and the comment that a parameter passed by Out can be pre-assigned (and used), perhaps the following is a better approach:
Sub byOutExample(ByRef foo As String)
Dim barTemp As String
barTemp = foo
' Do Something to assign a new value to foo (via barTemp)
barTemp = someString
' Must assign new variable
foo = barTemp
End Sub
In which case would it be true to say that, as long as foo only appears in the 2 locations shown above, the above code is an accurate way to replicate passing a parameter by Out in VBA?
The answer is unequivocally 'no' you cannot replicate the C# out parameter modifier in VBA. From https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier:
Variables passed as out arguments do not have to be initialized before
being passed in a method call. However, the called method is required
to assign a value before the method returns.
These aspects simply don't exist in VBA. All variables in VBA are initialised with default values, ie the concept of an unitialised variable does not exist in VBA, so point 1 isn't possible; and the compiler cannot object if a specified parameter has not had a value assigned within the procedure, so point 2 isn't possible either.
Even the coding patterns in your example would rely on the Do Something to assign foo not to resolve to the relevant data type's default value (which is obviously not the same as being unitialised). The following, for example, would wrongly throw an error:
Public Sub Main()
Dim income As Long, costs As Long
Dim result As Long
income = 1000
costs = 500
ProcessSpend income, costs, result
End Sub
Private Sub ProcessSpend(income As Long, costs As Long, ByRef outValue As Long)
Const TAX_RATE As Long = 2
Dim netCosts As Long
Dim vbDefaultValue As Long
netCosts = costs * TAX_RATE
outValue = income - netCosts
If outValue = vbDefaultValue Then Err.Raise 5, , "Unassigned value"
End Sub
So we're really left with the question of is there a way of getting close to the characteristics of out in VBA?
Unitialised variables: the closest I can think of are a Variant or Object type which by default initialise to Empty and Nothing respectively.
Assign value within the procedure: the simplest way would be to test if the address of the assigning procedure matches your desired procedure address.
It's all leaning towards a helper class:
Option Explicit
Private mNumber As Long
Private mTargetProc As LongPtr
Private mAssignedInProc As Boolean
Public Sub SetTargetProc(targetProc As LongPtr)
mTargetProc = targetProc
End Sub
Public Sub SetNumber(currentProc As LongPtr, val As Long)
mAssignedInProc = (currentProc = mTargetProc)
mNumber = val
End Sub
Public Property Get Number() As Long
If mAssignedInProc Then
Number = mNumber
Else
Err.Raise 5, , "Unassigned value"
End If
End Property
And then the previous example would look like this:
Public Sub Main()
Dim income As Long, costs As Long
Dim result As clsOut
income = 1000
costs = 500
ProcessSpend income, costs, result
Debug.Print result.Number
End Sub
Private Sub ProcessSpend(income As Long, costs As Long, outValue As clsOut)
Const TAX_RATE As Long = 2
Dim netCosts As Long
If outValue Is Nothing Then
Set outValue = New clsOut
End If
outValue.SetTargetProc AddressOf ProcessSpend
netCosts = costs * TAX_RATE
outValue.SetNumber AddressOf ProcessSpend, income - netCosts
End Sub
But that's all getting very onerous... and it really feels as if we are trying to force another language's syntax onto VBA. Stepping back a little from the out characteristics and developing in a syntax for which VBA was designed, then a function which returns a Variant seems the most obvious way to go. You could test if you forgot to set the 'out' value by checking if the function returns an Empty variant (which suits point 1 and 2 of the out characteristics):
Public Sub Main()
Dim income As Long, costs As Long
Dim result As Variant
income = 1000
costs = 500
result = ProcessedSpend(income, costs)
If IsEmpty(result) Then Err.Raise 5, , "Unassigned value"
End Sub
Private Function ProcessedSpend(income As Long, costs As Long) As Variant
Const TAX_RATE As Long = 2
Dim netCosts As Long
netCosts = costs * TAX_RATE
'Comment out the line below to throw the unassigned error
ProcessedSpend = income - netCosts
End Function
And if you wanted the option of passing in a pre-assigned value, then could just define an optional argument as a parameter to the function.
You can pseudo enforce an out type parameter in VBA by passing it in ByRef, and then checking that it is Nothing (or the default value for a value type) before continuing, much as you have done with the String in your example.
I wouldn't impose the exit condition - sometimes an empty string is a perfectly valid return value, as is a Nothing reference.
I want to sort a bunch of domainsdata object. I want to sort first based on countryCode then I want to sort based on revenue.
First I created a private comparer.
Private Function CompareDomainCountry(ByVal x As domainsData, ByVal y As domainsData) As Integer
If x.countryCode < y.countryCode Then
Return -1
ElseIf y.countryCode < x.countryCode Then
Return 1
ElseIf x.revenue < y.revenue Then
Return 1
ElseIf y.revenue < x.revenue Then
Return -1
Else
Return 0
End If
End Function
This has several problem.
The comparer returns 1,-1,0. I think there should be a normal enum for that.
Also I think my comparer should simply call standard vb.net comparer.
And after that, how do I sort list (of domainsdata)?
comparer?
You can use the CompareTo method to compare the values. If the first comparison is zero, then do the other comparison:
Private Function CompareDomainCountry(ByVal x As domainsData, ByVal y As domainsData) As Integer
Dim result As Integer = x.countryCode.CompareTo(y.countryCode)
If result = 0 Then
result = x.revenue.CompareTo(y.revenue)
End If
Return result
End Function
To sort the list using the comparison you use it in the Sort method call:
myList.Sort(AddressOf CompareDomainCountry)
First, note that your Revenue compare code is inconsistent: a smaller X should return -1. You also don't absolutely need that method. This gives the same result:
Dim sorted = DomainList.OrderBy(Function(x) x.CountryCode).
ThenBy(Function(y) y.Revenue).
ToList()
If you want to rely on a standard NET method, your method can be a class member:
Public Class DomainComparer
Implements IComparer(Of Domain)
Public Function Compare(x As Domain, y As Domain) As Integer _
Implements IComparer(Of Domain).Compare
' all your code
End Function
End Class
Then to use it:
Dim dSorter = New DomainComparer
DomainList.Sort(dSorter)
' or simply:
DomainList.Sort(New DomainComparer)
Mr Guffa's AddressOf method is simpler and more concise; I like the class method when there are other variables/properties such as a SortOrder.
The results are the same either way (when the revenue result is changed) unless a sort member is mixed alpha-numeric string (which seemed not to be the case based on the names and comparison).
If you were hoping to use your method with OrderBy(), I dont think you can - the signature doesn't match Func(Of T)(TKey). The return however is uniform with most all Compare() methods to indicate the larger value (DateTime indicates the lesser/earlier date; there may be others).
So I was coding a string search function and ended up with 4 since they needed to go forwards or backwards or be inclusive or exclusive. Then I needed even more functionality like ignoring certain specific things and blah blah.. I figured it would be easier to make a slightly bigger function with optional boolean parameters than to maintain the 8+ functions that would otherwise be required.
Since this is the main workhorse function though, performance is important so I devised a simple test to get a sense of how much I would lose from doing this. The code is as follows:
main window:
Private Sub testbutton_Click(sender As Object, e As RoutedEventArgs) Handles testbutton.Click
Dim rand As New Random
Dim ret As Integer
Dim count As Integer = 100000000
Dim t As Integer = Environment.TickCount
For i = 0 To count
ret = superfunction(rand.Next, False)
Next
t = Environment.TickCount - t
Dim t2 As Integer = Environment.TickCount
For i = 0 To count
ret = simplefunctionNeg(rand.Next)
Next
t2 = Environment.TickCount - t2
MsgBox(t & " " & t2)
End Sub
The functions:
Public Module testoptionality
Public Function superfunction(a As Integer, Optional b As Boolean = False) As Integer
If b Then
Return a
Else
Return -a
End If
End Function
Public Function simpleFunctionPos(a As Integer)
Return a
End Function
Public Function simplefunctionNeg(a As Integer)
Return -a
End Function
End Module
So pretty much as simple as it gets. The weird part is that the superfunction is consistently twice faster than either of the simple functions (my test results are "1076 2122"). This makes no sense.. I tried looking for what i might have done wrong but I cant see it. Can anybody explain this?
You didn't set a return type for simple function. So they return Object type.
So when you using simpleFunctionNeg function application convert Integer to Object type when returning value, and then back from Object to Integer when assigning returning value to your variable
After setting return value to Integer simpleFunctionNeg was little bid faster then superfunction
I have a list of type System.IO.FileInfo, and I would like to randomize the list. I thought I remember seeing something like list.randomize() a little while back but I cannot find where I may have seen that.
My first foray into this yielded me with this function:
Private Shared Sub GetRandom(ByVal oMax As Integer, ByRef currentVals As List(Of Integer))
Dim oRand As New Random(Now.Millisecond)
Dim oTemp As Integer = -1
Do Until currentVals.Count = IMG_COUNT
oTemp = oRand.Next(1, oMax)
If Not currentVals.Contains(oTemp) Then currentVals.Add(oTemp)
Loop
End Sub
I send it the max val I want it to iterate up to, and a reference to the list I want the randomized content in. The variable IMG_COUNT is set farther up in the script, designating how many random images I want displayed.
Thanks guys, I appreciate it :D
Check out the Fisher-Yates shuffle algorithm here: http://en.wikipedia.org/wiki/Knuth_shuffle
with a more concise discussion by this site's chief overlord here:
http://www.codinghorror.com/blog/archives/001015.html
There is a simple C# implementation in the blog entry that should be real easy to change to VB.NET
I've extended the List class with the following Randomize() function to use the Fisher-Yates shuffle algorithm:
''' <summary>
''' Randomizes the contents of the list using Fisher–Yates shuffle (a.k.a. Knuth shuffle).
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="list"></param>
''' <returns>Randomized result</returns>
''' <remarks></remarks>
<Extension()>
Function Randomize(Of T)(ByVal list As List(Of T)) As List(Of T)
Dim rand As New Random()
Dim temp As T
Dim indexRand As Integer
Dim indexLast As Integer = list.Count - 1
For index As Integer = 0 To indexLast
indexRand = rand.Next(index, indexLast)
temp = list(indexRand)
list(indexRand) = list(index)
list(index) = temp
Next index
Return list
End Function
Build a Comparer:
Public Class Randomizer(Of T)
Implements IComparer(Of T)
''// Ensures different instances are sorted in different orders
Private Shared Salter As New Random() ''// only as random as your seed
Private Salt As Integer
Public Sub New()
Salt = Salter.Next(Integer.MinValue, Integer.MaxValue)
End Sub
Private Shared sha As New SHA1CryptoServiceProvider()
Private Function HashNSalt(ByVal x As Integer) As Integer
Dim b() As Byte = sha.ComputeHash(BitConverter.GetBytes(x))
Dim r As Integer = 0
For i As Integer = 0 To b.Length - 1 Step 4
r = r Xor BitConverter.ToInt32(b, i)
Next
Return r Xor Salt
End Function
Public Function Compare(x As T, y As T) As Integer _
Implements IComparer(Of T).Compare
Return HashNSalt(x.GetHashCode()).CompareTo(HashNSalt(y.GetHashCode()))
End Function
End Class
Use it like this, assuming you mean a generic List(Of FileInfo):
list.Sort(New Randomizer(Of IO.FileInfo)())
You can also use a closure to make the random value 'sticky' and then just use linq's .OrderBy() on that (C# this time, because the VB lambda syntax is ugly):
list = list.OrderBy(a => Guid.NewGuid()).ToList();
Explained here, along with why it might not even be as fast as real shuffle:
http://www.codinghorror.com/blog/archives/001008.html?r=31644
There are several reasonable methods of shuffling.
One has already been mentioned. (The Knuth Shuffle.)
Another method would be to assign a "weight" to each element and sort the list according to that "weight." This method is possible but would be unweildy because you cannot inherit from FileInfo.
One final method would be to randomly select an element in the original list and add it to a new list. Of course, that is, if you don't mind creating a new list. (Haven't tested this code...)
Dim rnd As New Random
Dim lstOriginal As New List(Of FileInfo)
Dim lstNew As New List(Of FileInfo)
While lstOriginal.Count > 0
Dim idx As Integer = rnd.Next(0, lstOriginal.Count - 1)
lstNew.Add(lstOriginal(idx))
lstOriginal.RemoveAt(idx)
End While
You could also implement a shuffle, many ways to do this, the simplest is randomly pick a item and insert it into a new location a bunch of times.
If you have the number of elements then a pseudo-random method can be used whereby you choose the first element at random (e.g. using the inbuilt random number function) then add a prime and take the remainder after division by the number of values. e.g. for a list of 10 you could do i = (i + prime) % 10 to generated indices i from some starting value. As long as the prime is greater than the number of values in the list then you create a sequence which runs through all of the numbers 0...n where n is the number of values - 1, but in a pseudorandom order.
Dim oRand As New Random() 'do not seed!!!!
Private Sub GetRandom(ByRef currentVals As List(Of Integer))
Dim i As New List(Of Integer), j As Integer
For x As Integer = 0 To currentVals.Count - 1
j = oRand.Next(0, currentVals.Count)
i.Add(currentVals(j))
currentVals.RemoveAt(j)
Next
currentVals = i
End Sub
You could create custom comparer that just returns a random number, then sort the list using this comparer. It could be horribly inefficient and cause an almost infinite loop, but might be worth a try.