2013-03-24 35 views
1

我需要通过包含地图中的点的数组并检查它们之间的距离。我需要计算每个节点在200米和50米范围内有多少个节点。它适用于较小的数值。但是,当我试图通过它运行更多的值(大约4000用于可伸缩性测试)时发生错误,说我已经达到了300秒的最大执行时间。如果可能,它需要能够在300秒内处理至少这么多。PHP代码达到执行时间限制

我已经阅读并发现有一种方法来禁用/更改此限制,但我想知道是否有更简单的方式执行下面的代码,以便运行它的时间会减少。

for($i=0;$i<=count($data)-1;$i++) 
     { 
      $amount200a=0; 
      $amount200p=0; 
      $amount50a=0; 
      $amount50p=0; 
      $distance; 
      for($_i=0;$_i<=count($data)-1;$_i++) 
      { 
       $distance=0; 
       if($data[$i][0]===$data[$_i][0]) 
       { 
       } 
       else 
       { 
        //echo "Comparing ".$data[$i][0]." and ".$data[$_i][0]." "; 
        $lat_a = $data[$i][1] * PI()/180; 
        $lat_b = $data[$_i][1] * PI()/180; 
        $long_a = $data[$i][2] * PI()/180; 
        $long_b = $data[$_i][2] * PI()/180; 
        $distance = 
          acos(
            sin($lat_a) * sin($lat_b) + 
            cos($lat_a) * cos($lat_b) * cos($long_b - $long_a) 
          ) * 6371; 
        $distance*=1000; 
        if ($distance<=50) 
        { 
         $amount50a++; 
         $amount200a++; 
        } 
        else if ($distance<=200) 
        { 
         $amount200a++; 
        } 
       } 
      } 
      $amount200p=100*number_format($amount200a/count($data),2,'.',''); 
      $amount50p=100*number_format($amount50a/count($data),2,'.',''); 
      /* 
      $dist[$i][0]=$data[$i][0]; 
      $dist[$i][1]=$amount200a; 
      $dist[$i][2]=$amount200p; 
      $dist[$i][3]=$amount50a; 
      $dist[$i][4]=$amount50p; 
      //*/ 
      $dist.=$data[$i][0]."&&".$amount200a."&&".$amount200p."&&".$amount50a."&&".$amount50p."%%"; 
     } 

索引0包含的每个节点的唯一ID,1包含每个节点的纬度和 索引2包含各节点的经度。

错误发生在第一个循环内部的第二个循环中。该循环是将所选映射节点与其他节点进行比较的循环。我也使用Haversine公式。

+0

计算循环外的不变量:'count($ data)','PI()/ 100'等等 – 2013-03-24 03:12:01

回答

0

首先,你在大O表示法中执行:O(data^2),这样会慢一点,实际上或者有两种可能的解决方案。找到一个在更好的时间解决相同问题的经过验证的算法。或者,如果你不能,开始移动的内部循环的东西,并在数学上证明,如果你可以转换内循环为主要简单的计算,这是往往你可以做的事情。

经过一些重写后,我看到一些可能性: 如果$ data不是SPLFixedArray(它具有FAR更好的访问时间),那么创建它。因为您正在访问该数据很多次(4000^2)* 2。 secound,写入更清晰的代码。虽然optizmier会尽其所能,但如果您不尝试缩小代码(这只会使代码更具可读性),那么它可能无法做到尽可能好。

并将中间结果移出循环,也就像数组大小一样。

0

目前您正在检查所有其他积分的积分,实际上您只需要检查当前积分与其余积分的积分。 A到B的距离与B到A的距离相同,为什么要计算两次?

我可能会做一个相邻的数组来计算彼此的范围内有多少个节点,并且在计算出两个节点在彼此的范围内之后,增加该数组中的条目对。

在计算真实距离(永远不会超快)之前,您应该想出一个非常快的距离近似值,可以用来忽略尽可能多的节点。

一般来说,超越算法的优化,优化的基本规则是:

  • 不要你没有任何处理要做到:不一样乘以1000 $的距离只要改变你测试的值分别从20和50到0.02和0.05。

  • 不要比任何时候更频繁地调用任何函数:您只需在任何处理开始之前调用count($ data)一次。例如,不要多次计算常数值:PI()/180

  • 将所有可能的处理移到循环外部。即尽可能预先计算。

另一个小点,这将使你的代码变得更容易阅读:

for($i = 0; $i <= count($data) - 1; $i++)是一样的:

for($i = 0; $i < count($data); $i++)

0

试试这个:

$max = count($data); 
$CONST_PI = PI()/180; 

for($i=0;$i<$max;$i++) 
{ 
    $amount200a=0; 
    $amount50a=0; 

    $long_a = $data[$i][2] * $CONST_PI; 
    $lat_a = $data[$i][1] * $CONST_PI; 

    for($_i=0;$_i<=$max;$_i++) 
    //or use for($_i=($i+1);$_i<=$max;$_i++) if you did not need to calculate already calculated in other direction 
    { 
     $distance=0; 
     if($data[$i][0]===$data[$_i][0]) continue; 

     $lat_b = $data[$_i][1] * $CONST_PI; 
     $long_b = $data[$_i][2] * $CONST_PI; 
     $distance = 
       acos(
         sin($lat_a) * sin($lat_b) + 
         cos($lat_a) * cos($lat_b) * cos($long_b - $long_a) 
       ) * 6371; 
     if ($distance<=0.2) 
     { 
      $amount200a++; 
      if ($distance<=0.05) 
      { 
       $amount50a++; 
      } 
     } 
    } // for %_i 
    $amount200p=100*number_format($amount200a/$max,2,'.',''); 
    $amount50p=100*number_format($amount50a/$max,2,'.',''); 

    $dist.=$data[$i][0]."&&".$amount200a."&&".$amount200p."&&".$amount50a."&&".$amount50p."%%"; 
} // for $i 

阅读我认为,如果你查阅会更好注意$ _i的注释行将会更快:)