Нахождение внутренних точек выпуклой оболочки без предварительного вычисления оболочки

Я пытаюсь вычислить внутренние точки выпуклой оболочки, используя четыре вложенных цикла. Тем не менее, это дает мне правильные координаты, но они дублируются так много раз. Я не уверен, что я делаю неправильно.

Ниже мой метод

public final List<Point> interiorPoints(List<Point> TestPoints){
        int n = points.size();

        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(j != i){
                    for(int k = 0; k < n; k++){
                        if(k != j && j != i && i != k){
                            for(int L = 0; L < n; L++){
                                if(L != k && k != j && j != i && i != k && L != i && L != j){
                                    if(pointIsInsideTriangle(points.get(i), points.get(j), points.get(k), points.get(L)) == true){
                                        InsidePoints.add(points.get(L));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return InsidePoints;
    }

Метод pointIsInside возвращает true, если точка L лежит внутри треугольника i,j,k

Когда я проверяю это, используя набор точек ниже:

        TestPoints.add(new Point(300,200));
        TestPoints.add(new Point(600,500));
        TestPoints.add(new Point(100,100));
        TestPoints.add(new Point(200,200));
        TestPoints.add(new Point(100,500));
        TestPoints.add(new Point(600,100));

я получил

(200.0, 200.0)
(200.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(200.0, 200.0)
(200.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(300.0, 200.0)
(200.0, 200.0)
(300.0, 200.0)

Это должно быть только (200.0, 200.0) и (300.0, 200.0), но я не уверен, как решить эту проблему.

Это псевдокод, из которого я реализовал этот метод.

Algorithm: INTERIOR POINTS
for each i do
      for each j = i do
           for each k = j = i do
       for each L = k = j = i do 
        if pL in triangle(pi, pj, pk)
            then pL is non extreme

Вот мой класс очков

public class Point 
{
    private final double x, y;

    public Point(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public double getX() 
    {
        return x;
    }

    public double getY()
    {
        return y;
    }

    public void setX(double x)
    {
        return this.x;
    }

    public void setY(double y)
    {
        return this.y;
    }

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (!(obj instanceof Point)) {
        return false;
    }
    Point other = (Point) obj;
    EqualsBuilder equalsBuilder = new EqualsBuilder();
    equalsBuilder.append(x, other.x);
    equalsBuilder.append(y, other.y);
    return equalsBuilder.isEquals();
}

@Override
public int hashCode() {
    HashCodeBuilder hashCodeBuilder = new HashCodeBuilder();
    hashCodeBuilder.append(x);
    hashCodeBuilder.append(y);
    return hashCodeBuilder.toHashCode();
}
}

Ниже моя точка находится внутри класса

public boolean pointIsInsideTriangle(Point P, Point Q, Point r, Point t) {

        final double sum;
        //Area of triangle PQr
        double Area_PQr = AreaOfTriangle(P, Q, r);

        // Area of triangle PQr
        double Area_tQr = AreaOfTriangle(t, Q, r);

        // Area of triangle PQr
        double Area_Ptr = AreaOfTriangle(P, t, r);

        // Area of triangle PQr
        double Area_PQt = AreaOfTriangle(P, Q, t);

        // sum of Area_tQr, Area_Ptr and Area_PQt
        sum = Area_tQr + Area_Ptr + Area_PQt;

        if (Area_PQr == sum) {
            System.out.println("Point t Lies inside the triangle");
            return true;
        }

        System.out.println("Point t does not Lie inside the triangle");
        return false;
    }

Спасибо за вашу помощь.


person Lycon    schedule 21.03.2015    source источник
comment
Thanks for our help. Вероятно, это не то, что вы думаете.   -  person J-Dizzle    schedule 21.03.2015
comment
Если бы вы сделали InsidePoints набором вместо списка, вы бы вернули только один экземпляр. Если это не поможет, не могли бы вы опубликовать класс Point и функцию pointIsInsideTriangle.   -  person Gregory Basior    schedule 21.03.2015
comment
Я добавил класс Point и функцию pointIsInsideTriangle.   -  person Lycon    schedule 21.03.2015


Ответы (1)


В вашем примере точка (200, 200) находится внутри трех треугольников, определяемых точками:

[(100, 100), (100, 500), (300, 200)]
[(100, 100), (100, 500), (600, 100)]
[(100, 100), (100, 500), (600, 500)]

Обратите внимание, что любая перестановка точек вышеупомянутых треугольников будет представлять один и тот же треугольник. Это означает, что (200, 200) будет добавляться в ваш список каждый раз, когда L == 3 и значения i, j и k являются некоторой перестановкой:

[2, 4, 0]
[2, 4, 5]
[2, 4, 1]

Количество перестановок для n элементов равно n!, поэтому у нас будет 6 + 6 + 6 = 18 случаев, когда (200, 200) будет вставлено в список InsidePoints. Если вы посчитаете это, вы увидите, что 18 – это точное количество раз, когда (200, 200) появляется в вашем выводе. То же самое относится и к (300, 200).

Если вам нужно, чтобы каждая точка появлялась в результате только один раз, вы можете легко добиться этого, сделав InsidePoints Set вместо List:

Set<Point> InsidePoints = new HashSet<>();

Конечно, вам также потребуется реализовать equals() и hashCode() для класса Point. Тем не менее, вы все равно будете делать много бесполезных вычислений.

Чтобы сделать код более эффективным, в дополнение к преобразованию InsidePoints в Set вы можете проверить, находится ли точка внутри каждого треугольника только один раз. Это означает, что j и k должны начинаться со значения, на единицу превышающего предыдущий индекс, например:

for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
        for (int k = j + 1; k < n; k++) {
            for (int L = 0; L < n; L++) {
                if (L != i && L != j && L != k) {
                    if (pointIsInsideTriangle(
                            points.get(i),
                            points.get(j),
                            points.get(k),
                            points.get(L))) {
                        InsidePoints.add(points.get(L));
                    }
                }
            }
        }
    }
}

Чтобы убедиться, что это работает, вы можете просто вывести значения i, j и k для каждой итерации и убедиться, что ни одна строка не является перестановкой другой.

person Anderson Vieira    schedule 21.03.2015
comment
Как бы я реализовал набор вместо списка - person Lycon; 21.03.2015
comment
Обновлен ответ, показывающий, как это сделать. Не забудьте переопределить Point equals() и hashCode(), если вы еще этого не сделали. - person Anderson Vieira; 21.03.2015
comment
Мне удалось решить проблему без использования набора. Я в основном проверил, содержит ли InsidePoints points.get(L), и если да, то продолжаю. С другой стороны, если это не так, добавьте points.get(L) в InsidePoints. Я не знаю, почему я не мог подумать, если это. Но спасибо @Anderson Vieira - person Lycon; 21.03.2015
comment
Набор, вероятно, будет быстрее для большого количества очков, но ваше решение также является допустимым. Рад помочь! - person Anderson Vieira; 21.03.2015
comment
Я не мог использовать набор, так как я использую списки в своем графическом интерфейсе, и я не знаю, как манипулировать значениями в наборе. но спасибо за вашу помощь, это определенно помогло. - person Lycon; 21.03.2015