Your requirements are met most naturally by using another probability distribution than the uniform one rand is crafted to deliver. The requirement of a lower bound $Nl is easily dealt with by just considering the interval 0..($Nu-$Nl) and adding $Nl to all results. The requirement for an upper bound restricts us to distributions with a finite range. The requirement for a specified mean forces us to look at distributions with more parameters than rand provides.
The ad-hoccery one's tempted into, simply filtering or adjusting a uniform distribution, will lead to 'random' numbers with bizarre statistical properties.
Abramowitz and Stegun reveals one standard probability distribution for continuous finite intervals, the beta disribution, and two for integers, the binomial distribution and the hypergeometeric.distribution. All three have enough adjustable parameters to satisfy your requirements.
Both integer distributions make use of the binomial coefficients <wish cap="MathMl"/>, so I'll give a simple recursive implementation:
This is not very efficient, but it staves off large intermediate numbers and avoids copying as much as possible in a recursive definition. You may wish to do this using &Math::BigInt::bfac instead.sub binomial { my ($n,$s)=@_; $_[1]<=$_[0]/2 ? $_[1] ? 0 | ($_[0] - $_[1] + 1) * binomial( $_[0], $_[1] - 1) / +$_[1] : 1 : $_[1] != $_[0] ? 0 | ($_[1] + 1) * binomial( $_[0], $_[1] + 1) / ($_[0] - + $_[1]) : 1; }
The plan is to make a largeish array of numbers from the interval, each represented in proportion to its probability, and pick numbers from that array with rand as index. For the binomial distribution:
For the hypergeometric distribution you get a free extra parameter you can use to tweak your results. this is its point probability function, in terms of three integers $N1 < $N2 and $n with $n < $N1+$N2:# Usage: # my @probs = prob_binomial( $span, $avg); # these are 0 based arguments, subtract the min you want to get them. sub prob_binomial { my ($max, $avg) = @_; my $p = $avg / $max; map {binomial($max, $_) * $p**$_ * (1-$p)**($max-$_)} 0..$max; } my $min = 6; my $max = 21; my $avg = 17; my $num = 40; my @problist = prob_binomial($max-$min,$avg-$min); # using ~1000 element array, adjust to suit ranges my @picklist = map {($min+$_) x int( .5 + 1000 * $problist[$_])} 0..$m +ax-$min; print $picklist[rand @picklist],$" for 1..$num;
This has the great advantage that the relative probabilities can be made integers by making its @picklist binomial($N1+$N2,$n) in length. Then your results will be exact.sub prob_hyper { my ($N1,$N2,$n)=@_; my $span = $N1<$n? $N1 : $n; map {binomial($N1,$_) * binomial($N2,$n-$_) / binomial($N1+$N2,$n) +} 0..$span; }
Note that these methods don't force a particular list of randoms to take on the average. That will happens in the long run, it's all statistics! Enjoy.
Update: Forgot to mention, the mean for the hypergeometric distribution is $n*$N1/($N1+$N2).
After Compline,
Zaxo
In reply to Re: list of random numbers with given avarage
by Zaxo
in thread list of random numbers with given avarage
by LupoX
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |