控制台绘图

本文所有程序的运行环境为控制台,通过字符在控制台中输出一些有趣的图案。

控制台输出五角星

注:本程序首先发布在 CSDN 我的博客 上,这里作为引用。

刚开始学习语言的时候,老师都很喜欢让我们在控制台上使用“*”来输出某些图形,比如:三角形、菱形、方块什么的,这主要能让我们能更好地理解循环的语法和逻辑思维能力。

这些图形基本上都是直线的,所以在控制台上可以很方便地进行输出,不知道有没有人想过在控制台上输出一条有斜率的斜线呢?

在 AWT 上使用 Graphics 对象来做这些事的话是相当简单的,其中有内置的 drawLine 方法,告诉它 4 个点的坐标就可以很方便地在界面上画出一条斜线,但是在控制台我们如何做到这些呢?

闲着没事的时候,写了一个在控制台上输出五角星的小程序,不过这里面有点小小的 bug,bug 是什么、究竟在什么地方呢?先卖个关子吧,我弄了好久头都大了不想去想了,如果发现的话或者是解决了的话,别忘记告诉我。

public class Test {

    public static void main(String[] args) {
        // 画一个半径为10,旋转为0,空白为全身空格,填充为的五角星
        Pentagram pen = new Pentagram(10, 0, ' ', '');
        // 在控制台上输出这个五角星
        Draw.printCanvas(pen.getPentagram());
    }

    private static class Pentagram {
        private final char FILL_CHAR;   // 填充字符
        private final char SPACE_CHAR;  // 空档字符
        private final int R;            // 五角星的外接圆半径
        private final float ROTATION;   // 五角星逆时针旋转角度
        private final int X;            // 用于生成画图数组
        private final int Y;            // 用于生成画图数组

        /**
         * 构造一个Pentagram对象
         * @param radius 五角星的半径
         * @param rotation 五角星的逆时针旋转度数
         * @param spaceChar 画布上空白处填充字符
         * @param fillChar 画布上线条部分填充字符
         */
        public Pentagram(int radius, float rotation, char spaceChar, char fillChar) {
            this.R = radius;
            this.ROTATION = rotation;
            this.FILL_CHAR = fillChar;
            this.SPACE_CHAR = spaceChar;
            this.X = 2 * R + 1;
            this.Y = 2 * R + 1;
        }


        public char[][] getPentagram() {
            char[][] canvas = initCanvas();
            Draw draw = new Draw(FILL_CHAR);
            // 设五角星的最右边的一个点为 A,逆时针选取点 B~E
            // 通过圆的极坐标公式可以得出:
            // 得出以下各点的坐标
            // A 点坐标(0.951R, 0.309R)
            // B 点坐标(0, R)
            // C 点坐标(-0.951R, 0.309R)
            // D 点坐标(-0.588R, -0.809R)
            // E 点坐标(0.588R, -0.809R)

            // 画线段CA
            draw.drawLine(mcos(162) * R, msin(162) * R, mcos(18) * R, msin(18) * R, canvas);
            // 画线段DA
            draw.drawLine(mcos(234) * R, msin(234) * R, mcos(18) * R, msin(18) * R, canvas);
            // 画线段CE
            draw.drawLine(mcos(162) * R, msin(162) * R, mcos(306) * R, msin(306) * R, canvas);
            // 画线段DB
            draw.drawLine(mcos(234) * R, msin(234) * R, mcos(90) * R, msin(90) * R, canvas);
            // 画线段BE
            draw.drawLine(mcos(90) * R, msin(90) * R, mcos(306) * R, msin(306) * R, canvas);
            return canvas;
        }

        // 在方形的字符数组中指定两点画线条
        // 对图形数组进行初始化,填充空格
        private char[][] initCanvas() {
            char[][] canvas = new char[Y][X];
            for (int i = 0; i < Y; i++) {
                for (int j = 0; j < X; j++) {
                    canvas[i][j] = SPACE_CHAR;
                }
            }
            return canvas;
        }

        // 根据角度求正弦值,保留两位小数
        private double msin(float a) {
            return ((int) (Math.sin(Math.toRadians(a + ROTATION)) * 100)) / 100.0;
        }

        // 根据角度求余弦值,保留两位小数
        private double mcos(float a) {
            return ((int) (Math.cos(Math.toRadians(a + ROTATION)) * 100)) / 100.0;
        }
    }

    private static class Draw {

        private char fillChar;

        public Draw(char fillChar) {
            this.fillChar = fillChar;
        }

