在数组中查找匹配或最接近的值

时间:2011-03-28 20:51:45

标签: php arrays search sorting

对于给定的目标值,如何搜索和查找数组中最接近的值?

假设我有这个示例性数组:

array(0, 5, 10, 11, 12, 20)

例如,当我用目标值0搜索时,函数应返回0;当我用3搜索时,它将返回5;当我用14搜索时,它将返回12。

15 个答案:

答案 0 :(得分:93)

将您要搜索的号码作为第一个参数传递,将数字数组传递给第二个参数:

function getClosest($search, $arr) {
   $closest = null;
   foreach ($arr as $item) {
      if ($closest === null || abs($search - $closest) > abs($item - $search)) {
         $closest = $item;
      }
   }
   return $closest;
}

答案 1 :(得分:11)

一种特殊的懒惰方法是让PHP按照搜索到的数字的距离对数组进行排序:

$num = 3;    
$array = array(0, 5, 10, 11, 12, 20);

foreach ($array as $i) {
    $smallest[$i] = abs($i - $num);
}
asort($smallest);
print key($smallest);

答案 2 :(得分:11)

这是我为排序的大数组

编写的高性能函数

对于具有20000个元素的数组,主循环仅需要~20次迭代。

请注意数组必须排序(升序)!

define('ARRAY_NEAREST_DEFAULT',    0);
define('ARRAY_NEAREST_LOWER',      1);
define('ARRAY_NEAREST_HIGHER',     2);

/**
 * Finds nearest value in numeric array. Can be used in loops.
 * Array needs to be non-assocative and sorted.
 * 
 * @param array $array
 * @param int $value
 * @param int $method ARRAY_NEAREST_DEFAULT|ARRAY_NEAREST_LOWER|ARRAY_NEAREST_HIGHER
 * @return int
 */
function array_numeric_sorted_nearest($array, $value, $method = ARRAY_NEAREST_DEFAULT) {    
    $count = count($array);

    if($count == 0) {
        return null;
    }    

    $div_step               = 2;    
    $index                  = ceil($count / $div_step);    
    $best_index             = null;
    $best_score             = null;
    $direction              = null;    
    $indexes_checked        = Array();

    while(true) {        
        if(isset($indexes_checked[$index])) {
            break ;
        }

        $curr_key = $array[$index];
        if($curr_key === null) {
            break ;
        }

        $indexes_checked[$index] = true;

        // perfect match, nothing else to do
        if($curr_key == $value) {
            return $curr_key;
        }

        $prev_key = $array[$index - 1];
        $next_key = $array[$index + 1];

        switch($method) {
            default:
            case ARRAY_NEAREST_DEFAULT:
                $curr_score = abs($curr_key - $value);

                $prev_score = $prev_key !== null ? abs($prev_key - $value) : null;
                $next_score = $next_key !== null ? abs($next_key - $value) : null;

                if($prev_score === null) {
                    $direction = 1;                    
                }else if ($next_score === null) {
                    break 2;
                }else{                    
                    $direction = $next_score < $prev_score ? 1 : -1;                    
                }
                break;
            case ARRAY_NEAREST_LOWER:
                $curr_score = $curr_key - $value;
                if($curr_score > 0) {
                    $curr_score = null;
                }else{
                    $curr_score = abs($curr_score);
                }

                if($curr_score === null) {
                    $direction = -1;
                }else{
                    $direction = 1;
                }                
                break;
            case ARRAY_NEAREST_HIGHER:
                $curr_score = $curr_key - $value;
                if($curr_score < 0) {
                    $curr_score = null;
                }

                if($curr_score === null) {
                    $direction = 1;
                }else{
                    $direction = -1;
                }  
                break;
        }

        if(($curr_score !== null) && ($curr_score < $best_score) || ($best_score === null)) {
            $best_index = $index;
            $best_score = $curr_score;
        }

        $div_step *= 2;
        $index += $direction * ceil($count / $div_step);
    }

    return $array[$best_index];
}
  • ARRAY_NEAREST_DEFAULT找到最近的元素
  • ARRAY_NEAREST_LOWER找到最近的元素,即LOWER
  • ARRAY_NEAREST_HIGHER找到最接近的元素

<强>用法:

$test = Array(5,2,8,3,9,12,20,...,52100,52460,62000);

// sort an array and use array_numeric_sorted_nearest
// for multiple searches. 
// for every iteration it start from half of chunk where
// first chunk is whole array
// function doesn't work with unosrted arrays, and it's much
// faster than other solutions here for sorted arrays

sort($test);
$nearest = array_numeric_sorted_nearest($test, 8256);
$nearest = array_numeric_sorted_nearest($test, 3433);
$nearest = array_numeric_sorted_nearest($test, 1100);
$nearest = array_numeric_sorted_nearest($test, 700);

