Приложение не работает должным образом - PullRequest
0 голосов
/ 11 сентября 2009

У меня есть отдельное приложение Java ниже:

  • Генерация случайной строки
  • Применяется к двумерной сетке, где каждое значение ячейки - это расстояние вдоль линии, перпендикулярной линии
  • Находит рост / прогон и пытается вычислить исходное линейное уравнение из сетки
  • Применяет новую строку к другой сетке и выводит наибольшую разницу по сравнению с первой сеткой

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

Так проблема в плохом понимании математики, ошибке в моем коде или неправильном понимании значений с плавающей запятой?

import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.util.Iterator;
import java.util.ArrayList;

public final class TestGradientLine {
    private static int SIZE = 3;

    public TestGradientLine() {
        super();
    }

    //y = mx + b
    //b = y - mx
    //m is rise / run = gradient
    //width and height of bounding box
    //for a box 10x10 then width and height are 9,9
    public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
        if (run == 0 && rise == 0) {
            return new Line2D.Double(x, y, x + width, y + height);
        }

        //calculate hypotenuse
        //check for a vertical line
        if (run == 0) {
            return new Line2D.Double(x, y, x, y + height);
        }
        //check for a horizontal line
        if (rise == 0) {
            return new Line2D.Double(x, y, x + width, y);
        }
        //calculate gradient
        double m = rise / run;
        Point2D start;
        Point2D opposite;
        if (m < 0) {
            //lower left
            start = new Point2D.Double(x, y + height); 
            opposite = new Point2D.Double(x + width, y); 

        } else {
            //upper left 
            start = new Point2D.Double(x, y);
            opposite = new Point2D.Double(x + width, y + height); 
        }
        double b = start.getY() - (m * start.getX());

        //now calculate another point along the slope
        Point2D next = null;
        if (m > 0) {
            next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
        } else {
            if (rise < 0) {
                next = new Point2D.Double(start.getX() + run, start.getY() + rise);
            } else {
                next = new Point2D.Double(start.getX() - run, start.getY() - rise);
            }
        }
        final double actualWidth = width;
        final double actualHeight = height;
        final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
        extendLine(start, next, a);
        Line2D gradientLine = new Line2D.Double(start, next);
        return gradientLine;

    }

    public static void extendLine(Point2D p0, Point2D p1, double toLength) {
        final double oldLength = p0.distance(p1);
        final double lengthFraction =
                oldLength != 0.0 ? toLength / oldLength : 0.0;
        p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
            p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
    }

    public static Line2D generateRandomGradientLine(int width, int height) {
        //so true means lower and false means upper
        final boolean isLower = Math.random() > .5;
        final Point2D start = new Point2D.Float(0, 0);
        if (isLower) {
            //change origin for lower left corner
            start.setLocation(start.getX(), height - 1);
        }
        //radius of our circle
        double radius = Math.sqrt(width * width + height * height);
        //now we want a random theta
        //x = r * cos(theta)
        //y = r * sin(theta)
        double theta = 0.0;
        if (isLower) {
            theta = Math.random() * (Math.PI / 2);
        } else {
            theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
        }

        int endX = (int)Math.round(radius * Math.sin(theta));
        int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
        if (isLower) {
            endY = endY + (height - 1);
        }
        final Point2D end = new Point2D.Float(endX, endY);
        extendLine(start, end, radius);

        return new Line2D.Float(start, end);
    }

    public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
        final Point2D point = line.getP1();
        final Point2D start = line.getP2();
        double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
        double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
        final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a + b);
        final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a + b);
        final Point2D result = new Point2D.Double(x, y);
        return result;
    }

    public static double length(double x0, double y0, double x1, double y1) {
        final double dx = x1 - x0;
        final double dy = y1 - y0;

        return Math.sqrt(dx * dx + dy * dy);
    }

    public static void main(String[] args) {
        final Line2D line = generateRandomGradientLine(SIZE, SIZE);
        System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
        double[][] region = new double[SIZE][SIZE];
        //load up the region with data from our generated line
        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                final Point2D point = new Point2D.Double(x, y);
                final Point2D nearestPoint = getNearestPointOnLine(point, line);
                if (nearestPoint == null) {
                    System.err.println("uh -oh!");
                    return;
                }
                final double distance = length(line.getP1().getX(),
                        line.getP1().getY(), nearestPoint.getX() + 1,
                        nearestPoint.getY() + 1);

                region[x][y] = distance;    
            }
        }
        //now figure out what our line is from the region
        double runTotal = 0;
        double riseTotal = 0;
        double runCount = 0;
        double riseCount = 0;

        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                if (x < SIZE - 1) {
                    runTotal += region[x + 1][y] - region[x][y];
                    runCount++;
                }
                if (y < SIZE - 1) {
                    riseTotal += region[x][y + 1] - region[x][y];
                    riseCount++;
                }
            }
        }

        double run = 0;
        if (runCount > 0) {
            run = runTotal / runCount;
        }
        double rise = 0;
        if (riseCount > 0) {
            rise = riseTotal / riseCount;
        }

        System.out.println("rise is " + rise + " run is " + run);

        Line2D newLine = getGradientLine(run, rise, SIZE - 1, SIZE - 1, 0, 0);
        System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());

        double worst = 0.0;
        int worstX = 0;
        int worstY = 0;
        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                final Point2D point = new Point2D.Double(x, y);
                final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
                if (nearestPoint == null) {
                    System.err.println("uh -oh!");
                    return;
                }
                final double distance = length(line.getP1().getX(),
                        line.getP1().getY(), nearestPoint.getX() + 1,
                        nearestPoint.getY() + 1);
                final double diff = Math.abs(region[x][y] - distance);
                if (diff > worst) {
                    worst = diff;
                    worstX = x;
                    worstY = y;
                }
            }
        }
        System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
    }
}