        /**
         * 根据两个点画线在二维字符数组上画线
         * @param x1
         * @param y1
         * @param x2
         * @param y2
         * @param canvas
         */
        public void drawLine(double x1, double y1, double x2, double y2, char[][] canvas) {
            int radius = (canvas.length - 1) / 2;
            // 从 x 方向进行填充
            if (x1 > x2) {
                double t = x1;
                x1 = x2;
                x2 = t;
                t = y1;
                y1 = y2;
                y2 = t;
            }

            // 获得直线方程的两个系数
            double a = (y1 - y2) / (x1 - x2);
            double b = y1 - a * x1;

            // 根据 x 方向的值求出 y 值,并填充图形
            for (int i = (int) Math.round(x1); i <= (int) Math.round(x2); i++) {
                // 根据直线方程 y = ax + b,求 y
                int y = (int) Math.round(a * i + b);

                // 因为 y 和 i 算出来的结果有可能是负数,
                // 为了采用数组来表示坐标,做了以下变换
                // c[R][R] 即为坐标原点
                // c[R][0..R] 为 x 方向的负半轴
                // c[R][R+1..2*R] 为 x 方向的正半轴
                // c[0..R][R] 为 y 方向的正半轴
                // c[R+1..2*R][R] 为 y 方向的负半轴
                int yy = radius - y;
                int xx = radius + I;

                yy = yy < 0 ? 0 : yy;
                yy = yy > 2 * radius ? 2 * radius : yy;
                xx = xx < 0 ? 0 : xx;
                xx = xx > 2 * radius ? 2 * radius : xx;

                canvas[yy][xx] = fillChar;
            }

            // 从 y 方向进行填充,便于减少间距问题产生的字符空档
            if (y1 > y2) {
                double t = x1;
                x1 = x2;
                x2 = t;
                t = y1;
                y1 = y2;
                y2 = t;
            }

            // 根据 y 方向的值求出 x 值,并填充图形
            for (int i = (int) Math.round(y1); i <= (int) Math.round(y2); i++) {
                // 根据 x = (y - b) / a,求 x
                int y = (int) Math.round((i - b) / a);

                int yy = radius - I;
                int xx = radius + y;

                yy = yy < 0 ? 0 : yy;
                yy = yy > 2 * radius ? 2 * radius : yy;
                xx = xx < 0 ? 0 : xx;
                xx = xx > 2 * radius ? 2 * radius : xx;

                canvas[yy][xx] = fillChar;
            }
        }

        /**
         * 将画完图之后的画布输出到控制台上
         * @param canvas
         */
        public static void printCanvas(char[][] canvas) {
            for (int i = 0; i < canvas.length; i++) {
                for (int j = 0; j < canvas[i].length; j++) {
                    System.out.print(canvas[i][j]);
                }
                System.out.println();
            }
        }
    }
}

下图是一个半径为 20,旋转度为 0 度的五角星控制台输出。

Pentagram_1.png

下图是一个半径为 20,旋转度为 15 度的五角星控制台输出。

Pentagram_2.png

菱形绘制及思路

先上代码:

public class Test {
 
    public static void main(String[] args) {
        printDiamond(7, true);
        System.out.println();
        printDiamond(7, false);
    }
 
    /**
     * 输出菱形
     * @param line       菱形的行数
     * @param isSolid    是否为实心
     */
    private static void printDiamond(int line, boolean isSolid) {
        // 行数应为奇数,若为偶数时则向上取奇数
        line |= 1;
        for(int k = line / 2, i = -k; i <= k; i++) {
            for(int j = -k, m = k - Math.abs(i); j <= m; j++) {
                boolean b;
                if(isSolid) {
                    b = Math.abs(j) + Math.abs(i) > k;
                } else {
                    b = Math.abs(j) + Math.abs(i) != k;                    
                }
                System.out.print(b ? " " : "*");
            }
            System.out.println();
        }
    }
}

分析:菱形是一个上下、左右对称的图形,行列可以按照下面的方式来进行循环:

    j=
    -3  -2  -1   0   1   2   3
i= +---+---+---+---+
-3 |   |   |   | * |
   +---+---+---+---+---+
-2 |   |   | * | * | * |
   +---+---+---+---+---+---+
-1 |   | * | * | * | * | * |
   +---+---+---+---+---+---+---+
 0 | * | * | * | * | * | * | * |
   +---+---+---+---+---+---+---+
 1 |   | * | * | * | * | * |
   +---+---+---+---+---+---+
 2 |   |   | * | * | * |
   +---+---+---+---+---+
 3 |   |   |   | * |
   +---+---+---+---+

