70

I want to write a program that:

  • 80% of the time will say sendMessage("hi");
  • 5% of the time will say sendMessage("bye");
  • and 15% of the time will say sendMessage("Test");

Does it have to do something with Math.random()? like

if (Math.random() * 100 < 80) {
  sendMessage("hi");
}
else if (Math.random() * 100 < 5) {
  sendMessage("bye");
}
Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
0x29A
  • 911
  • 1
  • 8
  • 13
  • 7
    hmmm ... that adds up to 145%. – john.k.doe Jul 19 '12 at 00:07
  • 1
    it's possible, if you look at it as if he has three chances to say something or nothing; once 90%, once 50% and once 5% – Tom Jul 19 '12 at 00:09
  • its definetly not impossible , its like a scrolling add that give greater priority to certain ads, you could do something like an array where there are 100 items , 40 are a, 30 are b , and 30 are c , then randomly choose one, just not 90%, 50%, and 5% - rethink your math – Scott Selby Jul 19 '12 at 00:10
  • @Tom: as in, it is possible for it to say "hi bye test"? The code uses `else if`, not a simple `if`, so I doubt that's a real option, but still, good catch. – sarnold Jul 19 '12 at 00:10
  • now that it no longer says 145%, either the solutions below that suggest calling random once and using the result would be the way to accomplish this. – john.k.doe Jul 19 '12 at 00:14

8 Answers8

86

Yes, Math.random() is an excellent way to accomplish this. What you want to do is compute a single random number, and then make decisions based on that:

var d = Math.random();
if (d < 0.5)
    // 50% chance of being here
else if (d < 0.7)
    // 20% chance of being here
else
    // 30% chance of being here

That way you don't miss any possibilities.

David Callanan
  • 5,601
  • 7
  • 63
  • 105
Ernest Friedman-Hill
  • 80,601
  • 10
  • 150
  • 186
  • 4
    The key point to make is that you should generate and use ONE random number. – Stephen C Jul 19 '12 at 00:40
  • why'd you put the word "double" – jack blank Jan 21 '15 at 07:50
  • 4
    @user2537537 -- the first line declares (and defines) a Java double-precision floating-point variable; that's just the correct Java syntax for declaring a variable. – Ernest Friedman-Hill Jan 21 '15 at 12:55
  • 2
    Wait, shouldn't it be "<= 0.5" instead of "< 0.5", because how it is now is 49% chance, not 50%. – Dysanix Official Jan 20 '17 at 20:36
  • 2
    @DysanixOfficial Not 49% -- more like 49.999999999... which, as any mathematician can tell you, is pretty much 50%. But sure -- <= would be correct too. – Ernest Friedman-Hill Jan 20 '17 at 22:38
  • Why is it that when I google "coin flip in javascript" it says run this code: `Math.floor(Math.random() * 2) === 0` to determine if its heads. Doesn't your code do the same thing in a simpler manner? I.e., `Math.random() < 0.5`? – Ogen Mar 12 '17 at 05:55
  • @Ogen Indeed. Perhaps there is something special about JavaScript that makes the longer form preferable, but in Java, certainly, it would be ridiculous. – Ernest Friedman-Hill Mar 13 '17 at 01:10
  • 3
    @DysanixOfficial it should be < .5, because random can return 0, but cannot return 1. When you write <= .5, you're defining these ranges: (0 ; .5) and (0.5+0.(1) ; 0.(9) ). So you have 0.500...01% for true and 0.4999...9% for false – Fen1kz Mar 22 '18 at 15:31
  • 1
    better (and more generic) soluion here: https://stackoverflow.com/questions/44437797/pick-item-from-list-at-random-each-has-different-chance – symcbean Apr 24 '19 at 15:21
  • @Ogen perhaps that's because multiplying by 2 is a simple bit-wise left shift operation and Floor is a simple truncation. I think decimal numbers could be relatively expensive – Sgene9 Dec 23 '19 at 15:46
  • @Sgene9 but random() returns a double between 0 and 1 — multiplying by 2 is a floating-point operation – Ernest Friedman-Hill Dec 23 '19 at 16:22
  • @ErnestFriedman-Hill I'm making a guess based on possible low-level operations. Floating point numbers can be represented by bits and be bit shifted. 0.75(base of 10) -> 0.11(2) -> do bitshift -> 1.1(2) -> 1.5(10) – Sgene9 Jan 22 '20 at 20:38