答案 3 :(得分:3)

<?php
$arr = array(0, 5, 10, 11, 12, 20);

function getNearest($arr,$var){
    usort($arr, function($a,$b) use ($var){
        return  abs($a - $var) - abs($b - $var);
    });
    return array_shift($arr);
}
?>

答案 4 :(得分:1)

你可以简单地使用array_search,它会返回一个单独的密钥,如果在数组中找到了很多搜索实例,它将返回它找到的第一个。

Quote from PHP

  

如果在haystack中多次找到needle,则会返回第一个匹配的键。要返回所有匹配值的键,请使用带有可选search_value参数的array_keys()

示例用法:

if(false !== ($index = array_search(12,array(0, 5, 10, 11, 12, 20))))
{
    echo $index; //5
}

更新

function findNearest($number,$Array)
{
    //First check if we have an exact number
    if(false !== ($exact = array_search($number,$Array)))
    {
         return $Array[$exact];
    }

    //Sort the array
    sort($Array);

   //make sure our search is greater then the smallest value
   if ($number < $Array[0] ) 
   { 
       return $Array[0];
   }

    $closest = $Array[0]; //Set the closest to the lowest number to start

    foreach($Array as $value)
    {
        if(abs($number - $closest) > abs($value - $number))
        {
            $closest = $value;
        }
    }

    return $closest;
}

答案 5 :(得分:1)

Tim's implementation大部分时间会削减它。尽管如此,为了保持性能谨慎,您可以在迭代之前对列表进行排序,并在下一个差异大于最后一个差异时中断搜索。

<?php
function getIndexOfClosestValue ($needle, $haystack) {
    if (count($haystack) === 1) {
        return $haystack[0];
    }

    sort($haystack);

    $closest_value_index = 0;
    $last_closest_value_index = null;

    foreach ($haystack as $i => $item) {
        if (abs($needle - $haystack[$closest_value_index]) > abs($item - $needle)) {
            $closest_value_index = $i;
        }

        if ($closest_value_index === $last_closest_value_index) {
            break;
        }
    }
    return $closest_value_index;
}

function getClosestValue ($needle, $haystack) {
    return $haystack[getIndexOfClosestValue($needle, $haystack)];
}

// Test

$needles = [0, 2, 3, 4, 5, 11, 19, 20];
$haystack = [0, 5, 10, 11, 12, 20];
$expectation = [0, 0, 1, 1, 1, 3, 5, 5];

foreach ($needles as $i => $needle) {
    var_dump( getIndexOfClosestValue($needle, $haystack) === $expectation[$i] );
}

答案 6 :(得分:1)

要将最近的值搜索到对象数组中,您可以使用Tim Cooper's answer中的这个改编代码。

byte[] bytes = BitConverter.GetBytes(clientSocket.Receive(b, SocketFlags.None));
if (BitConverter.IsLittleEndian)
                Array.Reverse(bytes);

// receiving the data, buffer size is 1024

double[] values = new double[bytes.Length / 8];
for (int i = 0; i < values.Length; i++)
     values[i] = BitConverter.ToDouble(bytes, i * 8);

// converting back to double[]

this.receivingData = values;

// bytes is an array with only 4 bytes and receivingData is a double array filled with nothing. (this happens every time im receiving data and the bytes values is allways the same)

答案 7 :(得分:0)

function closestnumber($number, $candidates) {
$last = null;
foreach ($candidates as $cand) {
    if ($cand < $number) {
        $last = $cand;
    } else if ($cand == $number) {
        return $number;
    } else if ($cand > $number) {
        return $last;
    }
}
return $last;
}

这可以满足您的需求。

答案 8 :(得分:0)

例如,考虑到输入数组按升序asort()排序,您使用dichotomic search搜索的速度要快得多。

这是对我用于在按{Date}对象排序的Iterable事件列表中插入新事件的一些代码的快速而肮脏的改编...

因此,此代码将返回左侧最近的点(前/后)。

如果您想要找到数学上最近的点:请考虑将搜索值的距离与返回值和紧接在返回值右侧(下一个)的点(如果存在)进行比较。

