算法可視化

  • Java GUI可視化

運動的小球前端

小球實體類算法

@AllArgsConstructor
public class Circle {
    @Getter
    @Setter
    private int x;  //圓心橫座標
    @Getter
    @Setter
    private int y;  //圓心縱座標
    @Getter
    private int r;  //半徑
    @Getter
    @Setter
    private int vx; //橫座標運動速度
    @Getter
    @Setter
    private int vy; //縱座標運動速度
    @Getter
    @Setter
    private boolean isFilled; //是不是一個實心圓

    /**
     * 小球的移動
     * @param minx
     * @param miny
     * @param maxx
     * @param maxy
     */
    public void move(int minx,int miny,int maxx,int maxy) {
        x += vx;
        y += vy;
        checkCollision(minx,miny,maxx,maxy);
    }

    /**
     * 小球的邊緣碰撞
     * @param minx
     * @param miny
     * @param maxx
     * @param maxy
     */
    private void checkCollision(int minx,int miny,int maxx,int maxy) {
        if (x - r < minx) {
            x = r;
            vx = -vx;
        }
        if (x + r >= maxx) {
            x = maxx - r;
            vx = -vx;
        }
        if (y - r < miny) {
            y = r;
            vy = -vy;
        }
        if (y + r >= maxy) {
            y = maxy - r;
            vy = -vy;
        }
    }

    /**
     * 點是否在圓中
     * @param p
     * @return
     */
    public boolean contain(Point p) {
        return (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y) <= r * r;
    }
}

Java窗體類canvas

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            AlgoVisHelper.setStrokeWidth(g2d,1);
            AlgoVisHelper.setColor(g2d,Color.RED);
            for (Circle circle : circles) {
                if (!circle.isFilled()) {
                    AlgoVisHelper.strokeCircle(g2d,circle.getX(),circle.getY(),circle.getR());
                }else {
                    AlgoVisHelper.fillCircle(g2d,circle.getX(),circle.getY(),circle.getR());
                }
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    private Circle[] circles;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(Circle[] circles) {
        this.circles = circles;
        this.repaint();
    }
}

繪製工具類數組

public class AlgoVisHelper {
    private AlgoVisHelper() {
    }

    public static void setStrokeWidth(Graphics2D g2d,int w) {
        int strokeWidth = w;
        g2d.setStroke(new BasicStroke(strokeWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
    }

    public static void setColor(Graphics2D g2d,Color color) {
        g2d.setColor(color);
    }

    public static void strokeCircle(Graphics2D g2d,int x,int y,int r) {
        Ellipse2D circle = new Ellipse2D.Double(x - r,y - r,2 * r,2 * r);
        g2d.draw(circle);
    }

    public static void fillCircle(Graphics2D g2d,int x,int y,int r) {
        Ellipse2D circle = new Ellipse2D.Double(x - r,y - r,2 * r,2 * r);
        g2d.fill(circle);
    }

    public static void pause(int t) {
        try {
            Thread.sleep(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

可視化顯示器類緩存

public class AlgoVisualizer {
    private class AlgoKeyListener extends KeyAdapter {
        @Override
        public void keyReleased(KeyEvent e) {
            if (e.getKeyChar() == ' ') {
                isAnimated = !isAnimated;
            }
        }
    }

    private class AlgoMouseListener extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            e.translatePoint(0,-(frame.getBounds().height - frame.getCanvasHeight()));
            Stream.of(circles).filter(circle -> circle.contain(e.getPoint()))
                    .forEach(circle -> circle.setFilled(!circle.isFilled()));
        }
    }

    private Circle[] circles;
    private AlgoFrame frame;
    private boolean isAnimated = true;

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        circles = new Circle[N];
        int R = 50;
        for (int i = 0;i < N;i++) {
            int x = (int) (Math.random() * (sceneWidth - 2 * R)) + R;
            int y = (int) (Math.random() * (sceneHeight - 2 * R)) + R;
            int vx = (int) (Math.random() * 11) - 5;
            int vy = (int) (Math.random() * 11) - 5;
            circles[i] = new Circle(x,y,R,vx,vy,false);
        }
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            frame.addKeyListener(new AlgoKeyListener());
            frame.addMouseListener(new AlgoMouseListener());
            new Thread(() -> {
                run();
            }).start();
        });
    }

    private void run() {
        while (true) {
            frame.render(circles);
            AlgoVisHelper.pause(20);
            if (isAnimated) {
                Stream.of(circles)
                        .forEach(circle ->
                                circle.move(0, 0, frame.getCanvasWidth(),
                                        frame.getCanvasHeight()));
            }
        }
    }
}

main方法dom

public class FrameMain {
    public static void main(String[] args) {
        int sceneWidth = 800;
        int sceneHeight = 800;

        int N = 10;

        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight,N);
    }
}

顯示效果ide

這裏面有兩個交互事件,一個是點擊空格暫停,一個是鼠標點到圓的內部的時候變換顏色。工具

  • 繪製模版

根據以上的圓球的代碼,咱們將其抽象成一個之後用於填充各類算法的繪製模版,根據MVC的原理優化

顯示層動畫

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private Object data;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(Object data) {
        this.data = data;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    // TODO: 根據狀況決定是否實現鍵盤鼠標交互事件監聽器類
    private class AlgoKeyListener extends KeyAdapter {
    }

    private class AlgoMouseListener extends MouseAdapter {
    }
    //建立本身的數據
    private Object data; //數據
    private AlgoFrame frame; //視圖

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        //初始化數據
        // TODO: 初始化數據

        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            //根據狀況決定是否加入鍵盤鼠標監聽器
            frame.addKeyListener(new AlgoKeyListener());
            frame.addMouseListener(new AlgoMouseListener());
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        // TODO: 編寫本身的動畫邏輯
    }
}

