<?php
$target_average = 15;
$count = 100;
$min = 10;
$max = 20;
$precision = 3;
// ------------
$divider = pow( 10, $precision );
$generator = new AvgGenerator( $target_average, $min, $max, $precision );
$numbers = $generator->generate( $count );
stats( $numbers, $precision );
function stats( $numbers, $precision )
{
$sum = 0.0;
$min = reset( $numbers ); $max = reset( $numbers ); foreach ( $numbers as $n )
{
$sum += $n;
if ( $n < $min )
{
$min = $n;
}
if ( $n > $max )
{
$max = $n;
}
}
$average = $sum / (float)$c;
//printf( "count: %.{$precision}d, average: %.{$precision}f, min: %.{$precision}f, max: %.{$precision}f\n", $c, $average, $min, $max );
}
class AvgGenerator
{
private $target_average;
private $min;
private $max;
private $precision;
public function __construct( $target_average, $min, $max, $precision )
{
$this->target_average = $target_average;
$this->min = $min;
$this->max = $max;
$this->precision = $precision;
}
public function generate( $count )
{
$currentAverage = 0.0;
$currentSum = 0.0;
$divider = pow( 10, $this->precision );
$done = 0;
$left = $count;
for ( $i = 0; $i < $count; $i++ )
{
if ( $this->overTargetAverage( $currentAverage ) )
{
$currentMax = $this->countCurrentMax( $currentAverage, $done, $left );
$currentMin = $this->min;
}
else
{
$currentMin = $this->countCurrentMin( $currentAverage, $done, $left );
$currentMax = $this->max;
}
$done = $i + 1;
$left = $count - $done;
$nextNumber = $this->nextRandomFloat( $currentMin, $currentMax, $divider );
$numbers[] = $nextNumber;
$currentSum += $nextNumber;
$currentAverage = $currentSum / $done;
// printf( "[%d] x = %.3f ; sum = %.3f ; avg = %.3f ; tavg = %.3f<br />", $done, $nextNumber, $currentSum, $currentAverage, $this->target_average );
printf( "%d; %.3f\n", $done, $nextNumber); }
return $numbers;
}
private function nextRandomFloat( $min, $max, $divider )
{
$min = $min * $divider;
$max = $max * $divider;
$x = (float
)(rand( 0
, $max - $min ) + $min ) / (float
)$divider;
return $x;
}
private function countCurrentMax( $currentAverage, $done, $left )
{
$targetSum = $this->target_average * ( $done + $left );
$currentSum = $currentAverage * $done + $this->min * ( $left - 1 ); // minimum sum that we may get (excluding current number
$diffSum = $targetSum - $currentSum;
if ( $diffSum < $this->max )
{
return $diffSum;
}
else
{
return $this->max;
}
}
private function countCurrentMin( $currentAverage, $done, $left )
{
$targetSum = $this->target_average * ( $done + $left );
$currentSum = $currentAverage * $done + $this->max * ( $left - 1 ); // minimum sum that we may get (excluding current number
$diffSum = $targetSum - $currentSum;
if ( $diffSum > $this->min )
{
return $diffSum;
}
else
{
return $this->min;
}
}
private function overTargetAverage( $currentAverage )
{
return $currentAverage > $this->target_average;
}
private function underTargetAverage( $currentAverage )
{
return $currentAverage < $this->target_average;
}
}
?>
+ Trzyma sie sredniej,
- czasami przekracza nieznacznie zalozone min i max, ale nie mam teraz czasu zeby to dokladnie przebadac
- no i wada: koncowe wartosci czesto sa na sile dobierane zeby sie w sredniej zmiescic, np. 5% ostatnich wartosci to np. min lub tylko max
Wynik:
http://sunki.quickshareit.com/share/picture2af6b4.pngdla [10, 20] i sredniej 15 (czyli tak jak w kodzie)