So basically, I have this function that is supposed to generate two random integers between a high and a low number, to make a point on the form. I know that Random can handle this, but Random has consistency, whereas I need the numbers to be completely random on the form.
For example, most of my generated points appear in a diagonal line. This is what I want to avoid. It should go all over the form between the high and low numbers.
Here is my current function:
Function GetNewLocation() As Point
Randomize()
Dim int1 As Integer = RandomNumber(6, 345)
Randomize()
Dim int2 As Integer = RandomNumber(35, 286)
Return New Point(int1, int2)
End Function
Function RandomNumber(ByVal low As Integer, ByVal high As Integer) As Integer
Randomize()
Return New Random().Next(low, high)
End Function
How can I get true random number generation where the point is not on a diagonal line?
Each time you create a new instance of Random you are resetting the random number generator. Since the default constructor uses Environment.TickCount as the seed you are often returning precisely the same sequence of pseudo-random numbers. The system does not update TickCount that often. This is why it seems to you that you are getting non-random numbers.
Try changing your code like this:
Private _rnd As New Random()
Function RandomNumber(ByVal low As Integer, ByVal high As Integer) As Integer
Return _rnd.Next(low, high)
End Function
There is no true randomness in computer systems. There are many algorithm to generate "random" numbers but they are always based on a seed, like the time, process id, a mix of both, or on more advanced cases where randomness is taken seriously, on sounds being detected in a microphone, data from atmospheric sensors or cosmic microwave background, but it will always derivate from some existing source of information.
Thats also not the best slution.
I prefer my own code:
Dim old As Integer = 6572
Public Function Rand(ByVal min As Integer, ByVal max As Integer) As Integer
Dim random As New Random(old + Date.Now.Millisecond)
old = random.Next(min, max + CInt(IIf(Date.Now.Millisecond Mod 2 = 0, 1, 0)))
Return old
End Function
Thats a little bit better
I literally just came up with this solution. Maybe it will help =)
I went from:
Dim rand As Random = New Random(DateTime.Now.Millisecond)
To
Dim rand As Random = New Random(DateTime.Now.Millisecond * DateTime.Now.Second * DateTime.Now.Minute * DateTime.Now.Hour)
And it seems to have worked really well.
You can see the difference in the side by side.
in java(assuming the limit is (limit-1)):
random = System.currentTimeMillis()%limit;
You get the idea :)
Related
In my code,I have now realised I have to use the New Random function, my code was working before with the Randomize and then the numbers but now it comes up with loads of errors and wont even let me run the program. I think it is only a small error but I just need some help to get the final bit going
Heres the code and thanks for any help :)
I cannot get the code to work with the randomly generated number and I have to use the New Random function I cannot use randomize() Does anybody know how to help here is the code.
Dim timestook As Int32 = 1
Dim usersguess As Integer
Dim value = New Random(0 - 19)
Console.WriteLine("You have to guess this number. It is between 1 and 20. Good Luck !")
usersguess = Console.ReadLine()
'keep looping until they get the right value
While usersguess <> value
'now check how it compares to the random value
If usersguess < value Then
timestook = timestook + 1
Console.WriteLine("You're too low. Go higher ")
ElseIf usersguess > value Then
Console.WriteLine("You're too high. Go Lower.")
timestook = timestook + 1
End If
'If they are wrong the code will run again,after telling the user if they are too high or too low.
usersguess = Console.ReadLine()
End While
' Console.WriteLine("You're correct. Well Done")
If usersguess = value Then
Console.WriteLine("You took,{0}", timestook)
End If
Console.ReadLine()
End Sub
You'll want to do some googling on how to use random numbers. Your problem is that you aren't creating a Random object to handle the random number generation.
Here's how you can fix your code:
Dim randNumGen As New Random() 'Create Random object
Dim value As Integer = randNumGen.Next(0, 20) 'set value equal to a new random number between 0-19
Please note that this code could be further refactored for readability and simplicity (like changing timestook = timestook + 1 to timestook += 1 and selecting better variable names like numberOfGuesses as opposed to timestook, etc.
The expression New Random(0-19) does not do at all what you think it does, name it does NOT return an integer. Instead, it creates an instance of a Random object, which is a type that knows how to create new random values. The 0-19 part of the expression is the seed for the Random object's constructor, and is the same as just passing the value -19.
This looks like it's either homework or personal practice, so I feel like you will be better served in this case with a separate example using the Random type for reference than you would if I fixed the code sample in the question for you:
Dim rnd As New Random()
For i As Integer = 0 To 10
Console.WriteLine(rnd.Next(0, 20))
Next i
It's also worth mentioning here that you typically only want one Random object for your entire program, or at least only one Random object for each logical part of your program. Creating new Random objects resets the seeds, and for best results you want to follow the same seed on subsequent calls to the same instance for a while.
I'm doing basic random number generation using this Shared Function:
Public Shared Function RandomNumber(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
'initialize random number generator
Dim r As New Random(Date.Now.Ticks And &HFFFF)
If MinNumber > MaxNumber Then
Dim t As Integer = MinNumber
MinNumber = MaxNumber
MaxNumber = t
End If
Return r.Next(MinNumber, MaxNumber)
End Function
Called like this: dim x as integer = Random(2100000000)
Very simple, and the seed value comes straight from a MS example.
HERE'S THE PROBLEM: I'm getting duplicate numbers on occasion, but always created at times that are usually at least 5 or 10 minutes apart. I can see if I was calling the function multiple times per second or millisecond, because that'd kind of "breaks" the seed. But these are showing up at extended time spans. What else could be causing this?
Duplicate seed issue?
It might be better defining r as static so that it is initialised once when first invoked. Refer to this answer Random integer in VB.NET
The Random constructor takes an Integer as its parameter which is 32-bits. As spencer7593 said, with only 16 bits, you're repeating the sequence every 6.5ms. Try:
Dim r As New Random(Date.Now.Ticks And &HFFFFFFFF)
However, this will do the same thing:
Dim r As New Random()
Better yet, don't create a new Random object each time:
Private Static r As New Random()
Public Shared Function RandomNumber(MaxNumber As Integer, Optional MinNumber As Integer = 0) As Integer
...
Return r.Next(MinNumber, MaxNumber)
End Function
Q: What else could be causing this?
A: It could be happening purely by random. Random numbers are just that: random. At any point in time, whether its seconds or hours away from another point in time, its just as likely for a number to appear as any other number. There is no guarantee that a number won't be repeated.
On the other hand, it looks like your seed value is only on the order of 16 bits. And that's like a total of 65,536 possibilities. There's 10,000 ticks in a millisecond, so ever 6.5 milliseconds you have the possibility of reusing the same seed.
It's not at all clear whether the VB Random is using some other kind of entropy beyond that seed or not. (But gathering entropy for inclusion would slow down the initialization, so it may not be, as a performance consideration.)
According the docs, creating two Random objects using the same seed value results in Random objects that create duplicate sequences of unique numbers.
http://msdn.microsoft.com/en-us/library/ctssatww.aspx
I think that answers the question why it is happening.
I guess the next question is why do you need to instantiate a new Random object? If you need multiple objects, then instantiating several of them, but making sure you are using a different seed value for each one would be one approach.
But before you go there, I recommend you consider using just one Random. Calls to get random numbers can be serviced from an existing Random, rather than creating a new one every time you need a random number.
Try it another way:
Public Function RandomNumber2(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
' Initialize the random-number generator.
Randomize()
' Generate random value between MaxNumber and MinNumber.
Return CInt(Int((MaxNumber * Rnd()) + MinNumber))
End Function
See Randomize Function (Visual Basic) for more details. Hope this helps.
I have a requirement where in I need to sleep for a Poisson duration before sending the next packet. The current formula I can think of is
( e^(-lambda) X lambda^t ) / fact(t)
However, for time steps 280 and more the fact(t) would become obsolete due to overflow.
Can someone help me workaround this conventional way in VB .NET?
I think you are looking for the inter-arrival time. A random inter-arrival time can be generated using
t = (Math.log(1.0-Math.random())/-lambda
The formula you posted is the one that defines the probability that there are exactly t (in your case) arrivals within a specific time period.
See the Wikipedia article on generating Poisson distributions.
Even though the factorial value is getting extremely large (as you've observed), the λ k term is also getting somewhat large to compensate. For a way to represent the distribution that takes this into account, see the Wikipedia article on the Poisson distribution:
A VB implementation might look something like:
Module Module1
Sub Main()
Console.WriteLine(Poisson(4, 250))
Console.ReadKey()
End Sub
Function Poisson(ByVal lambda As Integer, ByVal k As Integer) As Double
Poisson = Math.Exp(k * Math.Log(lambda) - lambda - SumOverLn(1, k))
End Function
Function SumOverLn(ByVal start As Integer, ByVal endval As Integer) As Long
Dim i As Integer
SumOverLn = 0
For i = start To endval
SumOverLn = SumOverLn + Math.Log(i)
Next
End Function
End Module
It looks like after a while it is so close to 0 that it registers as such. You may be able to adjust the precision of the display to get more decimal places, but 0 might be a decent enough approximation for high values (the notion that you get from the probability theory seems to be that those values are indeed very close to zero anyway).
Ok this code works but I think it has some necessary steps in the middle to get to the outcome. Any thoughts on how to make it tighter?
Public Function CalCheckSum(ByVal ByteList As List(Of Byte)) As List(Of Byte)
Dim total As Integer = 0
For Each b As Byte In ByteList
total = total + b
Next
Dim modedVal As Integer = 0
modedVal = total Mod &H100
Dim negatedValue As Integer = 0
negatedValue = &H100 - modedVal
Dim charList As List(Of Char) = Hex(negatedValue).ToCharArray.ToList
Dim returnList As New List(Of Byte)
For Each ch As Char In charList
returnList.Add(Asc(ch))
Next
Return returnList
End Function
BTW This is what I'm using to test it:
Dim blist As New List(Of Byte)
blist.Add(&H52)
blist.Add(&H34)
blist.Add(&H35)
blist.Add(&H31)
blist.Add(&H32)
blist.Add(&H33)
blist.Add(&H34)
blist.Add(&H30)
blist.Add(&H30)
blist.Add(&H30)
blist.Add(&H30)
blist.Add(&H46)
blist.Add(&H46)
blist.Add(&H46)
blist.Add(&H46)
blist.Add(&H42)
blist.Add(&H4B)
blist.Add(&H9)
blist.Add(&H44)
Dim b As List(Of Byte) = CalCheckSum(blist)
The correct values for b are:
b(0) = &H43
b(1) = &H39
I'm honestly not sure why you'd waste the time it takes to optimize this. Calling that function over 100,000 times in a loop takes less than 20 milliseconds. Even if this is one of the "hot points" in your application (since you say it communicates with an embedded hardware device), it's unlikely that you'll see any appreciable speed increase by optimizing the code that you have.
But just for fun, I decided to see if I couldn't optimize things a little anyway... Here's what I came up with:
Remove the redundant List(Of Char) creation. You're already converting the values to an array with the ToCharArray method. Why go through the expense of calling ToList on that, just to iterate through it? You can iterate just as well through an array. This shaves the time down to around 8 seconds, a pretty massive speed-up for minimal effort.
You can also pass in the approximate size of your new List(Of Byte) as an argument to the constructor. You already know this from the size of your charArray, since you're just adding each of those elements back in. This doesn't make any difference when you're only working with two items, as in the example you provided, but it could make things slightly more efficient for substantially larger numbers of elements because the List wouldn't have to be dynamically resized at any point during the loop.
There's absolutely no difference between Asc, AscW, and Convert.ToInt32. I measured each of them explicitly just to see. My gut instinct was to change that to AscW, but apparently it doesn't matter. Lots of people will turn their nose up at the use of VB-specific idioms, and recommend what they consider more universal methods provided by the .NET Framework. It turns out that since all the VB-specific code is written in the same managed code as the alternatives, it's a simple matter of preference which you use.
Otherwise replacing List(Of T) with simple arrays doesn't make any appreciable difference, either. Since a List is easier to work with outside of the function, you might as well keep it as the returned type.
So my final code looks something like this:
Public Function CalCheckSum(ByVal ByteList As List(Of Byte)) As List(Of Byte)
Dim total As Integer = 0
For Each b As Byte In ByteList
total = total + b
Next
Dim negatedValue As Integer = 0
negatedValue = &H100 - (total Mod &H100)
Dim charArray As Char() = Hex(negatedValue).ToCharArray()
Dim returnList As New List(Of Byte)(charArray.Length)
For Each ch As Char In charArray
returnList.Add(CByte(Asc(ch)))
Next
Return returnList
End Function
Even running this 999,000 times in a loop, I consistently clock it somewhere between 62 and 64 ms.
You could also use LINQ. It's not really my area, and I doubt you'll see any measurable speed increases (it still has to do the same amount of looping and iterating under the covers). The big benefit it provides is that your code is simpler and looks cleaner. I'm surprised someone hasn't already posted this solution.
EDIT: As an aside, your original code didn't compile for me. I had to add in the CByte operator to convert the Integer value returned from the Asc operator into a Byte type. That tells me that you're not programming with Option Strict On. But you should be. You have to explicitly set the option in your project's properties, but the benefits of strong typing far outweigh the cost of going through and fixing some of your existing code. You might even notice a performance increase, especially if you've been using a lot of late binding inadvertently.
I am trying to generate a random number during testing using NUnit, but it keeps generating the same number. I am using the following function for this purpose.
dim dCount As Integer = Math.Floor((High - Low + 1) * Rnd() + Low)
dim divName As String = "abc" & dCount
Any idea why it is doing this?
Regards,
Sam
Presumably you're executing many tests in quick succession. I don't know exactly what Rnd() does in VB, but it sounds like it's got the typical "new RNG per call" problem.
Create a single instance of Random and use it repeatedly. Note that your maths can be replaced by a simple:
dim dCount as Integer = myRandom.Next(Low, High+1)
One caveat - Random isn't thread-safe. If you need to generate random numbers from different threads, either use locking or thread statics.
On another point: using random numbers will make your unit tests non-deterministic. Are you sure you have to? Sometimes it's appropriate, but not often IME.
Dim dCount As Integer = between(low, high)
Dim divName As String = "abc" & dCount
Dim myRandom As New Random
Private Function between(ByVal low As Integer, ByVal high As Integer) As Integer
between = myRandom.Next(low, high + 1)
End Function