顯示工具類

public class AlgoVisHelper {
    private AlgoVisHelper() {
    }

    public static final Color Red = new Color(0xF44336);
    public static final Color Pink = new Color(0xE91E63);
    public static final Color Purple = new Color(0x9C27B0);
    public static final Color DeepPurple = new Color(0x673AB7);
    public static final Color Indigo = new Color(0x3F51B5);
    public static final Color Blue = new Color(0x2196F3);
    public static final Color LightBlue = new Color(0x03A9F4);
    public static final Color Cyan = new Color(0x00BCD4);
    public static final Color Teal = new Color(0x009688);
    public static final Color Green = new Color(0x4CAF50);
    public static final Color LightGreen = new Color(0x8BC34A);
    public static final Color Lime = new Color(0xCDDC39);
    public static final Color Yellow = new Color(0xFFEB3B);
    public static final Color Amber = new Color(0xFFC107);
    public static final Color Orange = new Color(0xFF9800);
    public static final Color DeepOrange = new Color(0xFF5722);
    public static final Color Brown = new Color(0x795548);
    public static final Color Grey = new Color(0x9E9E9E);
    public static final Color BlueGrey = new Color(0x607D8B);
    public static final Color Black = new Color(0x000000);
    public static final Color Whitr = new Color(0xFFFFFF);

