Coding in PHP, I currently have an array of 100 values, which looks like this:
$cmassarray = array(630.00,629.70,629.40,629.10,628.80,628.50,628.20,627.90,627.60,627.30,627.00,
626.70,626.40,626.10,625.80,625.50,625.20,624.90,624.60,624.30,624.00,623.70,
623.40,623.10,622.80,622.50,622.20,621.90,621.60,621.30,621.00,620.70,620.40,
620.10,619.80,619.50,619.20,618.90,618.60,618.30,618.00,617.70,617.40,617.10,
616.80,616.50,616.20,615.90,615.60,615.30,615.00,614.70,614.40,614.10,613.80,
613.50,613.20,612.90,612.60,612.30,612.00,611.70,611.40,611.10,610.80,610.50,
610.20,609.90,609.60,609.30,609.00,608.70,608.40,608.10,607.80,607.50,607.20,
606.90,606.60,606.30,606.00,605.70,605.40,605.10,604.80,604.50,604.20,603.90,
603.60,603.30,603.00,602.70,602.40,602.10,601.80,601.50,601.20,600.90,600.60,
600.30,600.00);
All of the steps are the same length, and I may need to change them (and/or the max/min values) at a future stage, so I would like to find a way to avoid having to re-calculate them manually and type them each time.
If I know the maximum value is 630.00 and the minimum value is 600.00, and that I have 100 steps, would it be possible to create an array specifying that each value is an increment on this equation?
x (array value) = 600+((Max-Min)/100)*y)
where y is the incremental step in the scale.
Thank you for any help!
An alternative to using loops and all that would be range
$start=630;
$stop=600;
$steps=100;
$cmassarray=range( $start, $stop, ( ( $start-$stop ) / $steps ) );
You might want to have a look at the range function, which takes an optional third argument for step. This argument you can easily derive from your maximum, minimum and amount of steps. Here's an example:
$max = 630;
$min = 600;
$steps = 100;
$step = ($max - $min) / $steps;
$your_result = range( $max, $min, $step );
Start with this code:
$max = 630;
$min = 600;
$steps = 100;
$step = ($max - $min) / $steps;
$ar = [];
for ($i = $max; $i >= $min; $i -= $step) {
$ar[] = $i;
}
Related
I've encountered a problem similar to the old two sum problem but instead of solving for a value, it needs to be within a range, and I'm not sure how to efficiently approach this. Here is a simplified version of my problem:
Given an array of integers in order of preference, find the first two integers whose sum lies between the range of X and Y s.t. X <= sum <= Y (where X < Y and are known, i.e. arbitrarily X=20 and Y=40).
I've done a brute force approach using a for loop, but I'm unsure if this is the most performant solution. I've considered using a hash table, but I don't know how to apply it.
note: by order of preference I mean, the return the first two integers that fulfill this criteria
You could use the binary search method of resolving the 2 sum problem, and tweak your binary search function to search within a range. Something like this:
$arr = [1,2,4,6,8,14,15,17];
print_r(first_sum_in_range($arr, 25, 40));
function first_sum_in_range($array, $min, $max){
foreach ($array as $k=>$a) {
$b = binary_search_range($array, $a, $min, $max);
if ($b !== false) {
return [$a,$b];
}
}
}
function binary_search_range($array, $a, $min, $max) {
$top = sizeof($array) -1;
$bot = 0;
while($top >= $bot)
{
$p = floor(($top + $bot) / 2);
if ($a+$array[$p] < $min) $bot = $p + 1;
elseif ($a+$array[$p] > $max) $top = $p - 1;
else return $array[$p];
}
return false;
}
OUTPUT:
Array
(
[0] => 8
[1] => 17
)
Add each element to a tree map with key as element and value as list of indices where that element occurs.
While adding element to tree map check if there is a submap whose keys range from X - current_element to Y - current_element both inclusive. If you have a submap your answer is [curr_element, A[first_index_of_submap's value_of_first_key] ]
Maybe this is the brute force method you've already tried, but I think this is the simplest way.
Starting with a subset of the first two elements, iterate subsets of increasing size comparing the sum of the value of each element in the subset and the value of the last element. When you find a sum within the range, you're done.
This will find the first pair of numbers within the range based on the definition of "first" being "the pair with the lowest maximum index".
function findFirstSumInRange(int $min, int $max, array $values = []): array
{
for ($b = 1, $n = count($values); $b < $n; $b++) {
for ($a = 0; $a < $b; $a++) {
if ($min <= ($sum = $values[$a] + $values[$b]) && $sum <= $max) {
return [$values[$a], $values[$b]];
// or return [$a => $values[$a], $b => $values[$b]]; if you need the keys as well
}
}
}
return [];
}
You can make it faster by skipping any values that are already greater than the upper limit of the range.
function findFirstSumInRangeB(int $min, int $max, array $values = []): array
{
for ($b = 1, $n = count($values); $b < $n; $b++) {
if ($values[$b] < $max) { // else these sums will all be > the range because one addend is
for ($a = 0; $a < $b; $a++) {
if ($values[$a] < $max && $min <= ($sum = $values[$a] + $values[$b]) && $sum <= $max) {
return [$a => $values[$a], $b => $values[$b]];
}
}
}
}
return [];
}
Regarding "the most performant solution", I'd prefer to go for simplicity rather than optimizing for performance unless the performance is causing problems. Just my opinion.
I have a function like this:
<?php
function keepInRange($n){
$min = 5;
$max = 15;
if ( $n < $min ) {
$res = $min;
} elseif ( $n > $max ) {
$res = $max;
} else {
$res = $n;
}
return $res;
}
It always returns a number between $min and $max. It works as well, but doesn't seem clean an professional to me. I think it can be better (without those conditions). Any idea how can I make it shorter and cleaner?
If you are trying to make it shorter (and probably cleaner) and also removing those if statements, you can use max() and min() functions:
function keepInRange($n){
$min = 5;
$max = 15;
return max(min($max, $n), $min);
}
Also as #admcfajn mentioned, you can pass $min and $max as arguments to make the function more flexible:
function keepInRange($n, $min = 5, $max = 15){
return max(min($max, $n), $min);
}
Definitely its not the best way, You can use Rand() to generate numbers between a particular range.
Try this
print rand(10, 30) . "";
It'll generate and print a random number between 10 and 30 (10 and 30 are included).
I think it can be better (without those conditions). Any idea how can I make it shorter and cleaner?
Well if you want it without explicit conditions, then you could simply use min() and max():
function keepInRange2($n) {
$min = 5;
$max = 15;
return max($min, min($max, $n));
}
The order of the functions and parameters might seem odd at first glance - but since you want 15 to be the maximum value, we need to get the minimum of 15 and whatever the value is first - and then vice versa for the minimum 5.
This can be achieved cleaner using ternary operator.
function keepInRange($n){
$min = 5;
$max = 15;
return ( $n < $min ) ? $min : (( $n > $max ) ? $max : $n);
}
I've searched through a number of similar questions, but unfortunately I haven't been able to find an answer to this problem. I hope someone can point me in the right direction.
I need to come up with a PHP function which will produce a random number within a set range and mean. The range, in my case, will always be 1 to 100. The mean could be anything within the range.
For example...
r = f(x)
where...
r = the resulting random number
x = the mean
...running this function in a loop should produce random values where the average of the resulting values should be very close to x. (The more times we loop the closer we get to x)
Running the function in a loop, assuming x = 10, should produce a curve similar to this:
+
+ +
+ +
+ +
+ +
Where the curve starts at 1, peeks at 10, and ends at 100.
Unfortunately, I'm not well versed in statistics. Perhaps someone can help me word this problem correctly to find a solution?
interesting question. I'll sum it up:
We need a funcion f(x)
f returns an integer
if we run f a million times the average of the integer is x(or very close at least)
I am sure there are several approaches, but this uses the binomial distribution: http://en.wikipedia.org/wiki/Binomial_distribution
Here is the code:
function f($x){
$min = 0;
$max = 100;
$curve = 1.1;
$mean = $x;
$precision = 5; //higher is more precise but slower
$dist = array();
$lastval = $precision;
$belowsize = $mean-$min;
$abovesize = $max-$mean;
$belowfactor = pow(pow($curve,50),1/$belowsize);
$left = 0;
for($i = $min; $i< $mean; $i++){
$dist[$i] = round($lastval*$belowfactor);
$lastval = $lastval*$belowfactor;
$left += $dist[$i];
}
$dist[$mean] = round($lastval*$belowfactor);
$abovefactor = pow($left,1/$abovesize);
for($i = $mean+1; $i <= $max; $i++){
$dist[$i] = round($left-$left/$abovefactor);
$left = $left/$abovefactor;
}
$map = array();
foreach ($dist as $int => $quantity) {
for ($x = 0; $x < $quantity; $x++) {
$map[] = $int;
}
}
shuffle($map);
return current($map);
}
You can test it out like this(worked for me):
$results = array();
for($i = 0;$i<100;$i++){
$results[] = f(20);
}
$average = array_sum($results) / count($results);
echo $average;
It gives a distribution curve that looks like this:
I'm not sure if I got what you mean, even if I didn't this is still a pretty neat snippet:
<?php
function array_avg($array) { // Returns the average (mean) of the numbers in an array
return array_sum($array)/count($array);
}
function randomFromMean($x, $min = 1, $max = 100, $leniency = 3) {
/*
$x The number that you want to get close to
$min The minimum number in the range
$max Self-explanatory
$leniency How far off of $x can the result be
*/
$res = [mt_rand($min,$max)];
while (true) {
$res_avg = array_avg($res);
if ($res_avg >= ($x - $leniency) && $res_avg <= ($x + $leniency)) {
return $res;
break;
}
else if ($res_avg > $x && $res_avg < $max) {
array_push($res,mt_rand($min, $x));
}
else if ($res_avg > $min && $res_avg < $x) {
array_push($res, mt_rand($x,$max));
}
}
}
$res = randomFromMean(22); // This function returns an array of random numbers that have a mean close to the first param.
?>
If you then var_dump($res), You get something like this:
array (size=4)
0 => int 18
1 => int 54
2 => int 22
3 => int 4
EDIT: Using a low value for $leniency (like 1 or 2) will result in huge arrays, since testing, I recommend a leniency of around 3.
I want to generate in PHP an array of random numbers, but each number should not be the same as any of the X (for example 2 ) numbers bofore it and not even close to any of them by a define range (for example 5).
So for example:
I need numbers between 1 and 100
i've set my "range" to 5
the first two generated number are 20 and 50.
the third number will be a random number between 1 and 100, excluding all the numbers between 15 and 25, and between 45 and 55.
I can't figure out a function to achieve it. Ideally I want to call something like this:
getRandomNumbers( $min, $max, $previous, $range);
where $previous is the number of previous elements to take in consideration when generating the next one and $range is the "proximity" to those number where I don't want the next number to be.
I hope I explained in a decent way my request. :) Please, add a comment if you have any question about it.
I just came up with this:
function getRandomNumbers($min, $max, $previous, $range) {
static $generated = array();
$chunk = array_slice($generated, -$previous);
// Added this infinite loop check to save you some headache.
if (((($max - $min + 1) / (($range * 2) + 1)) + 1) <= $previous) {
die("Values set have the potential of running into an infinite loop. Min: $min, Max: $max, Previous: $previous, Range: $range");
}
while(true) {
$number = rand($min, $max);
$found = true;
foreach ($chunk as $value) {
if (in_array($number, range($value-$range, $value+$range))) {
$found = false;
}
}
if ($found) {
$generated[] = $number;
return $number;
}
}
}
Test it using this:
for ($i = 1; $i < 25; $i++) {
echo getRandomNumbers(1, 100, 5, 5) . "<br />";
}
PHPFiddle Link: http://phpfiddle.org/main/code/51ke-4qzs
Edit: Added a check to prevent a possible infinite loop. For example: if you set the following values:
$min = 1;
$max = 100;
$previous = 5;
$range = 12;
echo getRandomNumbers($min, $max, $previous, $range);
Then let's say, in a really unfortunate situation it would generate 13, 38, 63 and 88. So the 5th number cannot be anything between 1 and 25, 26 and 50, 51 and 75, 76 and 100. So it would result in an infinite loop. I've updated the PHPFiddle link as well.
getRandomNumbers( $previous, $range ) {
//I'm assuming that previous will be an array of your previous X that you don't want to be close to
$num = getRandomNumber() //However you are doing this now
foreach( $previous as $key => $value ) {
if ( ( $value - $range ) > $num && ( $value + $range ) < $num ) {
return getRandomNumbers($previous, $range);
}
}
//You need to also replace a value in previous
return num;
}
I have two numbers (for this example lets say 695 is the smallest and 36000 is the largest). Currently once I know the min and the max I divide the difference by ten and then cycle it through a loop to crate the ranges. It looks something like this:
$min = (int)$min-1;
$max = (int)$max;
$diff = ($max - $min) / 10;
$range = array();
for ( $i=1; $i<10; $i++){
$range[] = array(
"low"=>($i==1? $min: ($i*$diff) + $min),
"high"=>($i+1)*$diff + $min
);
}
This works great when the numbers are 695-36000. When the numbers get close together it becomes a little cumbersome to have 10 ranges. For example, the min is 34000 and the max is 36000 the ranges would be 34000-34200, 34200-34400, etc, etc.
Ideally if 695-36000 is 10 different ranges than 34000-36000 would be one range.
What would be an easy way to calculate how many ranges should show up and what those ranges should be?
Here is something to consider:
$diff = ($max - $min) / 10;
$range_max = 2000;
if($diff>$range_max) $range_size = $range_max;
else $range_size = $diff;
$creep = ($max-$min-$range_size)/9-$diff;
$range = array();
for($i=0; $i<10; $i++) {
$range[] = array (
'low' => (int)(($diff+$creep)*$i+$min),
'high' => (int)(($diff+$creep)*$i+$min+$range_size)
);
}