行数为 7,则 7/2 = 3,这个数值很有用处,暂且称为 k

ij 的循环起始均为 -k,而 i 的终止循环为 kj 的终止循环条件理应为 k,注意右边,我特意把它挖空了,因为右边的都是空格,为了优化程序就没有必要输出了,这样 j 的循环终止条件与 i 是有密切关系的,为 k - \left | i \right |,即:当 i = -1 时,k - \left | i \right | = 2,因此 j 只要从 -3 循环到 2 就可以了。

再看看 * 位置上的规律,注意最左边的 *,在 \left | i \right | + \left | j \right | > k 时输出的是空格,小于等于 k 时,输出的是 *

  • i = -2, j = -2 坐标上,\left | i \right | + \left | j \right | = 44 是大于 k 的,因此输出空格
  • i = -2, j = -1 坐标上,\left | i \right | + \left | j \right | = 33 是不大于 k 的,因此输出 *

如果需要的是空心菱心,只要把判断条件设为 \left | i \right | + \left | j \right | = k 时才输出 *,否则输出空格。

因此,这样我们就利用于图形的对称性完成了菱形的输出。

杨辉三角形

public class Yanghui {
 
    public static void main(String[] args) {
        Yanghui yang = new Yanghui();
        yang.printYanghuiTriangle(13);
    }
 
    /**
     * 生成指定行数的杨辉三角形
     *
     * @param lines 杨辉三角形的行数
     */
    public void printYanghuiTriangle(int lines) {
        if(lines < 1) {
            throw new IllegalArgumentException("lines must be great than 0.");
        }
        if(lines > 30) {
            throw new IllegalArgumentException("lines is too big.");
        }
        int[] line = new int[lines];
        int maxLen = getMaxLen(lines);
        for(int i = 0; i < lines; i++) {
            line[0] = line[i] = 1;
            for(int j = 1, k = i / 2, pre = line[0]; j <= k; j++) {
                int cur = line[j];
                line[i - j] = line[j] += pre;
                pre = cur;
            }
            printLine(line, i + 1, maxLen);
        }
    }
 
    /**
     * 根据指定行数的杨辉三角形,计算其中最大数字的长度
     * @param lines 杨辉三角形的行数
     * @return      最大数字的长度
     */
    private int getMaxLen(int lines) {
        int k = lines / 2;
        long maxNum = factorial(k + 1, lines - 1) / factorial(1, lines - 1 - k);
        return getLength(maxNum);
    }
 
    /**
     * 阶乘计算
     * @param start 阶乘计算的起始数字
     * @param num   阶乘计算的终止数字
     * @return      阶乘计算结果
     */
    private long factorial(int start, int num) {
        long result = start > 0 ? start : 1L;
        while(num > start) {
            result *= num--;
        }
        return result;
    }
 
    /**
     * 根据指定数字计算数字的长度
     * @param num   数字
     * @return      数字的长度
     */
    private int getLength(long num) {
        int len = 0;
        while(num > 0L) {
            num /= 10L;
            len++;
        }
        return len;
    }
 
    private void printLine(int[] yanghui, int line, int width) {
        printSpaces((yanghui.length - line) * width);
 
        for(int i = 0; i < line; i++) {
            if(i > 0) {
                printSpaces(width);
            }
            printSpaces(width - getLength(yanghui[i]));
            System.out.print(yanghui[I]);
        }
        System.out.println();
        if(width > 1) {
            System.out.println();
        }
    }
 
    private void printSpaces(int spaceCount) {
        for(int i = 0; i < spaceCount; i++) {
            System.out.print(" ");
        }
    }
}
 
                                      1
 
                                   1     1
 
                                1     2     1
 
                             1     3     3     1
 
                          1     4     6     4     1
 
                       1     5    10    10     5     1
 
                    1     6    15    20    15     6     1
 
                 1     7    21    35    35    21     7     1
 
              1     8    28    56    70    56    28     8     1
 
           1     9    36    84   126   126    84    36     9     1
 
        1    10    45   120   210   252   210   120    45    10     1
 
     1    11    55   165   330   462   462   330   165    55    11     1
 
  1    12    66   220   495   792   924   792   495   220    66    12     1

正弦函数

public class Test {
    public static void main(String[] args) {
        TriFunc tri = new TriFunc();
         
        // 生成一块25×100的画布
        Canvas canvas = new Canvas(25, 120);
 
        // 画sin曲线,周期为2
        tri.drawSin(canvas, 2.0);
        canvas.printCanvas();
         
        System.out.println();
        canvas.reset();
        // 画cos曲线,周期为2
        tri.drawCos(canvas, 2.0);
        canvas.printCanvas();
    }
}
 
