转换时间:[距离,速度]数据到距离:[时间,速度]数据

时间:2014-01-09 23:14:13

标签: php algorithm time distance

我在搞算算法时遇到了麻烦......

我有一系列GPS数据,以1秒的间隔记录时间,速度,距离。假设距离是米,速度是m / s。可能有超过2小时的数据,或7200点。这里的“时间”字段主要供参考。

所以,前5秒会是这样的值,[1-5]为秒。

$data = array(
  1 : array('distance'=>0, 'time'=>'2014-01-09 17:50:00', 'speed'=>0.0),
  2 : array('distance'=>2, 'time'=>'2014-01-09 17:50:01', 'speed'=>2.0),
  3 : array('distance'=>6, 'time'=>'2014-01-09 17:50:02', 'speed'=>4.0),
  4 : array('distance'=>10, 'time'=>'2014-01-09 17:50:03', 'speed'=>4.0),
  5 : array('distance'=>12, 'time'=>'2014-01-09 17:50:04', 'speed'=>2.0)
);

我想将其转换为以1米间隔列出的数据,例如[1-6]为米。

$data = array(
  1 : array('seconds'=>1.5, 'time'=>'2014-01-09 17:50:01.500', 'speed'=>.666),
  2 : array('seconds'=>2, 'time'=>'2014-01-09 17:50:02', 'speed'=>2.0),
  3 : array('seconds'=>2.25, 'time'=>'2014-01-09 17:50:02.250', 'speed'=>4.0),
  4 : array('seconds'=>2.5, 'time'=>'2014-01-09 17:50:02.500', 'speed'=>4.0),
  5 : array('seconds'=>2.75, 'time'=>'2014-01-09 17:50:02.750', 'speed'=>4.0),
  6 : array('seconds'=>3, 'time'=>'2014-01-09 17:50:03', 'speed'=>4.0)
);

这可以在没有时间字段的情况下完成。我在计算上遇到了麻烦,因为它肯定不是1比1。如果我们从7200秒的数据开始,我们可能会或多或少地取决于所覆盖的距离(大于或小于7200米)。

编辑(01/10/2014)

以下是两种方法的实际实现。我实际上在确定哪个更好,迭代或递归方法时遇到了麻烦。我可以选择迭代

方法1,迭代(@Ezequiel Muns,我做了很少的修改):

function timeToDistance($data) {
  if(sizeof($data) == 0){ return; }
  $startTime = $data[0]['time'];

  $prev = null;
  $result = array();
  foreach ($data as $secs => $row) {
    $row['seconds'] = $secs; // to simplify passing in secs
    if ($prev == null) {
      // make sure we have a pair
      $prev = array( 'distance'=>0 );
    }
    foreach (distanceRowsBetween($startTime,$prev, $row) as $dist => $distRow) {
      $result[$dist] = $distRow;
    }
    $prev = $row;
  }
  return $result;
}

function distanceRowsBetween($startTime,$prevRow, $nextRow) {
  // Return the by-distance rows that are between $prevRow (exclusive)
  // and $nextRow (inclusive)
  $rows = array();
  $currDist = $prevRow['distance'];
  while (true) {
    // try to move to the next whole unit of distance
    $dDist = ceil($currDist) - $currDist;
    $dDist = $dDist == 0.0? 1.0 : $dDist; // dDist is 1 unit if currDist is whole
    $currDist += $dDist;
    if ($currDist > $nextRow['distance'])
      break;

    $currSpeed = $nextRow['speed'];
    $currSecs = strtotime($nextRow['time']) - strtotime($startTime);
    $currTime = $nextRow['time'];

    $rows[$currDist] =  array(
                          'speed' => $currSpeed,
                          'seconds' => $currSecs,
                          'time' => $currTime,
                         );
  }
  return $rows;
}

方法2,递归(@Nathaniel Ford伪代码,我的实际代码):

function data2dist($time_data = array()){
  $dist_data = array();
  if(sizeof($time_data) == 0){ return $dist_data; }

  $start_point = array_shift($time_data);
  $start_time = $start_point['time'];

  data2dist_sub($start_time, $time_data,$dist_data,$start_point);

  return $dist_data;
}

