【问题标题】:How can I optimize my code that uses nested for loops?如何优化使用嵌套 for 循环的代码?
【发布时间】:2020-09-07 04:04:16
【问题描述】:

我正在努力解决一个问题,我需要确定 Point 是否位于连接另外两个 Points 的线上。例如,如果我有Point a, b, c,我想确定c 是否在连接ab 的线段上。在我的代码中,我有一个点 me(示例中的 a)和两个点列表 hits(示例中的 b)和 reachable(示例中的 c)。对于命中中的每个点,我想确定在连接我和命中点的线段上是否有任何可到达的点。如果该线段上有一个点,则需要减少 numHits。这是我的代码:

Point me; //Point a from the example above
ArrayList<Point> hits = new ArrayList<>();  //a list of Point b's from the example above
ArrayList<Point> reachable = new ArrayList<>();  //a list of point c's from the example above

for(Point hit : hits) {
    for(Point p : reachable) {
        if(!hit.equals(p) && !me.equals(p)) {
            //find the equation of a line from me to hit
            if(hit.x - me.x == 0) { //if the line has an undefined slope... if the line is vertical
                if( (((p.y <= hit.y) && (p.y >= me.y)) || ((p.y >= hit.y) && (p.y <= me.y))) && p.x - me.x == 0) {  //if there is any occupied point on that line in between me and the hit, that point blocks the hit
                    numHits--;
                    break;
                }
            } else {
                //create a line from me to the hit... if there is any occupied point on that line in between me and the hit, that point blocks the hit
                double deltaY = hit.y - me.y;
                double deltaX = hit.x - me.x;

                double m = deltaY / deltaX; //slope
                double b = me.y - (double)(m*me.x);  //y intercept

                if((double) p.y == ((double)(m * p.x) + b)) {  //if this point is on the same line 
                    if( ((p.x <= hit.x && p.x >= me.x) && (p.y <= hit.y && p.y >= me.y)) ||
                                ((p.x <= hit.x && p.x >= me.x) && (p.y >= hit.y && p.y <= me.y)) || 
                                ((p.x >= hit.x && p.x <= me.x) && (p.y >= hit.y && p.y <= me.y)) ||
                                ((p.x >= hit.x && p.x <= me.x) && (p.y <= hit.y && p.y >= me.y))) {  //if the point is in between me and the hit
                        numHits--;
                        break;
                    }
                }
            }
        }
    }
}

我的代码用于确定reachable 中的任何点在mehits 中的每个点之间是否存在任何点,它会变得非常慢,较大的hitsreachable 得到。例如,如果 hits 的大小为 780,000,reachable 的大小为 1,500,000,则代码需要很长时间才能运行。我想知道如何优化它以更快地运行。我不确定瓶颈问题是出在循环本身还是出在循环内的代码中。非常感谢任何帮助或优化想法。谢谢!

【问题讨论】:

  • 使用输入 780,000 hits 和 1,500,000reachables,这是 1 万亿次潜在迭代。我可以想象这需要很长时间。您是否需要一次性处理这么多数据?有什么方法可以重新设计您的系统以要求/处理更少的输入?也许您可以合并/展平输入中一些更一致的部分(例如线性运动序列)?
  • @VinceEmigh 在大多数情况下,正在处理的数据量将远小于780,0001,500,000。但是,在极少数情况下,代码仍然需要能够处理与这些一样大的数字。
  • 你是什么意思,in between?。如果一个点是原点,另一个是10,10,那么5,5 介于两者之间,3,7 也是如此 或者您的意思是在由点ab 形成的同一个line segment 上?跨度>
  • @WJS 我想说一个点是否在原点和另一个点之间的线上。所以如果原点是(0,0),另一点是(10,10),那么(5,5)在中间,(3,7)不在中间,(11,11)不在中间。我将更新问题以使这一点更清楚。
  • 所以你的意思是line segment

标签: java performance optimization time-complexity nested-loops


【解决方案1】:

将每行ac 投影到x = 0y = 0,以较小的幅度为准(以尽量减少舍入误差)。对这些截取进行排序或散列,保留对每个 c 的引用,确保保留重复项。

然后对于每一行ab,也将其投影到x = 0y = 0,以较小的数量级为准,并在排序或散列集合中搜索匹配的截距。这为您提供了少量候选线段,它们都位于同一条线上,通过abc;您只需检查该点是否位于端点内(a.x &lt; c.x &lt; b.xa.x &gt; c.x &gt; b.x 以及 y)。

小心舍入错误。

【讨论】:

  • 我该怎么做呢?我对您将一条线投影到 x = 0 或 y = 0 的意思有点困惑
【解决方案2】:

您可以优化代码的一种方法是将点移到原点,然后将所有点划分到各个象限,因此现在您只需比较相同象限中的点。

下面我会写一个粗略的算法。

1. Shift the point me to origin

2. Shift all other points, ie hits and reachable to origin ( this can be done by  subtracting X and Y of point me from all other X and Y ) 

3. Now divide hits and reachable to 4 quadrants based on their X and Y components, now you will have 4 arrays for hits and 4 arrays for reachable


4. You can also reduce the number of points in reachable by comparing them against the greatest point in hits like below, do this for all quadrants


    a. find greatest |X| (mode(X)) and greatest |Y| of hits in each quadrant, lets call it me.|x| and me.|y|

     b. for(Point p : reachable) {
              if(p.|x| > me.|x| or p.|y| > me.|y|) then remove p from reachable }
    since these points will be outside of the  line segment formed by me and hits

5. Now you can compare the values, using nested for loops as you did, you will have to use different conditions for each quadrant

不要忘记添加边缘条件并从原点移回

【讨论】:

    【解决方案3】:

    我建议您查看Line2D.Double。它有很多方法,包括确定一个点甚至一条线是否在给定的线段上。

    我还建议您使用映射到memoize 现有点和您可能已经遇到的其他信息。这样你就不会一直重复计算。因为浮点运算可能会导致微小的差异,您可能必须使用一些可接受的error 来确定结果的有效性。

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 2019-04-21
      • 1970-01-01
      • 1970-01-01
      • 2012-01-27
      • 2020-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多