use strict; use warnings; my @constraints = ( {mid => 20, sd => 15}, {mid => 30, sd => 25}, {mid => 50, sd => 10}, ); my $sum; for my $cnst (@constraints) { my $value = RandFlat ($cnst->{mid}, $cnst->{sd}); $cnst->{value} = $value; $sum += $value; } # want $diff = 0 my $diff = $sum - 100; my $adj_sum = 0; # positive $diff means $sum is too high, go lower if ( $diff > 0 ) { for my $cnst (@constraints) { # how far can this value be adjusted? my $adj = $cnst->{value} - ($cnst->{mid} - $cnst->{sd}); $cnst->{adj} = $adj; $adj_sum += $adj; } } # negative $diff means $sum is too low, go higher elsif ( $diff < 0 ) { for my $cnst (@constraints) { # how far can this value be adjusted? my $adj = $cnst->{mid} + $cnst->{sd} - $cnst->{value}; $cnst->{adj} = $adj; $adj_sum += $adj; } } # possible adjustment vs. adjustment needed my $scale = $adj_sum / $diff; for my $cnst (@constraints) { my $adj = $cnst->{adj}; $cnst->{value} -= $cnst->{adj} / $scale; } $sum = 0; for my $cnst (@constraints) { my ($mid, $sd, $value) = @{$cnst}{'mid', 'sd', 'value'}; printf "%5.1f +-%5.1f: %5.1f", $mid, $sd, $value; print " - bad" if ($value < ($mid - $sd)) || ($value > ($mid + $sd)); print "\n"; $sum += $value; } print "sum: $sum\n"; sub RandFlat { #Return a rand () value with a flat distribution about the $mean +- $stdDev my ($mean, $stdDev) = @_; my $range = 2.0 * $stdDev; my $value = rand ($range); return $value + ($mean-$stdDev); }