How to represent a number in the form of A*10-^n? - vb.net

I recently wrote this program in Visul Basic 13.
it searchs for the nth catalan number but after 48 even Decimal type is too short.
Is there any other way to represent them? like in the form of A*10^n?
Public Class Try_Catalan_Number
'Catalan numbers form a sequence of natural numbers that occur in various counting problems,
'often involving recursively defined objects.
Inherits Base_Number
Public Overrides Sub Test()
Dim Return_Catalan_Value As Decimal
If Function_Catalan(Return_Catalan_Value) = False Then
Return_To_Form_Boolean = False
Else
Return_To_Form_Boolean = True
End If
Return_To_Form_Value = Function_Catalan(Return_Catalan_Value)
End Sub
Private Function Function_Catalan(Return_Catalan_Value As Decimal) As Decimal
'We return a Decimal function because catalan numbers can be very big and decimal is the biggest type.
Dim Binomial_Cofficients As Decimal
Dim Result As Decimal
Dim Number_Of_Loops As Integer
Dim tmpNumber As Object
Dim K As Decimal
Dim N As Decimal
If (Number > 48) Then
Return False
Exit Function
End If
'48 is the largest catalan number position which can be displayed...any position above 48 is too big.
tmpNumber = Number - 1
N = 2 * tmpNumber
K = tmpNumber
Result = 1
For Number_Of_Loops = 1 To K
Result = Result * (N - (K - Number_Of_Loops))
Result = Result / Number_Of_Loops
Next Number_Of_Loops
Binomial_Cofficients = Result
tmpNumber = Number - 1
tmpNumber = ((1 / (1 + tmpNumber)) * Binomial_Cofficients)
Return_Catalan_Value = tmpNumber
Return Return_Catalan_Value
End Function
End Class

[I assume by "Visul Basic 13" you mean the VB which is associated with Visual Studio 2013, i.e. VB version 12.0.]
You can use System.Numerics.BigInteger (you'll have to add a reference to System.Numerics):
Imports System.Numerics
Module Module1
Friend Function Factorial(n As Integer) As BigInteger
If n < 2 Then Return 1
If n = 2 Then Return 2
Dim f As BigInteger = BigInteger.Parse("2")
For i = 3 To n
f *= i
Next
Return f
End Function
Friend Function CatalanNumber(n As Integer) As BigInteger
Return Factorial(2 * n) / (Factorial(n + 1) * Factorial(n))
End Function
Sub Main()
For i = 0 To 550
Console.WriteLine(CatalanNumber(i).ToString())
Next
Console.ReadLine()
End Sub
End Module
I did not test to see the maximum Catalan number it can calculate, and I have no inclination to verify the results beyond those shown on the Wikipedia page.
Optimisations are left as an exercise for the reader ;)
Edit: FWIW, I can get it to run a bit faster by using
Function CatalanNumber(n As Integer) As BigInteger
Dim nFactorial = Factorial(n)
Dim twonFactorial = nFactorial
For i = (n + 1) To 2 * n
twonFactorial = BigInteger.Multiply(twonFactorial, i)
Next
Return twonFactorial / (BigInteger.Pow(nFactorial, 2) * (n + 1))
End Function
The speed increase varies from roughly 50% (n=50) to 20% (n=5000). If you're only using the function a few times for fairly small n, there may be little point worrying about it.
Edit2 Re-writing your function a bit to make it easier to read and removing the off-by-one error, we get:
Private Function Function_Catalan(a As Integer) As BigInteger
If a = 0 Then Return 1
Dim binomialCofficient As BigInteger = BigInteger.One
Dim n As Integer = 2 * a
Dim k As Integer = a - 1
For i As Integer = 1 To k
binomialCofficient = binomialCofficient * (n - (k - i)) / i
Next i
Return binomialCofficient / a
End Function

to get this format you could use:
String.Format("{0:E4}", InputNumber)

Related

Reversing Digits

