前言
有些數字就是特別的。圓周率 pi 描述了圓的完美,自然對數的底 e 掌管了指數增長的律動,而黃金比例 phi——1.6180339887…——則是「和諧比例」的代名詞。
從古希臘的帕德嫩神殿到達文西的維特魯威人,從向日葵的種子排列到颱風的雲帶——黃金比例和它的好搭檔費波那契數列,不斷地在自然和藝術中被發現。
這篇文章不打算神化黃金比例(它沒有某些流行文化說得那麼無所不在),但我想帶你看看它真的很美、很有用的那些地方。我們會用 p5.js 來視覺化費波那契螺旋,理解為什麼 phi 出現在向日葵裡,以及如何在創意編程中運用這些概念。
黃金比例 phi
定義
黃金比例的定義來自一個簡單的比例關係:
把一條線段分成長短兩段 a 和 b(a > b),使得:
(a + b) / a = a / b = φ
也就是說:整體對長邊的比,等於長邊對短邊的比。解這個方程:
φ = (1 + √5) / 2 ≈ 1.6180339887...
phi 是一個無理數,它的小數位永遠不會重複。
phi 的奇妙性質
phi 有一大堆令人驚訝的數學性質:
φ² = φ + 1 (平方等於自己加一)
1/φ = φ - 1 (倒數等於自己減一)
φ = 1 + 1/(1 + 1/(1 + 1/(1 + ...))) (無窮連分數)
φ = √(1 + √(1 + √(1 + √(1 + ...)))) (無窮巢狀根號)
其中「φ² = φ + 1」這個性質特別重要——它意味著一個黃金比例的矩形,切掉一個正方形之後,剩下的小矩形仍然是黃金比例。這就是黃金螺旋的幾何來源。
費波那契數列
定義
費波那契數列的規則很簡單:每一個數是前兩個數的和。
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ...
與黃金比例的關聯
費波那契數列的相鄰兩項之比會趨近黃金比例:
1/1 = 1.000
2/1 = 2.000
3/2 = 1.500
5/3 = 1.667
8/5 = 1.600
13/8 = 1.625
21/13 = 1.615
34/21 = 1.619
55/34 = 1.618...
越往後越接近 phi = 1.618…。這不是巧合——可以嚴格證明這個極限就是黃金比例。
p5.js 視覺化:費波那契矩形
function setup() {
createCanvas(800, 500);
background(30);
drawFibonacciRectangles();
}
function drawFibonacciRectangles() {
let fib = [1, 1, 2, 3, 5, 8, 13];
let scale_ = 30;
// 起始位置
let x = 300, y = 250;
let directions = [
{ dx: 1, dy: 0 }, // 右
{ dx: 0, dy: -1 }, // 上
{ dx: -1, dy: 0 }, // 左
{ dx: 0, dy: 1 } // 下
];
let corners = []; // 記錄弧線的角點
let currentX = x, currentY = y;
for (let i = 0; i < fib.length; i++) {
let size = fib[i] * scale_;
let dir = directions[i % 4];
// 計算矩形位置(根據生長方向調整)
let rx, ry;
if (i === 0) {
rx = currentX;
ry = currentY;
} else {
// 根據前一個矩形和方向來決定位置
rx = currentX;
ry = currentY;
if (dir.dx === 1) {
// 向右生長
} else if (dir.dy === -1) {
ry = currentY - size;
} else if (dir.dx === -1) {
rx = currentX - size;
ry = currentY - size;
} else {
rx = currentX - size;
}
}
// 畫矩形
let hue = map(i, 0, fib.length, 0, 200);
stroke(hue + 55, 100, 200);
strokeWeight(2);
noFill();
rect(rx, ry, size, size);
// 在矩形內畫四分之一弧
stroke(255, 80, 80);
strokeWeight(2);
let arcStartAngle, arcEndAngle;
let arcCX, arcCY;
// 根據方向決定弧的位置
switch (i % 4) {
case 0: arcCX = rx + size; arcCY = ry + size;
arcStartAngle = PI; arcEndAngle = PI + HALF_PI; break;
case 1: arcCX = rx + size; arcCY = ry;
arcStartAngle = HALF_PI; arcEndAngle = PI; break;
case 2: arcCX = rx; arcCY = ry;
arcStartAngle = 0; arcEndAngle = HALF_PI; break;
case 3: arcCX = rx; arcCY = ry + size;
arcStartAngle = -HALF_PI; arcEndAngle = 0; break;
}
arc(arcCX, arcCY, size 2, size 2, arcStartAngle, arcEndAngle);
// 更新位置
// ... (省略複雜的位置更新邏輯)
// 標註數字
fill(255, 150);
noStroke();
textSize(max(10, size / 3));
textAlign(CENTER, CENTER);
text(fib[i], rx + size / 2, ry + size / 2);
}
}
向日葵的秘密
這是黃金比例在自然界中最著名的例子。向日葵的種子排列方式不是隨機的——每顆新種子相對於前一顆旋轉了一個固定角度,而這個角度就是黃金角(Golden Angle):
黃金角 = 360° / φ² = 360° × (1 - 1/φ) ≈ 137.5077...°
為什麼是這個角度?因為它是「最不理性的」旋轉角——它讓每顆新種子都不會正好對齊之前的任何種子,從而實現最均勻的空間填充。
p5.js 向日葵種子排列
let n = 0;
let maxN = 500;
let goldenAngle = 137.5077; // 度
function setup() {
createCanvas(600, 600);
background(30);
angleMode(DEGREES);
}
function draw() {
background(30);
translate(width / 2, height / 2);
for (let i = 0; i < n; i++) {
let angle = i * goldenAngle;
let r = sqrt(i) * 8; // sqrt 讓密度均勻
let x = r * cos(angle);
let y = r * sin(angle);
// 根據螺旋臂的位置著色
let hueVal = (angle * 0.1) % 360;
let size = map(i, 0, maxN, 8, 3);
// 簡單的色彩映射
let red = sin(hueVal 0.0174) 100 + 155;
let green = sin((hueVal + 120) 0.0174) 80 + 120;
let blue = sin((hueVal + 240) 0.0174) 60 + 80;
fill(red, green, blue);
noStroke();
ellipse(x, y, size, size);
}
if (n < maxN) n++;
// 顯示資訊
fill(255);
textSize(14);
textAlign(LEFT);
text("種子數: " + n, -280, -270);
text("黃金角: " + nf(goldenAngle, 3, 2) + "°", -280, -250);
}
為什麼 137.5 度最好?
讓我們做一個比較實驗——嘗試不同的角度:
let angle = 137.5; // 試試改成 137, 138, 120, 90 等
function setup() {
createCanvas(600, 600);
background(30);
angleMode(DEGREES);
}
function draw() {
background(30);
translate(width / 2, height / 2);
// 用滑鼠 X 座標控制角度
angle = map(mouseX, 0, width, 100, 180);
for (let i = 0; i < 500; i++) {
let a = i * angle;
let r = sqrt(i) * 8;
let x = r * cos(a);
let y = r * sin(a);
fill(255, 180, 60);
noStroke();
ellipse(x, y, 5, 5);
}
fill(255);
textSize(16);
text("角度: " + nf(angle, 3, 2) + "°", -280, -270);
// 標示黃金角的位置
stroke(255, 80, 80);
let gx = map(137.5077, 100, 180, 0, width) - width / 2;
line(gx, 280, gx, 290);
}
移動滑鼠你會看到:
- 角度 = 整數(如 137° 或 138°):種子會排成明顯的直線束,浪費了大量空間
- 角度 = 簡單分數 × 360°(如 120° = 360°/3):只有 3 條螺旋臂
- 角度 ≈ 137.508°(黃金角):最均勻的分佈,沒有明顯的間隙
這就是為什麼自然選擇了黃金角——它是最有效率的空間填充策略。
費波那契螺旋動畫
一個完整的費波那契螺旋動畫,帶有漸進生長效果:
let points = [];
let maxPoints = 2000;
let goldenAngle;
function setup() {
createCanvas(700, 700);
goldenAngle = PI * (3 - sqrt(5)); // 弧度制的黃金角
}
function draw() {
background(30, 30, 30, 40);
translate(width / 2, height / 2);
// 逐步增加點數
let currentMax = min(frameCount * 2, maxPoints);
for (let i = 0; i < currentMax; i++) {
let angle = i * goldenAngle;
let r = sqrt(i) * 5;
let x = r * cos(angle);
let y = r * sin(angle);
// 大小隨距離變化
let size = map(sqrt(i), 0, sqrt(maxPoints), 10, 2);
// 脈搏效果
let pulse = sin(frameCount 0.03 - i 0.02) * 0.3 + 1;
size *= pulse;
// 顏色:根據索引漸變
let t = i / maxPoints;
let red = lerp(255, 80, t);
let green = lerp(200, 180, t);
let blue = lerp(50, 255, t);
let alpha = map(i, 0, currentMax, 255, 150);
fill(red, green, blue, alpha);
noStroke();
ellipse(x, y, size, size);
}
}
黃金比例在設計中的應用
黃金矩形分割
在介面設計和攝影構圖中,黃金比例常被用來決定元素的比例:
function setup() {
createCanvas(800, 500);
}
function draw() {
background(30);
let phi = (1 + sqrt(5)) / 2;
// 黃金矩形
let w = 700;
let h = w / phi;
let x = 50, y = (height - h) / 2;
stroke(255, 80, 80);
strokeWeight(2);
noFill();
rect(x, y, w, h);
// 黃金分割點
let splitX = x + w / phi;
stroke(80, 180, 255);
line(splitX, y, splitX, y + h);
// 繼續分割
let smallW = w - w / phi;
let splitY = y + smallW;
stroke(80, 255, 180);
line(splitX, splitY, x + w, splitY);
// 標注比例
fill(255);
textSize(14);
text("a = " + nf(w / phi, 1, 1), x + w / phi / 2 - 20, y + h + 25);
text("b = " + nf(smallW, 1, 1), splitX + smallW / 2 - 20, y + h + 25);
text("a/b = " + nf((w/phi) / smallW, 1, 4) + " ≈ φ", x + w / 2 - 40, y - 10);
}
黃金螺旋在構圖中的運用
function drawGoldenSpiral(x, y, w, h, depth) {
if (depth <= 0 || w < 2) return;
let phi = (1 + sqrt(5)) / 2;
// 畫矩形
stroke(255, 255, 255, 100);
strokeWeight(1);
noFill();
rect(x, y, w, h);
// 畫四分之一圓弧
stroke(255, 80, 80);
strokeWeight(2);
// 弧的位置和角度取決於當前的分割方向
// ... (省略方向邏輯,概念同費波那契矩形)
// 遞迴分割
let newW = w / phi;
drawGoldenSpiral(x + w - newW, y, newW, newW, depth - 1);
}
phi 在音樂中的出現
有趣的是,黃金比例也出現在音樂中。鋼琴的八度有 13 個半音(8 白鍵 + 5 黑鍵),而 5、8、13 都是費波那契數。雖然這可能只是巧合,但一些作曲家(如 Bartok 和 Debussy)確實刻意在作品中使用黃金比例來決定高潮的位置。
程式碼藝術:費波那契花
結合以上所有概念,做一個漂亮的費波那契花朵動畫:
let goldenAngle;
let totalSeeds = 0;
function setup() {
createCanvas(700, 700);
goldenAngle = PI * (3 - sqrt(5));
colorMode(HSB, 360, 100, 100, 100);
}
function draw() {
background(0, 0, 12, 8);
translate(width / 2, height / 2);
totalSeeds = min(totalSeeds + 3, 1000);
for (let i = 0; i < totalSeeds; i++) {
let angle = i * goldenAngle;
let r = sqrt(i) * 6;
// 呼吸效果
let breathe = sin(frameCount 0.02 + i 0.005);
r = (1 + breathe 0.05);
let x = r * cos(angle);
let y = r * sin(angle);
// 顏色隨角度和距離變化
let hue = (angle 30 + frameCount 0.5) % 360;
let sat = 70 + breathe * 20;
let bri = map(r, 0, 250, 100, 60);
let size = map(i, 0, 1000, 12, 3);
fill(hue, sat, bri, 80);
noStroke();
ellipse(x, y, size, size);
}
// 中心的光點
fill(50, 30, 100, 50);
ellipse(0, 0, 20, 20);
}
小結
黃金比例和費波那契數列是數學與自然之間最美的橋樑之一。向日葵用黃金角來最大化光照面積,鸚鵡螺用對數螺旋來保持成長中的比例,松果用費波那契數來排列鱗片——這些都不是巧合,而是自然在億萬年的演化中找到的最優解。
對程式藝術家來說,phi 和費波那契數列是一個取之不盡的靈感來源。無論是做構圖、排版、配色、還是生成式藝術,它們都能為你的作品注入一種難以言喻的和諧感。
不過我也要提醒:黃金比例不是萬靈丹。很多關於「黃金比例出現在 XX 建築/畫作/Logo 中」的說法都是事後牽強附會。真正重要的不是數字本身,而是理解「為什麼」它在某些場景下有效。
延伸閱讀
- Vi Hart 的 YouTube 系列 “Doodling in Math: Spirals, Fibonacci, and Being a Plant”
- Keith Devlin 的書《The Man of Numbers: Fibonacci’s Arithmetic Revolution》
- Mario Livio 的書《The Golden Ratio: The Story of Phi, the World’s Most Astonishing Number》
- Numberphile 上的黃金比例相關影片
- 嘗試修改黃金角動畫中的角度值,感受不同角度的排列效果