【问题标题】:iPhone hard computation and cachingiPhone 硬计算和缓存
【发布时间】:2009-12-05 08:20:49
【问题描述】:

我有问题。我有 500k 记录的数据库。每个记录存储纬度、经度、动物种类、观察日期。我必须在 mapkit 视图上方绘制网格(15x10),以显示该网格单元中物种的浓度。每个单元格是 32x32 的盒子。

如果我在运行时计算它会非常。 有人知道如何缓存它吗?在内存或数据库中。

数据结构:

观察:

  • 纬度
  • 经度
  • 日期
  • 物种
  • 其他一些不重要的数据

屏幕示例:

alt text http://img6.imageshack.us/img6/7562/20091204201332.png

每个红色方框显示该区域的物种数量。

我现在使用的代码: 数据 -> 从数据库中选择,都是地图区域内的观察数据

for (int row = 0; row < rows; row++)
 { 
  for (int column = 0; column < columns; column++)
  {
   speciesPerBox=0;
   minG=boxes[row][column].longitude;
   if (column!=columns-1) {
    maxG=boxes[row][column+1].longitude;
   } else {
    maxG=buttomRight.longitude;
   }

   maxL=boxes[row][column].latitude;
   if (row!=rows-1) {
    minL=boxes[row+1][column].latitude;
   } else {
    minL=buttomRight.latitude;
   }

   for (int i=0; i<sightingCount; i++) {
    l=data[i].latitude;
    g=data[i].longitude;

    if (l>=minL&&l<maxL&&g>=minG&&g<maxG) {
     for (int j=0; j<speciesPerBox; j++) {
       if (speciesCountArray[j]==data[i].specie) {
        hasSpecie=YES;
       }
      }

      if (hasSpecie==NO) {
       speciesCountArray[speciesPerBox]=data[i].specie;
       speciesPerBox++;
      }

      hasSpecie=NO;


     }
    }
   }


   mapData[row][column].count = speciesPerBox;
  }
 }

【问题讨论】:

  • 顺便说一句,您确定您的数据库键适合您的查询吗?

标签: iphone database algorithm


【解决方案1】:

由于您的数据是静态的,您可以预先计算每个网格的每个物种并将其存储在数据库中,而不是所有位置坐标。

由于您有 15 x 10 = 150 个单元格,您最终将在数据库中获得 150 * [物种数量] 记录,这应该是一个小得多的数字。

另外,请确保您在正确的列上有索引。否则,您的查询将不得不一遍又一遍地扫描每条记录。

【讨论】:

  • 如果用户缩放地图我需要重新计算所有这些数据,用户可以通过触摸移动地图,我也需要重新计算。
  • 您有多少缩放级别?预先计算最高级别的缩放(因为它们的计算成本最高)。如果您允许用户任意缩放,那么您就不能真正减少所需的计算量。您的问题超出了 iPhone 的容量。
  • 绝对值得预先计算顶层(如上所示)并返回实时放大模式。
【解决方案2】:

循环for (int i=0; i&lt;sightingCount; i++) 正在扼杀你的表现。尤其是大量的if (l&gt;=minL&amp;&amp;l&lt;maxL&amp;&amp;g&gt;=minG&amp;&amp;g&lt;maxG) 语句,其中大部分的目击将被跳过。

SightingCount 有多大?

首先你应该使用一种空间优化,例如一个简单的:存储每个单元格的物种计数列表(我们称它们为“区域”)。将这些区域定义得相当大,以免浪费空间。但是较小的区域提供更好的性能,而太小的区域会逆转效果。所以,让它可配置并测试不同的区域大小以找到一个好的折衷方案!

当需要汇总单元格中的物种数量以进行渲染时,确定给定单元格重叠的区域(相当简单快速的“矩形重叠”测试)。然后你只需要检查这些区域的物种数量。这在很大程度上减少了内部循环的迭代!

这就是(大多数“空间优化”)的想法:分而治之;在这里您将划分您的空间,然后您可以以最小的努力及早拒绝处理大量不相关的“目击”(增加的努力是矩形重叠测试,但每个测试都拒绝多个目击,您当前的代码测试每个寻找相关性)。