    /**
     * 設置線條寬度
     * @param g2d
     * @param w
     */
    public static void setStrokeWidth(Graphics2D g2d,int w) {
        int strokeWidth = w;
        g2d.setStroke(new BasicStroke(strokeWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
    }

    /**
     * 設置顏色
     * @param g2d
     * @param color
     */
    public static void setColor(Graphics2D g2d,Color color) {
        g2d.setColor(color);
    }

    /**
     * 繪製空心圓形
     * @param g2d
     * @param x
     * @param y
     * @param r
     */
    public static void strokeCircle(Graphics2D g2d,int x,int y,int r) {
        Ellipse2D circle = new Ellipse2D.Double(x - r,y - r,2 * r,2 * r);
        g2d.draw(circle);
    }

    /**
     * 繪製實心圓形
     * @param g2d
     * @param x
     * @param y
     * @param r
     */
    public static void fillCircle(Graphics2D g2d,int x,int y,int r) {
        Ellipse2D circle = new Ellipse2D.Double(x - r,y - r,2 * r,2 * r);
        g2d.fill(circle);
    }

    /**
     * 繪製空心矩形
     * @param g2d
     * @param x
     * @param y
     * @param w
     * @param h
     */
    public static void strokeRectangle(Graphics2D g2d,int x,int y,int w,int h) {
        Rectangle2D rectangle = new Rectangle2D.Double(x,y,w,h);
        g2d.draw(rectangle);
    }

    /**
     * 繪製實心矩形
     * @param g2d
     * @param x
     * @param y
     * @param w
     * @param h
     */
    public static void fillRectangel(Graphics2D g2d,int x,int y,int w,int h) {
        Rectangle2D rectangle = new Rectangle2D.Double(x,y,w,h);
        g2d.fill(rectangle);
    }

    public static void pause(int t) {
        try {
            Thread.sleep(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 導入圖片
     * @param g2d
     * @param x
     * @param y
     * @param imageURL
     */
    public static void putImage(Graphics2D g2d,int x,int y,String imageURL) {
        ImageIcon icon = new ImageIcon(imageURL);
        Image image = icon.getImage();
        g2d.drawImage(image,x,y,null);
    }

    /**
     * 繪製文字
     * @param g2d
     * @param text
     * @param centerx
     * @param centery
     */
    public static void drawText(Graphics2D g2d,String text,int centerx,int centery) {
        if (text == null) {
            throw new IllegalArgumentException("文字爲空");
        }
        FontMetrics metrics = g2d.getFontMetrics();
        int w = metrics.stringWidth(text);
        int h = metrics.getDescent();
        g2d.drawString(text,centerx - w / 2,centery + h);
    }
}
  • 一個有意思的分錢問題

房間裏有100我的,每一個人都有100元錢,他們在玩一個遊戲。每輪遊戲中,每一個人都要拿出1元錢隨機給另外一我的,最後這100我的的財富分佈是怎樣的?

經過以前的模版進行修改。

顯示層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            AlgoVisHelper.setColor(g2d,AlgoVisHelper.Blue);
            int w = canvasWidth / money.length;
            for (int i = 0;i < money.length;i++) {
                AlgoVisHelper.fillRectangel(g2d,i * w + 1,canvasHeight - money[i],
                                            w - 1,money[i]);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private int[] money;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(int[] money) {
        this.money = money;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    //建立本身的數據
    private int[] money; //數據
    private AlgoFrame frame; //視圖
    private static int DELAY = 10;

    public AlgoVisualizer(int sceneWidth,int sceneHeight) {
        //初始化數據
        // TODO: 初始化數據
        money = new int[100];
        for (int i = 0;i < money.length;i++) {
            money[i] = 100;
        }
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        // TODO: 編寫本身的動畫邏輯
        while (true) {
            frame.render(money);
            AlgoVisHelper.pause(DELAY);
            for (int i = 0;i < money.length;i++) {
                if (money[i] > 0) {
                    int j = (int) (Math.random() * money.length);
                    money[i] -= 1;
                    money[j] += 1;
                }
            }
        }
    }
}

main方法

public class MoneyMain {
    public static void main(String[] args) {
        int sceneWidth = 1000;
        int sceneHeight = 800;

        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight);
    }
}

圖像顯示

加快該進程,並進行排序

public class AlgoVisualizer {
    //建立本身的數據
    private int[] money; //數據
    private AlgoFrame frame; //視圖
    private static int DELAY = 10;

    public AlgoVisualizer(int sceneWidth,int sceneHeight) {
        //初始化數據
        // TODO: 初始化數據
        money = new int[100];
        for (int i = 0;i < money.length;i++) {
            money[i] = 100;
        }
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        // TODO: 編寫本身的動畫邏輯
        while (true) {
            Arrays.sort(money);
            frame.render(money);
            AlgoVisHelper.pause(DELAY);
            for (int k = 0;k < 50;k++) {
                for (int i = 0; i < money.length; i++) {
                    if (money[i] > 0) {
                        int j = (int) (Math.random() * money.length);
                        money[i] -= 1;
                        money[j] += 1;
                    }
                }
            }
        }
    }
}

圖像顯示

這樣咱們就能夠看到,通過一段時間後,整個屋子的人的財富分配狀況。

如今咱們把它調整爲容許爲負值

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data

            int w = canvasWidth / money.length;
            for (int i = 0;i < money.length;i++) {
                if (money[i] > 0) {
                    AlgoVisHelper.setColor(g2d, AlgoVisHelper.Blue);
                    AlgoVisHelper.fillRectangel(g2d, i * w + 1, canvasHeight / 2 - money[i],
                            w - 1, money[i]);
                }else if (money[i] < 0) {
                    AlgoVisHelper.setColor(g2d, AlgoVisHelper.Red);
                    AlgoVisHelper.fillRectangel(g2d, i * w + 1, canvasHeight / 2,
                            w - 1, -money[i]);
                }
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    
public class AlgoVisualizer {
    //建立本身的數據
    private int[] money; //數據
    private AlgoFrame frame; //視圖
    private static int DELAY = 10;

    public AlgoVisualizer(int sceneWidth,int sceneHeight) {
        //初始化數據
        // TODO: 初始化數據
        money = new int[100];
        for (int i = 0;i < money.length;i++) {
            money[i] = 100;
        }
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        // TODO: 編寫本身的動畫邏輯
        while (true) {
            Arrays.sort(money);
            frame.render(money);
            AlgoVisHelper.pause(DELAY);
            for (int k = 0;k < 50;k++) {
                for (int i = 0; i < money.length; i++) {
//                    if (money[i] > 0) {
                        int j = (int) (Math.random() * money.length);
                        money[i] -= 1;
                        money[j] += 1;
//                    }
                }
            }
        }
    }
}

圖像顯示

  • 蒙特卡洛方法

蒙特卡洛方法是一種統計學的方法;是一種模擬。

蒙特卡洛模擬是二戰期間,爲解決原子彈研製工做中,裂變物質的中子隨機擴散問題,美國數學家馮諾伊曼和烏拉姆等提出的一種統計方法,代號:蒙特卡洛。

經過大量的隨機樣本,去了解一個系統,進而獲得所要計算的值。

求PI的值

這裏咱們依然修改以前的模版

圓的實體類

@AllArgsConstructor
public class Circle {
    @Getter
    private int x;  //圓心橫座標
    @Getter
    private int y;  //圓心縱座標
    @Getter
    private int r;  //半徑

    /**
     * 點是否在圓中
     * @param p
     * @return
     */
    public boolean contain(Point p) {
        return Math.pow(p.getX() - x,2) + Math.pow(p.getY() - y,2) <= r * r;
    }
}

視圖層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            AlgoVisHelper.setStrokeWidth(g2d,3);
            AlgoVisHelper.setColor(g2d,AlgoVisHelper.Blue);
            AlgoVisHelper.strokeCircle(g2d,circle.getX(),circle.getY(),circle.getR());
            for (int i = 0;i < points.size();i++) {
                Point p = points.get(i);
                if (circle.contain(p)) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Red);
                }else {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Green);
                }
                AlgoVisHelper.fillCircle(g2d,p.x,p.y,3);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private Circle circle;
    private List<Point> points;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(Circle circle,List<Point> points) {
        this.circle = circle;
        this.points = points;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    private static int DELAY = 10;
    //建立本身的數據
    private Circle circle; //數據
    private List<Point> points;
    private int insideCircle = 0;
    private AlgoFrame frame; //視圖
    private int N;

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        if (sceneWidth != sceneHeight) {
            throw new IllegalArgumentException("必須建立爲一個正方形窗口");
        }
        //初始化數據
        // TODO: 初始化數據
        this.N = N;
        circle = new Circle(sceneWidth / 2,sceneHeight / 2,sceneWidth / 2);
        points = new LinkedList<>();
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        // TODO: 編寫本身的動畫邏輯
        for (int i = 0;i < N;i++) {
            frame.render(circle,points);
            AlgoVisHelper.pause(DELAY);
            int circleArea = insideCircle;
            int squareArea = points.size();
            double piEstimation = 4 * (double) circleArea / squareArea;
            System.out.println(piEstimation);
            int x = (int) (Math.random() * frame.getCanvasWidth());
            int y = (int) (Math.random() * frame.getCanvasHeight());
            Point p = new Point(x,y);
            points.add(p);
            if (circle.contain(p)) {
                insideCircle++;
            }
        }
    }
}

main方法

public class PIMain {
    public static void main(String[] args) {
        int sceneWidth = 800;
        int sceneHeight = 800;
        int N = 10000;

        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight,N);
    }
}

顯示圖

圓周率打印結果

...............

3.1404947656197906
3.140523420570095
3.1405520736098147
3.1404473780711406
3.140342689512634
3.140238007933598

創建數據層,來分離控制層的數據運算。

public class MonteCarloPiData {
    @Getter
    private Circle circle;
    private List<Point> points;
    private int insideCircle = 0;

    public MonteCarloPiData(Circle circle) {
        this.circle = circle;
        points = new LinkedList<>();
    }

    public Point getPoint(int i) {
        if (i < 0 || i >= points.size()) {
            throw new IllegalArgumentException("超出點列表的範圍");
        }
        return points.get(i);
    }

    public int getPointsNumber() {
        return points.size();
    }

    public void addPoint(Point p) {
        points.add(p);
        if (circle.contain(p)) {
            insideCircle++;
        }
    }

    public double estimatePi() {
        if (points.size() == 0) {
            return 0.0;
        }
        int circleArea = insideCircle;
        int squareArea = points.size();
        return (double) circleArea * 4 / squareArea;
    }
}

視圖層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            Circle circle = data.getCircle();
            AlgoVisHelper.setStrokeWidth(g2d,3);
            AlgoVisHelper.setColor(g2d,AlgoVisHelper.Blue);
            AlgoVisHelper.strokeCircle(g2d,circle.getX(),circle.getY(),circle.getR());
            for (int i = 0;i < data.getPointsNumber();i++) {
                Point p = data.getPoint(i);
                if (circle.contain(p)) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Red);
                }else {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Green);
                }
                AlgoVisHelper.fillCircle(g2d,p.x,p.y,3);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private MonteCarloPiData data;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(MonteCarloPiData data) {
        this.data = data;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    private static int DELAY = 10;
    //建立本身的數據
    private MonteCarloPiData data; //數據
    private AlgoFrame frame; //視圖
    private int N;

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        if (sceneWidth != sceneHeight) {
            throw new IllegalArgumentException("必須建立爲一個正方形窗口");
        }
        //初始化數據
        // TODO: 初始化數據
        this.N = N;
        Circle circle = new Circle(sceneWidth / 2,sceneHeight / 2,sceneWidth / 2);
        data = new MonteCarloPiData(circle);
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        // TODO: 編寫本身的動畫邏輯
        for (int i = 0;i < N;i++) {
            if (i % 100 == 0) {
                frame.render(data);
                AlgoVisHelper.pause(DELAY);
                System.out.println(data.estimatePi());
            }
            int x = (int) (Math.random() * frame.getCanvasWidth());
            int y = (int) (Math.random() * frame.getCanvasHeight());
            data.addPoint(new Point(x,y));
        }
    }
}