I'm trying to make a function that takes a three digit number and reverses it (543 into 345)
I can't take that value from a TextBox because I need it to use the three numbers trick to find a value.
RVal = ReverseDigits(Val)
Diff = Val - RVal
RDiff = ReverseDigits(Diff)
OVal = Diff + RDiff
543-345=198
198+891=1089
Then it puts 1089 in a TextBox
Function ReverseDigits(ByVal Value As Integer) As Integer
' Take input as abc
' Output is (c * 100 + b * 10 + a) = cba
Dim ReturnValue As Boolean = True
Dim Val As String = CStr(InputTextBox.Text)
Dim a As Char = Val(0)
Dim b As Char = Val(1)
Dim c As Char = Val(2)
Value = (c * 100) + (b * 10) + (a)
Return ReturnValue
End Function
I've tried this but can't figure out why it won't work.
You can convert the integer to a string, reverse the string, then convert back to an integer. You may want to enforce the three digit requirement. You can validate the argument before attempting conversion
Public Function ReverseDigits(value As Integer) As Integer
If Not (value > 99 AndAlso value < 1000) Then Throw New ArgumentException("value")
Return Integer.Parse(New String(value.ToString().Reverse().ToArray()))
End Function
My code is pretty simple and will also work for numbers that don't have three digits assuming you remove that validation. To see what's wrong with your code, there are a couple of things. See the commented lines which I changed. The main issue is using Val as a variable name, then trying to index the string like Val(0). Val is a built in function to vb.net and the compiler may interpret Val(0) as a function instead of indexing a string.
Function ReverseDigits(ByVal Value As Integer) As Integer
' Dim ReturnValue As Boolean = True
' Dim Val As String = CStr(InputTextBox.Text)
Dim s As String = CStr(Value)
Dim a As Char = s(0)
Dim b As Char = s(1)
Dim c As Char = s(2)
Value = Val(c) * 100 + Val(b) * 10 + Val(a)
'Return ReturnValue
Return Value
End Function
(Or the reduced version of your function, but I would still not hard-code the indices because it's limiting your function from expanding to more or less than 3 digits)
Public Function ReverseDigits(Value As Integer) As Integer
Dim s = CStr(Value)
Return 100 * Val(s(2)) + 10 * Val(s(1)) + Val(s(0))
End Function
And you could call the function like this
Dim inputString = InputTextBox.Text
Dim inputNumber = Integer.Parse(inputString)
Dim reversedNumber = ReverseDigits(inputNumber)
Bonus: If you really want to use use math to find the reversed number, here is a version which works for any number of digits
Public Function ReverseDigits(value As Integer) As Integer
Dim s = CStr(value)
Dim result As Integer
For i = 0 To Len(s) - 1
result += CInt(Val(s(i)) * (10 ^ i))
Next
Return result
End Function
Here's a method I wrote recently when someone else posted basically the same question elsewhere, probably doing the same homework:
Private Function ReverseNumber(input As Integer) As Integer
Dim output = 0
Do Until input = 0
output = output * 10 + input Mod 10
input = input \ 10
Loop
Return output
End Function
That will work on a number of any length.

pascal triangle gives overflow for 13

I wrote a code to output Pascal triangle in a multi-line textbook. The program works fine for inputs between 1 to 12 but gives an overflow error once a value of 13 is inputed.
Is there any modifications I can make to enable the program to accurately give outputs for 13 and higher?
Here is the code I used:
Public Class pascal_triangle
Private Function factorial(ByVal k As Integer) As Integer
If k = 0 Or k = 1 Then
Return 1
Else
Return k * factorial(k - 1)
End If
End Function
Private Sub BtnGen_Click(sender As Object, e As EventArgs) Handles BtnGen.Click
Dim nCr As Integer
Dim i, j, k As Integer
Dim output As String
output = ""
j = Val(TxtColumn.Text)
For k = 0 To j
For i = 0 To k
Dim fact, fact1, fact2 As Integer
fact = factorial(k)
fact1 = factorial(k - i)
fact2 = factorial(i)
nCr = fact / (fact1 * fact2)
TxtOutput.Text += Str(nCr) & output
Next
TxtOutput.Text += vbCrLf
Next
End Sub
End Class
The overflow is because 13! is too big to fit in an integer.
The largest representable integer (32-bit signed) is
2147483647 (0x7FFFFFFF == 01111111 11111111 11111111 11111111b)
so :
12! = 479001600
MaxInt = 2147483647
13! = 6227020800
If you want to use larger numbers than this you need to use a larger number type. The next larger types are Long (64-bit signed, max 9223372036854775807) or, for your purposes, ULong (unsigned 64-bit, since you don't need negative numbers, which is twice that at 18446744073709551615).
This will let you calculate up to20!, which is 2432902008176640000. For numbers larger than this you will need to look into using either BigInteger or other dedicated libraries that allow for holding and calculating arbitrarily large numbers.
Alternatively, you can look into other methods of computing an arbitrary row without using factorials.
Your main problem is that you are using Integer which is too small to hold the factorial of 13. Change your Factorial function to return Long. It would also be a good idea to turn Option Strict On and make nCr a Double.
Private Function factorial(ByVal k As Integer) As Long
If k = 0 Or k = 1 Then
Return 1
Else
Return k * factorial(k - 1)
End If
End Function
Private Sub BtnGen_Click(sender As Object, e As EventArgs) Handles BtnGen.Click
Dim nCr As Double
Dim i, j, k As Integer
Integer.TryParse(TxtColumn.Text, j)
For k = 0 To j
For i = 0 To k
Dim fact, fact1, fact2 As Long
fact = factorial(k)
fact1 = factorial(k - i)
fact2 = factorial(i)
nCr = fact / (fact1 * fact2)
TxtOutput.Text += nCr.ToString & " "
Next
TxtOutput.Text += vbCrLf
Next
End Sub

Decimal to binary for large number (>2253483438943167)

I have a VB.Net application that goes through a series of processes to decode a string, one problem with this is that I have found a function that converts from binary to decimal for any number, but I cannot find a function that will convert a supplied number (in string format) into a binary string. For reference, the binary to decimal conversion function is below:
Public Function baseconv(d As String)
Dim N As Long
Dim Res As Long
For N = Len(d) To 1 Step -1
Res = Res + ((2 ^ (Len(d) - N)) * CLng(Mid(d, N, 1)))
Next N
Return Str(Res)
End Function
What i would do if the number is less than 16*1e18 <-> can hold in a Uint64 :
1) store the number inside an unsigned 64 bits integer : num.
2) then just loop on each bit to test it :
mask = 1<<63
do
if ( num AND mask ) then ->> push("1")
else ->> push("0")
mask = mask >> 1
until mask = 0
(where push builds the output with a string concatenation, or, if performance matters, a StringBuilder, or it can be a stream,... )
what about this?
Module Module1
Sub Main()
Console.WriteLine(Convert.ToString(2253483438943167 * 5, 2))
Console.ReadKey()
End Sub
End Module
Try using the System.Numerics.BigInteger class like this:
Dim d As String
d = "2253483438943167"
Dim bi As BigInteger
bi = BigInteger.Parse(d)
Dim ba() As Byte
ba = bi.ToByteArray()
Dim s As String
Dim N As Long
Dim pad as Char
pad = "0"c
For N = Len(ba) To 1 Step -1
s = s + Convert.ToString(ba(N)).PadLeft(8, pad)
Next N
How about
Dim foo As BigInteger = Long.MaxValue
foo += Long.MaxValue
foo += 2
Dim s As New System.Text.StringBuilder
For Each b As Byte In foo.ToByteArray.Reverse
s.Append(Convert.ToString(b, 2).PadLeft(8, "0"c))
Next
Debug.WriteLine(s.ToString.TrimStart("0"c))
'10000000000000000000000000000000000000000000000000000000000000000

