tags:

views:

113

answers:

7

Hello,

I have a section of code that is being used to determine if a certain event should happen. The code looks like this.

        If (Date.Now.Ticks Mod 100) < 4 Then
            Return True
        Else
            Return False
        End If

The idea here is that this event should happen 4 time out of 100, or 4%. However, in production, the actually percentages range from 10% to 60%. The application is being hosted on two load balanced Win2k3 servers.

Any suggestion would be helpful.

Keith

+3  A: 

Why don't you use this :

Dim r As New Random(System.DateTime.Now.Millisecond)
Return r.Next(0, 100)
Canavar
Seeding with Now.Millisecond might do more harm than good. The random object should be created once and reused. It will automatically seed based on the time.
Jon B
If I were to rewrite the code now, I would take this appraoch. However, since both approaches work fine under unit testing I don't want to make a change to a production system until I can nail down the problem.
Keith
A: 

4 times out of 100? So you want "Ticks mod 25 == 0"? Also, I'm not a VB programmer, but can you not get rid of all that "If -- Else" and just "Return (Date.Now.Ticks mod 25) == 0"?

JMD
The percentage is a user supplied value so I don't want to have to change the MOD part of the equations. And yes, very true, I don't really need the ELSE part here.
Keith
A: 

The nature of randomness is that you get random results. If your actual percentages run between 10% and 60%, your code may be running correctly, but your sample sizes may just be too small for the probability to converge.

Jekke
A: 

Using a truly random number generator would be much better than relying on a function of time.

Garry Shutler
Only if security is a factor. In other cases, pseudo random is random enough.
Jon B
Why use pseudo random when truly random is so easy?
Garry Shutler
RNGCryptoServiceProvider takes 100 times longer to generate a number.
Jon B
I'm sure the time taken is _really_ significant
Garry Shutler
+1  A: 

Look at the results of DateTime.Now.Ticks. On my box (which is a Vista box - the OS is almost certainly important here) taking samples for 5 seconds and getting all the distinct values, all the tick counts ended with "91". The code for this is below.

While it's clearly not that bad on your server, the timer granularity will certainly have a significant effect. Using a random number generator would certainly be a better bet. Be aware that you ought to create a single instance of Random, or perhaps one per thread - and that Random isn't threadsafe. You might like to use my StaticRandom class in MiscUtil for simplicity.

Here's the tick testing code:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

public class Test
{
    static void Main()
    {
        foreach(long tick in GetTicks().Distinct())
        {
            Console.WriteLine(tick);
        }
    }

    static IEnumerable<long> GetTicks()
    {
        DateTime end = DateTime.Now.AddSeconds(5);
        DateTime now;
        while ((now = DateTime.Now) < end)
        {
            yield return now.Ticks;
        }
    }
}
Jon Skeet
Jon, you could add that his code is not asking for something which occurs 4% of the time. It is asking that when this gets run if the tick count mod 100 is less than four make it true. A small snippet explaining how to get 4% 'true' would be more helpful.
sixlettervariables
Looks like you were able to provide such a snippet yourself and earn the acceptance bonus entirely reasonably :)
Jon Skeet
Heh, I sat there and said if I took the time to comment on yours, perhaps I should just add it to mine. I liked your benchmarking code for proof though!
sixlettervariables
+2  A: 

The reason you're getting that behavior is you're not asking for 4% of the time that you be given a True value, you're asking that any time the number of Ticks modulo 100 is less than 4.

Given a uniform random number generator, as in System.Random:

Dim random As New Random()
Return random.NextDouble() < 0.04

With Random.NextDouble you will have an equal probability that any number from 0.0 to 1.0 will be selected. Hence I can ask for True 4% of the time by returning true whenever the random double is less than 0.04.

sixlettervariables
Meta comment: please don't put the Dim random As New Random inside the method. Make it a class variable. Provided here for example completeness.
sixlettervariables
+1  A: 

First off, the number of ticks returned may be scaled by a constant factor from whatever system timer it's taken from, so that it won't hit every integer, and taking it mod 100 won't give you every number from 0 to 99 with equal probability.

Second, if your code is event-driven, the system will determine when it's activated; this activation time may tend to be a fixed multiple of ticks. Also, the run-times between activation and execution of your code will tend to be similar. So, depending on what else is happening on your computer, the tick number will be semi-predictable.

The upshot is, the tick number is highly system-dependent; you want to use an actual random number generator to get your random numbers...

comingstorm