7

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.

Community
  • 1
  • 1
bugBurger
  • 6,950
  • 9
  • 32
  • 39
  • 1
    Do you need the results to be such that, out of your 300 light blinks, exactly 30 are red and 270 are green? None of the answers so far posted will do that (except by chance, ironically). – MusiGenesis Oct 05 '09 at 20:42
  • 3
    If so, see this question: http://stackoverflow.com/questions/910215/need-for-predictable-random-generator – Jodi Oct 05 '09 at 20:47

8 Answers8

33

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

if (random.NextDouble() < 0.90)
{
    BlinkGreen();
}
else
{
    BlinkRed();
}
Michael
  • 54,279
  • 5
  • 125
  • 144
  • +1 I faced a similar problem a while back. This is the most elegant solution I've seen. – Chuck Conway Oct 05 '09 at 21:38
  • This definitely works if OP can use a random distribution that *favors* 90% green. – Rex M Oct 05 '09 at 22:11
  • 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 Oct 05 '09 at 22:41
  • 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 Oct 05 '09 at 22:44
  • +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 Oct 06 '09 at 14:29
  • 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 Oct 06 '09 at 15:09
  • 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 Oct 06 '09 at 15:13
  • 0.90 should be cast to a double. Like this: 0.90d – Chuck Conway Oct 06 '10 at 05:23
7

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
  • 236,483
  • 35
  • 423
  • 525
  • 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 Oct 05 '09 at 22:19
  • 2
    @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 Oct 05 '09 at 23:10
3

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
  • 142,167
  • 33
  • 283
  • 313
  • 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 Oct 05 '09 at 22:26
  • This would be a lot more efficient if your first loop was `while (i < 30)` and you set the value to `Light.Red`. – MusiGenesis Oct 06 '09 at 16:48
  • @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 Oct 06 '09 at 18:45
3
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
  • 74,184
  • 40
  • 190
  • 334
1

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
  • 7,690
  • 7
  • 41
  • 64
  • 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 Oct 05 '09 at 21:18
  • He probably meant that it would be better to use a timer than to sleep the thread. – Fantius Oct 05 '09 at 21:23
  • But in fairness to you, Scott, you can't tell from the question whether sleeping would be a problem or not. – Fantius Oct 05 '09 at 21:24
  • @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 Oct 06 '09 at 14:57
1

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
  • 49,173
  • 15
  • 109
  • 139
0
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
  • 104,481
  • 22
  • 209
  • 256
0

If you want these just to look random, you might want to implement shuffle bag

http://web.archive.org/web/20111203113141/http://kaioa.com:80/node/53

and

Need for predictable random generator

This way the blinking period should look more natural and you can simply implement the restricted number of blinks.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Luka Rahne
  • 10,336
  • 3
  • 34
  • 56