周其仁:停止改革,我们将面临三大麻烦

抛开立场观点不谈,且看周小平写一句话能犯多少语病

罗马尼亚的声明:小事件隐藏着大趋势——黑暗中的风:坚持做对的事相信未来的结果

布林肯突访乌克兰,为何选择去吃麦当劳?

中国不再是美国第一大进口国,贸易战殃及纺织业? 美国进一步延长352项中国商品的关税豁免期

生成图片,分享到微信朋友圈

自由微信安卓APP发布,立即下载! | 提交文章网址
查看原文

Koch 雪花曲线(分形曲线)

耿祥义 Java教学与小提琴耿祥义 2023-01-21

Koch 雪花曲线(分形曲线)

耿祥义

本帖目的:学习用递归绘制分形,保存绘制的图形到图像文件中(基础知识点见教材11章-输入输出流)。

一、雪花

   小学时,有个可爱的、讲常识的王老师,上课喜欢拿个大水杯,讲课风趣幽默,经常逗的孩子们前仰后合。王老师经常讲水有三态:固态,气态和液态,而且无色无味。突然有一天,莫名其妙的问了王老师一句:雪花为啥是白色的呢?王老师拿着水杯,大口喝了一口水,然后自言自语:“是啊,为啥是白色的呢?......”。后来读大学时,遇到一个学物理的校友,他说,"这个问题简单,我来告诉你...(省略)"。也许您现在可以百度一下,就立刻能知道答案,但我们小时候,没有这条件,就必须让子弹飞一会,留下思考的时间。

二、分形

  1967年,数学家曼德布劳特(B.B.Mandelbrot)在科学杂志上发表了一篇严谨的学术文章,“英国的海岸线有多长?”,常人会误解为是个地理常识标题。但实际上,这篇论文标志分形几何的诞生(分形几何的概念是Mandelbrot首先提出的)。可以想象,由于海岸线曲曲折折,非常不规则,人们很难准确测量出它的长度。假设我们用一把固定长度的直尺,比如一米直尺,那么就需要在一米的整数倍处不断地拐弯抹角的来测量海岸线,因此,测得的海岸线的长度肯定是不精确的。 曼德布劳特认为,既使用更短的尺子来量,同样也无法准确测量。而且曼德布劳发现,尺子越短,测量出的海岸线越长,比如,用1纳米长的直尺子(假设有这样的尺子)测量的海岸线的长度,就比用毫米直尺子测量的长度要长很多。

三、Koch雪花曲线

     科克曲线(Koch curve)是一种典型的分形曲线。它是科克(Koch,H.von)于1904年构造出来的,当时是为了构造处处连续、处处不可导(处处不可微,即导数处处不存在,曲线处处是棱角)的曲线。

   为了编程方便,我们可以如下叙述简化的Koch曲线的构造过程,用 n表示构造Koch曲线的第n步骤,所构造出的图形简称 n-Koch

  (0)  0-Koch

      0-Koch是一个线段,比如长度为side , 角度为angle,起点是(x,y)终点是(endX,endY)


  (1)1-Koch

      将0-Koch缩小3倍,按照一定角度放置在四处(细节代码)


  ...

  (n) n-Koch

      将n-1-Koch段缩小3倍,按照一定角度放置在四处(细节见代码)


       如果 0-Koch的长度是1,那么1-Koch的长度就是4/3n-Koch的长度就是(4/3 )n   。也就是说,尺子越短,n-Koch的长度就越大,Koch曲线就是n-Koch的极限,因此Koch曲线的(欧几里德)长度是无穷大

     在欧几里德1维空间看,Koch曲线是有界的,但长度是无限的,在欧几里德2维空间看,Koch曲线是有界的,但面积又是0。Mandelbrot跳出欧几里德的圈圈,提出了新的维数思想,即分维数,按照Mandelbrot的理论,Koch曲线的Hausdoff -维数是log4/log3,大于1小于2(欧几里德维数属于特殊的Hausdoff-维数)。在该Hausdoff -维数下看,Koch曲线的Hausdoff -测度(非欧几里德测度)是一个有限数(但这个有限数到底是多少,在我学习分形时还没有解决,20多年过去了,我也不清楚目前是否已经解决)。

三、代码与运行效果

     原始的n-Koch曲线就是把前面说的简化的n-Koch曲线,在三个不同角度画三次而已,见代码(您可以练习在5个角度(每次逆时针(或顺时针)转72度)画5次)。

 


App.java

import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class App {
   public static void main(String args[]) {
       File file = new File("koch.bmp"); // 目的地
       try{  
          DrawKoch draw= new  DrawKoch();
          double side = 300; 
          draw.x =draw.width/2-side/2;
          draw.y =draw.height/2;
          BufferedImage image = null;
          for(int i=1;i<=3;i++) { //把简易的3-Koch曲线画三次
            image = draw.drawKoch(side,3);
            draw.image = image;
            draw.clockwise(120);  //angle角度顺时针转120度
          }
          side = 100; 
          draw.x =draw.width/4-side/2;
          draw.y =draw.height/4;
          for(int i=1;i<=3;i++) { //把简易的4-Koch曲线画3次
            image = draw.drawKoch(side,4);
            draw.image = image;
            draw.clockwise(120);  //angle角度顺时针转120度
          }
          side = 180; 
          draw.x =draw.width/2+side;
          draw.y =draw.height/4;
          for(int i=1;i<=3;i++) { //把简易的2-Koch曲线画3次
            image = draw.drawKoch(side,2);
            draw.image = image;
            draw.clockwise(120);  //angle角度顺时针转72度
          }
          ImageIO.write(image,"bmp",file);
       }
       catch(IOException e) { }
   }
}

DrawKoch.java

import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.geom.*;
public class DrawKoch  {//负责绘制雪花曲线
    public double angle = 0;   //线段的角度
    public double x,y;         //线段的起点
    public double endX,endY;   //线段的终点
    public BufferedImage image;
    Graphics g;
    Graphics2D g_2d;
    public int width=1000, height=860;
    public DrawKoch() {
        image = 
        new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        g = image.getGraphics();
        g.setColor(Color.white);//g默认用白色绘制图形
        g_2d =(Graphics2D)g;
        g_2d.setColor(Color.pink);
        g_2d.setFont(new Font("",Font.BOLD,32));
        g_2d.drawString
        ("耿祥义Java老师绘制2021-12-02",width/4,height-90);
        g_2d.setColor(Color.white);//用白色绘制图形
    }
    public BufferedImage drawKoch(double side,int n){
       if(n==0) {
            //绘制从(x,y)到 (endX,endY)的长度为side直线段
            double α= angle*Math.PI/180 ; //角度angle用弧度α表示
            endX = Math.cos(α) *side+x;
            endY = Math.sin(α) *side+y;   
            Line2D line= new Line2D.Double(x,y,endX,endY);
            g_2d.draw(line);
            x = endX ;  //改变线段的起点,为画下条线段准备好起点
            y=  endY ;
            
        }
        else { //一共画4条直线
            drawKoch(side/3.0,n-1);
            antiClockwise(60);
            drawKoch(side/3.0,n-1);
            clockwise(120);
            drawKoch(side/3.0,n-1);
            antiClockwise(60);
            drawKoch(side/3.0,n-1);
        }
        return image;
   }
   public void antiClockwise(double α) { //逆时针旋转α度
       angle = angle-α; 
   }
   public void clockwise(double α) { //顺时针旋转α度
       angle = angle+α; 
   }
}  

推荐阅读

《飘雪》-初学多线程

一个简单问题与多重递归


文章有问题?点此查看未经处理的缓存