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.
Related
I'm using the windows forms charts in Visual Studio, and I want to write a function that, given a chart area, and an axis returns the maximum or minimum value (XValue or Yvalue, depending on the axis argument) from the points in that chart area. I want to use the AxisName enumeration for the second argument but, as per usual, the official documentation from msdn does not cover me. Does the enum name represent an index for the Axes() property of the ChartArea class or is it a direct link to an Axis object? Do i need to declare the enum in my class (that inherits DataVisualisation.Charts), or is it already known? pls help me
Public Function getAxisMinimum(ByVal area As AreaEnum, ByVal axe As AxisName) As Double
Dim min As Double = Double.NaN
For Each ser As Series In Series
If ser.ChartArea = ChartAreas(area).Name And ser.Points.Count > 0 Then
For Each p As DataPoint In ser.Points
'compare X or Y values depending on the axe argument to set the min
Next
End If
Next
'If there are no points in any series in the area, it will return NaN
Return min
End Function
AreaEnum is an integer enumeration that represents the index of the ChartArea() property that corresponds to each name.
I don't need a solution as to how to compare my points' values or how to return them, I just need an explanation on how to use the AxisName enumeration
Nevermind, I think I solved it. Visual Studio's auto-complete gave me the answer, because it recognized AxisName enumeration and corrected me in the select statement. I think this works:
Public Function getAxisMinimum(ByVal area As AreaEnum, ByVal axe As AxisName) As Double
Dim min As Double = Double.NaN
For Each ser As Series In Series
If ser.ChartArea = ChartAreas(area).Name AndAlso ser.Points.Count > 0 Then
For Each p As DataPoint In ser.Points
Select Case axe
Case AxisName.X
If Double.IsNaN(min) OrElse p.XValue < min Then
min = p.XValue
End If
Case AxisName.Y
For Each Yval As Double In p.YValues
If Double.IsNaN(min) OrElse Yval < min Then
min = Yval
End If
Next
Case Else
' Not an acceptable AxisName
Return Double.NaN
End Select
Next
End If
Next
'If there are no points in any series in the area, it will return NaN
Return min
End Function
Suppose I've got a couple of classes, like class1 and class2. These classes can have many properties and they can even change from an execution to another (depending on the version of the software).
The user can define a "formula" in plain text and the software should convert the names of the "variables" (class+property) with their actual values. So, given an instance of class1 and an instance of class2, I should cycle the properties (and variables) and check if the formula contains them. For each one I should then replace the name with the value.
For example, I could have:
Dim myClass1 As Class1 = New Class1()
myClass1.PropertyA = "FOO"
myClass1.PropertyB = 5
Dim myClass2 As Class2 = New Class2()
myClass2.PropertyX = "BAR"
myClass2.PropertyY = 7
And the user could have declared this formula (the formula here is simplified in VB.NET, but actually it can be in SQL or different type. The formula is in plain text, so is a string in code):
Dim i = 0
While (i + myClass1.PropertyB) < myClass2.PropertyY
str = "myClass1.PropertyA" & "myClass2.PropertyX"
i += 1
End While
The result, after the replace, should be:
Dim i = 0
While (i + 5) < 7
str = "FOO" & "BAR"
i += 1
End While
Now, the classes and the variables can vary, so I can't search for specific strings. I think I should use reflection, but I'm not used to it and I prefer to avoid it if a better solution exists. Any advice and/or best practice?
This can be done as a class or as a function
Consider the formula.
x=(aa)+(bb)+(2*a*b)
Public Function formularesult(Byval a1 as double,Byval b1 as double) as double
Dim x as double
x=0
Dim a as double
a=0
Dim b as double
b=0
a =a1
b=b1
x=(a*a)+(b*b)+(2*a*b)
return x
End Function
Call to the function can bs follows
Let values of a and b can be 12 and 20 respectively.
Dim result as double
result =formularesult(12,20)
I hope this can be a help for someone.
Improvements to my suggestions are always welcome.
How can I change the order of data in a list on a random order (Shuffle). easiest method with the least coding effort without definition of new functions or sub please.
I usually tag the items with random data and sort that. You can implement the shuffle directly, but that's more work - especially proving the algorithm actually shuffles randomly...
Well, I just made this code snippet here for a future reference, if you want to use a list just replace all instances of "Stack" with "List" and make sure to change ".Push" to ".Add" and it should work fine. To be honest I'm surprised a shuffle function isn't built in.
Dim Deck As New Stack
Sub Main()
For i As Integer = 1 To 10
Deck.Push("Card #" & i)
Next
Do
Console.Clear()
For i As Integer = 0 To Deck.Count - 1
Console.WriteLine(Deck(i))
Next
Console.ReadKey(True)
Shuffle()
Loop
End Sub
Private Sub Shuffle()
Dim NewDeck As New Stack
Dim i As Integer
Dim s As String 'Change type depending on what is in your stack.
Dim r As New Random
Do
i = r.Next(0, Deck.Count)
s = Deck(i)
'Stops you getting several of one item and then none of others, etc.
If Not NewDeck.Contains(Deck(i)) Then
NewDeck.Push(s)
End If
Loop Until NewDeck.Count = Deck.Count
Deck = NewDeck
End Sub
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.
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.