class TriFunc {
 
    /**
     * 画sin曲线
     * @param canvas 画布
     * @param period 曲线周期
     */
    public void drawSin(Canvas canvas, double period) {        
        char[][] chars = canvas.getCanvas();
        // x 轴的比率
        double xRatio = (2 * period * Math.PI) / (canvas.getWidth() - 1);
        // y 轴的放大倍率
        int yMulti = (canvas.getHeight() - 1) / 2;
        for(int i = 0; i < canvas.getWidth(); i++) {
            // 将数组索引映射为横坐标值
            double k = (i - canvas.getWidth() / 2) * xRatio;
            // 将sin值映射为数组索引
            int h = yMulti - (int)Math.round(Math.sin(k) * yMulti);
            chars[h][i] = Canvas.FILL_CHAR;
        }
    }
     
    /**
     * 画cos曲线
     * @param canvas 画布
     * @param period 曲线周期
     */
    public void drawCos(Canvas canvas, double period) {
        char[][] chars = canvas.getCanvas();
        double xRatio = (2 * period * Math.PI) / (canvas.getWidth() - 1);
        int yMulti = (canvas.getHeight() - 1) / 2;
        for(int i = 0; i < canvas.getWidth(); i++) {
            double k = (i - canvas.getWidth() / 2) * xRatio;
            int h = yMulti - (int)Math.round(Math.cos(k) * yMulti);
            chars[h][i] = Canvas.FILL_CHAR;
        }
    }
}
 
 
class Canvas {
     
    private int height;
    private int width;
    private char[][] canvas;   
     
    // 填充字符
    public static char FILL_CHAR = '+';
    // 空白字符
    public static char BLANK_CHAR = ' ';
     
    /**
     * 构建一块画布
     * @param height
     * @param width
     */
    public Canvas(int height, int width) {
        // 由于需要画坐标轴,所以得采用奇数
        this.height = height % 2 == 0 ? height + 1 : height;
        this.width = width % 2 == 0 ? width + 1 : width;               
        init();
    }
     
    /**
     * 初始化画布
     */
    private void init() {
        this.canvas = new char[height][width];
        for(int i = 0; i < height; i++) {
            for(int j = 0; j < width; j++) {
                canvas[i][j] = BLANK_CHAR;
            }
        }
        addAxis();
    }
     
    /**
     * 添加坐标轴
     */
    private void addAxis() {
        // 添加横坐标
        int y = height / 2;
        for(int x = 0; x < width; x++) {
            canvas[y][x] = '-';
        }
        // 添加纵坐标
        int xx = width / 2;
        for(int yy = 0; yy < height; yy++) {
            canvas[yy][xx] = '|';
        }
        // 添加原点
        canvas[y][xx] = '+';
    }
     
    /**
     * 输出画布
     */
    public void printCanvas() {
        for(int i = 0; i < height; i++) {
            for(int j = 0; j < width; j++) {
                System.out.print(canvas[i][j]);
            }
            System.out.println();
        }
    }
     
    /**
     * 清空画布
     */
    public void reset() {
        init();
    }
     
    public int getHeight() {
        return height;
    }
    public int getWidth() {
        return width;
    }
 
    public char[][] getCanvas() {
        return canvas;
    }    
}

           ####                                   |          ####                                    
         ##    ##                                 |        ##    ##                                  
        #        #                                |       #        #                                 
       #          #                               |      #          #                                
      #            #                              |     #            #                               
     #              #                             |    #              #                              
    #                #                            |   #                #                             
                                                  |                                                  
   #                  #                           |  #                  #                            
  #                    #                          | #                    #                           
 #                      #                         |#                      #                          
                                                  |                                                  
#------------------------#------------------------#------------------------#------------------------#
                                                  |                                                  
                          #                      #|                         #                      # 
                           #                    # |                          #                    #  
                            #                  #  |                           #                  #   
                                                  |                                                  
                             #                #   |                            #                #    
                              #              #    |                             #              #     
                               #            #     |                              #            #      
                                #          #      |                               #          #       
                                 #        #       |                                #        #        
                                  ##    ##        |                                 ##    ##         
                                    ####          |                                   ####           
 
 
