Alright so I am trying to find out how to get a number that is closer to result
For example
$number_1 = 100
$number_2 = 150
$result = 130
In this case number_2 is 20 away therefore its closer than number_1. Now I could simply substract number_2 and number_1 from result and see what is closer but I dont want to do it since I have to do bunch of if statements to check maybe number_2 is bigger than result and so on...
Question: How do I find out which number is closer to result in a fast & efficient way & how to check if their distance to result is same?
Give this a try:
<?php
$nums = [
100, 150, 200, 225, 6, 17
];
function closestNumber($numToMatch, array $numbers)
{
$distances = [];
foreach ($numbers as $num) {
if ($num == $numToMatch) {
$distances[$num] = 0;
} elseif ($num < $numToMatch) {
$diff = $numToMatch - $num;
$distances[(string)$num] = $diff;
} else {
$diff = $num - $numToMatch;
$distances["$num"] = $diff;
}
}
asort($distances);
$keys = array_keys($distances);
return $keys[0];
}
echo closestNumber(130, $nums);
We create an array of distances, and then calculate and sort them, finally returning the closest match, being the first element in the sorted array. Have a try of it here: https://3v4l.org/SeqZR
Loop through an array:
$number[0] = 100;
$number[1] = 150;
$baseNumber = 130;
for ($i=0;$i < count($number);$i++) {
$difference[$i] = abs($number[$i] - $baseNumber);
}
$index = array_search(min($difference), $difference);
echo $number[$index];
This will give you the value with the smallest difference in the array.
idk maybe that ?
dist_1 = fabs(result - number_1)
dist_2 = fabs(result - number_2)
if there is more than three values :
an asc list
find the index of result (dichotomic search)
check left & right index
This should work -
$number_1 = 100;
$number_2 = 150;
$number_3 = 160;
$number_4 = 170;
$number_5 = 180;
$result = 130;
// A array to store all the elements
$arr = array($number_1, $number_2, $number_3, $number_4, $number_5, $result);
sort($arr); // sort the array in asc
$pos = array_search($result, $arr); // find the result element
if($pos === 0)
{// if element is the first then closest is second
$closest = $arr[1];
}
elseif ($pos === (count($arr) - 1))
{// if last then closest is 2nd last
$closest = $arr[$pos - 1];
}
else
{// calculate the difference and set closest
$prev = $arr[$pos] - $arr[$pos - 1];
$next = $arr[$pos + 1] - $arr[$pos];
$closest = ($prev >= $next) ? $arr[$pos + 1] : $arr[$pos - 1];
}
echo $closest;
Output
150
Working code
Related
I am trying to display possibilities for additions of specific numbers but have not been getting the right results.
<?php
$num3 = 20;
$set = null;
$balance = $num3;
$dig = mt_rand(1,5);
for($i = $balance; $i > 0; $i -= $dig ){
echo $dig.'<br>';
if($i < 1){
$set .= $i;
$balance = 0;
}
else{
$set .= $dig.'+';
$dig = mt_rand(1,5);
}
}
echo $set.'='.$num3;
?>
Here are some of the outputs:
2+5+1+4+5+3+=20
1+4+3+5+3+=20
3+1+1+2+3+4+4+1+3+=20
Appreciate any pointers. Thank in advance...
Ok, even though the requirement isn't completely clear, here's an approach:
(edit: demonstrating prevention of endless loop)
$max_loops = 1000;
$balance = 20;
$found = [];
while($balance > 0 && $max_loops-- > 0) {
$r = mt_rand(1, 5);
if ($balance - $r >= 0) {
$found[] = $r;
$balance -= $r;
}
}
echo implode(' + ', $found) . ' = '.array_sum($found);
Note: This code has a small risk of getting caught in an endless loop... though it's doubtful that it'll ever happen :)
Edit: Now the loop contains a hard-limit of 1000 iterations, after which the loop will end for sure...
To provoke an endless loop, set $balance = 7 and modify mt_rand(4, 5).
You can use a recursive function for this:
function randomAddends($target, $maxAddend = 5, $sum = 0, $addends = [])
{
// Return the array of addends when the target is reached
if ($target <= $sum) {
return $addends;
}
// generate a new random addend and add it to the array
$randomAddend = mt_rand(1, min($maxAddend, $target - $sum));
$addends[] = $randomAddend;
// make the recursive call with the new sum
return randomAddends($target, $maxAddend, $sum + $randomAddend, $addends);
}
What's the best way of searching an array for a number within a range? e.g. between 1000 and 1500.
<?php
$numbers = array(1105,2140,3170);
$needle = **value between 1000 and 1500**;
if (in_array($needle, $numbers)) {
echo "Match found";
} else {
echo "Match not found";
}
?>
In this case, this would return 'Match found' as 1105 is between 1000 and 1500.
Can I set a range for the $needle variable?
What's the best way of searching an array for a number within a range. To answer this, there are 2 approaches as mentioned below:
Approach #1:
You can use a simple foreach loop to get all values from your unordered data which lie in the range.
<?php
$numbers = array(1105,2140,3170);
$start = 1000;
$end = 1500;
$result = [];
foreach($numbers as $num){
if($num >= $start && $num <= $end) $result[] = $num;
}
print_r($result);
Demo: https://3v4l.org/D1Rfu
Approach #2:(recommended for future lookups)
You can sort your data and use binary search to get the starting point of where your numbers start falling in the range of start and end for faster lookups. Then, you can just look from that index till the index you get a higher number or reach the end of the array.
<?php
$numbers = array(1105,2140,3170,1000,1500,1501);
$start = 1000;
$end = 1500;
$start_index = -1;
sort($numbers);
$low = 0; $high = count($numbers) - 1;
while($low <= $high){
$mid = intval(($low + $high) / 2);
if($numbers[$mid] > $end){
$high = $mid - 1;
}else if($numbers[$mid] < $start){
$low = $mid + 1;
}else{
$start_index = $mid;
$high = $mid - 1;
}
}
$result = [];
for($i = $start_index; $i < count($numbers); ++$i){
if($numbers[$i] > $end) break;
$result[] = $numbers[$i];
}
print_r($result);
Demo: https://3v4l.org/WcgXv
Yes, you can create a range and use it as the "needle", but not using in_array. You can create a range and compute the intersection of the numbers array. To just check for any match:
$numbers = array(1105, 2140, 3170);
$needle = range(1000, 1500);
if (array_intersect($numbers, $needle)) {
echo "Match found";
} else {
echo "Match not found";
}
Or to get the possible match(es):
$numbers = array(1105, 2140, 3170);
$needle = range(1000, 1500);
$result = array_intersect($numbers, $needle);
Or you can filter out the ones not in the range:
$numbers = array(1105, 2140, 3170);
$min = 1000;
$max = 1500;
$result = array_filter($numbers, function($v) use($min, $max) {
return $v >= $min && $v <= $max;
});
You will get an empty array if there are no matches in either case. Also, you don't state what you want if there is more than one, but you'll get an array in either case so you could use current for one or min and/or max instead:
$one = current(array_intersect($numbers, $needle));
$min = min(array_intersect($numbers, $needle));
$max = max(array_intersect($numbers, $needle));
There is no built-in function for these purpose. So create a function that will help you.
function searchOnRange(array $stack, $min=0, $max=0)
{
// Loop through each value of array
foreach($stack as $value){
// CHeck value is between the given range
if(($value >= $min) && ($value <= $max)){
echo "Match found - ".$value;
} else {
echo "Match not found - ".$value;
}
}
}
$numbers = array(1105,2140,3170);
searchOnRange($numbers, 1000, 1500);
Reference of my Question
how can I get the closest pairs from this array
// Input
$required = 1.3;
$array_result = [0.6, 0.5, 0.8, 0.7];
// Algorithm
$remaining = $required;
$whichtakes = [];
foreach($array_result as $i => $a) {
foreach(array_slice($array_result, $i+1) as $b) {
$test = $required - $a - $b;
if ($test >= 0 and $test < $remaining) {
$remaining = $test;
$whichtakes = [$a, $b];
}
}
}
// Output
print_r($whichtakes); // [0.5, 0.8]
print_r($remaining); // 0
Thanks to trincot
its working fine with pairs but there is little change , Algorithm is getting pairs but i want array which match my result array, just need this little change. if $required = 1.3 change to $required = 1.8 now it should give me array 0.6,0.5,0.7 and if $required = 1.9 now it should give me array 0.6,0.5,0.7 and 0.1 remaining
This algorithm is part of dynamic programming. Here is the modified code.
$res = array();
function isSubsetSum($set, $n, $sum)
{
if ($sum == 0)
return true;
if ($n == count($set) - 1 && $sum != 0)
return false;
if ($set[$n + 1] > $sum) {
return isSubsetSum($set, $n + 1, $sum);
}
return isSubsetSum($set, $n + 1, $sum) ||
isSubsetSum($set, $n + 1, $sum - $set[$n + 1]);
}
$set = array(0.6, 0.5, 0.8, 0.7);
$sum = 1.8;
$n = 0;
$remaining = 0;
while (!isSubsetSum($set, 0, $sum)) {
$remaining += 0.1;
$sum -= 0.1;
}
echo $res; // the array result
echo $remaining;
This code doesn't store the array subset. You can store the array result inside the function. I don't know how to optimize this algorithm though.
For more information about dynamic programming, check this link
This seems really simple but I can't figure out the cleanest way to do it...
Basically I have a sorted Array with numbers like this:
$array1 = [3, 7, 12, 63, 120, 512, 961];
What I need to do is check each element of the array against a number which can be like this:
$number = 320;
And I need to get the element which is next to the number in this example it would be 120 because 120 < $number < 512.
Well, my way approach which kinda works is pretty messy I think:
foreach ($i = 0; $i < count($array1); $i++) {
if ($array[$i] < $number) {
// echo "do nothing, elements are smaller than number
} else {
if ($flag == true) {
// echo "elements are not smaller anymore and flag is set"
$getValue = $array[$i-1]; // last element which was smaller
$flag == false;
}
}
}
Another problem is, I need to cover the cases if $number is smaller than the smallest element of the array or if its larger than the largest element of the array. For that case I create another Variable $t and check it with the length of the array in each iteration
$t = 0;
$len = count($array1);
// if element bigger than number and first iteration
if ($array[$i] > $number && $t == 0) {
}
$t += 1;
I left the foreach loop out here but as you can probably see it gets really long and its certainly not clean. How can it be done better?
As it is an sorted array you are working with then i recommend you to implement binary search algorithm which has O(log(n)) in time complexity .
int binary_search(int A[], int key, int imin, int imax)
{
// continue searching while [imin,imax] is not empty
while (imin <= imax)
{
// calculate the midpoint for roughly equal partition
int imid = midpoint(imin, imax);
if(A[imid] == key)
// key found at index imid
return imid;
// determine which subarray to search
else if (A[imid] < key)
// change min index to search upper subarray
imin = imid + 1;
else
// change max index to search lower subarray
imax = imid - 1;
}
// key was not found
return KEY_NOT_FOUND;
}
source: wikipedia.org
$array = [3, 7, 12, 63, 120, 512, 961];
$min = 0;
$max = count($array)-1;
$no = 320;
while ($min < $max)
{
$mid = (int)(($min+$max)/2);
if ($array[$mid] == $no) {
print $array[$mid-1]."<".$no."<".$array[$mid+1];
return;
}
else if($array[$mid] < $no) {
$min = $mid+1;
} else {
$max = $mid-1;
}
if($min == $max ) {
if($array[$min] > $no) {
print $array[$min-1]."<".$no."<".$array[$min];
return;
} else {
print $array[$min]."<".$no."<".$array[$min+1];
return;
}
}
}
So lets say I have 2 numbers in decimals (eg .75 and .25). I am trying to make a function that gets these 2 numbers and chooses a "winner" randomly but based on those 2 numbers' percentages. In short terms, I need the .75 number to have a better chance at getting picked then the .25 number (the .25 can still get picked, but it has only a 25% chance). How should I go about doing this?
$prob = array(25, 75);
$total = array_sum($prob);
$rand = mt_rand(1, $total);
var_dump($rand);
foreach ($prob as $i => $p) {
$rand -= $p;
if ($rand <= 0) {
$winner = $i;
break;
}
}
var_dump($winner);
If they don't always add up to 1, this will still work:
$winner = ( (rand(0,1000) / 1000) <= ($first / ($first + $second)) ) ? $first : $second;
$var1 = 25;
$var2 = 75;
$total = $var1 + $var2;
$rand = mt_rand(1, $total);
if($rand <= $var1)
echo("Value one ({$var1}) Wins!");
else
echo("Value two ({$var2}) Wins!");
Something like that should work.
In this simplistic case you could use:
if (rand(0,1000)/1000 <= 0.25)
{
$winner = $first; // 25% number
}
else {
$winner = $second; // 75% number
}