A Sobol sequence, a series of low-discrepancy random numbers in range [0,1], can be generated by first initializing a vector of direction numbers:
Dim V(31) As ULong
For i = 1 To 31
V(i-1) = 2 ^ (32 - i)
Next
and then enumerating the elements generated by a Sobol() function (provided below) as:
For Each lnumber As ULong In Sobol(V)
MessageBox.Show(lnumber / 2 ^ 32)
Next
This can be done as long as there are less than 2^32 values generated, the typical period of a linear congruential generator.
The question is, how can individual Sobol numbers be pulled from Sobol() without using a loop, and instead obtaining one number at a time from a single call-out to the function? The idea would be to instantiate a public class, anywhere in my code, like
Dim ran As New SobolNumber()
and then calling for the next number using for example:
Dim x as Double
x = ran.NextSobolNumber
The Sobol() function and required Ruler() function are listed below:
Public Iterator Function Sobol(ByVal v() As ULong) As IEnumerable(Of ULong)
Dim s As ULong = 0
For Each r In Ruler()
Yield s = s Xor v(r)
Next r
End Function
Public Iterator Function Ruler() As IEnumerable(Of Integer)
Yield 0
For Each r In Ruler()
Yield r + 1
Yield 0
Next r
End Function
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.
As shown here: https://gregstoll.com/~gregstoll/floattohex/
I need to convert a 32 bit float to a little endian hex (click the swap endiness button before converting). I've managed to do this in python by converting to big endian then reordering, but I have no idea how to approach this issue in VB as I'm entirely new to the language. Using the Hex inbuilt function returns 19a, which i assume means its not correctly evaluating my input as a single.
I've found a recommended solution here but cant get it working:
https://www.tek-tips.com/faqs.cfm?fid=6404
Any suggestions would be great, thanks in advance.
There are a number of ways you could do this - the most obvious being the Copy Memory API. Some time ago, a pretty neat solution was published here: Extracting bits from a float in vba which avoided the need for the API
Basically, you'd just need a couple of short functions:
Option Explicit
Type SingleType
Value As Single
End Type
Type FourBytesType
Value(3) As Byte
End Type
Private Function SingleToBytes(f As Single) As Variant
Dim sngType As SingleType
Dim bytesType As FourBytesType
sngType.Value = f
LSet bytesType = sngType
SingleToBytes = bytesType.Value
End Function
Private Function BytesToHex(bytes As Variant) As String
Dim result As String
Dim i As Long
For i = LBound(bytes) To UBound(bytes)
result = result & IIf(bytes(i) < 16, "0", "") & Hex(bytes(i))
Next
BytesToHex = result
End Function
If you wanted to test Endianness and reverse the array, then something like the following, which kind of uses a Byte Order Mark, could be added. I haven't tested it on a big-endian processor but I think it'd work:
Private Function IsLittleEndianProcessor() As Boolean
Const BOM As Single = 1
Const MSB As Byte = 63
Dim bytes() As Byte
Dim n As Long
bytes = SingleToBytes(BOM)
n = UBound(bytes)
IsLittleEndianProcessor = (bytes(n) = MSB)
End Function
Private Function ChangeEndianness(bytes As Variant) As Variant
Dim result() As Byte
Dim n As Long, m As Long
ReDim result(UBound(bytes))
m = UBound(bytes)
For n = LBound(bytes) To UBound(bytes)
result(m) = bytes(n)
m = m - 1
Next
ChangeEndianness = result
End Function
I'm not actually sure how you want the hex string displayed but you could step backwards through the array to write the hex if needed. Sample test would be:
Public Sub TestMe()
Dim bytes As Variant
Dim output As String
bytes = SingleToBytes(3.1415)
If Not IsLittleEndianProcessor Then
bytes = ChangeEndianness(bytes)
End If
output = BytesToHex(bytes)
Debug.Print output
End Sub
I've got a question about using two-dimensional array.
Public twolist(,) As String
For i As Integer = 0 To twolist.length()-1
If Func(twolist(i, )) Then 'this part is hard for me
'doing something
End If
Public Function Func(ByVal CancelInput() As String) As Boolean
What i want to do is Passing two-dimensional array to an array.
I want to read one row in two-dimensional array and pass to function(Func), which is using an array.
Hope You can understand my question... and Thank you!
As an alternative to the For Next Loop, you could use Linq (if you are confortable with it) to perform the same task.
This transforms each element of the source array to a String, groups them in an IEnumerable(Of String) and the result is converted to an unidimensional Array of Strings:
Dim twolist(N, N) As String
Dim CancelInput() As String = twolist.Cast(Of String).Select(Function(str) str).ToArray()
Dim result As Boolean = Func(CancelInput)
I have just used an arbitrary size for your array. You need nested For loops to iterate through a 2 dimensional array. The outer loop goes through the rows and the inner loop adds the value in each field to another array that you are passing to your Function. Each row is passed individually as a single dimension array.
Private Sub TwoDimensionalArray()
Dim twolist(,) As String
ReDim twolist(10, 5)
'First you will need to add data to your array
For x As Integer = 0 To 10
Dim arrayRow(5) As String
For y As Integer = 0 To 5
arrayRow(y) = twolist(x, y)
Next
If Func(arrayRow) Then 'this part is hard for me
'doing something
End If
Next
End Sub
Public Function Func(ByVal CancelInput() As String) As Boolean
Return True
End Function
Mary's answer is good, but assumes you know the length of each dimension.
I have changed it slightly to use the Array.GetLength function:
Private Sub TwoDimensionalArray()
Dim twolist(,) As String
ReDim twolist(10, 5)
'First you will need to add data to your array
For x As Integer = 0 To 10
'Fetch the length of this dimension:
Dim i As Integer = twolist.GetLength(x)
Dim arrayRow(i) As String
For y As Integer = 0 To i - 1
arrayRow(y) = twolist(x, y)
Next
If Func(arrayRow) Then
'do something
End If
Next
End Sub
Public Function Func(ByVal CancelInput() As String) As Boolean
Return True
End Function
Note:
In VB.Net, ReDim twoList(10,5) actually gives you an array of (11,6).
Array.GetLength(0) will return 6 (0,1,2,3,4,5).
In short, Dim specifies the maximum index in each dimension, Length & GetLength return the count of elements.
How are you?
I wrote a program manipulating big binary chains (string variables). This said manipulation requires me to store my chains in a variable so I can use them as numbers. The only variable type that I have found big enough to store such lengthy numbers is BigInteger (we are talking 1.0E100+).
I would like to use something like:
val = BigInteger.Parse(bin, 2)
But the second parameter needed is a NumberStyles object, which can only refer to a NumberStyles.HexNumber.
Is there a simple/optimal way to do this?
Thank you very much. :)
This converts a binary string to BigInteger in 8 bit chunks. It assumes that the binary string represents a positive number.
Private Function BinToBI(ByRef binstr As String) As BigInteger
Dim t As New List(Of Byte)
Dim s As String
Dim idx As Integer = binstr.Length
Do While idx > 0
'get 8 bits
If idx >= 8 Then
s = binstr.Substring(idx - 8, 8)
Else
s = binstr.Substring(0, idx).PadLeft(8, "0"c)
End If
'convert to byte and add to list
Dim b As Byte = Convert.ToByte(s, 2)
t.Add(b)
idx -= 8
Loop
'force to positive
If t(t.Count - 1) >= 128 Then
t.Add(0)
End If
Dim rv As New BigInteger(t.ToArray)
Return rv
End Function
for testing
Dim d As Double = 1.0E+101
Debug.WriteLine(d.ToString("n2"))
Dim bi As BigInteger
' Dim bin As String = "1111111111111111111111111111111" 'Integer.MaxValue
' Dim bin As String = "111111111111111111111111111111111111111111111111111111111111111" 'Long.MaxValue
Dim bin As String = "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110"
bi = BinToBI(bin)
Debug.WriteLine(bi.ToString("n2"))
This was not well tested but should give you some ideas.
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.