###                                             #####                                             ###
   ##                                         ##  |  ##                                         ##   
     #                                       #    |    #                                       #     
      #                                     #     |     #                                     #      
       #                                   #      |      #                                   #       
                                                  |                                                  
        #                                 #       |       #                                 #        
         #                               #        |        #                               #         
          #                             #         |         #                             #          
                                                  |                                                  
           #                           #          |          #                           #           
            #                         #           |           #                         #            
--------------------------------------------------+--------------------------------------------------
             #                       #            |            #                       #             
              #                     #             |             #                     #              
                                                  |                                                  
               #                   #              |              #                   #               
                #                 #               |               #                 #                
                 #               #                |                #               #                 
                                                  |                                                  
                  #             #                 |                 #             #                  
                   #           #                  |                  #           #                   
                    #         #                   |                   #         #                    
                     ##     ##                    |                    ##     ##                     
                       #####                      |                      #####                       

LED

public class Test {
    public static void main(String[] args) {
        LED led = new LED();
        char[][] chss = led.getLED("0123456789");
        LED.print(chss);
    }
}
 
class LED {
    /** 
     * 每个 LED 的大小,可以进行调整 
     */
    public final static int ROW = 7;
    public final static int COL = 7;
    /**
     * 每个 LED 的间隔
     */
    private final static int SEPARATOR = 1;
    private final static char FILL_CHAR = '#';
    private final static char SPACE_CHAR = ' ';
 
    /**
     * 工具方法,用于输出 LED
     * @param chs
     */
    public static void print(char[][] chs) {
        for (int i = 0; i < chs.length; i++) {
            for (int j = 0; j < chs[i].length; j++) {
                System.out.print(chs[i][j]);
            }
            System.out.println();
        }
    }
 
    /**
     * 根据数字得到 LED 显示数组
     * @param num *
     * @return
     */
    public char[][] getLED(String num) {
        char[] chs = num.toCharArray();
        char[][][] chsss = new char[chs.length][][];
        for (int i = 0; i < chs.length; i++) {
            chsss[i] = showLed(chs[i] - '0');
        }
        return putManyLed(chsss);
    }
 
    /**
     * 将多个 LED 组成一排
     * @param chsss
     * @return
     */
    private char[][] putManyLed(char[][][] chsss) {
        if (chsss.length < 1) {
            throw new IllegalArgumentException("LED is NULL!");
        }
        if (chsss.length == 1) {
            return chsss[0];
        }
        char[][] chss = new char[ROW][chsss.length * (COL + SEPARATOR)
                - SEPARATOR];
        for (int i = 0; i < chsss.length; i++) {
            int m = i * (COL + SEPARATOR);
            for (int j = 0; j < chsss[i].length; j++) {
                for (int k = 0; k < chsss[i][j].length; k++) {
                    chss[j][m + k] = chsss[i][j][k];
                }
            }
        }
        for (int i = 0; i < chss.length; i++) {
            for (int j = 0; j < chss[i].length; j++) {
                if (chss[i][j] != FILL_CHAR) {
                    chss[i][j] = SPACE_CHAR;
                }
            }
        }
        return chss;
    }
 
    /**
     * 
     * @param num
     * @return
     */
    private char[][] showLed(int num) {
        boolean[] b = getLed(num);
        char[][] chs = new char[ROW][COL];
        if (b[0])
            for (int i = 0; i < COL; I++)
                chs[0][i] = FILL_CHAR;
        if (b[1])
            for (int i = 0; i <= ROW / 2; I++)
                chs[i][COL - 1] = FILL_CHAR;
        if (b[2])
            for (int i = ROW / 2; i < ROW; I++)
                chs[i][COL - 1] = FILL_CHAR;
        if (b[3])
            for (int i = 0; i < COL; I++)
                chs[ROW - 1][i] = FILL_CHAR;
        if (b[4])
            for (int i = ROW / 2; i < ROW; I++)
                chs[i][0] = FILL_CHAR;
        if (b[5])
            for (int i = 0; i <= ROW / 2; I++)
                chs[i][0] = FILL_CHAR;
        if (b[6])
            for (int i = 0; i < COL; I++)
                chs[ROW / 2][i] = FILL_CHAR;
        return chs;
    }
 