因爲可視化在屏幕上繪製佔用的內存較大,咱們很難用更大的數據來進行模擬,因此咱們使用非可視化,控制檯的輸出來進行模擬

public class MonteCarloExperiment {
    private int squareSide;
    private int N;
    private int outputInterval = 100;

    public MonteCarloExperiment(int squareSide,int N) {
        if (squareSide <= 0 || N <= 0) {
            throw new IllegalArgumentException("squareSide,N必須大於0");
        }
        this.squareSide = squareSide;
        this.N = N;
    }

    public void setOutputInterval(int outputInterval) {
        if (outputInterval <= 0) {
            throw new IllegalArgumentException("outputInterval必須大於0");
        }
        this.outputInterval = outputInterval;
    }

    public void run() {
        Circle circle = new Circle(squareSide / 2,squareSide / 2,squareSide / 2);
        MonteCarloPiData data = new MonteCarloPiData(circle);
        for (int i = 0;i < N;i++) {
            if (i % outputInterval == 0) {
                System.out.println(data.estimatePi());
            }
            int x = (int) (Math.random() * squareSide);
            int y = (int) (Math.random() * squareSide);
            data.addPoint(new Point(x,y));
        }
    }

    public static void main(String[] args) {
        int squareSide = 800;
        int N = 10000000;
        MonteCarloExperiment exp = new MonteCarloExperiment(squareSide,N);
        exp.setOutputInterval(10000);
        exp.run();
    }
}

這裏咱們看到,咱們將模擬數據調到了一千萬。

運行結果

................

3.14157139979859
3.1415686116700203
3.141554974874372
3.141551004016064
3.14154704112337
3.1415038076152304
3.141508308308308

  • 三門問題

三門問題(Monty Hall Problem)

出自美國的電視遊戲節目Let's Make a Deal。問題的名字來自該節目的主持人蒙提.霍爾(Monty Hall)。

參賽者會看見三扇關閉了的門,其中一扇的後面有一輛汽車,選中後面有車的那扇門就能夠贏得該汽車,而另外兩扇門後面則什麼都沒有。當參賽者選定一扇門,但開啓的時候,節目主持人會開啓剩下兩扇門的其中一扇,這扇門背後必定沒有汽車。主持人會問參賽者要不要換另外一扇門。問題是:換另外一扇門會否會增長參賽者獲獎機率?

因爲該問題須要大量模擬數據,就不進行可視化操做了,咱們直接使用控制檯輸出

public class ThreeGatesExperiment {
    private int N;

    public ThreeGatesExperiment(int N) {
        if (N <= 0) {
            throw new IllegalArgumentException("N必須大於0");
        }
        this.N = N;
    }

    public void run(boolean changeDoor) {
        int wins = 0;
        for (int i = 0; i < N; i++) {
            if (play(changeDoor)) {
                wins++;
            }
        }
        System.out.println(changeDoor ? "換門" : "不換門");
        System.out.println("中獎機率: " + (double) wins / N);
    }

