【问题标题】:Finding two farthest objects between array of coordinates in PHP在PHP中的坐标数组之间找到两个最远的对象
【发布时间】:2019-08-03 00:29:57
【问题描述】:

我想在我的 $user_devices 数组中找到两个最远的对象(彼此相距)。

$user_devices 的每个对象都有属性:id、name、imei 和坐标。例如:

$user_devices = array(
    'id' => '1',
    'name' => 'First object',
    'imei' => '123456789',
    'coordinates' => '51.313032,11.798092'
);

我要做的是遍历整个数组,将纬度和经度转换为 x 和 y,最后计算位置之间的距离。

问题在于,该算法应该能够在至少 5000 条记录(位置)的情况下快速运行,但将其集成到网站需要大约 20 秒的加载时间。

如何优化这个算法?

public static function get_farthest_devices($user_devices)
{

    $r = 6378; // Earth radius in km
    $max_distance = 0;
    $count = count($user_devices);

    for ($i = 0; $i < $count - 1; $i++) {
        $coordinates = $user_devices[$i]->coordinates;
        $coordinates_explode = explode(',', $coordinates);

        $lat = $coordinates_explode[0];
        $lng = $coordinates_explode[1];

        $x1 = $r * cos(deg2rad($lat)) * cos(deg2rad($lng));
        $y1 = $r * cos(deg2rad($lat)) * sin(deg2rad($lng));

        for ($j = $i + 1; $j < $count; $j++) {
            $coordinates = $user_devices[$j]->coordinates;
            $coordinates_explode = explode(',', $coordinates);

            $lat = $coordinates_explode[0];
            $lng = $coordinates_explode[1];

            $x2 = $r * cos(deg2rad($lat)) * cos(deg2rad($lng));
            $y2 = $r * cos(deg2rad($lat)) * sin(deg2rad($lng));

            $distance_between_points = sqrt( pow($x2-$x1, 2) + pow($y2-$y1, 2) );

            if($distance_between_points > $max_distance)
            {
                $max_distance = $distance_between_points;
                $obj_i = $user_devices[$i];
                $obj_j = $user_devices[$j];
            }
        }
    }

    echo 'MAX distance is between: ' . $obj_i->name . ' (' . $obj_i->imei . ') and ' . $obj_j->name . ' (' . $obj_j->imei . ') ' .  $max_distance . ' km<br/>';     
}

【问题讨论】:

  • 对象可以在地球上的任何地方还是在有限的区域(例如,在某个国家或城市)?
  • 对象可以在任何地方。
  • 为什么不使用数据库呢?在表中插入每条记录并执行查询
  • 整个 user_devices 数组是从数据库中获取的。每个设备位置可以每 x 秒更改一次。在每次页面加载时,用户都应该看到哪两个设备相距最远。
  • 从前三个点创建一个三角形,然后为每个给定的下一个点扩展它(如果点在结果图形之外)或保持原样,具体取决于该点是否在结果内数字。对于图中的每个点,存储坐标,这样您就不必一次又一次地爆炸/deg2rad/sin/cos。

标签: php coordinates distance geocoding haversine


【解决方案1】:
public static function get_farthest_devices($user_devices)
{

$xyImei[] = array();
$r = 6378; // Earth radius in km
$max_distance = 0;

foreach($user_devices as $dev) {   // x,y calculate only one for one device
  $coordinates_explode = explode(',', $dev->coordinates);
  $lat = $coordinates_explode[0];
  $lng = $coordinates_explode[1];
  $xyImei[$dev->imei]['x'] = $r * cos(deg2rad($lat)) * cos(deg2rad($lng));
  $xyImei[$dev->imei]['y'] = $r * cos(deg2rad($lat)) * sin(deg2rad($lng));
}

foreach($user_devices as $dev1) {  // calculate distance 
  $x1 = $xyImei[$dev1->imei]['x'];
  $y1 = $xyImei[$dev1->imei]['y'];

  foreach($user_devices as $dev2) {
  $x2 = $xyImei[$dev2->imei]['x'];
  $y2 = $xyImei[$dev2->imei]['y'];

  if ($dev1->imei != $dev2->imei)  // special case
  if ($x2-$x1 == 0) 
    $distance_between_points = abs($y2-$y1);
  else
    if ($y2-$y1 == 0) 
      $distance_between_points = abs($x2-$x1);
    else
      $distance_between_points = sqrt( pow($x2-$x1, 2) + pow($y2-$y1, 2) );

    if($distance_between_points > $max_distance)
    {
    $max_distance = $distance_between_points;
    $obj_i = $dev1;
    $obj_j = $dev2;
    }

  }

}

echo 'MAX distance is between: ' . $obj_i->name . ' (' . $obj_i->imei . ') and ' . $obj_j->name . ' (' . $obj_j->imei . ') ' .  $max_distance . ' km<br/>';     
}

【讨论】:

    【解决方案2】:

    主要问题是 5k 个对象产生大约 1200 万对,而做任何事情 1000 万次都需要一些时间。因此,解决方案将围绕从考虑中丢弃对。

    如果对象被限制在一个足够小的区域(例如,一个国家或大陆),那么最远的一对肯定会位于convex hull。凸包可能有大约 100 个点,然后您会在这 100 个点中找到最远的一对。但如果您的对象可以在全球范围内找到,这将不起作用。

    如果可以接受概率答案,您可以使用Monte Carlo algorithm 并选择随机点对并计算它们之间的距离。有了足够大的样本量(或者可能是智能采样算法),您很可能会得到接近真实值的答案。

    如果您需要确切的答案,您可以将地球细分为 10x10 度的 18x36 块(例如)。

    • 仅查看每个块的角(以及考虑对映块和极点的一些特殊情况),您可以计算出两个不同块中的对象可以走多远。例如,块 (0,0) 和 (0,2) 中的对象之间的距离最多可以为 3300 公里。让我们称之为两个块之间的最大距离distmax
    • 对于每对非空块 B1 和 B2(包括 B1=B2),从具有最大 distmax 的那对开始:
    • 找到最远的一对对象,其中一个在 B1,另一个在 B2,并调用距离d
    • 丢弃具有distmax &lt; d 的块对,因为您知道其中的对象彼此之间必须比d 更近。 您将避免计算大量对象对之间的距离.

    【讨论】:

      猜你喜欢
      • 2013-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-04
      • 2019-09-09
      • 1970-01-01
      • 1970-01-01
      • 2015-08-28
      相关资源
      最近更新 更多