    /**
     *
     * 译码器 
     * 
     *       0 
     *    #######
     *    #     # 1
     *  5 #  6  #
     *    #######
     *    #     #
     *  4 #     # 2
     *    #######
     *       3
     *      
     * 0 表示 leds[0],若为 true 表示该 LED 显示,否则不显示
     * 
     * @param num
     * @return
     */
    private boolean[] getLed(int num) {        
        boolean a = (num & 8) >>> 3 == 1;
        boolean b = (num & 4) >>> 2 == 1;
        boolean c = (num & 2) >>> 1 == 1;
        boolean d = (num & 1) == 1;
        boolean[] leds = new boolean[7];
        leds[0] = a | (!a & c) |(!a & !b & !c & !d) | (!a & b & !c & d);
        leds[1] = a | (!a & !b) | (!a & b & c & d) | (!a & b & !c & !d);   
        leds[2] = a | b | !c | d;
        leds[3] = a | (!a & !b & c) | (!a & !b & !c & !d) | (!a & b & c & !d) |
                  (!a & b & !c & d);
        leds[4] = (!a & c & !d) | (!b & !c & !d);
        leds[5] = a | (!a & !b & !c & !d) | (!a & b & !d) | (!a & b & !c & d);
        leds[6] = a | (!a & !b & c) | (!a & b & !c) | (!a & b & c & !d);
        return leds;
    }
}

#######       # ####### ####### #     # ####### ####### ####### ####### #######
#     #       #       #       # #     # #       #             # #     # #     #
#     #       #       #       # #     # #       #             # #     # #     #
#     #       # ####### ####### ####### ####### #######       # ####### #######
#     #       # #             #       #       # #     #       # #     #       #
#     #       # #             #       #       # #     #       # #     #       #
#######       # ####### #######       # ####### #######       # ####### #######

下面的部分涉及数字电路中译码电路、卡诺图等方面的知识,有兴趣的话可以去查找相关资料。

七段 LED 各段位的真值表如下:

A, B, C, D 表示数字的各二进制位
a, b, c, d, e, f, g 表示 LED 的各段,为 1 时该段显示,为 0 时该段不显示
 
      a
   #######
   #     # b
 f #  g  #
   #######
   #     # c
 e #     # 
   #######
      d
 
+---+---+---+---+---+  +---+---+---+---+---+---+---+
|   | A | B | C | D |  | a | b | c | d | e | f | g |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 |  | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 1 | 0 | 0 | 0 | 1 |  | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 2 | 0 | 0 | 1 | 0 |  | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 2 | 0 | 0 | 1 | 1 |  | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 4 | 0 | 1 | 0 | 0 |  | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 5 | 0 | 1 | 0 | 1 |  | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 6 | 0 | 1 | 1 | 0 |  | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 7 | 0 | 1 | 1 | 1 |  | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 8 | 1 | 0 | 0 | 0 |  | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+
| 9 | 1 | 0 | 0 | 1 |  | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+---+---+---+---+---+  +---+---+---+---+---+---+---+

根据这个真值表可以得出这个反映了数字二进制位与 LED 各段之间的逻辑关系:

\begin{aligned} a &= \left (A+B+C+ \overline{D} \right) \left (A+\overline{B}+C+D \right ) \\ b &= \left (A+\overline{B}+C+\overline{D} \right ) \left (A+\overline{B}+\overline{C}+D \right) \\ c &= \left (A+B+\overline{C}+D \right ) \\ d &= \left (A+B+C+\overline{D}\right ) \left (A+\overline{B}+C+D\right ) \left (A+\overline{B}+\overline{C}+\overline{D} \right ) \\ e &= \overline{A}\overline{B}C\overline{D} + \overline{A} \overline{B} C \overline{D} + \overline{A} B C \overline{D} + A \overline{B} \overline{C} \overline{D} \\ f &= \left (A+B+C+\overline{D}\right ) \left (A+B+\overline{C}+D\right ) \left (A+B+\overline{C}+\overline{D}\right ) \left (A+\overline{B}+\overline{C}+\overline{D} \right) \\ g &= \left (A+B+C+D \right ) \left (A+B+C+\overline{D}\right ) \left (A+\overline{B}+\overline{C}+\overline{D} \right) \end{aligned}

采用卡诺图化简后可以得到:

\begin{aligned} a &= A + \overline{A}C + \overline{A}\overline{B}\overline{C}\overline{D} + \overline{A}B\overline{C}D \\ b &= A + \overline{A}\overline{B} + \overline{A}BCD + \overline{A}B\overline{C}\overline{D} \\ c &= A + B + \overline{C} + D \\ d &= A + \overline{A}\overline{B}C + \overline{A}\overline{B}\overline{C}\overline{D} + \overline{A}B\overline{C}D + \overline{A}BC\overline{D} \\ e &= \overline{A}C\overline{D} + \overline{B}\overline{C}\overline{D} \\ f &= A + \overline{A}\overline{B}\overline{C}\overline{D} + \overline{A}B\overline{D} + \overline{A}B\overline{C}D \\ g &= A + \overline{A}\overline{B}C + \overline{A}B\overline{C} + \overline{A}BC\overline{D} \end{aligned}

