6

I have a few variables which can be inherited to child agents by a variation of + 0.1 and -0.1 or without any changes, or random again, What I have done is like this: (The code is just an example)

to reproduce
  ask turtle 1
  [
  let X-Of-Mother X

  hatch 1
  [

    set X one-of (list (X-Of-Mother) (X-Of-Mother + 0.1) (X-Of-Mother - 0.1) (random-float 1))

    ]

  ]

end

Currently I have to check if X of child turtle is always within range by something like this:

if X > 1 [set X X - 0.2]
if X < 0 [set X X + 0.2]

What could be a better way to do it?

What if I have to use random-normal 0.5 0.1 , how can I limit that to values between 0 and 1 , I have done many repetitions of generating such random numbers I think the quality of random-normal is good and there is not that many times that I need to check if it's beyond the range.

for example :

  to test 
    Let c 0
    let b 0 
    repeat 100000000
    [Set b random-normal 0.5 0.1
      if b > 1 [set C C + 1]
      If b < 0 [set C C + 1]

      ]
    print c  

  end

OUTPUT is *67 times out of 100000000 Time* 67 is biggest one I got, I got 58 , 51 , ...

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Marzy
  • 1,884
  • 16
  • 24

3 Answers3

10

As you've discovered, random-normal can be problematic because the result you get back can be literally any number.

One possible solution is to clamp the output of random-normal within boundaries, as in Matt's answer. Note that this approach creates spikes at the boundaries of the range:

observer> clear-plot set-plot-pen-interval 0.01 set-plot-x-range -0.1 1.1
observer> histogram n-values 1000000 [ median (list 0 (random-normal 0.5 0.2) 1) ]

enter image description here

Another possible solution, as Marzy describes in the question itself, is to discard any out-of-bounds results random-normal gives you and just keeping trying again until you get an in-bounds result. This avoids the spikes at the boundaries:

to-report random-normal-in-bounds [mid dev mmin mmax]
  let result random-normal mid dev
  if result < mmin or result > mmax
    [ report random-normal-in-bounds mid dev mmin mmax ]
  report result
end

observer> clear-plot set-plot-pen-interval 0.01 set-plot-x-range -0.1 1.1
observer> histogram n-values 1000000 [ random-normal-in-bounds 0.5 0.2 0 1 ]

enter image description here

Another solution is to ask yourself whether you really need a bell curve, or whether a triangle-shaped distribution would be just fine. You can get a triangle-shaped distribution of results very simply just by summing two calls to random-float:

observer> clear-plot set-plot-pen-interval 0.01 set-plot-x-range 0 1
observer> histogram n-values 10000000 [ 0.5 + random-float 0.5 - random-float 0.5 ]

histogram

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
  • Thank you very much :) I need the bell curve based on my model requirements, but I liked the way you did it , its really cool to learn new ways and methods :) – Marzy Nov 27 '13 at 04:28
  • Thanks AGAIN , its good to know how can you see the problems and how you solve them , it helps me learning better . – Marzy Nov 27 '13 at 04:45
  • I'm trying to use the `random-normal-in-bounds` in the following: `set homerange patches in-radius ((sqrt ((( random-normal-in-bounds [ HomerangeSizeMales 35.8 19 151 ] * 1000000)/ pi))/ 100) * 0.4)` where homerangesizeMales is on a slider. However I keep getting the error that random-normal-in-bounds expected 4 inputs. I'm sure it's something silly I'm doing but it looks like 4 inputs to me. Any thoughts? – user2359494 Jan 14 '14 at 22:56
6

My favorite trick is this:

set x median (list 0 (y) 1)

Where y is the random number (or put in an expression), 0 is the minimum, and 1 is the maximum.

It works because if y is greater than 1, then the median will be 1. If y is less than 0, then the median will be 0. Otherwise the median is y.

For example, here is the random number in your example clamped to the range [0, 1]:

 to test
    let b median (list 0 (random-normal 0.5 0.1) 1)
    print b
 end
Matt
  • 5,478
  • 9
  • 56
  • 95
  • one-of (list (X-Of-Mother) (median (list 0 (X-Of-Mother - 0.1) 1)) (median (list 0 (X-Of-Mother + 0.1) 1)) (median (list 0 (random-normal 0.5 0.1) 1)) ) work good too :) but is there any more efficient way ? – Marzy Nov 27 '13 at 03:50
  • 1
    Thanks Seth, and thanks for pointing out some of the downsides to this approach in your answer – Matt Nov 27 '13 at 07:02
3

Answering very late to add another option for future seekers...

Another option if you're looking for a distribution that is bell-shaped like a normal distribution, but bounded, the Beta distribution can be a good choice. If you use parameters like 3,3 or 4,4, it looks a lot like a Normal distribution, but won't have any out-of-bounds values (it scales from 0 to 1, so it may have to be moved/scaled like you would a normal).

Netlogo doesn't have a built-in Beta, but you can get it from drawing from the built-in gamma twice, like this:

to-report random-beta [ #shape1 #shape2 ]

  let Xa random-gamma #shape1 1
  let Xb random-gamma #shape2 1
  report Xa / (Xa + Xb)

end

For more mathematical detail, see: https://math.stackexchange.com/questions/190670/how-exactly-are-the-beta-and-gamma-distributions-related