-1

It occurred to me today that I have forgotten almost all of my high school math, so I need some assistance working out some basic math in Ruby.

Essentially, I have a set of values from 1 to 10. Right now, I simply multiply them by 10 to get a figure I use as a percentage. eg 2 = 20%, 4 = 40% etc.

This is a little too linear and so what I really need to do is calculate a exponential decay style value, where by values closer down to 1 are given a more generous percentage and numbers closer up to 9 and 10 the percentage flattens out.

There is a possibility that there may be more than 10 numbers in the future too, so for example, 1 to 20. Im assuming this is simply enough to change?

Here is a quick chart that describes what im trying to do.

enter image description here

Can anyone help me figure this one out? Preferably in Ruby but otherwise any pseudo code would help.

Cheyne
  • 1,964
  • 4
  • 27
  • 43

2 Answers2

2

First, you need to have the probability distribution function, say F(x). It doesn't need to be normalized. In your case (exponential decay), this function could be

F(x) = e^(-kx)

You can tune k to adjust how fast it decays. Then calculate the range of F(x) over your domain.

Then work out its inverse function G(x), which is

G(x) = -(1/k)ln(x)

Then you can do sampling by G(rand(range of F))

Here's my ruby code:

k = 1

f = -> x {Math.exp(-k * x)}
l, h = f.call(10), f.call(1)

g = -> x {-Math.log(x) / k}
r = -> {rand(h - l) + l}

samples = 10_000.times.map{g.call(r.call)}

samples.count{|x| x < 1}  #=> 6327
samples.count{|x| x < 2}  #=> 8620
samples.count{|x| x < 3}  #=> 9477
samples.count{|x| x < 4}  #=> 9809
samples.count{|x| x < 5}  #=> 9929
samples.count{|x| x < 6}  #=> 9970
samples.count{|x| x < 7}  #=> 9986
samples.count{|x| x < 8}  #=> 9994
samples.count{|x| x < 9}  #=> 9997
samples.count{|x| x < 10}  #=> 10000
Aetherus
  • 8,720
  • 1
  • 22
  • 36
  • The exponential distribution function is `F(x) = 1-e**(-kx)`, not `F(x) = e**(-kx)`, but you can of course sample from zero to one from `F(x)` or `1-F(x)`. – Cary Swoveland Nov 25 '15 at 03:01
  • @CarySwoveland Thank you for your correction. I'm not a native English speaker so I don't know what **exponential decay** really is. Anyway this method can be applied to any monotone distributions, can't it? – Aetherus Nov 25 '15 at 03:31
  • 1
    Yes, but, as you know, for some distributions (including the exponential), you can derive the inverse function (as you have done), which makes it easy; for others, you may have to use numerical methods (e.g., Newton's method) to compute inverse values. Dentists refer to "exponential decay" as the situation when a patient's cavity gets worse very quickly. – Cary Swoveland Nov 25 '15 at 04:58
  • Thanks for taking the time to write this Aetherus. I'll have a play around with your solution and see what I can come up with – Cheyne Nov 25 '15 at 18:39
2

Another curve that may suit you is square/squareroot/parabola

> 11.times{|x| puts "#{x}, #{100-(10-x)**2}"}
0, 0
1, 19
2, 36
3, 51
4, 64
5, 75
6, 84
7, 91
8, 96
9, 99
10, 100

To change the scale to 20, use

> 21.times{|x| puts "#{x}, #{100-(10-x/2.0)**2}"}
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Thanks John. I'm marking this as the answer as it's very clean and i'm able to make use of this right away, and the curve looks about right for what i'm trying to do. – Cheyne Nov 25 '15 at 18:40