上面代码中的 getLed 方法中的算法就是这样来的。

螺旋矩阵

public class SpiralMatrix {
 
    public final static int DOWN_FIRST = 0;
    public final static int RIGHT_FIRST = 1;
 
    public static void main(String[] args) {
 
        final int N = 9;
        final int DIRECT = RIGHT_FIRST;
 
        int[][] spiralMatrix = new int[N][N];
        int[] rc = { 0, 0 };
 
        for(int c = 0, n = 1, t = (N << 1) - 1; c < t; ) {
            int p = (c + 1) >> 1;
            while(p++ < N) {
                spiralMatrix[rc[0]][rc[1]] = n++;
                if(p == N) {
                    c++;
                }
                rc[(c & 1) ^ DIRECT] += 1 - (c & 2);
            }
        }
        output(spiralMatrix);
    }
 
    private static void output(int[][] matrix) {
        for(int i = 0; i < matrix.length; i++) {
            for(int j = 0; j < matrix[i].length; j++) {
                if(j > 0) {
                    System.out.print(' ');
                }
                System.out.printf("%2d", matrix[i][j]);
            }
            System.out.println();
        }
    }
}
 
 1  2  3  4  5  6  7  8  9
32 33 34 35 36 37 38 39 10
31 56 57 58 59 60 61 40 11
30 55 72 73 74 75 62 41 12
29 54 71 80 81 76 63 42 13
28 53 70 79 78 77 64 43 14
27 52 69 68 67 66 65 44 15
26 51 50 49 48 47 46 45 16
25 24 23 22 21 20 19 18 17

阿基米德螺线

根据阿基米德螺线参数方程画图:

\begin{aligned} r &= 10 \times (1 + t) \\ x &= r \times \cos(t \times 360) \\ y &= r \times \sin(t \times 360) \end{aligned}

import java.io.PrintStream;

public class ArchimedeanSpiral {
 
    public static void main(String[] args) {
        CoordinateCanvas canvas = new CoordinateCanvas(22);
        for (double t = 0; t < 13; t += 0.00005) {
            double r = 1.8 * t;
            double radians = Math.toRadians(t * 180);
            double x = r * Math.cos(radians);
            double y = r * Math.sin(radians);
            canvas.putPoint(x, y);
        }
        canvas.output(System.out, false);
    }
}
 
class CoordinateCanvas {
 
    private final int positiveX;
    private final int negativeX;
    private final int positiveY;
    private final int negativeY;
    private final char plotChar;
 
    private final char[][] canvas;
 
    public CoordinateCanvas(int quadrant) {
        this(quadrant, '#');
    }
 
    public CoordinateCanvas(int quadrant, char plotChar) {
        this(quadrant, quadrant, plotChar);
    }
 
    public CoordinateCanvas(int x, int y) {
        this(x, y, '#');
    }
 
    public CoordinateCanvas(int x, int y, char plotChar) {
        this(x, x, y, y, plotChar);
    }
 
    public CoordinateCanvas(int positiveX, int negativeX,
            int positiveY, int negativeY, char plotChar) {
        this.positiveX = Math.abs(positiveX);
        this.negativeX = Math.abs(negativeX);
        this.positiveY = Math.abs(positiveY);
        this.negativeY = Math.abs(negativeY);
        this.plotChar = plotChar;
        this.canvas = new char[getHeigh()][getWidth()];
        init();
    }
 
    public void putPoint(double x, double y) {
        int ix = (int)Math.round(x);
        int iy = (int)Math.round(y);
        if (ix > positiveX || ix < -negativeX) {
            return;
        }
        if (iy > positiveY || iy < -negativeY) {
            return;
        }
        canvas[positiveY - iy][negativeX + ix] = plotChar;
    }
 
    public void output(PrintStream output, boolean line) {
        for (int i = 0; i < canvas.length; i++) {
            for (int j = 0; j < canvas[i].length; j++) {
                output.print(canvas[i][j]);
                if (line && i == negativeY) {
                    output.print('─');
                }
                if (line && j == negativeX) {
                    output.print('│');
                }
                if (line && i == negativeY && j == negativeX) {
                    output.print('┼');
                }
            }
            output.println();
        }
    }
 
    private int getHeigh() {
        return positiveY + negativeY + 1;
    }
 
    private int getWidth() {
        return positiveX + negativeX + 1;
    }
 