    private boolean play(boolean changeDoor) {
        int prizeDoor = (int) (Math.random() * 3);
        int playerChoice = (int) (Math.random() * 3);
        if (playerChoice == prizeDoor) {
            return !changeDoor;
        } else {
            return changeDoor;
        }
    }

    public static void main(String[] args) {
        int N = 10000000;
        ThreeGatesExperiment exp = new ThreeGatesExperiment(N);
        exp.run(true);
        exp.run(false);
    }
}

運行結果

換門
中獎機率: 0.6667727
不換門
中獎機率: 0.3335902

  • 抽寶箱的機率

在遊戲裏有一種寶箱,打開這個寶箱得到傳奇武器的機率是20%,如今你開5個這樣的寶箱,得到傳奇武器的機率是多少?

這裏咱們依然使用控制檯來輸出。

public class WinningPrize {
    private double chance;
    private int playTime;
    private int N;

    public WinningPrize(double chance,int playTime,int N) {
        if (chance < 0.0 || chance > 1.0) {
            throw new IllegalArgumentException("chance必須在0和1之間");
        }
        if (playTime <= 0 || N <= 0) {
            throw new IllegalArgumentException("playTime和N必須大於0");
        }
        this.chance = chance;
        this.playTime = playTime;
        this.N = N;
    }

    public void run() {
        int wins = 0;
        for (int i = 0;i < N; i++) {
            if (play()) {
                wins++;
            }
        }
        System.out.println("中獎機率爲: " + (double) wins / N);
    }

    private boolean play() {
        for (int i = 0;i < playTime;i++) {
            if (Math.random() < chance) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        double chance = 0.2;
        int playTime = 5;
        int N = 1000000;
        WinningPrize winningPrize = new WinningPrize(chance,playTime,N);
        winningPrize.run();
    }
}

這段代碼的意思是咱們連續開100萬次,每次開5個寶箱。

運行結果

中獎機率爲: 0.672504

根據結果可知,咱們連續開5個寶箱的機率大概在67%。固然在機率論中也有一個基本的公式

1 - (0.8)^5 = 0.67232

如今咱們能夠來看一下,若是咱們的心理值是95%的機率能拿到傳奇武器,那麼咱們須要開多少次呢?

1 - (0.8)^x > 0.95,根據該公式,咱們能夠反解x的值。

而具備迷惑的1 / 0.2 = 5的真實意義是平均值(指望值),也就是說咱們是有可能被平均的,好比別人可能開1個寶箱就拿到了傳奇武器,而咱們卻可能要開10個,20個。

  • 選擇排序可視化

選擇排序算法就是經過掃描數組中的最小值而後跟數組最前端的值交換來達到排序的目的的算法,它是一個O(n^2)時間複雜度的算法。如下紅色表示發生變更的元素,藍色表示固定下來的元素。

如今咱們仍是經過咱們的模版來進行修改

數據層

public class SelectionSortData {
    private int[] numbers;

    public SelectionSortData(int N,int randomBound) {
        numbers = new int[N];
        for (int i = 0;i < N;i++) {
            numbers[i] = (int) (Math.random() * randomBound) + 1;
        }
    }

    public int N() {
        return numbers.length;
    }

    public int get(int index) {
        if (index < 0 || index >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        return numbers[index];
    }

    public void swap(int i,int j) {
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

視圖層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            int w = canvasWidth / data.N();
            AlgoVisHelper.setColor(g2d,AlgoVisHelper.LightBlue);
            for (int i = 0;i < data.N();i++) {
                AlgoVisHelper.fillRectangel(g2d,i * w,canvasHeight - data.get(i),
                                            w - 1,data.get(i));
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private SelectionSortData data;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(SelectionSortData data) {
        this.data = data;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    private static int DELAY = 10;
    //建立本身的數據
    private SelectionSortData data; //數據
    private AlgoFrame frame; //視圖

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        //初始化數據
        // TODO: 初始化數據
        data = new SelectionSortData(N,sceneHeight);
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        frame.render(data);
        AlgoVisHelper.pause(DELAY);
        // TODO: 編寫本身的動畫邏輯
        for (int i = 0;i < data.N();i++) {
            //尋找[i,n)區間裏的最小值索引
            int minIndex = i;
            for (int j = i + 1;j < data.N();j++) {
                if (data.get(j) < data.get(minIndex)) {
                    minIndex = j;
                }
            }
            data.swap(i,minIndex);
            frame.render(data);
            AlgoVisHelper.pause(DELAY);
        }
        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }
}

main方法

public class SelectionSortMain {
    public static void main(String[] args) {
        int sceneWidth = 800;
        int sceneHeight = 800;
        int N = 100;
        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight,N);
    }
}

顯示的圖樣

因爲此種方式沒法展示排序的主要過程,因此咱們作出修改。

數據層

public class SelectionSortData {
    private int[] numbers;
    @Getter
    @Setter
    private int orderedIndex = -1;  //[0..orderedIndex)是有序的
    @Getter
    @Setter
    private int currentMinIndex = -1; //當前找到的最小元素的索引
    @Getter
    @Setter
    private int currentCompareIndex = -1; //當前正在比較的元素索引

    public SelectionSortData(int N,int randomBound) {
        numbers = new int[N];
        for (int i = 0;i < N;i++) {
            numbers[i] = (int) (Math.random() * randomBound) + 1;
        }
    }

    public int N() {
        return numbers.length;
    }

    public int get(int index) {
        if (index < 0 || index >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        return numbers[index];
    }

    public void swap(int i,int j) {
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

視圖層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            int w = canvasWidth / data.N();
            for (int i = 0;i < data.N();i++) {
                if (i < data.getOrderedIndex()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Red);
                }else {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Grey);
                }
                if (i == data.getCurrentCompareIndex()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.LightBlue);
                }
                if (i == data.getCurrentMinIndex()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Indigo);
                }
                AlgoVisHelper.fillRectangel(g2d,i * w,canvasHeight - data.get(i),
                                            w - 1,data.get(i));
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private SelectionSortData data;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(SelectionSortData data) {
        this.data = data;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    private static int DELAY = 20;
    //建立本身的數據
    private SelectionSortData data; //數據
    private AlgoFrame frame; //視圖

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        //初始化數據
        // TODO: 初始化數據
        data = new SelectionSortData(N,sceneHeight);
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        setData(0,-1,-1);
        // TODO: 編寫本身的動畫邏輯
        for (int i = 0;i < data.N();i++) {
            //尋找[i,n)區間裏的最小值索引
            int minIndex = i;
            setData(i,-1,minIndex);
            for (int j = i + 1;j < data.N();j++) {
                setData(i,j,minIndex);
                if (data.get(j) < data.get(minIndex)) {
                    minIndex = j;
                    setData(i,j,minIndex);
                }
            }
            data.swap(i,minIndex);
            setData(i + 1,-1,-1);
        }
        setData(data.N(),-1,-1);
    }

    private void setData(int orderedIndex,int currentCompareIndex,int currentMinIndex) {
        data.setOrderedIndex(orderedIndex);
        data.setCurrentCompareIndex(currentCompareIndex);
        data.setCurrentMinIndex(currentMinIndex);
        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }
}

運行效果圖