23

For cases like this it is usually best to generate one random number and select the case based on that single number, like so:

int foo = Math.random() * 100;
if (foo < 80) // 0-79
    sendMessage("hi");
else if (foo < 85) // 80-84
    sendMessage("bye");
else // 85-99
    sendMessage("test");
David
  • 3,223
  • 3
  • 29
  • 41
sarnold
  • 102,305
  • 22
  • 181
  • 238
4

I made a percentage chance function by creating a pool and using the fisher yates shuffle algorithm for a completely random chance. The snippet below tests the chance randomness 20 times.

var arrayShuffle = function(array) {
   for ( var i = 0, length = array.length, swap = 0, temp = ''; i < length; i++ ) {
      swap        = Math.floor(Math.random() * (i + 1));
      temp        = array[swap];
      array[swap] = array[i];
      array[i]    = temp;
   }
   return array;
};

var percentageChance = function(values, chances) {
   for ( var i = 0, pool = []; i < chances.length; i++ ) {
      for ( var i2 = 0; i2 < chances[i]; i2++ ) {
         pool.push(i);
      }
   }
   return values[arrayShuffle(pool)['0']];
};

for ( var i = 0; i < 20; i++ ) {
   console.log(percentageChance(['hi', 'test', 'bye'], [80, 15, 5]));
}
TURTLE
  • 3,728
  • 4
  • 49
  • 50
2
const t = 10; // time in seconds
const a = Math.floor(Math.random() * (t + 1));
if (a > 8) {
    // 20% chance code
} else {
    // 80% chance code
}

It has +1 as Math.random() and Math.floor() lower the maximum by 1 when put together.

ethry
  • 731
  • 6
  • 17
0

Generate a 20% chance to get "Yupiii!" in the console.log

const testMyChance = () => {

  const chance = [1, 0, 0, 0, 0].sort(() => Math.random() - 0.5)[0]

  if(chance) console.log("Yupiii!")
  else console.log("Oh my Duck!")
}

testMyChance()
gildniy
  • 3,528
  • 1
  • 33
  • 23
-2

Java

/**
 * Zero or less returns 'false', 100 or greater returns 'true'. Else return probability with required percentage.
 * @param percentage for example 100%, 50%, 0%.
 * @return true or false with required probability.
 */
private static boolean probably(int percentage) {
    double zeroToOne = Math.random(); // greater than or equal to 0.0 and less than 1.0
    double multiple = zeroToOne * 100; // greater than or equal to 0.0 and less than 100.0
    return multiple < percentage;
}

JavaScript

function probably(percentage) {
  return Math.random() * 100 < percentage;
}
Kyrylo Semenko
  • 866
  • 9
  • 11
-2

You can try this package

chance-percent

import { random } from 'chance-percent';

const options = [
  {value: 1, percentage: 10},
  {value: 3, percentage: 60},
  {value: 2, percentage: 30},
]

const value = random(options);
tieu le
  • 37
  • 1
  • 5
-3

Here is a very simple approximate solution to the problem. Sort an array of true/false values randomly and then pick the first item.

This should give a 1 in 3 chance of being true..

var a = [true, false, false]
a.sort(function(){ return Math.random() >= 0.5 ? 1 : -1 })[0]
seanbehan
  • 1,463
  • 15
  • 23
  • Array.prptotype.sort may call the sort function more than once with the same arguments. If you want the probability to be exactly 1/3 you need to make your sort function idempotent. – Chris Browne Jun 27 '18 at 07:30
  • @chris-browne I did say that it's an "approximates solution" but I will make the fix so that it's closer to 1/3. Thanks for the input. – seanbehan Jun 27 '18 at 23:24