Is there a way to force circular integer overflow in Excel VBA?

I'm trying to convert some Java code to Excel and the required hashcode function generates an overflow error, instead of wrapping to the negative
Function FnGetStringHashCode(ByVal str As String) As Integer
Dim result, i
FnGetStringHashCode = 17
For i = 1 To Len(str)
Dim c, a
c = Mid(str, i, 1)
a = AscW(c)
FnGetStringHashCode = 31 * FnGetStringHashCode + a
Next i
End Function
Is there a way of doing this in Excel VBA?
Although there is no built-in way to do this, the computation is simple:
Public Function coerceLongToInt(toCoerce As Long) As Integer
Const MIN_INT As Long = -32768
Const MAX_INT As Long = 32767
Const NUM_INTS As Long = MAX_INT - MIN_INT + 1
Dim remainder As Long
remainder = toCoerce Mod NUM_INTS
If remainder > MAX_INT Then
coerceLongToInt = remainder - NUM_INTS
ElseIf remainder < MIN_INT Then
coerceLongToInt = remainder + NUM_INTS
Else
coerceLongToInt = remainder
End If
End Function
This is the behavior you want, right?
?coerceLongToInt(-32769)
32767
?coerceLongToInt(-32768)
-32768
?coerceLongToInt(-1)
-1
?coerceLongToInt(0)
0
?coerceLongToInt(1)
1
?coerceLongToInt(32767)
32767
?coerceLongToInt(32768)
-32768
You would use it like this:
Function FnGetStringHashCode(ByVal str As String) As Integer
Dim result, i
FnGetStringHashCode = 17
For i = 1 To Len(str)
Dim c, a
c = Mid(str, i, 1)
a = AscW(c)
FnGetStringHashCode = coerceLongToInt(31 * CLng(FnGetStringHashCode) + a)
Next i
End Function
You need the 'CLng' call in there to prevent VBA from raising an overflow error when it computes the intermediate value (31 * [some integer >= 1058]).
I have modified a little the script of ours. The main difference is returning type of your function. Now it returns Variant. As decimal is a subset of Variant, and it can store bigger numbers than long I think it is a good solution (see VBA data types) - I do not know is it possible to explicitly return Decimal. Here is the script
Function FnGetStringHashCode(ByVal str As String) As Variant
Dim tmp As Variant, c As String, a As Integer, i As Integer
tmp = 17
For i = 1 To Len(str)
c = Mid$(str, i, 1)
a = AscW(c)
tmp = 31 * tmp + a
Next i
FnGetStringHashCode = tmp
End Function
And a little test routine
Sub test()
Debug.Print CStr(FnGetStringHashCode("dawdaedae"))
End Sub