第二步中,还应用一些明显的代码优化:例如minL 和 maxL 不会每列更改。计算 minL 和 maxL 可以移到外循环(就在 for( int column=0; ... 之前)。

由于网格的纬度分布均匀,您甚至可以将它们从网格单元格中移除,这样可以节省一些迭代时间。这里是一个例子(不包括空间优化):

maxL=boxes[0][0].latitude;
minL=boxes[rows-1][0].latitude;
incL=maxL-minL;
for( int row = 0; row < rows; row++ )
{
    for( int column = 0; column < columns; column++ )
    {
        speciesPerBox=0;
        minG=boxes[row][column].longitude;
        if (column!=columns-1) {
            maxG=boxes[row][column+1].longitude;
        } else {
            maxG=buttomRight.longitude;
        }
        ...
        ...
    }
    ...

    minL = maxL; // left edge = right edge of previous step
    maxL += incL; // increment right edge
    if( maxL >= 90 ) maxL -= 90; // check your scale, i assume 90°
}

也许这也适用于经度循环,但经度可能不是均匀分布的(即“incG”在每个步骤中都不同)。

请注意,空间优化会产生巨大的差异,循环优化只有很小(但仍然值得)的差异。

【讨论】:

  • sightingCount - 最大情况下为 500-600k
【解决方案3】:

拥有 50 万条记录,这听起来像是核心数据的工作。最好是桌面上的核心数据。如果数据没有实时更新,您应该在较重的硬件上处理数据并使用 iPhone 来显示它。这将大大简化应用程序,因为您只需存储每个地图单元格的值。

即使您确实想在 iPhone 上处理它,您也应该让应用程序处理一次数据并保存结果。似乎没有理由让应用程序在每次想要显示一个单元格时重新计算每个地图单元格的物种值。

我建议在核心数据中创建一个实体来表示观察结果。然后是另一个实体来表示地理方格。设置正方形和正方形内的观察值之间的关系。然后在方形实体中创建物种的计算值。如果其中一个观察值发生变化,您只需重新计算物种值。

这就是创建对象图的问题。即使数据不断更新。核心数据只会执行那些需要适应在任何给定时间发生变化的少量观察对象所需的计算,并且会以高度优化的方式进行。

编辑01:

从完全不同的角度解决问题。在核心数据中。

(1) 创建一个观察记录的对象图,使得每个观察对象与地理上最接近它的其他观察对象具有互惠关系。这将创建一个看起来像扁平不规则网络的对象图。

(2) 为 observationRecords 类创建方法 (a) 确定记录是否位于任意地理方格的范围内 (b) 询问其每个相关记录是否也在方格内 (c) 返回它自己的物种计数和所有相关记录的计数。

(3) 将您的地图划分为一些合理的小方块,例如一秒的圆弧正方形。在该方块中选择一个链接记录并将其添加到列表中。选择所有记录的某个百分比,例如每 100 条或 1,000 条中的 1 条,以便将列表从 500k 减少到创建可以通过蛮力谓词快速搜索的子列表。我们将列表中的这些记录称为网格标志。

(4) 用户放大时,使用蛮力查找所有带有地理网格的gridflag记录。然后要求每个 gridflag 记录向其每个链接记录发送消息,以查看 (a) 它们是否在网格内,(b) 它们的物种计数是多少,以及 (c) 它们的链接记录的计数是多少网格内。 (使用标志确保每次搜索只查询每条记录一次,以防止递归失控。)

这样,您只需在每个任意大小的网格单元格中找到一条记录,该记录就会为您找到所有其他记录。无需逐条查看每条记录以查看哪条记录每次进入哪个单元格,您只需处理每个单元格中的记录以及紧邻的那些记录。当您放大时,您实际查询的记录数量会减少,而不是保持不变。如果一个网格单元格只有少量记录,您只需查询少量记录。

这需要一些努力和时间来设置,但是一旦你这样做了,它会非常有效,尤其是在放大时。对于顶级,只需有一个预处理的静态地图。

希望我解释得足够好。很难用语言表达。

【讨论】:

  • 我在项目中使用核心数据,但是原始sql查询更快。问题是盒子坐标和大小取决于地图区域,会改变用户。
猜你喜欢
  • 2018-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-09
  • 1970-01-01
  • 2016-07-22
  • 2021-10-10
相关资源
最近更新 更多