Koch 的雪花实现中的小错误

Posted

技术标签:

【中文标题】Koch 的雪花实现中的小错误【英文标题】:Small bug in Koch's Snowflake Implementation 【发布时间】:2013-10-08 12:16:08 【问题描述】:

所以我正在编写一个递归程序,该程序应该使用 OpenGL 绘制科赫的雪花,除了一个小问题外,我的程序基本上可以运行。递归越深,得到的 2 个特定顶点就越奇怪。图片在底部。

编辑:我并不真正关心 OpenGL 方面,我已经把那部分记下来了。如果您不了解 OpenGL,glVertex 所做的只是在 2 个方法调用中指定的两个顶点之间画一条线。假装它的drawLine(v1,v2)。同样的区别。

我怀疑是我找点的方法有问题,但我找不到任何看起来不正确的东西。

我是按照基本的标准绘图方法,这里是相关的代码片段

(V代表顶点V1是左下角,v2是右下角,v3是上角):

        double dir = Math.PI;
        recurse(V2,V1,n);

        dir=Math.PI/3;
        recurse(V1,V3,n);

        dir= (5./3.)* Math.PI ;
        recurse(V3,V2,n);

递归方法:

public void recurse(Point2D v1, Point2D v2, int n)
    double newLength = v1.distance(v2)/3.;
    if(n == 0)
        gl.glVertex2d(v1.getX(),v1.getY());
        gl.glVertex2d(v2.getX(),v2.getY());

    else

        Point2D p1 = getPointViaRotation(v1, dir, newLength);
        recurse(v1,p1,n-1);
        dir+=(Math.PI/3.);

        Point2D p2 = getPointViaRotation(p1,dir,newLength);
        recurse(p1,p2,n-1);
        dir-=(Math.PI*(2./3.));

        Point2D p3 = getPointViaRotation(p2, dir, newLength);
        recurse(p2,p3,n-1);
        dir+=(Math.PI/3.);

        recurse(p3,v2,n-1);
    


我真的怀疑我的数学是问题所在,但这对我来说是正确的:

public static Point2D getPointViaRotation(Point2D p1, double rotation, double length)
    double xLength = length * Math.cos(rotation);
    double yLength = length * Math.sin(rotation);
    return new Point2D.Double(xLength + p1.getX(), yLength + p1.getY());

N = 0(一切正常):

N = 1(也许有点弯曲,也许)

N = 5 (WAT)

【问题讨论】:

在不知道算法的情况下:这可能是double 精度问题。 【参考方案1】:

我在代码方面看不到任何明显的问题。不过,我确实对发生的事情有一个理论。

图表中的所有点似乎都基于它之前的点的位置。因此,在此过程中发生的任何舍入错误最终都会开始累积,最终以失控和偏离而告终。

我会为初学者做的是在递归之前计算每个段的起点和终点,以限制内部调用的舍入误差的影响。

【讨论】:

嗯,整条曲线的起点和终点都是用户提供的,它们是V1,V2,V3(初始三角形)。事先计算内部点是不可能的,这就是递归所做的一切。 稍微改变我自己的建议并更具体一点:我渲染它的方式是将它分成要绘制的“部分”。初始级别由三个部分组成,如果 n=0,则表示为行。对于 n>0,每个部分将改为执行以下操作:获取起点 A 和终点 B(可能作为参数传递)。计算进路的1/3和2/3点的AB'和AB''。定义A-AB'和AB''-B为两个新块,然后计算“尖峰”的点AB*并将 AB'-AB* 和 AB*-AB'' 定义为新件。递归。这样做可以防止大规模的舍入错误。【参考方案2】:

关于 Koch 的雪花的一件事是,该算法会导致一次舍入问题(它是递归的,所有舍入错误都会累加)。诀窍是,尽可能长时间地保持它。您可以做三件事:

如果你想更详细,唯一的办法就是扩大Double的可能性。每次在屏幕上实际绘制时,您都需要使用自己的坐标范围并将它们转换为屏幕坐标。您自己的坐标应缩放并显示坐标系中的最后一个递归步骤(最后一个三角形),例如100x100。然后在此基础上计算三个新三角形,转换为屏幕坐标并进行绘制。 dir=Math.PI/3; 行除以 3 而不是 (double) 3。在3 之后添加. 确保在任何地方都使用Point2D.Double。你的代码应该这样做,但我会在任何地方明确地写出来。

你赢了比赛,当你仍然有一个漂亮的雪花但得到一个***。

【讨论】:

我不得不用 *** 开这个小玩笑。我是被我的基因强迫的;-) 编译器会将3转换为3.0;无需任何操作。 可能是的。当我看到它们时,这些东西总是让我感到紧张。最重要的一点是第一点。 如果问题太大,我推荐 BigDecimal。然而你坚持建议用户添加小数点,即使我们同意这没有必要。 我通常也对此感到偏执,这就是为什么我在代码的其他地方都这样做了。不管怎样,我现在已经做到了,但没有帮助:(【参考方案3】:

所以,事实证明我是世上最愚蠢的人。

感谢大家的尝试,感谢您的帮助。

此代码用于处理等边三角形,对此非常具体(您可以通过角度来判断)。

我放入一个高度等于底边的三角形(不是等边的)。当我修复输入三角形时,一切正常。

【讨论】:

以上是关于Koch 的雪花实现中的小错误的主要内容,如果未能解决你的问题,请参考以下文章

算法设计中的小错误

存储加载后 extjs 4.0.7 中 Combobox 中的小错误

开发中的小错误,大麻烦

好奇的小错误

CSS网页布局中易犯的10个小错误

一个小错误记录