Random integer in VB.NET

I need to generate a random integer between 1 and n (where n is a positive whole number) to use for a unit test. I don't need something overly complicated to ensure true randomness - just an old-fashioned random number.
How would I do that?
As has been pointed out many times, the suggestion to write code like this is problematic:
Public Function GetRandom(ByVal Min As Integer, ByVal Max As Integer) As Integer
Dim Generator As System.Random = New System.Random()
Return Generator.Next(Min, Max)
End Function
The reason is that the constructor for the Random class provides a default seed based on the system's clock. On most systems, this has limited granularity -- somewhere in the vicinity of 20 ms. So if you write the following code, you're going to get the same number a bunch of times in a row:
Dim randoms(1000) As Integer
For i As Integer = 0 to randoms.Length - 1
randoms(i) = GetRandom(1, 100)
Next
The following code addresses this issue:
Public Function GetRandom(ByVal Min As Integer, ByVal Max As Integer) As Integer
' by making Generator static, we preserve the same instance '
' (i.e., do not create new instances with the same seed over and over) '
' between calls '
Static Generator As System.Random = New System.Random()
Return Generator.Next(Min, Max)
End Function
I threw together a simple program using both methods to generate 25 random integers between 1 and 100. Here's the output:
Non-static: 70 Static: 70
Non-static: 70 Static: 46
Non-static: 70 Static: 58
Non-static: 70 Static: 19
Non-static: 70 Static: 79
Non-static: 70 Static: 24
Non-static: 70 Static: 14
Non-static: 70 Static: 46
Non-static: 70 Static: 82
Non-static: 70 Static: 31
Non-static: 70 Static: 25
Non-static: 70 Static: 8
Non-static: 70 Static: 76
Non-static: 70 Static: 74
Non-static: 70 Static: 84
Non-static: 70 Static: 39
Non-static: 70 Static: 30
Non-static: 70 Static: 55
Non-static: 70 Static: 49
Non-static: 70 Static: 21
Non-static: 70 Static: 99
Non-static: 70 Static: 15
Non-static: 70 Static: 83
Non-static: 70 Static: 26
Non-static: 70 Static: 16
Non-static: 70 Static: 75
To get a random integer value between 1 and N (inclusive) you can use the following.
CInt(Math.Ceiling(Rnd() * n)) + 1
Use System.Random:
Dim MyMin As Integer = 1, MyMax As Integer = 5, My1stRandomNumber As Integer, My2ndRandomNumber As Integer
' Create a random number generator
Dim Generator As System.Random = New System.Random()
' Get a random number >= MyMin and <= MyMax
My1stRandomNumber = Generator.Next(MyMin, MyMax + 1) ' Note: Next function returns numbers _less than_ max, so pass in max + 1 to include max as a possible value
' Get another random number (don't create a new generator, use the same one)
My2ndRandomNumber = Generator.Next(MyMin, MyMax + 1)
Microsoft Example Rnd Function
https://msdn.microsoft.com/en-us/library/f7s023d2%28v=vs.90%29.aspx
1- Initialize the random-number generator.
Randomize()
2 - Generate random value between 1 and 6.
Dim value As Integer = CInt(Int((6 * Rnd()) + 1))
Public Function RandomNumber(ByVal n As Integer) As Integer
'initialize random number generator
Dim r As New Random(System.DateTime.Now.Millisecond)
Return r.Next(1, n)
End Function
All the answers so far have problems or bugs (plural, not just one). I will explain. But first I want to compliment Dan Tao's insight to use a static variable to remember the Generator variable so calling it multiple times will not repeat the same # over and over, plus he gave a very nice explanation. But his code suffered the same flaw that most others have, as i explain now.
MS made their Next() method rather odd. the Min parameter is the inclusive minimum as one would expect, but the Max parameter is the exclusive maximum as one would NOT expect. in other words, if you pass min=1 and max=5 then your random numbers would be any of 1, 2, 3, or 4, but it would never include 5. This is the first of two potential bugs in all code that uses Microsoft's Random.Next() method.
For a simple answer (but still with other possible but rare problems) then you'd need to use:
Private Function GenRandomInt(min As Int32, max As Int32) As Int32
Static staticRandomGenerator As New System.Random
Return staticRandomGenerator.Next(min, max + 1)
End Function
(I like to use Int32 rather than Integer because it makes it more clear how big the int is, plus it is shorter to type, but suit yourself.)
I see two potential problems with this method, but it will be suitable (and correct) for most uses. So if you want a simple solution, i believe this is correct.
The only 2 problems i see with this function is:
1: when Max = Int32.MaxValue so adding 1 creates a numeric overflow. altho, this would be rare, it is still a possibility.
2: when min > max + 1. when min = 10 and max = 5 then the Next function throws an error. this may be what you want. but it may not be either. or consider when min = 5 and max = 4. by adding 1, 5 is passed to the Next method, but it does not throw an error, when it really is an error, but Microsoft .NET code that i tested returns 5. so it really is not an 'exclusive' max when the max = the min. but when max < min for the Random.Next() function, then it throws an ArgumentOutOfRangeException. so Microsoft's implementation is really inconsistent and buggy too in this regard.
you may want to simply swap the numbers when min > max so no error is thrown, but it totally depends on what is desired. if you want an error on invalid values, then it is probably better to also throw the error when Microsoft's exclusive maximum (max + 1) in our code equals minimum, where MS fails to error in this case.
handling a work-around for when max = Int32.MaxValue is a little inconvenient, but i expect to post a thorough function which handles both these situations. and if you want different behavior than how i coded it, suit yourself. but be aware of these 2 issues.
Happy coding!
Edit:
So i needed a random integer generator, and i decided to code it 'right'. So if anyone wants the full functionality, here's one that actually works. (But it doesn't win the simplest prize with only 2 lines of code. But it's not really complex either.)
''' <summary>
''' Generates a random Integer with any (inclusive) minimum or (inclusive) maximum values, with full range of Int32 values.
''' </summary>
''' <param name="inMin">Inclusive Minimum value. Lowest possible return value.</param>
''' <param name="inMax">Inclusive Maximum value. Highest possible return value.</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function GenRandomInt(inMin As Int32, inMax As Int32) As Int32
Static staticRandomGenerator As New System.Random
If inMin > inMax Then Dim t = inMin : inMin = inMax : inMax = t
If inMax < Int32.MaxValue Then Return staticRandomGenerator.Next(inMin, inMax + 1)
' now max = Int32.MaxValue, so we need to work around Microsoft's quirk of an exclusive max parameter.
If inMin > Int32.MinValue Then Return staticRandomGenerator.Next(inMin - 1, inMax) + 1 ' okay, this was the easy one.
' now min and max give full range of integer, but Random.Next() does not give us an option for the full range of integer.
' so we need to use Random.NextBytes() to give us 4 random bytes, then convert that to our random int.
Dim bytes(3) As Byte ' 4 bytes, 0 to 3
staticRandomGenerator.NextBytes(bytes) ' 4 random bytes
Return BitConverter.ToInt32(bytes, 0) ' return bytes converted to a random Int32
End Function
You should create a pseudo-random number generator only once:
Dim Generator As System.Random = New System.Random()
Then, if an integer suffices for your needs, you can use:
Public Function GetRandom(myGenerator As System.Random, ByVal Min As Integer, ByVal Max As Integer) As Integer
'min is inclusive, max is exclusive (dah!)
Return myGenerator.Next(Min, Max + 1)
End Function
as many times as you like. Using the wrapper function is justified only because the maximum value is exclusive - I know that the random numbers work this way but the definition of .Next is confusing.
Creating a generator every time you need a number is in my opinion wrong; the pseudo-random numbers do not work this way.
First, you get the problem with initialization which has been discussed in the other replies. If you initialize once, you do not have this problem.
Second, I am not at all certain that you get a valid sequence of random numbers; rather, you get a collection of the first number of multiple different sequences which are seeded automatically based on computer time. I am not certain that these numbers will pass the tests that confirm the randomness of the sequence.
If you are using Joseph's answer which is a great answer, and you run these back to back like this:
dim i = GetRandom(1, 1715)
dim o = GetRandom(1, 1715)
Then the result could come back the same over and over because it processes the call so quickly. This may not have been an issue in '08, but since the processors are much faster today, the function doesn't allow the system clock enough time to change prior to making the second call.
Since the System.Random() function is based on the system clock, we need to allow enough time for it to change prior to the next call. One way of accomplishing this is to pause the current thread for 1 millisecond. See example below:
Public Function GetRandom(ByVal min as Integer, ByVal max as Integer) as Integer
Static staticRandomGenerator As New System.Random
max += 1
Return staticRandomGenerator.Next(If(min > max, max, min), If(min > max, min, max))
End Function
Dim rnd As Random = New Random
rnd.Next(n)
Just for reference, VB NET Fuction definition for RND and RANDOMIZE (which should give the same results of BASIC (1980 years) and all versions after is:
Public NotInheritable Class VBMath
' Methods
Private Shared Function GetTimer() As Single
Dim now As DateTime = DateTime.Now
Return CSng((((((60 * now.Hour) + now.Minute) * 60) + now.Second) + (CDbl(now.Millisecond) / 1000)))
End Function
Public Shared Sub Randomize()
Dim timer As Single = VBMath.GetTimer
Dim projectData As ProjectData = ProjectData.GetProjectData
Dim rndSeed As Integer = projectData.m_rndSeed
Dim num3 As Integer = BitConverter.ToInt32(BitConverter.GetBytes(timer), 0)
num3 = (((num3 And &HFFFF) Xor (num3 >> &H10)) << 8)
rndSeed = ((rndSeed And -16776961) Or num3)
projectData.m_rndSeed = rndSeed
End Sub
Public Shared Sub Randomize(ByVal Number As Double)
Dim num2 As Integer
Dim projectData As ProjectData = ProjectData.GetProjectData
Dim rndSeed As Integer = projectData.m_rndSeed
If BitConverter.IsLittleEndian Then
num2 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4)
Else
num2 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0)
End If
num2 = (((num2 And &HFFFF) Xor (num2 >> &H10)) << 8)
rndSeed = ((rndSeed And -16776961) Or num2)
projectData.m_rndSeed = rndSeed
End Sub
Public Shared Function Rnd() As Single
Return VBMath.Rnd(1!)
End Function
Public Shared Function Rnd(ByVal Number As Single) As Single
Dim projectData As ProjectData = ProjectData.GetProjectData
Dim rndSeed As Integer = projectData.m_rndSeed
If (Number <> 0) Then
If (Number < 0) Then
Dim num1 As UInt64 = (BitConverter.ToInt32(BitConverter.GetBytes(Number), 0) And &HFFFFFFFF)
rndSeed = CInt(((num1 + (num1 >> &H18)) And CULng(&HFFFFFF)))
End If
rndSeed = CInt((((rndSeed * &H43FD43FD) + &HC39EC3) And &HFFFFFF))
End If
projectData.m_rndSeed = rndSeed
Return (CSng(rndSeed) / 1.677722E+07!)
End Function
End Class
While the Random CLASS is:
Public Class Random
' Methods
<__DynamicallyInvokable> _
Public Sub New()
Me.New(Environment.TickCount)
End Sub
<__DynamicallyInvokable> _
Public Sub New(ByVal Seed As Integer)
Me.SeedArray = New Integer(&H38 - 1) {}
Dim num4 As Integer = If((Seed = -2147483648), &H7FFFFFFF, Math.Abs(Seed))
Dim num2 As Integer = (&H9A4EC86 - num4)
Me.SeedArray(&H37) = num2
Dim num3 As Integer = 1
Dim i As Integer
For i = 1 To &H37 - 1
Dim index As Integer = ((&H15 * i) Mod &H37)
Me.SeedArray(index) = num3
num3 = (num2 - num3)
If (num3 < 0) Then
num3 = (num3 + &H7FFFFFFF)
End If
num2 = Me.SeedArray(index)
Next i
Dim j As Integer
For j = 1 To 5 - 1
Dim k As Integer
For k = 1 To &H38 - 1
Me.SeedArray(k) = (Me.SeedArray(k) - Me.SeedArray((1 + ((k + 30) Mod &H37))))
If (Me.SeedArray(k) < 0) Then
Me.SeedArray(k) = (Me.SeedArray(k) + &H7FFFFFFF)
End If
Next k
Next j
Me.inext = 0
Me.inextp = &H15
Seed = 1
End Sub
Private Function GetSampleForLargeRange() As Double
Dim num As Integer = Me.InternalSample
If ((Me.InternalSample Mod 2) = 0) Then
num = -num
End If
Dim num2 As Double = num
num2 = (num2 + 2147483646)
Return (num2 / 4294967293)
End Function
Private Function InternalSample() As Integer
Dim inext As Integer = Me.inext
Dim inextp As Integer = Me.inextp
If (++inext >= &H38) Then
inext = 1
End If
If (++inextp >= &H38) Then
inextp = 1
End If
Dim num As Integer = (Me.SeedArray(inext) - Me.SeedArray(inextp))
If (num = &H7FFFFFFF) Then
num -= 1
End If
If (num < 0) Then
num = (num + &H7FFFFFFF)
End If
Me.SeedArray(inext) = num
Me.inext = inext
Me.inextp = inextp
Return num
End Function
<__DynamicallyInvokable> _
Public Overridable Function [Next]() As Integer
Return Me.InternalSample
End Function
<__DynamicallyInvokable> _
Public Overridable Function [Next](ByVal maxValue As Integer) As Integer
If (maxValue < 0) Then
Dim values As Object() = New Object() { "maxValue" }
Throw New ArgumentOutOfRangeException("maxValue", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", values))
End If
Return CInt((Me.Sample * maxValue))
End Function
<__DynamicallyInvokable> _
Public Overridable Function [Next](ByVal minValue As Integer, ByVal maxValue As Integer) As Integer
If (minValue > maxValue) Then
Dim values As Object() = New Object() { "minValue", "maxValue" }
Throw New ArgumentOutOfRangeException("minValue", Environment.GetResourceString("Argument_MinMaxValue", values))
End If
Dim num As Long = (maxValue - minValue)
If (num <= &H7FFFFFFF) Then
Return (CInt((Me.Sample * num)) + minValue)
End If
Return (CInt(CLng((Me.GetSampleForLargeRange * num))) + minValue)
End Function
<__DynamicallyInvokable> _
Public Overridable Sub NextBytes(ByVal buffer As Byte())
If (buffer Is Nothing) Then
Throw New ArgumentNullException("buffer")
End If
Dim i As Integer
For i = 0 To buffer.Length - 1
buffer(i) = CByte((Me.InternalSample Mod &H100))
Next i
End Sub
<__DynamicallyInvokable> _
Public Overridable Function NextDouble() As Double
Return Me.Sample
End Function
<__DynamicallyInvokable> _
Protected Overridable Function Sample() As Double
Return (Me.InternalSample * 4.6566128752457969E-10)
End Function
' Fields
Private inext As Integer
Private inextp As Integer
Private Const MBIG As Integer = &H7FFFFFFF
Private Const MSEED As Integer = &H9A4EC86
Private Const MZ As Integer = 0
Private SeedArray As Integer()
End Class
I see a lot of answers of users that are not satisfied with using System.Random.
Despite the fact that I personally would still use System.Random, I was thinking about a way to use a GUID as the base for a random value. A GUID can be converted to a byte array using its ToByteArray method, and the resulting byte array can be converted to a numeric value using a BitConverter.
'Function for reuse (min is inclusive and max is exclusive)
Function GetRandom(min As Integer, max As Integer) As Integer
Return BitConverter.ToUInt64(Guid.NewGuid.ToByteArray) Mod (max - min) + min
End Function
'one-liner specific for your purpose (n is exclusive)
BitConverter.ToUInt64(Guid.NewGuid.ToByteArray) Mod (n - 1) + 1
Note that this is just a little thought experiment. I haven't tested the performance, nor have I investigated the actual "randomness" of the results. But for your purpose, it might just do the job.
The accepted answer uses the Microsoft.VisualBasic.VBMath.Rnd method, which indeed offers a simple and attractive oneliner, but I personally would avoid writing new code that uses the Microsoft.VisualBasic namespace.
Function xrand() As Long
Dim r1 As Long = Now.Day & Now.Month & Now.Year & Now.Hour & Now.Minute & Now.Second & Now.Millisecond
Dim RAND As Long = Math.Max(r1, r1 * 2)
Return RAND
End Function
[BBOYSE]
This its the best way, from scratch :P