在無圖形庫的純文字控制台環境中,利用字元矩陣近似繪製數學函數曲線,是經典的程式設計練習。本文詳細解析兩種繪製餘弦曲線與兩種繪製正弦曲線的演算法,涵蓋反三角函數定域、座標映射、對稱性利用與週期性延伸等核心技巧。
acos(x) 或 asin(x) 回傳 [0, π],乘 10 後取整得到 0~31 範圍,再對稱擴展至 0~62。for (i = 1.0; i >= -0.4; i -= 0.1) { // 垂直掃描,從 1 降至 -0.4
j = (int)(acos(i) * 10); // 左側星號欄位
for (k = 0; k < j; k++) putchar(' ');
putchar('*');
for (; k < 62 - j; k++) putchar(' '); // 中間空白
putchar('*'); // 右側對稱星號
putchar('\n');
}
特點:直接利用 acos 定義域 [-1,1] 對應 [π,0],垂直掃描列印雙側曲線。
for (i = 1.0; i >= -1.0; i -= 0.1) {
k = (int)(acos(i) * 10);
for (j = 0; j < 62; j++) {
if (j == k || j == 62 - k) putchar('*');
else putchar(' ');
}
putchar('\n');
}
優點:單一水平掃描迴圈,邏輯清晰,易擴展多曲線疊加。
// 正半週:asin 定義域 [0,1] → [0, π/2]
for (i = 1.0; i > 0.0; i -= 0.1) {
k = (int)(asin(i) * 10);
for (j = 0; j < k; j++) putchar(' ');
putchar('*');
for (; j < 31 - k; j++) putchar(' ');
putchar('*');
putchar('\n');
}
// 負半週:asin 定義域 [0,-1] → [0, -π/2],需平移 33 格
for (i = 0.0; i >= -1.0; i -= 0.1) {
k = (int)(asin(i) * 10); // k 為負或零
for (j = 0; j < k + 33; j++) putchar(' ');
putchar('*');
for (; j < 62 - k; j++) putchar(' ');
putchar('*');
putchar('\n');
}
關鍵:負半週星號位置須整體右移半個週期(31 格)再加上對稱偏移。
for (i = 1.0; i >= -1.0; i -= 0.1) {
k = (int)(asin(i) * 10); // k ∈ [-15, 15] 約略
for (j = 0; j <= 62; j++) {
// 三個對稱點:原點、水平鏡像、半週期平移
if (j == k || j == 62 + k || j == 31 - k)
putchar('*');
else
putchar(' ');
}
putchar('\n');
}
數學依據:sin(θ) = sin(π-θ)(水平對稱),sin(θ+π) = -sin(θ)(半週期翻轉)。三條件涵蓋一個完整週期的所有極值點。
#include <stdio.h>
#include <math.h>
#define WIDTH 62
#define STEP 0.1
void draw_cosine(void) {
puts("=== Cosine Curve ===");
for (double y = 1.0; y >= -1.0; y -= STEP) {
int k = (int)(acos(y) * 10);
for (int x = 0; x <= WIDTH; ++x) {
putchar((x == k || x == WIDTH - k) ? '*' : ' ');
}
putchar('\n');
}
}
void draw_sine(void) {
puts("\n=== Sine Curve ===");
for (double y = 1.0; y >= -1.0; y -= STEP) {
int k = (int)(asin(y) * 10);
for (int x = 0; x <= WIDTH; ++x) {
putchar((x == k || x == WIDTH + k || x == WIDTH/2 - k) ? '*' : ' ');
}
putchar('\n');
}
}
int main(void) {
draw_cosine();
draw_sine();
return 0;
}
| 需求 | 實作策略 |
|---|---|
| 平滑度提升 | 減小 STEP 至 0.05,WIDTH 擴至 120,配合 ANSI 捲動 |
| 多函數疊加 | 建立字元緩衝區 char buf[H][W],合成後一次列印 |
| 座標軸與刻度 | 預先在緩衝區繪製 X/Y 軸、標註關鍵角度 (0, π/2, π...) |
| 動畫效果 | 配合 usleep + printf("\033[H") 刷新畫面 |
| 高精度繪圖 | 改用 Bresenham 演算法在字元格內插值,減少鋸齒 |
acos/asin 引數必須在 [-1,1],迴圈邊界嚴格控制,避免浮點誤差導致 NaN。(int)(acos(y)*10) 向零取整,關鍵點可能偏移 1 格,可改用 lround 四捨五入。62 + k 若 k 為負可能小於 0,需加邊界檢查 if (idx >= 0 && idx <= WIDTH)。這些範例雖屬「玩具程式」,卻濃縮了:
建議讀者以此為基礎,嘗試繪製 tan(x)、e^-x sin(x) 阻尼震盪、李薩茹曲線等進階圖形,並嘗試移植至 SDL/OpenGL 進行像素級真實繪圖對比。