    private void init() {
        for (int i = 0; i < canvas.length; i++) {
            for (int j = 0; j < canvas[i].length; j++) {
                canvas[i][j] = ' ';
            }
        }
    }
}
 
 
 
               ##############                
            ####            ####             
          ###                  ###           
         ##       ########       ##          
       ###    #####      #####    ###        
      ##    ###              ###    ##       
     ##    ##                  ##    ##      
    ##   ###     ##########     ###   ##     
   ##   ##    ####        ####    ##   #     
   #   ##    ##              ##    ##  ##    
  ##  ##   ###     ######     ##    #   ##   
 ##   #   ##    ####    ####   ###  ##   #   
 #   ##  ##    ##          ##    #   ##  ##  
##  ##   #   ###            ##   ##   #   #  
#   #   ##  ##    ########   ##   ##  ##  #  
#   #  ##   #    ##      ##   ##   #   #  ## 
#  ##  #   ##  ###        ##   ##  ##  #   # 
   #   #  ##   #     ##    ##   #   #  ##  # 
   #  ##  #   ##  #######   ##  #   #   #  # 
  ##  #   #   #   #     ##   #  ##  #   #  ##
  #   #   #  ##  ##      ##  #   #  #   #   #
  #   #  ##  #   #  ###   #  #   #  #   #   #
  #   #  #   #   #  # #  ##  #   #  #   #   #
  #   #  #   #   #  #    #   #   #  #   #   #
  #   #  ##  #   #  ##  ##   #  ##  #   #  ##
  #   #   #  #   #   ####   ##  #   #   #  # 
  ##  #   #  ##  ##        ##   #  ##  ##  # 
   #  ##  #   #   ##      ##   ##  #   #   # 
   #   #  ##  ##   ########   ##   #   #  ## 
   #   #   #   ##            ##   ##  ##  #  
   ##  ##  ##   ##          ##   ##   #   #  
    #   #   ##   ####    ####   ##   ##  ##  
    ##  ##   ##     ######     ##   ##   #   
     #   ##   ###             ##   ##   ##   
     ##   ##    ##          ###   ##   ##    
      ##   ##    ############    ##   ##     
       ##   ###       ##        ##   ##      
        ##    ##              ###   ##       
         ##    ####        ####    ##        
          ###     ##########      ##         
            ##                  ###          
             ####             ###            
                #####     #####              
                    #######

约瑟夫问题

算法过程参考 Ronald L.Graham, Donald E.Knuth, Oren Patashnik 编写的 Concrete Mathematics(《具体数学》)第 1.3 节。

public class JosephusTest {
 
    public static void main(String[] args) {
        System.out.println(josephus2(5));
    }
 
    /**
     * 计算约瑟夫问题,间隔 1 个的值,这个有数学解法,即:
     * 将最高位的 1 取出,将数值左移一位,再将最高位的那
     * 个 1 添至最低位即可。
     * 例如 1010 的约瑟夫间隔为 1 的值为 0101(5)
     * Concrete Mathematics 书中并未加 1,那是由于其第一个
     * 从 0 开始的,如果从 1 开始时需要加 1
     */
    public static int josephus2(int count) {
        int n = (count ^ leftOneBit(count)) << 1;
        return (n | 1) + 1;
    }
 
    /**
     * 获得一个数最高位为 1 的值,比如 1111(15)
     * 的最高位值为 1000
     * 算法参考:Hacker's Delight 一书第三章
     */
    public static int leftOneBit(int num) {
        for (int i = 0; i < 5; i++) {
            num |= (num >> (1 << i));
        }
        return num - (num >>> 1);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 阿基米德(公元前287年—公元前212年),伟大的古希腊哲学家、百科式科学家、数学家、物理学家、力学家,静态力学和...
    我是石一钏阅读 1,512评论 0 2
  • 数据结构与算法 1.算法的有穷性是指( )。答案:A A)算法程序的运行时间是有限的 B)算法程序所处理的数据量是...
    织梦学生阅读 3,349评论 1 15
  • 一切有都尘埃落定,未来之门即将开启…… 时间是把杀猪刀,我也无法避免,而这一年的心路历程更是一波三折。 也曾沉湎与...
    爱文艺的女流氓阅读 206评论 1 1
  • 我想要一只兔子 但我养不起 事实上 连养自己都很乏力 请别让我承当生命 因为真的很沉重 说着像个笑话 谁又会在乎 ...
    栖支阅读 197评论 0 2
  • #!/usr/bin/env python def qsort3(alist, lower, upper): pr...
    zhangwj999阅读 405评论 0 1