应用程序未按预期工作

时间:2009-09-10 21:25:09

标签: java math

我在下面有一个独立的Java应用程序:

  • 生成随机行
  • 应用于2D网格,其中每个单元格值是沿线的直线到线的距离
  • 查找上升/下降并尝试从网格
  • 计算原始线性方程
  • 将新行应用于另一个网格,并打印出与第一个网格相比最大的差异

我希望两个网格具有相同的值。渐变线可能不同,因为线可以延伸到网格区域之外,但应该相似并且在两种情况下相同。

问题是对数学的理解不足,我的代码中的错误还是对浮点值的误解?

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 :(得分:0)

为什么在这一行的末尾乘以-1?

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

这意味着endY总是负数,除了半径小于0.(cosinus总是返回正值)

这是打算还是我弄错了?

问候

答案 1 :(得分:0)

你可能会误解浮动和/或加倍。这是实现浮点数和双精度的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,可以缓解很多问题,或者使用整数。

答案 2 :(得分:0)

我想我已经修好了你的程序。

a)我拿出整数演员。

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);
    }
}