function data2dist_sub($start_time,&$time_data, &$dist_data, $start_point = array()){
  if(sizeof($time_data) == 0 && !isset($start_point)){
    return;
  }

  if(sizeof($dist_data) == 0){
    $prev_dist = 0;
  } else {
    $prev_dist = $dist_data[sizeof($dist_data)-1]['distance'];
  }
  // since distances are accumulating, get curr distance by subtracting last one
  $point_dist = $start_point['distance'] - $prev_dist;

  if($point_dist == 1){
    // exactly 1: perfect, add and continue
    $dist_data[] = $start_point;
    $start_point = array_shift($time_data);
  } else if($point_dist > 1){
    // larger than 1: effectively remove 1 from current point and send it forward
    $partial_point = $start_point;
    $partial_point['distance'] = 1 + $prev_dist;
    $dist_data[] = $partial_point;

  } else if($point_dist < 1){
    // less than 1, carry forward to the next item and continue (minor: this partial speed is absorbed into next item)
    $start_point = array_shift($time_data);
    if(!isset($start_point)){   return;   }

    $start_point['distance'] += $point_dist;
  }
  data2dist_sub($start_time,$time_data,$dist_data,$start_point);
}

2 个答案:

答案 0 :(得分:2)

您可以通过注意每个连续的一对时间行来计算0个或更多的距离行来简化这一点,这些行仅取决于这两个时间行。

所以从一个函数开始做这个更简单的计算,这是一个骨架,为简单起见,将转换后的'秒','速度'和'时间'值的计算结果保留。

function distanceRowsBetween($prevRow, $nextRow) {
    // Return the by-distance rows that are between $prevRow (exclusive)
    // and $nextRow (inclusive)
    $rows = array();
    $currDist = $prevRow['distance'];
    while (true) {
        // try to move to the next whole unit of distance
        $dDist = ceil($currDist) - $currDist;
        $dDist = $dDist == 0.0? 1.0 : $dDist; // dDist is 1 unit if currDist is whole
        $currDist += $dDist;
        if ($currDist > $nextRow['distance'])
            break;

        // calculate $currSecs at distance $currDist
        // calculate $currSpeed 
        // calculate $currTime 

        $rows[$currDist] = array(
            'speed' => $currSpeed,
            'seconds' => $currSecs,
            'time' => $currTime,
        );
    }
    return $rows;
}

现在您已经完成剩下的工作了迭代输入中的每个连续对并累积得到的距离行:

function timeToDistance($data) {
    $prev = null;
    $result = array();
    foreach ($data as $secs => $row) {
        $row['seconds'] = $secs; // to simplify passing in secs
        if ($prev == null) {
            $prev = $row; // make sure we have a pair
            continue;
        }
        foreach (distanceRowsBetween($prev, $row) as $dist => $distRow) {
            $result[$dist] = $distRow;
        }
        $prev = $row;
    }
    return $result;
}

注意在这个函数中我填充并传入行中当前的'seconds'值,以减少传递给前一个函数的参数数量。

答案 1 :(得分:1)

这有点令人费解,并且有一些边缘情况使其变得困难。但是,您的基本算法应该归结为:

Take in an array of by-Time data points
Create a new array of by-Distance data points
Create a first by-Distance data point with 'zero' speed/distance
Pass this to your subfunction 

Subfunction (Takes by-Time array, by-Distance array and 'start point')
Take the first by-Time data point and 'add' it to the by-Distance data point, call this 'temp'
  Convert to seconds/speed
If distance covered by temp is exactly 1, add this new array to the by-Distance array
If it is more than one, subtract the portion that would equal one
  back-calculate distance/speed/time, add to by-Distance array
  Recurse into the subfunction, using the remainder as your new start point
If it is less than one
  Recurse into the subfunction, using the modified start point as new start point

请注意,子函数需要使用数组的可变副本:by-Time数组应该慢慢收缩,by-Distance数组会增长。此外,你会想要蹦出函数(而不是使用直接递归),因为有了7200个数据点,你可能会有更多的堆栈帧,并且你遇到了潜在的内存问题。

相关问题