6

So i'm implementing a heuristic algorithm, and i've come across this function.

I have an array of 1 to n (0 to n-1 on C, w/e). I want to choose a number of elements i'll copy to another array. Given a parameter y, (0 < y <= 1), i want to have a distribution of numbers whose average is (y * n). That means that whenever i call this function, it gives me a number, between 0 and n, and the average of these numbers is y*n.

According to the author, "l" is a random number: 0 < l < n . On my test code its currently generating 0 <= l <= n. And i had the right code, but i'm messing with this for hours now, and i'm lazy to code it back.

So i coded the first part of the function, for y <= 0.5 I set y to 0.2, and n to 100. That means it had to return a number between 0 and 99, with average 20. And the results aren't between 0 and n, but some floats. And the bigger n is, smaller this float is.

This is the C test code. "x" is the "l" parameter.

//hate how code tag works, it's not even working now  
int n = 100;  
float y = 0.2;  
float n_copy;  

for(int i = 0 ; i < 20 ; i++)  
{  
    float x = (float) (rand()/(float)RAND_MAX);  // 0 <= x <= 1  
    x = x * n;                                // 0 <= x <= n  
    float p1 = (1 - y) / (n*y);  
    float p2 = (1 - ( x / n ));  
    float exp = (1 - (2*y)) / y;  
    p2 = pow(p2, exp);  
    n_copy = p1 * p2;  
    printf("%.5f\n", n_copy);  
}  

And here are some results (5 decimals truncated):

0.03354  
0.00484  
0.00003  
0.00029  
0.00020  
0.00028  
0.00263  
0.01619  
0.00032  
0.00000  
0.03598  
0.03975    
0.00704  
0.00176  
0.00001  
0.01333  
0.03396   
0.02795  
0.00005  
0.00860 

The article is:

http://www.scribd.com/doc/3097936/cAS-The-Cunning-Ant-System

pages 6 and 7.

or search "cAS: cunning ant system" on google.

So what am i doing wrong? i don't believe the author is wrong, because there are more than 5 papers describing this same function.

all my internets to whoever helps me. This is important to my work.

Thanks :)

user
  • 5,335
  • 7
  • 47
  • 63
hfingler
  • 1,931
  • 4
  • 29
  • 36
  • 1
    Don't use the code tag. SO is wierd, it uses 4 spaces to indicate code. Just copy the code, then select it all and then press the 1010 button to make it code. –  Nov 05 '10 at 03:58
  • 3
    That's because question and answer boxes use Markdown: http://daringfireball.net/projects/markdown/syntax – zwol Nov 05 '10 at 04:21

3 Answers3

6

You may misunderstand what is expected of you.

Given a (properly normalized) PDF, and wanting to throw a random distribution consistent with it, you form the Cumulative Probability Distribution (CDF) by integrating the PDF, then invert the CDF, and use a uniform random predicate as the argument of the inverted function.


A little more detail.

f_s(l) is the PDF, and has been normalized on [0,n).

Now you integrate it to form the CDF

g_s(l') = \int_0^{l'} dl f_s(l)

Note that this is a definite integral to an unspecified endpoint which I have called l'. The CDF is accordingly a function of l'. Assuming we have the normalization right, g_s(N) = 1.0. If this is not so we apply a simple coefficient to fix it.

Next invert the CDF and call the result G^{-1}(x). For this you'll probably want to choose a particular value of gamma.

Then throw uniform random number on [0,n), and use those as the argument, x, to G^{-1}. The result should lie between [0,1), and should be distributed according to f_s.

Like Justin said, you can use a computer algebra system for the math.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • So the `f_s(l)` is a CDF? What should i do to get the [0,n] random number? Sorry, i'm not really into probability, i actually almost got disapproved (this word sounds strange.. translated fro google) on probability class. Anyway, we haven't learnt stuff like this either.. Oh, thanks for your explanation :) – hfingler Nov 05 '10 at 19:04
  • So the only way to get the number i want ( [0, n] and with avg y), is doing what you said? I don't think integrating is gonna be a good idea, since it's not that simple/fast. I need this to be as fast as possible, with the less calculations possible. If i HAVE to do that, i think i'll study some other, simpler, distributions. – hfingler Nov 05 '10 at 20:25
  • @polar: You want to integrate and invert *symbolically* if possible, and implement only the result ( `G^{-1}` ) in code. You **certainly** don't want to integrate numerically on every pass! If you must integrate numerically you would do that once, and store the inverted result as a normalized histogram. There are other questions about that detail how to draw numbers against a histogram. You can also find all this in most numerical analysis texts. – dmckee --- ex-moderator kitten Nov 05 '10 at 20:36
