本文所有程序的运行环境为控制台,通过字符在控制台中输出一些有趣的图案。
控制台输出五角星
注:本程序首先发布在 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 度的五角星控制台输出。
下图是一个半径为 20,旋转度为 15 度的五角星控制台输出。
菱形绘制及思路
先上代码:
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 | | | | * |
+---+---+---+---+
行数为 ,则 ,这个数值很有用处,暂且称为 。
和 的循环起始均为 ,而 的终止循环为 , 的终止循环条件理应为 ,注意右边,我特意把它挖空了,因为右边的都是空格,为了优化程序就没有必要输出了,这样 的循环终止条件与 是有密切关系的,为 ,即:当 时,,因此 只要从 循环到 就可以了。
再看看 *
位置上的规律,注意最左边的 *
,在 时输出的是空格,小于等于 时,输出的是 *
。
- 当 , 坐标上,, 是大于 的,因此输出空格
- 当 , 坐标上,, 是不大于 的,因此输出
*
如果需要的是空心菱心,只要把判断条件设为 时才输出 *
,否则输出空格。
因此,这样我们就利用于图形的对称性完成了菱形的输出。
杨辉三角形
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 各段之间的逻辑关系:
采用卡诺图化简后可以得到:
上面代码中的 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
阿基米德螺线
根据阿基米德螺线参数方程画图:
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);
}
}