  • 插入排序可視化

插入排序算法是將數組中的元素不斷向前比較,直到放入到一個適當合適的位置的排序算法,它就好像咱們在玩撲克牌的時候進行整理牌面同樣。插入排序也是一個O(n^2)時間複雜度的算法。

因爲8是第一個元素,它前面沒有元素,因此8不須要移動,並被肯定下來

咱們來看第二個元素6,6與前面的元素8比較,6比8小,因此6要放到8的前面,並被肯定下來。

而後咱們來看第三個元素2,2與以前的8比較,2比8小,與8交換位置,再與6比較,2比6小,再與6交換位置,此時肯定了下來。

而後咱們來看第四個元素3

數據層

public class InsertionSortData {
    private int[] numbers;
    @Getter
    @Setter
    private int orderedIndex = -1;  //[0..orderedIndex)是有序的
    @Getter
    @Setter
    private int currentIndex = -1; //當前正在處理的元素的索引

    public InsertionSortData(int N,int randomBound) {
        numbers = new int[N];
        for (int i = 0;i < N;i++) {
            numbers[i] = (int) (Math.random() * randomBound) + 1;
        }
    }

    public int N() {
        return numbers.length;
    }

    public int get(int index) {
        if (index < 0 || index >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        return numbers[index];
    }

    public void swap(int i,int j) {
        if (i < 0 || i >= numbers.length || j <0 || j >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

視圖層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            int w = canvasWidth / data.N();
            for (int i = 0;i < data.N();i++) {
                if (i < data.getOrderedIndex()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Red);
                }else {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Grey);
                }
                if (i == data.getCurrentIndex()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.LightBlue);
                }
                AlgoVisHelper.fillRectangel(g2d,i * w,canvasHeight - data.get(i),
                        w - 1,data.get(i));
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private InsertionSortData data;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(InsertionSortData data) {
        this.data = data;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    private static int DELAY = 20;
    //建立本身的數據
    private InsertionSortData data; //數據
    private AlgoFrame frame; //視圖

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        //初始化數據
        // TODO: 初始化數據
        data = new InsertionSortData(N,sceneHeight);
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            //根據狀況決定是否加入鍵盤鼠標監聽器
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        setData(0,-1);
        // TODO: 編寫本身的動畫邏輯
        for (int i = 0;i < data.N();i++) {
            setData(i,i);
            for (int j = i;j > 0 && data.get(j) < data.get(j - 1);j--) {
                data.swap(j,j - 1);
                setData(i + 1,j - 1);
            }
        }
        setData(data.N(),-1);
    }

    private void setData(int orderedIndex,int currentIndex) {
        data.setOrderedIndex(orderedIndex);
        data.setCurrentIndex(currentIndex);
        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }
}

main方法

public class InsertionSortMain {
    public static void main(String[] args) {
        int sceneWidth = 800;
        int sceneHeight = 800;
        int N = 100;
        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight,N);
    }
}

展現圖形

插入排序的優化

因爲以上的插入排序算法須要通過大量的交換的過程,咱們能夠對這個交換的過程進行優化

此時咱們不直接將6與8比較,而是將6複製一份出來。

因爲8比6大,咱們將8後移一位。

因爲第0個位置前面沒有元素,6不須要再作比較,就將6放入該位置,並確認。

而後考察2的位置,將2複製一份。

因爲8比2大,因此8後移一位

因爲6比2大,因此將6也後移一位。

因爲6是第0個位置,沒有可比較的了,咱們將2放到該位置。

如今咱們將3複製一份出來

8比3大,將8後移一位

6比3大,6後移一位

2比3小,因此3放到第1個位置,並確認

優化後跟原方法的對比

public class InsertionSort {
    private InsertionSort() {
    }

    @SuppressWarnings("unchecked")
    public static Comparable[] betterSort(Comparable[] arr) {
        for (int i = 0;i < arr.length;i++) {
            Comparable e = arr[i];
            int j;
            for (j = i;j > 0 && arr[j - 1].compareTo(e) > 0;j--) {
                arr[j] = arr[j - 1];
            }
            arr[j] = e;
        }
        return arr;
    }

