获取矩形的交点Not O(n²)和OutOfMemory

时间:2016-04-23 09:50:45

标签: java geometry

我正在尝试获得两个矩形的交点。我有一个方法可行,但当我用40000矩形的算法测试它时,我得到一个OutOfMemory错误。我检查交叉点的次数完全是O(n²),但是它花费的时间不是。 我认为内存不足只是因为有太多的对象,但是它花费的时间不是O(n²)(通过加倍测试测试)对我没有意义,我无法弄清楚为什么它正在这样做。

这是我获取交集的代码

 public void getIntersections(Rectangle r, Collection<double[]> c) {

    x1 = Math.max(this.getLowerLeftX(), r.getLowerLeftX());
    y1 = Math.max(this.getLowerLeftY(), r.getLowerLeftY());

    x2 = Math.min(this.getUpperRightX(), r.getUpperRightX());
    y2 = Math.min(this.getUpperRightY(), r.getUpperRightY());


    if(this.contains(x1,y1) && r.contains(x1,y1)) {
        inter[0] = x1;
        inter[1] = y1;
        c.add(inter.clone());
    }

    if(this.contains(x1,y2) && r.contains(x1,y2)){
        inter[0] = x1;
        inter[1] = y2;
        c.add(inter.clone());
    }

    if(this.contains(x2,y1) && r.contains(x2,y1)){
        inter[0] = x2;
        inter[1] = y1;
        c.add(inter.clone());
    }

    if(this.contains(x2,y2) && r.contains(x2,y2)){
        inter[0] = x2;
        inter[1] = y2;
        c.add(inter.clone());
    }
}

我试图将此作为内存和CPU高效,但它仍然无法正常工作。 非常感谢任何帮助。

编辑: 调用此函数的算法:

public void execute() {
    List<Rectangle> rectangles = this.getRectangles();
    Queue<Rectangle> q = new LinkedList<Rectangle>();
    q.addAll(rectangles);
    System.out.println(q.size());
    while(!q.isEmpty()){
        Rectangle check_rect = q.poll();
        for (Rectangle rect: q) {
            check_rect.getIntersections(rect, this.getIntersections());
        }
    }
}

助手功能:

public boolean contains(double x, double y){
    return ((x == this.getLowerLeftX() || x == this.getUpperRightX()) ||
            (y == this.getLowerLeftY() || y == this.getUpperRightY())) &&
            x >= this.getLowerLeftX() && x <= this.getUpperRightX() &&
            y >= this.getLowerLeftY() && y <= this.getUpperRightY();
}

对于我使用的十字路口集合:

this.intersections = new ArrayDeque<>();

outOfMemory Exception总是在尝试放大ArrayDeque&lt;&gt;()时发生,它只存储double [2]中的交叉点。所以40000矩形之间似乎有太多的交叉点。 另一个问题似乎是内存耗尽之前的迭代,它真的变慢了,这是因为交换还是其他内存管理?

1 个答案:

答案 0 :(得分:-1)

好的,这是:你的算法或多或少是二次的。如果你在每次运行中进行多次运行的次数与之前的运行次数相同,并且计算后续运行对的持续时间的商数,你会发现它或多或少地落在3.5到4.5之间(丢弃运行的元素很少,你需要几万才能让它足够稳定。)

问题是20K很好,但乘以2得到40K就会有所改变。

更改的是JVM将所有结果保存在其堆中的能力。正如user3707125所说。我用51200个矩形运行你的算法。

您可以在此处获得结果:CPU and heap usage in a failed run

堆已满后,垃圾收集器开始疯狂尝试释放一些内存,但没有任何东西可以释放 - 所有对象都可以访问。过了一会儿,你得到一个OutOfMemoryException,上面写着“超出GC开销限制”的信息。这意味着GC使用了太多时间但回收的内存太少(我认为默认值是CPU时间的98%和堆的2%)。

您可以在此处查看堆的结构:heap dump just before the failure

正如您所看到的,包含坐标的double []对象占据了堆的大部分,然后是Object [] - 这将是ArrayDeque的内部数组。

你能做什么?当你运行应用程序时,你必须增加JVM的堆大小,就像user3707125所说的那样。你通过传递,例如-Xmx8g标记为VM选项。 8g这里意味着8千兆字节。

您可以在此处看到使用该选项的游戏:CPU and heap usage of a successful run

我在成功完成该过程后接受了它。

您可以做的另一件事是考虑如何减少内存占用。也许使用花车而不是双打?您可以为动态数组doubles编写自定义解决方案(但不是Doubles!使用此处的基元)并保持平面结构,即不是将每个点都作为两个双精度的单独数组,将它们全部并排放在动态双阵列中(两个点将占用4个槽)。这将消除对一个指针层的需要,每个指针需要8个字节(假设为64位架构)和数组长度(4个字节),这会增加大量内存。尽管如此,无论你如何调整算法的空间使用,增加矩形的数量都会非常快地占用内存。 Meier说40000矩形并不多,但我认为你的测试数据有很多交叉点 - 这意味着你需要存储的数据量是O(n²),而不仅仅是计算它的时间。