【问题标题】:Reducing the number of iterations in double loop减少双循环中的迭代次数
【发布时间】:2021-07-30 13:08:28
【问题描述】:

我有一个圆列表(中心和半径),对于每个圆,我需要对所有圆心在圆内的圆做一些处理。

我做了一个简单的双循环

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

typedef struct Points
{
    double x;
    double y;
    double r;
} Points;

int main()
{

    int num = 10000; // number of circles
    Points c[num];   // circles

    for (int i = 0; i < num; i++)
    {
        for (int j = 0; j < num; j++)
        {
            // checking if the distance of point i and j is small than i's radius
            if (c[i].r > sqrt(pow(c[i].x - c[j].x, 2) + pow(c[i].y - c[j].y, 2)))
            {
                // do the processing
            }
        }
    }

    return 0;
}

问题在于性能。对于 10,000 个圆圈,我进行 100M 次迭代。

【问题讨论】:

  • 一方面,您可以在外循环中计算pow(c[i].r, 2),并在内循环中跳过sqrt()
  • 根据数据,可能值得使用曼哈顿或切比雪夫距离排除候选人
  • 然后你可以在你的内部 (j) 循环中检查'内在性'。您只需要计算一次距离,然后进行两次比较(每个半径一个)。
  • 感觉应该可以进行更多根本性的改进......
  • 根据圆圈的密集程度,按x 坐标排序可能让您只检查几个相邻条目(那些在当前中心的r 内具有x 坐标的条目)。

标签: c loops


【解决方案1】:

您可以比实际找到实际距离更快地限制计算次数。如果任何 X 或 Y 坐标离您正在处理的圆的 X、Y 中心太远,则另一个圆的中心不能在该圆内。

因此,过滤掉您在 X 和 Y 坐标上与简单的 &gt;&lt; 比较的圈数(并尝试将 sqrt() 替换为 hypot(),这可能优于 sqrt( pow() + pow() )。):

int main()
{
    int num = 10000; // number of circles
    Points c[num];   // circles

    for (int i = 0; i < num; i++)
    {
        double max_x = c[i].x + c[i].r;
        double min_x = c[i].x - c[i].r;
        double max_y = c[i].y + c[i].r;
        double min_y = c[i].y - c[i].r;

        for (int j = 0; j < num; j++)
        {
            // if the point is too far in any one dimension
            // it can't be within the current circle
            if ( ( c[j].x > max_x ) || ( c[j].x < min_x ) ||
                 ( c[j].y > max_y ) || ( c[j].y < min_y ) )
            {
                continue;
            }

            // checking if the distance of point i and j is small than i's radius
            if (c[i].r > hypot(c[i].x - c[j].x, c[i].y - c[j].y))
            {
                // do the processing
            }
        }
    }

    return 0;
}

【讨论】:

  • 这个花哨的名字是Chebyshev distance
  • 您还可以根据完全在圆圈内的较小正方形来规则候选人
  • hypot() 效果微乎其微,但使代码更美观。您的过滤方法使它快了大约 4 倍!
【解决方案2】:

这不是答案,而是寻找内圈时的一般建议:

您的(伪)代码(基本上)说:

let C = centerpoint of your circle;
let R = radius of your circle;
for each point p: 
  if distance(p,C) <= R 
  then treat_as_inside_circle(p)
  else treat_as_outside_circle(p);

这是正确的,但在性能方面你可以做得更好(也是伪代码):

let C = centerpoint of your circle; => (x_C, y_C)
let R = radius of your circle;
for each point p (x_P, y_P) : 
  if ((abs(x_C - x_P) <= R) AND (abs(y_C - y_P) <= R))
  then
       {
         if distance(p,C) <= R
         then treat_as_inside_circle(p);
         else treat_as_outside_circle(p);
       }
  else treat_as_outside_circle(p);

这首先检查 p 是否在正方形内(中心 C,“半径”R),只有在这种情况下,才会检查 p 在圆内。
(你可以想象,一个点只存在于一个圆内,如果它位于具有相同中心和该圆“半径”的正方形内。)

最大的优势是(性能方面),只有“简单”的计算就足以检查平方,而 检查圆需要消耗性能的计算(浮点平方根)。

【讨论】:

  • 这个花哨的名字是Chebyshev distance
  • 您还可以根据完全在圆圈内的较小正方形来规则候选人
【解决方案3】:

考虑将数组预处理为更好的数据结构。这是我会尝试的一件事。

  1. 从列表中获取最大半径以及最小和最大 x 和 y
  2. 使用 (1) 中的数字作为指导,创建一个表示单元格网格的数据结构。
  3. 根据中心将每个圆形元素放入网格中的一个单元格中(有些单元格有多个圆形)。

现在,对于任何圆,您只需要在周围的单元格中查看最大半径即可。它应该会大大减少每个圆圈的搜索空间

作为替代方案,在第 3 步中,将圆圈放入它接触的每个单元格中。因此,每个圆圈都在多个单元格中。然后,对于每个圆圈,您只需要处理其所在的每个单元格。

【讨论】:

  • 最终得到四叉树或任何用于地理索引的数据库...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-16
  • 2014-06-01
  • 2014-10-11
  • 2012-04-06
  • 1970-01-01
相关资源
最近更新 更多