function dichotomicSearch($search, $haystack, $position=false)
{
    // Set a cursor between two values
    if($position === false)
    {    $position=(object)  array(
            'min' => 0,
            'cur' => round(count($haystack)/2, 0, PHP_ROUND_HALF_ODD),
            'max' => count($haystack)
            );
    }

    // Return insertion point (to push using array_splice something at the right spot in a sorted array)
    if(is_numeric($position)){return $position;}

    // Return the index of the value when found
    if($search == $haystack[$position->cur]){return $position->cur;}

    // Searched value is smaller (go left)
    if($search <= $haystack[$position->cur])
    {
        // Not found (closest value would be $position->min || $position->min+1)
        if($position->cur == $position->min){return $position->min;}

        // Resetting the interval from [min,max[ to [min,cur[
        $position->max=$position->cur;
        // Resetting cursor to the new middle of the interval
        $position->cur=round($position->cur/2, 0, PHP_ROUND_HALF_DOWN);
        return dichotomicSearch($search, $haystack, $position);
    }

    // Search value is greater (go right)
        // Not found (closest value would be $position->max-1 || $position->max)
        if($position->cur < $position->min or $position->cur >= $position->max){return $position->max;}
        // Resetting the interval from [min,max[ to [cur,max[
        $position->min = $position->cur;
        // Resetting cursor to the new middle of the interval
        $position->cur = $position->min + round(($position->max-$position->min)/2, 0, PHP_ROUND_HALF_UP);
        if($position->cur >= $position->max){return $position->max;}
        return dichotomicSearch($search, $haystack, $position);        
}

答案 9 :(得分:0)

尝试这些。.这不仅提供最接近的匹配,而且还提供比给定数字最近的较高值和最近的较低值。

 getNearestValue(lookup_array, lookup_value) {

        if (lookup_array.length > 0) {
          let nearestHighValue = lookup_array[0];
          let nearestLowValue = lookup_array[0];
          let nearestValue=0;
          let diff, diffPositive = Infinity;
          let diffNeg = -Infinity;
          lookup_array.forEach(num => {
            diff = lookup_value - num;
            if (diff >= 0 && diff <= diffPositive) {
              nearestLowValue = num;
              diffPositive = diff;
            }
            if (diff <= 0 && diff >= diffNeg) {
              nearestHighValue = num;
              diffNeg = diff;
            }

          })
          //If no value is higher than GivenNumber then  keep nearest low value as clossets
          if (diffNeg == -Infinity) {
            nearestHighValue = nearestLowValue;
          }
              //If no value is Lower than Givennumber then  keep nearest High value as clossets
          if (diffPositive == Infinity) {
            nearestLowValue = nearestHighValue;
          }
          if((lookup_value-nearestLowValue)<=(nearestHighValue-lookup_value))
            {
              nearestValue=nearestLowValue;
             }
         else
            {
            nearestValue=nearestHighValue;
            }
          return { NearHighest: nearestHighValue, NearLowest: nearestLowValue,NearestValue:nearestValue };
        }
        else {
          return null;
        }
      }

答案 10 :(得分:0)

给出的代码可用于获取解决方案:

var counts = [4, 9, 15, 6, 2],
  goal = 5;

var closest = counts.reduce(function(prev, curr) {
  return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});

console.log(closest);

答案 11 :(得分:0)

    $closest = 0;
    while ($rowDecontos = mysql_fetch_array($pvDescontos)) {
        if ($rowDecontos['n_dias'] > $closest && $rowDecontos['n_dias'] <= $numDias) {
            $closest = $rowDecontos['n_dias'] ;
        }
    };

答案 12 :(得分:0)

我通过一些修改将接受的答案转换为Kotlin。请随时根据您的要求进行修改。

private fun getClosestTo1(array: List<Float>): Float? {
            var closest: Float? = null
            for (item in array) {
                if (item == 1.0f) {
                    return item
                } else if (closest == null || abs(1.minus(closest)) > abs(item - 1)) {
                    closest = item
                }
            }

            return closest
        }

答案 13 :(得分:0)

二分查找最接近的值(数组必须排序):

function findClosest($sortedArr, $val)
{
    $low = 0;
    $high = count($sortedArr) - 1;
    while ($low <= $high) {
        if ($high - $low <= 1) {
            if (abs($sortedArr[$low] - $val) < abs($sortedArr[$high] - $val)) {
                return $sortedArr[$low];
            } else {
                return $sortedArr[$high];
            }
        }

        $mid = (int)(($high + $low) / 2);
        if ($val < $sortedArr[$mid]) {
            $high = $mid;
        } else {
            $low = $mid;
        }
    }

    // Empty array
    return false;
}

答案 14 :(得分:-1)

试试这个:(尚未经过测试)

function searchArray($needle, $haystack){
    $return = $haystack[0];
    $prevReturn = $return;
    foreach($haystack as $key=>$val){
         if($needle > $val) {
            $prevReturn = $return;
            $return = $val;
         }
         if($val >= $needle) {
            $prevReturn = $return;
            $return = $val;
            break;
         }
    }
    if((($return+$needle)/2) > (($prevReturn+$needle)/2)){
         //means that the needle is closer to $prevReturn
         return $prevReturn;
    }
    else return $return;
}