    @SuppressWarnings("unchecked")
    public static Comparable[] sort(Comparable[] arr) {
        for (int i = 0;i < arr.length;i++) {
            for (int j = i;j > 0 && arr[j].compareTo(arr[j - 1]) < 0;j--) {
                swap(j,j - 1,arr);
            }
        }
        return arr;
    }

    private static void swap(int i,int j,Comparable[] arr) {
        Comparable temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        Integer[] numbers1 = new Integer[1000];
        for (int i = 0;i < numbers1.length;i++) {
            numbers1[i] = (int) (Math.random() * 1000) + 1;
        }
        Integer[] numbers2 = numbers1.clone();
        long start1 = System.nanoTime();
        System.out.println(Arrays.toString(InsertionSort.sort(numbers1)));
        System.out.println((System.nanoTime() - start1) / 1000000000.0);
        long start2 = System.nanoTime();
        System.out.println(Arrays.toString(InsertionSort.betterSort((numbers2))));
        System.out.println((System.nanoTime() - start2) / 1000000000.0);
    }
}

運行結果

[2, 5, 5, 5, 6, 6, 9, 10, 12, 12, 14, 14, 14, 15, 15, 15, 18, 19, ...]
0.007879546
[2, 5, 5, 5, 6, 6, 9, 10, 12, 12, 14, 14, 14, 15, 15, 15, 18, 19, ...]
0.00452523

在一個近乎有序的數組進行排序時,咱們的插入排序的時間複雜度能夠進化到O(n)級別,在這種特殊的狀況下,它是最快的一種排序方式。

數據層

public class InsertionSortData {
    public enum Type {
        Default,
        NearlyOrdered;
    }
    private int[] numbers;
    @Getter
    @Setter
    private int orderedIndex = -1;  //[0..orderedIndex)是有序的
    @Getter
    @Setter
    private int currentIndex = -1; //當前正在處理的元素的索引

    public InsertionSortData(int N,int randomBound,Type dataType) {
        numbers = new int[N];
        for (int i = 0;i < N;i++) {
            numbers[i] = (int) (Math.random() * randomBound) + 1;
        }
        //生成一個近乎有序的數組
        if (dataType == Type.NearlyOrdered) {
            Arrays.sort(numbers);
            int swapTime = (int)(0.02 * N);
            for (int i = 0;i < swapTime;i++) {
                int a = (int)(Math.random() * N);
                int b = (int)(Math.random() * N);
                swap(a,b);
            }
        }
    }

    public InsertionSortData(int N,int randomBound) {
        this(N,randomBound,Type.Default);
    }

    public int N() {
        return numbers.length;
    }

    public int get(int index) {
        if (index < 0 || index >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        return numbers[index];
    }

    public void swap(int i,int j) {
        if (i < 0 || i >= numbers.length || j <0 || j >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

控制層

public class AlgoVisualizer {
    private static int DELAY = 20;
    //建立本身的數據
    private InsertionSortData data; //數據
    private AlgoFrame frame; //視圖

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N,InsertionSortData.Type dataType) {
        //初始化數據
        // TODO: 初始化數據
        data = new InsertionSortData(N,sceneHeight,dataType);
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            //根據狀況決定是否加入鍵盤鼠標監聽器
            new Thread(() -> {
                run();
            }).start();
        });
    }

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        this(sceneWidth,sceneHeight,N,InsertionSortData.Type.Default);
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        setData(0,-1);
        // TODO: 編寫本身的動畫邏輯
        for (int i = 0;i < data.N();i++) {
            setData(i,i);
            for (int j = i;j > 0 && data.get(j) < data.get(j - 1);j--) {
                data.swap(j,j - 1);
                setData(i + 1,j - 1);
            }
        }
        setData(data.N(),-1);
    }

    private void setData(int orderedIndex,int currentIndex) {
        data.setOrderedIndex(orderedIndex);
        data.setCurrentIndex(currentIndex);
        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }
}

main方法

public class InsertionSortMain {
    public static void main(String[] args) {
        int sceneWidth = 800;
        int sceneHeight = 800;
        int N = 100;
        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight,N,InsertionSortData.Type.NearlyOrdered);
    }
}

顯示圖形

最後值得一提的是,在n比較小的時候,插入排序比O(nlog n)的排序算法有優點。插入排序算法常常用做是高級排序算法在處理到小樣本時的一個優化。

  • 歸併排序可視化

歸併排序算法是將一個數組分紅兩部分——左邊和右邊,而後使用一樣的算法對左邊進行排序,再使用一樣的算法對右邊進行排序。以後將兩個有序的數組,歸併成一個有序的數組。

對於劃分紅一個元素的部分,它自己就是有序的

而後咱們對其不一樣的劃分,進行兩兩歸併

再經過相應的劃分,兩兩歸併成有序數組

最後將剩下的兩個有序數組,歸併成一個有序的數組

因此咱們這裏給出一個歸併排序的算法類

public class MergeSort {
    private MergeSort() {
    }

    public static void sort(Comparable[] arr) {
        int n = arr.length;
        sort(arr,0,n - 1);
    }

    /**
     * 遞歸使用歸併排序,對arr[l..r]的範圍進行排序
     * @param arr
     * @param l
     * @param r
     */
    private static void sort(Comparable[] arr,int l,int r) {
        if (l >= r) {
            return;
        }
        //此處l + r是有可能形成整型溢出的
//        int mid = (l + r) /2;
        int mid = l + (r - l) / 2;
        sort(arr,l,mid);
        sort(arr,mid + 1,r);
        merge(arr,l,mid,r);
    }

