前言

有些數字就是特別的。圓周率 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 上的黃金比例相關影片
  • 嘗試修改黃金角動畫中的角度值,感受不同角度的排列效果