本文所有程序的運行環(huán)境為控制臺,通過字符在控制臺中輸出一些有趣的圖案。
控制臺輸出五角星
注:本程序首先發(fā)布在 CSDN 我的博客 上,這里作為引用。
剛開始學習語言的時候,老師都很喜歡讓我們在控制臺上使用“*”來輸出某些圖形,比如:三角形、菱形、方塊什么的,這主要能讓我們能更好地理解循環(huán)的語法和邏輯思維能力。
這些圖形基本上都是直線的,所以在控制臺上可以很方便地進行輸出,不知道有沒有人想過在控制臺上輸出一條有斜率的斜線呢?
在 AWT 上使用 Graphics 對象來做這些事的話是相當簡單的,其中有內(nèi)置的 drawLine 方法,告訴它 4 個點的坐標就可以很方便地在界面上畫出一條斜線,但是在控制臺我們?nèi)绾巫龅竭@些呢?
閑著沒事的時候,寫了一個在控制臺上輸出五角星的小程序,不過這里面有點小小的 bug,bug 是什么、究竟在什么地方呢?先賣個關(guān)子吧,我弄了好久頭都大了不想去想了,如果發(fā)現(xiàn)的話或者是解決了的話,別忘記告訴我。
public class Test {
public static void main(String[] args) {
// 畫一個半徑為10,旋轉(zhuǎn)為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; // 五角星逆時針旋轉(zhuǎn)角度
private final int X; // 用于生成畫圖數(shù)組
private final int Y; // 用于生成畫圖數(shù)組
/**
* 構(gòu)造一個Pentagram對象
* @param radius 五角星的半徑
* @param rotation 五角星的逆時針旋轉(zhuǎn)度數(shù)
* @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;
}
// 在方形的字符數(shù)組中指定兩點畫線條
// 對圖形數(shù)組進行初始化,填充空格
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;
}
// 根據(jù)角度求正弦值,保留兩位小數(shù)
private double msin(float a) {
return ((int) (Math.sin(Math.toRadians(a + ROTATION)) * 100)) / 100.0;
}
// 根據(jù)角度求余弦值,保留兩位小數(shù)
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;
}
/**
* 根據(jù)兩個點畫線在二維字符數(shù)組上畫線
* @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;
}
// 獲得直線方程的兩個系數(shù)
double a = (y1 - y2) / (x1 - x2);
double b = y1 - a * x1;
// 根據(jù) x 方向的值求出 y 值,并填充圖形
for (int i = (int) Math.round(x1); i <= (int) Math.round(x2); i++) {
// 根據(jù)直線方程 y = ax + b,求 y
int y = (int) Math.round(a * i + b);
// 因為 y 和 i 算出來的結(jié)果有可能是負數(shù),
// 為了采用數(shù)組來表示坐標,做了以下變換
// 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 方向進行填充,便于減少間距問題產(chǎn)生的字符空檔
if (y1 > y2) {
double t = x1;
x1 = x2;
x2 = t;
t = y1;
y1 = y2;
y2 = t;
}
// 根據(jù) y 方向的值求出 x 值,并填充圖形
for (int i = (int) Math.round(y1); i <= (int) Math.round(y2); i++) {
// 根據(jù) 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,旋轉(zhuǎn)度為 0 度的五角星控制臺輸出。

下圖是一個半徑為 20,旋轉(zhuǎn)度為 15 度的五角星控制臺輸出。

菱形繪制及思路
先上代碼:
public class Test {
public static void main(String[] args) {
printDiamond(7, true);
System.out.println();
printDiamond(7, false);
}
/**
* 輸出菱形
* @param line 菱形的行數(shù)
* @param isSolid 是否為實心
*/
private static void printDiamond(int line, boolean isSolid) {
// 行數(shù)應為奇數(shù),若為偶數(shù)時則向上取奇數(shù)
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();
}
}
}
分析:菱形是一個上下、左右對稱的圖形,行列可以按照下面的方式來進行循環(huán):
j=
-3 -2 -1 0 1 2 3
i= +---+---+---+---+
-3 | | | | * |
+---+---+---+---+---+
-2 | | | * | * | * |
+---+---+---+---+---+---+
-1 | | * | * | * | * | * |
+---+---+---+---+---+---+---+
0 | * | * | * | * | * | * | * |
+---+---+---+---+---+---+---+
1 | | * | * | * | * | * |
+---+---+---+---+---+---+
2 | | | * | * | * |
+---+---+---+---+---+
3 | | | | * |
+---+---+---+---+
行數(shù)為 ,則
,這個數(shù)值很有用處,暫且稱為
。
和
的循環(huán)起始均為
,而
的終止循環(huán)為
,
的終止循環(huán)條件理應為
,注意右邊,我特意把它挖空了,因為右邊的都是空格,為了優(yōu)化程序就沒有必要輸出了,這樣
的循環(huán)終止條件與
是有密切關(guān)系的,為
,即:當
時,
,因此
只要從
循環(huán)到
就可以了。
再看看 * 位置上的規(guī)律,注意最左邊的 *,在 時輸出的是空格,小于等于
時,輸出的是
*。
- 當
,
坐標上,
,
是大于
的,因此輸出空格
- 當
,
坐標上,
,
是不大于
的,因此輸出
*
如果需要的是空心菱心,只要把判斷條件設為 時才輸出
*,否則輸出空格。
因此,這樣我們就利用于圖形的對稱性完成了菱形的輸出。
楊輝三角形
public class Yanghui {
public static void main(String[] args) {
Yanghui yang = new Yanghui();
yang.printYanghuiTriangle(13);
}
/**
* 生成指定行數(shù)的楊輝三角形
*
* @param lines 楊輝三角形的行數(shù)
*/
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);
}
}
/**
* 根據(jù)指定行數(shù)的楊輝三角形,計算其中最大數(shù)字的長度
* @param lines 楊輝三角形的行數(shù)
* @return 最大數(shù)字的長度
*/
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 階乘計算的起始數(shù)字
* @param num 階乘計算的終止數(shù)字
* @return 階乘計算結(jié)果
*/
private long factorial(int start, int num) {
long result = start > 0 ? start : 1L;
while(num > start) {
result *= num--;
}
return result;
}
/**
* 根據(jù)指定數(shù)字計算數(shù)字的長度
* @param num 數(shù)字
* @return 數(shù)字的長度
*/
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
正弦函數(shù)
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++) {
// 將數(shù)組索引映射為橫坐標值
double k = (i - canvas.getWidth() / 2) * xRatio;
// 將sin值映射為數(shù)組索引
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 = ' ';
/**
* 構(gòu)建一塊畫布
* @param height
* @param width
*/
public Canvas(int height, int width) {
// 由于需要畫坐標軸,所以得采用奇數(shù)
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 的大小,可以進行調(diào)整
*/
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();
}
}
/**
* 根據(jù)數(shù)字得到 LED 顯示數(shù)組
* @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;
}
}
####### # ####### ####### # # ####### ####### ####### ####### #######
# # # # # # # # # # # # # #
# # # # # # # # # # # # # #
# # # ####### ####### ####### ####### ####### # ####### #######
# # # # # # # # # # # # #
# # # # # # # # # # # # #
####### # ####### ####### # ####### ####### # ####### #######
下面的部分涉及數(shù)字電路中譯碼電路、卡諾圖等方面的知識,有興趣的話可以去查找相關(guān)資料。
七段 LED 各段位的真值表如下:
A, B, C, D 表示數(shù)字的各二進制位
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 |
+---+---+---+---+---+ +---+---+---+---+---+---+---+
根據(jù)這個真值表可以得出這個反映了數(shù)字二進制位與 LED 各段之間的邏輯關(guān)系:
采用卡諾圖化簡后可以得到:
上面代碼中的 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
阿基米德螺線
根據(jù)阿基米德螺線參數(shù)方程畫圖:
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(《具體數(shù)學》)第 1.3 節(jié)。
public class JosephusTest {
public static void main(String[] args) {
System.out.println(josephus2(5));
}
/**
* 計算約瑟夫問題,間隔 1 個的值,這個有數(shù)學解法,即:
* 將最高位的 1 取出,將數(shù)值左移一位,再將最高位的那
* 個 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;
}
/**
* 獲得一個數(shù)最高位為 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);
}
}