adkane
adkane

Reputation: 1441

In NetLogo how do use random-float with known percentage chances?

I'm getting a bit confused about the random-float function because it produces a value strictly less than the one you provide.

In my model I have 7 agents that I want to produce according to the following probabilities:

0.493 0.368 0.060 0.067 0.006 0.004 0.002

So I transform them into a cumulative sum giving:

0.493 0.861 0.921 0.988 0.994 0.998 1.000

Then I plug these into my code:

ask patches [
    while [sum [energy] of turtles < 1000] [
let choose-size random-float 1
  if choose-size < 0.493 [ sprout-ones 1 ]
  if choose-size >= 0.493 and choose-size < 0.861 [ sprout-twos 1  ]
  if choose-size >= 0.861 and choose-size < 0.921 [ sprout-threes 1  ]
  if choose-size >= 0.921 and choose-size < 0.988 [ sprout-fours 1 ]
  if choose-size >= 0.988 and choose-size < 0.994 [ sprout-fives 1 ]
  if choose-size >= 0.994 and choose-size < 0.998 [ sprout-sixes 1 ]
  if choose-size >= 0.998  [ sprout-sevens 1 ]

]]

Could some kind soul verify these, in particular because the last value will never actually hit one, am I underestimating it? Thanks!

Upvotes: 0

Views: 159

Answers (1)

LeirsW
LeirsW

Reputation: 2305

What you have written is the correct way to go about it. While you can indeed never reach 1, a random-float outcome has 16 decimals so the difference is negligible. But even if that minuscule number matters your approach is correct.

Let's start with considering a random number X, that is generated by random-float 1. This number has a few properties

  • X is an element of [0,1[
  • X has a total of 16 decimals
  • X could be (0.0000000000000000, 0.0000000000000001, ... , 0.9999999999999999)
  • There are a total of 10^16 different options for X

Now if we want to use this number to calculate an easy probability, say a coinflip. For that we would want exactly half of the results for X to give one outcome, and half of the results for X to give another outcome.

For that we will need to compare X to 0.5000000000000000, or 0.5 for short. If we check X <= 0.5, we will get

  • P(true) = (0.0000000000000000, 0.0000000000000001, ... , 0.5000000000000000)
  • P(false) = (0.5000000000000001, 0.5000000000000002, ... , 0.9999999999999999)

If you count all those together, the number of possible results for X that give true is 5 * 10^15 + 1, while the number of possible results for X that are false is 5 * 10^15 - 1. So this distribution is very close but slightly off.

If instead we check X < 0.5, we will get

  • P(true) = (0.0000000000000000, 0.0000000000000001, ... , 0.4999999999999999)
  • P(false) = (0.5000000000000000, 0.5000000000000001, ... , 0.9999999999999999)

If you count all those together, the number of possible results for X that give true is 5 * 10^15, while the number of possible results for X that are false is 5 * 10^15. A perfectly equal result.

You are basically always dividing your possible results into two groups and have to decide which of the two groups gets the border value. Since the lower of the two groups already includes 0, the value against which you are comparing should go to the other group. This even works with cumulative sum of your probabilities, since each subsequent group will contain their lower border but will not contain their upper border.

Also note that if random-float 1 actually included both 1 and 0, we would have 10^16 + 1 possible outcomes. This number is odd, so we could never perfectly split it into two equal groups.

Upvotes: 1

Related Questions