【问题标题】:What is the fastest way to sort a lot of locations on distance?根据距离对大量位置进行排序的最快方法是什么?
【发布时间】:2011-04-25 11:12:20
【问题描述】:

我想根据与当前位置的距离对许多位置(航点)进行排序。 当前位置当然是一个移动的目标,因此对于每次位置更新,都需要重新计算每个位置的距离。但仅对附近位置重新计算就足够了。

我目前使用核心数据,并将到当前位置的距离作为属性存储在表中(但只有在更改时才更新),来自 configurecell:atindexpath: 方法。 那种工作,但是当核心数据自动更新所有距离时应用程序没有响应。这适用于 250 个位置,但对于 5000 个它会崩溃。 我需要它为 10.000 个位置工作,尽管我可能只需要 1000 个最近的位置左右。

我还没有尝试过的想法: 将所有距离存储在一个单独的内存数组中,除了记录 id 和距离之外什么都没有。然后按距离对数组进行排序。问题是我不能使用 FetchedResultsController 因为数据库中没有排序字段。

使用谓词根据纬度和经度过滤位置。然后只显示过滤后的位置。

在单独的线程中重新计算距离。

这些想法似乎都不够简单,可以尝试一下。

任何人有建议,不同的想法,我的想法的变化?

【问题讨论】:

    标签: iphone objective-c core-data core-location


    【解决方案1】:

    我的最终解决方案如下: 我选择经纬度 1 度内的所有航点(通常约 1000 个航点),然后计算并存储到表中当前位置的距离。然后我可以按距离排序。缓慢的事情是节省核心数据。但是在排序(和获取)之后,我只是取消了更改。节省的费用占了整个事情的 90% 以上,所以效果很好。

    【讨论】:

      【解决方案2】:

      听起来您需要将您的位置转换为希尔伯特曲线上的位置 - 然后点“靠近”您是一个简单的减法?

      Mapping N-dimensional value to a point on Hilbert curve

      不能说我对这项技术了如指掌,但这就是我开始关注的地方

      【讨论】:

      • 为此我需要一个“中心位置”。所以我可以将所有位置视为平面上的点。我认为这适合对所有位置进行“预排序”,以便附近的位置在附近进行预排序。我所有的地点现在都在欧洲。它可能是解决方案的一部分。
      • 希尔伯特曲线当然是“最好的”方法,但如果我对空间填充曲线的理解是正确的,任何空间填充曲线都可以(想到皮亚诺。尽管选择一个能很好地保留局部性的曲线)。要考虑的另一件事是将您的位置存储在自适应 kd 树中,以快速排除太远的位置。
      • 对于低维度,kd-tree 优于空间填充曲线。对于高维度,只有当 N >= 2^D(N = 点数,D = 维度)时才有用。原因是,如果你在 kd-tree 中的每一层沿着不同的维度进行分割,如果你的点太少,在你沿着每个维度分割之前,你将在每个叶子中只有一个点。这意味着树结构忽略了剩余的维度和质量。
      【解决方案3】:

      如果重要的是顺序(而不是准确的距离),您可以在移动窗口中对航路点序列的切片进行排序(即,将项目 i 排序到 i+ n,其中 i 发生变化)。从航点序列的开头开始。对 n 个项目进行排序(n = 10 是一个很好的起点)。如果任何项目改变了位置,将窗口向前移动 n/2 (尝试不同的偏移量或算法来选择偏移量)并重复。根据航路点在当前位置附近的密集程度,我预计这只会在几次排序后停止。

      请注意,我还没有考虑足够长的时间来说明这是否真的有效。

      在您提到的三个选项中,我最喜欢使用线程。这是处理因繁重计算而无法响应的 UI 的经典方式。

      【讨论】:

      • 我现在认为我的策略是:在地图的一个小“正方形”部分标记当前位置为中心,并找到该区域中的所有航点。就像stackoverflow.com/questions/2176127/core-data-and-core-location/… 中描述的那样,如果我有足够多的航点我喜欢,我会停下来。如果还不够,我将范围加倍(获得 4 倍的覆盖区域)并重复。通过在后台执行此操作,UI 保持响应。最后,如果有新的位置更新出现,我会从一个小的边界框重新开始。
      • 您可以将其发布为答案。也许你会得到一个自学徽章。
      【解决方案4】:

      我将我的位置作为纬度/经度坐标存储在数据模型中。然后我写了一些辅助扩展来找到纬度/经度坐标的半矩形区域并通过它进行查询。这是我正在使用的代码。我知道这个问题是针对 Objective-C 的,但这个问题已经过时了,而且现在可能大多数人都在寻找 Swift 的答案。

      斯威夫特 3

         extension CLLocationDistance {
              var feet: Double {
                  return self * 3.28084
              }
              var miles: Double {
                  return self.feet / 5280.0
              }
          }
      
          extension CLLocationDegrees {
              static var north: CLLocationDegrees {
                  return 90.0
              }
              static var south: CLLocationDegrees {
                  return -90.0
              }
              static var east: CLLocationDegrees {
                  return 180.0
              }
              static var west: CLLocationDegrees {
                  return -180.0
              }
      
              var radians: Double {
                  return Double.pi * self / 180.0
              }
          }
      
          extension CLLocationCoordinate2D {
              static var origin: CLLocationCoordinate2D {
                  return CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
              }
      
              static var northPole: CLLocationCoordinate2D {
                  return CLLocationCoordinate2D(latitude: 90.0, longitude: 0.0)
              }
      
              static var southPole: CLLocationCoordinate2D {
                  return CLLocationCoordinate2D(latitude: 90.0, longitude: 0.0)
              }
      
              var metersPerDegreeLatitude: CLLocationDistance {
                  return 111319.4907932736
              }
              var metersPerDegreeLongitude: CLLocationDistance {
                  return max(0.0, cos(self.latitude.radians) * self.metersPerDegreeLatitude)
              }
          }
      
          extension CLCircularRegion {
              var northernmostLatitude: CLLocationDegrees {
                  let longitude = self.center.latitude + self.radius / self.center.metersPerDegreeLatitude
                  return min(longitude, .north)
              }
      
              var southernmostLatitude: CLLocationDegrees {
                  let longitude = self.center.latitude - self.radius / self.center.metersPerDegreeLatitude
                  return max(longitude, .south)
              }
      
              var easternmostLongitude: CLLocationDegrees {
                  guard self.northernmostLatitude <= .north else {
                      return .east
                  }
                  guard self.southernmostLatitude >= .south else {
                      return .east
                  }
                  return min(.east, self.center.longitude + self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
              }
      
              var westernmostLongitude: CLLocationDegrees {
                  guard self.northernmostLatitude <= .north else {
                      return .west
                  }
                  guard self.southernmostLatitude >= .south else {
                      return .west
                  }
                  return max(.west, self.center.longitude - self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
              }
      
              func buildPredicate(latitudeName: String = "latitude", longitudeName: String = "longitude") -> NSPredicate {
                  let args = [self.southernmostLatitude, self.northernmostLatitude, self.westernmostLongitude, self.easternmostLongitude]
                  return NSPredicate(format: "\(latitudeName) >= %@ && \(latitudeName) <= %@ && \(longitudeName) >= %@ && \(longitudeName) <= %@", argumentArray: args)
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-10
        • 1970-01-01
        • 2017-10-29
        • 1970-01-01
        相关资源
        最近更新 更多