Ответы [ 3 ]

0 голосов
/ 11 сентября 2009

Вы, вероятно, неправильно поняли float и / или double. Это общая проблема с любым языком, который реализует спецификацию ieee для чисел с плавающей запятой и двойников, как это делают Java, C, C ++ и практически любой другой язык. По существу

double val = 0;  
for(int i=0;i<10;i++) {  
  val+=0.1;  
 System.out.println(val);  

}

Результаты в

0.1  
0.2  
0.30000000000000004  
0.4  
0.5  
0.6  
0.7  
0.7999999999999999  
0.8999999999999999  
0.9999999999999999  

А иногда даже хуже. Либо используйте BigDecimal, который решает большую часть проблемы, либо используйте целые числа.

0 голосов
/ 11 сентября 2009

Мне кажется, я исправил вашу программу.

а) Я вынул целочисленный состав.

b) Я удалил все выдумки «x + 1» и «x - 1», которые вы использовали.

Я думаю, что при работе с числами с плавающей запятой и удваиванием вычитание '1' из конца строки - это нет-нет! Что такое 1 в любом случае? - это можно сделать прямо перед тем, как отобразить его на экране, как только оно станет целым числом. Но не во время расчета! длина строки является нулевой величиной.

Эта версия всегда возвращает около 4E-16.

   import java.awt.geom.Point2D;
    import java.awt.geom.Line2D;
    import java.awt.geom.QuadCurve2D;
    import java.util.Iterator;
    import java.util.ArrayList;

    public final class TestGradientLine {

    private static int SIZE = 3;

    public TestGradientLine() {
        super();
    }

    //y = mx + b
    //b = y - mx
    //m is rise / run = gradient
    //width and height of bounding box
    //for a box 10x10 then width and height are 9,9
    public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
        if (run == 0 && rise == 0) {
     return new Line2D.Double(x, y, x + width, y + height);
        }

        //calculate hypotenuse
        //check for a vertical line
        if (run == 0) {
     return new Line2D.Double(x, y, x, y + height);
        }
        //check for a horizontal line
        if (rise == 0) {
     return new Line2D.Double(x, y, x + width, y);
        }
        //calculate gradient
        double m = rise / run;
        Point2D start;
        Point2D opposite;
        if (m < 0) {
     //lower left
     start = new Point2D.Double(x, y + height); 
     opposite = new Point2D.Double(x + width, y); 

        } else {
     //upper left 
     start = new Point2D.Double(x, y);
     opposite = new Point2D.Double(x + width, y + height); 
        }
        double b = start.getY() - (m * start.getX());

        //now calculate another point along the slope
        Point2D next = null;
        if (m > 0) {
     next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
        } else {
     if (rise < 0) {
  next = new Point2D.Double(start.getX() + run, start.getY() + rise);
     } else {
  next = new Point2D.Double(start.getX() - run, start.getY() - rise);
     }
        }
        final double actualWidth = width;
        final double actualHeight = height;
        final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
        extendLine(start, next, a);
        Line2D gradientLine = new Line2D.Double(start, next);
        return gradientLine;

    }

    public static void extendLine(Point2D p0, Point2D p1, double toLength) {
        final double oldLength = p0.distance(p1);
        final double lengthFraction =
     oldLength != 0.0 ? toLength / oldLength : 0.0;
        p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
         p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
    }

    public static Line2D generateRandomGradientLine(int width, int height) {
        //so true means lower and false means upper
        final boolean isLower = Math.random() > .5;
        final Point2D start = new Point2D.Float(0, 0);
        if (isLower) {
     //change origin for lower left corner
     start.setLocation(start.getX(), height );
        }
        //radius of our circle
        double radius = Math.sqrt(width * width + height * height);
        //now we want a random theta
        //x = r * cos(theta)
        //y = r * sin(theta)
        double theta = 0.0;
        if (isLower) {
     theta = Math.random() * (Math.PI / 2);
        } else {
     theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
        }

        float endX = (float)(radius * Math.sin(theta));
        float endY = (float)(radius * Math.cos(theta)) * -1;
        if (isLower) {
     endY = endY + (height );
        }
        final Point2D end = new Point2D.Float(endX, endY);
        extendLine(start, end, radius);

        return new Line2D.Float(start, end);
    }

    public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
        final Point2D point = line.getP1();
        final Point2D start = line.getP2();
        double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
        double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
        final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a+b);
        final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a+b);
        final Point2D result = new Point2D.Double(x, y);
        return result;
    }

    public static double length(double x0, double y0, double x1, double y1) {
        final double dx = x1 - x0;
        final double dy = y1 - y0;

        return Math.sqrt(dx * dx + dy * dy);
    }

    public static void main(String[] args) {
        final Line2D line = generateRandomGradientLine(SIZE, SIZE);
        System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
        double[][] region = new double[SIZE][SIZE];
        //load up the region with data from our generated line
        for (int x = 0; x < SIZE; x++) {
     for (int y = 0; y < SIZE; y++) {
  final Point2D point = new Point2D.Double(x, y);
  final Point2D nearestPoint = getNearestPointOnLine(point, line);
  if (nearestPoint == null) {
      System.err.println("uh -oh!");
      return;
  }
  final double distance = length(line.getP1().getX(),
            line.getP1().getY(), nearestPoint.getX() ,
            nearestPoint.getY() );

  region[x][y] = distance;        
     }
        }
        //now figure out what our line is from the region
        double runTotal = 0;
        double riseTotal = 0;
        double runCount = 0;
        double riseCount = 0;

        for (int x = 0; x < SIZE; x++) {
     for (int y = 0; y < SIZE; y++) {
  if (x < SIZE - 1) {
      runTotal += region[x + 1][y] - region[x][y];
      runCount++;
  }
  if (y < SIZE - 1) {
      riseTotal += region[x][y + 1] - region[x][y];
      riseCount++;
  }
     }
        }

        double run = 0;
        if (runCount > 0) {
     run = runTotal / runCount;
        }
        double rise = 0;
        if (riseCount > 0) {
     rise = riseTotal / riseCount;
        }

        System.out.println("rise is " + rise + " run is " + run);

        Line2D newLine = getGradientLine(run, rise, SIZE, SIZE , 0, 0);
        System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());

        double worst = 0.0;
        int worstX = 0;
        int worstY = 0;
        for (int x = 0; x < SIZE; x++) {
     for (int y = 0; y < SIZE; y++) {
  final Point2D point = new Point2D.Double(x, y);
  final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
  if (nearestPoint == null) {
      System.err.println("uh -oh!");
      return;
  }
  final double distance = length(line.getP1().getX(),
            line.getP1().getY(), nearestPoint.getX() ,
            nearestPoint.getY() );
  final double diff = Math.abs(region[x][y] - distance);
  if (diff > worst) {
      worst = diff;
      worstX = x;
      worstY = y;
  }
     }
        }
        System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
    }
}
0 голосов
/ 11 сентября 2009

почему вы умножаете на -1 в конце этой строки?

int endY = (int)Math.round(radius * Math.cos(theta)) * -1;

это означает, что endY всегда отрицателен, за исключением того, что радиус меньше 0. (косинус всегда возвращает положительное значение)

это предназначено или я что-то не так делаю?

привет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...