views:

650

answers:

8

Question

How can I randomly generate one of two states, with the probability of 'red' being generated 10% of the time, and 'green' being generated 90% of the time?

Background

Every 2 second either a green or a red light will blink.

This sequence will continue for 5 minutes.

The total number of occurrences of a blinking light should be 300.

+6  A: 

Either

Random rg = new Random();

int n = rg.Next(10); 
if(n == 0) {
    // blink red
}
else {
    // blink green
}

or

Random rg = new Random();

double value = rg.NextDouble();
if(value < 0.1) {
    // blink red
}
else {
    // blink green
}

This works because Random.Next(int maxValue) returns a uniformly distributed integer in [0, maxValue) and Random.NextDouble returns a uniformly distributed double in [0, 1).

Jason
If Next(int maxValue) return in the range [0,maxValue] then surely you want maxValue = 9? 10% is 1 out of 10, not 1 out of 11.
Kirk Broadhurst
@Krik Broadhurst: Note that the right symbol on `[0, maxValue)` is a `')'` and not a `']'`. This means that the extreme value (`maxValue`) is not included in the range. Thus, `[0, maxValue) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}`.
Jason
+24  A: 

Random.NextDouble returns a number between 0 and 1, so the following should work:

if (random.NextDouble() < 0.90)
{
    BlinkGreen();
}
else
{
    BlinkRed();
}
Michael
+1 I faced a similar problem a while back. This is the most elegant solution I've seen.
Chuck Conway
This definitely works if OP can use a random distribution that *favors* 90% green.
Rex M
The Random class is based on time, in most cases it's random enough. For more details: http://japikse.blogspot.com/2008/10/random-numbers-in-c.html
Chuck Conway
I believe what Rex is referring to is that this isn't guaranteed to create a 90/10 distribution. It is possible (though incredibly unlikely) it will blink red 100% of the time. I'm not sure if exactly 90% was what the original poster intended - the question has been heavily edited and its meaning may have changed.
Michael
+1 for very elegant solution. Also note that this does not guarantee to generate 90%-10% distribution all the time specially when you are calling this routine repeatedly in very short amount of time.
bugBurger
Actually, if the function in this answer also includes the presumed initial line `Random random = new Random();` (as in most of the other examples here), and the function is then called repeatedly in a `for` loop, you will get a surprising and undesirable result. This will occur because the empty constructor for Random() seeds the generator with the system time, and thus NextDouble() will return exactly the same value if a method such as this is called within a few milliseconds. See my answer for a way to avoid this potential problem.
MusiGenesis
Run this code from a button click or whatever to see the problem for yourself: for (int i = 0; i < 20; i++){ Random rnd = new Random(); System.Diagnostics.Debug.Print(rnd.NextDouble().ToString());}
MusiGenesis
0.90 should be cast to a double. Like this: 0.90d
Chuck Conway
+2  A: 

The other answers will definitely work if you need a random distribution that favors 90% green.

However, if you need a precise distribution, something like this will work:

void Main()
{
    Light[] lights = new Light[300];
    int i=0;
    Random rand = new Random();
    while(i<270)
    {
        int tryIndex = rand.Next(300);
        if(lights[tryIndex] == Light.NotSet)
        {
            lights[tryIndex] = Light.Green;
            i++;
        }
    }
    for(i=0;i<300;i++)
    {
        if(lights[i] == Light.NotSet)
        {
            lights[i] = Light.Red;
        }
    }

    //iterate over lights and do what you will
}


enum Light
{
    NotSet,
    Green,
    Red
}
Rex M
I think you have a bug - it should be "int tryIndex = rand.Next(300);", otherwise you're only ever assigning Green to one of the first 270 lights.
Kirk Broadhurst
@Kirk oops! thanks
Rex M
This would be a lot more efficient if your first loop was `while (i < 30)` and you set the value to `Light.Red`. </nitpicking>
MusiGenesis
@Musi true. I could also just have the default value of Light be Green and have one loop to change to red. I thought this was a little easier to understand.
Rex M
A: 
var random = new Random();
for (var i = 0; i < 150; ++i) {
  var red = random.Next(10) == 0;
  if (red)
    // ..
  else
    // Green
}

random.Next(10) will randomly return the numbers 0..9 and there is 10% chance of it returning 0.

Martin Liversage
+2  A: 
public class NewRandom
{
    private static Random _rnd = new Random();
    public static bool PercentChance(int percent)
    {
        double d = (double)percent / 100.0;
        return (_rnd.NextDouble() <= d);
    }
}

To use:

if (NewRandom.PercentChance(10))
{
    // blink red
}
else
{
    // blink green
}
MusiGenesis
+1  A: 

Building on Michaels answer, but adding further context from the question:

public static void PerformBlinks()
{
    var random = new Random();
    for (int i = 0; i < 300; i++)
    {
        if (random.Next(10) == 0)
        {
            BlinkGreen();
        }
        else
        {
            BlinkRed();
        }
        // Pause the thread for 2 seconds.
        Thread.Sleep(2000);
    }
}
Scott Ferguson
-1 for Thread.Sleep(2000).
MusiGenesis
Hi MusiGenesis. Perhaps you would like to provide a code sample that pauses the thread for 2 seconds, but does not use Thread.Sleep ?
Scott Ferguson
He probably meant that it would be better to use a timer than to sleep the thread.
Fantius
But in fairness to you, Scott, you can't tell from the question whether sleeping would be a problem or not.
Fantius
@fantius: yes, I meant use a timer rather than Thread.Sleep(n). Using Thread.Sleep to achieve timing is a bad practice, regardless of any details of this question.
MusiGenesis
A: 

I'm guessing you have the timing part down (so this code doesn't address that). Assuming "nice" division, this will generate 10% reds and 90% greens. If the exactness isn't important, Michael's answer already has my vote.

static void Main(string[] args)
{
    int blinkCount = 300, redPercent = 10, greenPercent = 90;
    List<BlinkObject> blinks = new List<BlinkObject>(300);

    for (int i = 0; i < (blinkCount * redPercent / 100); i++)
    {
        blinks.Add(new BlinkObject("red"));
    }

    for (int i = 0; i < (blinkCount * greenPercent / 100); i++)
    {
        blinks.Add(new BlinkObject("green"));
    }

    blinks.Sort();

    foreach (BlinkObject b in blinks)
    {
        Console.WriteLine(b);
    }
}

class BlinkObject : IComparable<BlinkObject>
{
    object Color { get; set; }
    Guid Order { get; set; }

    public BlinkObject(object color)
    {
        Color = color;
        Order = Guid.NewGuid();
    }

    public int CompareTo(BlinkObject obj)
    {
        return Order.CompareTo(obj.Order);
    }

    public override string ToString()
    {
        return Color.ToString();
    }
}
Austin Salonen
A: 

IF you want these just to look random, you might want implement shuffle bag http://kaioa.com/node/53 and http://stackoverflow.com/questions/910215/need-for-predictable-random-generator

This way the blnking period should look more naturaly and you can simple implement the restricted number of blinks.

ralu