4

dmckee is actually correct, but I thought that I would elaborate more and try to explain away some of the confusion here. I could definitely fail. f_s(l), the function you have in your pretty formula above, is the probability distribution function. It tells you, for a given input l between 0 and n, the probability that l is the segment length. The sum (integral) for all values between 0 and n should be equal to 1.

The graph at the top of page 7 confuses this point. It plots l vs. f_s(l), but you have to watch out for the stray factors it puts on the side. You notice that the values on the bottom go from 0 to 1, but there is a factor of x n on the side, which means that the l values actually go from 0 to n. Also, on the y-axis there is a x 1/n which means these values don't actually go up to about 3, they go to 3/n.

So what do you do now? Well, you need to solve for the cumulative distribution function by integrating the probability distribution function over l which actually turns out to be not too bad (I did it with the Wolfram Mathematica Online Integrator by using x for l and using only the equation for y <= .5). That however was using an indefinite integral and you are really integration along x from 0 to l. If we set the resulting equation equal to some variable (z for instance), the goal now is to solve for l as a function of z. z here is a random number between 0 and 1. You can try using a symbolic solver for this part if you would like (I would). Then you have not only achieved your goal of being able to pick random ls from this distribution, you have also achieved nirvana.

A little more work done

I'll help a little bit more. I tried doing what I said about for y <= .5, but the symbolic algebra system I was using wasn't able to do the inversion (some other system might be able to). However, then I decided to try using the equation for .5 < y <= 1. This turns out to be much easier. If I change l to x in f_s(l) I get

y / n / (1 - y) * (x / n)^((2 * y - 1) / (1 - y))

Integrating this over x from 0 to l I got (using Mathematica's Online Integrator):

(l / n)^(y / (1 - y))

It doesn't get much nicer than that with this sort of thing. If I set this equal to z and solve for l I get:

l = n * z^(1 / y - 1)      for .5 < y <= 1

One quick check is for y = 1. In this case, we get l = n no matter what z is. So far so good. Now, you just generate z (a random number between 0 and 1) and you get an l that is distributed as you desired for .5 < y <= 1. But wait, looking at the graph on page 7 you notice that the probability distribution function is symmetric. That means that we can use the above result to find the value for 0 < y <= .5. We just change l -> n-l and y -> 1-y and get

n - l = n * z^(1 / (1 - y) - 1)

l = n * (1 - z^(1 / (1 - y) - 1))      for 0 < y <= .5

Anyway, that should solve your problem unless I made some error somewhere. Good luck.

Justin Peel
  • 46,722
  • 6
  • 58
  • 80
  • Thanks, Justin. Having written my answer on the bases of intuition and the post title I went off to actually, you know, read the paper--and I'm glad I did because it's full of all kinds of good stuff I hadn't seen before. In any case, I think you've hit a couple of important points here. Notably that the normalization is over [0,n), when I would have naively expect it over [0,1). – dmckee --- ex-moderator kitten Nov 05 '10 at 04:57
  • @dmckee, yes, I think this stuff is pretty cool. I've read a little bit about these sorts of algorithms, but not much. I want to get more into this stuff myself. – Justin Peel Nov 05 '10 at 04:59
  • Oh boy... i saw those factors on the graph, and i did understand the x axis, but the `x 1/n` i did ignore. So i read your text, and i reminded my two years of calculus, but i got to say you something.. it's in english, and i couldn't understand a thing (im brazilian, so my course was in portuguese). Can you help me doing this the simplest way? i'll implement this, and i want the best performance, not too many calculations to get one normal distribution random number. Btw, this is for my final assignment on my degree, it's about Ant Systems and QAP. – hfingler Nov 05 '10 at 18:55
  • Oh boy, all my internets to you! that really solved the whole problem! i was thinking of giving up on this and use two parameters to randomize a number, but this is perfect! i wrote a test program to calculate the average, and with n = 100 and 3000 numbers generated with a wanted avg of 20, it popped 19 average, with the lowest value being 0 and the highest 84. So it works! this is for y between 0 and 0.5, and i'll probably only use this part of the function, since y is recomended to be between 0.2 and 0.4. Thanks man, great success! – hfingler Nov 11 '10 at 01:21
0

Given that for any values l, y, n as described, the terms you call p1 and p2 are both in [0,1) and exp is in [1,..) making pow(p2, exp) also in [0,1) thus I don't see how you'd ever get an output with the range [0,n)

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150