    /**
     * 將arr[l..mid]和arr[mid+1..r]兩部分進行歸併
     * @param arr
     * @param l
     * @param mid
     * @param r
     */
    @SuppressWarnings("unchecked")
    private static void merge(Comparable[] arr,int l,int mid,int r) {
        Comparable[] aux = Arrays.copyOfRange(arr,l,r + 1);
        int i = l; // i指向左半部分的起始索引位置l
        int j = mid + 1; // j指向右半部分起始索引位置mid + 1
        for (int k = l;k <= r;k++) {
            if (i > mid) { //若是左半部分元素已經所有處理完畢
                arr[k] = aux[j - l];
                j++;
            }else if (j > r) { //若是右半部分元素已經所有處理完畢
                arr[k] = aux[i - l];
                i++;
            }else if (aux[i - l].compareTo(aux[j - l]) < 0) { //左半部分所指元素 < 右半部分所指元素
                arr[k] = aux[i - l];
                i++;
            }else { //左半部分所指元素 >= 右半部分所指元素
                arr[k] = aux[j - l];
                j++;
            }
        }
    }
}

如今咱們來寫可視化的部分

數據層

public class MergeSortData {
    @Getter
    private int[] numbers;
    @Getter
    @Setter
    private int l;
    @Getter
    @Setter
    private int r;
    @Getter
    @Setter
    private int mergeIndex;

    public MergeSortData(int N, int randomBound) {
        numbers = new int[N];
        for (int i = 0;i < N;i++) {
            numbers[i] = (int) (Math.random() * randomBound) + 1;
        }
    }

    public int N() {
        return numbers.length;
    }

    public int get(int index) {
        if (index < 0 || index >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        return numbers[index];
    }

    public void set(int index,int value) {
        if (index < 0 || index >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        numbers[index] = value;
    }

    public void swap(int i,int j) {
        if (i < 0 || i >= numbers.length || j <0 || j >= numbers.length) {
            throw new IllegalArgumentException("無效的排序數組索引");
        }
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

視圖層

@Getter
public class AlgoFrame extends JFrame {
    private class AlgoCanvas extends JPanel {
        public AlgoCanvas() {
            //雙緩存
            super(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            //抗鋸齒
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                                                      RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.addRenderingHints(hints);
            //具體繪製
            // TODO: 繪製本身的數據data
            int w = canvasWidth / data.N();
            for (int i = 0;i < data.N();i++) {
                if (i >= data.getL() && i <= data.getR()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Green);
                }else {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Grey);
                }
                if (i >= data.getL() && i <= data.getMergeIndex()) {
                    AlgoVisHelper.setColor(g2d,AlgoVisHelper.Red);
                }
                AlgoVisHelper.fillRectangel(g2d,i * w,canvasHeight - data.get(i),
                        w - 1,data.get(i));
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(canvasWidth,canvasHeight);
        }
    }

    private int canvasWidth;
    private int canvasHeight;
    //設置本身的數據
    private MergeSortData data;

    public AlgoFrame(String title,int canvasWidth,int canvasHeight) {
        super(title);
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
        AlgoCanvas canvas = new AlgoCanvas();
        canvas.setOpaque(true);
        this.setContentPane(canvas);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public AlgoFrame(String title) {
        this(title,1024,768);
    }

    public void render(MergeSortData data) {
        this.data = data;
        this.repaint();
    }
}

控制層

public class AlgoVisualizer {
    private static int DEALY = 20;
    //建立本身的數據
    private MergeSortData data; //數據
    private AlgoFrame frame; //視圖

    public AlgoVisualizer(int sceneWidth,int sceneHeight,int N) {
        //初始化數據
        // TODO: 初始化數據
        data = new MergeSortData(N,sceneHeight);
        //初始化視圖
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("歡迎",sceneWidth,sceneHeight);
            new Thread(() -> {
                run();
            }).start();
        });
    }

    /**
     * 動畫邏輯
     */
    private void run() {
        setData(-1,-1,-1);
        // TODO: 編寫本身的動畫邏輯
        mergeSort(0,data.N() - 1);
        setData(0,data.N() - 1,data.N() - 1);
    }

    private void mergeSort(int l,int r) {
        if (l >= r) {
            return;
        }
        setData(l,r,-1);
//        int mid = (l + r) / 2;
        int mid = l + (r - l) / 2;
        mergeSort(l,mid);
        mergeSort(mid + 1,r);
        merge(l,mid,r);
    }

    private void merge(int l,int mid,int r) {
        int[] aux = Arrays.copyOfRange(data.getNumbers(),l,r + 1);
        int i = l;
        int j = mid + 1;
        for (int k = l;k <= r;k++) {
            if (i > mid) {
                data.set(k,aux[j - l]);
                j++;
            }else if (j > r) {
                data.set(k,aux[i - l]);
                i++;
            }else if (aux[i - l] < aux[j - l]) {
                data.set(k,aux[i - l]);
                i++;
            }else {
                data.set(k,aux[j - l]);
                j++;
            }
            setData(l,r,k);
        }
    }

    private void setData(int l,int r,int mergeIndex) {
        data.setL(l);
        data.setR(r);
        data.setMergeIndex(mergeIndex);
        frame.render(data);
        AlgoVisHelper.pause(DEALY);
    }
}

main方法

public class MergeSortMain {
    public static void main(String[] args) {
        int sceneWidth = 800;
        int sceneHeight = 800;
        int N = 100;
        AlgoVisualizer visualizer = new AlgoVisualizer(sceneWidth,sceneHeight,N);
    }
}

圖像顯示

相關文章
相關標籤/搜索