แฟร็กทัลกับ Python

สำรวจความงามอันไม่สิ้นสุดของ fractal ผ่านการเขียนโปรแกรม — บทเรียนเชิงโต้ตอบพร้อมภาพจำลองที่ปรับค่าได้

สนามทดลองแฟร็กทัล

พร้อมเรนเดอร์
x: 0.00, y: 0.00 zoom: 1.0x iter: 0
100
1.0x

แฟร็กทัลคืออะไร?

Fractal คือรูปทรงทางคณิตศาสตร์ที่มีคุณสมบัติ self-similarity — หมายความว่าไม่ว่าจะ zoom เข้าไปมากเท่าไร รูปแบบที่เห็นจะคล้ายคลึงกับโครงสร้างทั้งหมดเสมอ แนวคิดนี้ถูกนำเสนอโดย Benoît Mandelbrot ในทศวรรษ 1970 และเปลี่ยนแปลงวิธีที่เราเข้าใจรูปทรงในธรรมชาติ

ในธรรมชาติเราพบแฟร็กทัลอยู่ทุกหนทุกแห่ง — จากกิ่งก้านของต้นไม้ เส้นเลือดในร่างกาย แนวชายฝั่งทะเล ไปจนถึงเกล็ดหิมะ ทั้งหมดนี้ล้วนมีโครงสร้างที่ซ้ำตัวเอง (recursion) ในระดับที่ต่างกัน

ภาษา Python เป็นเครื่องมือที่เหมาะสมอย่างยิ่ง สำหรับการสร้างและสำรวจแฟร็กทัล เพราะมีไลบรารีจำนวนมากสำหรับการคำนวณตัวเลข (numerical computation) และการสร้างภาพ (visualization)

ทฤษฎีพื้นฐาน

เซตแมนเดลบรอท (Mandelbrot Set)

Mandelbrot set เป็นชุดของ complex number c ที่ทำให้ฟังก์ชัน zn+1 = zn² + c ไม่ diverge เมื่อทำ iteration ซ้ำไปเรื่อย ๆ โดยเริ่มจาก z₀ = 0

ถ้าหลังจากทำซ้ำหลายรอบแล้วค่าสัมบูรณ์ของ z ยังไม่เกิน 2 เราจะถือว่าจุดนั้น converge และเป็นส่วนหนึ่งของเซต สีที่ใช้แสดงผลจะขึ้นอยู่กับจำนวนรอบที่ค่า diverge ออกไป

เซตจูเลีย (Julia Set)

Julia set ใช้สมการเดียวกัน (zn+1 = zn² + c) แต่ต่างกันตรงที่ค่า c จะคงที่ (constant) ขณะที่จุดเริ่มต้น z₀ จะเปลี่ยนไปตามตำแหน่งบนระนาบ ค่า c แต่ละค่าจะให้ Julia set ที่มีรูปร่างแตกต่างกัน

สามเหลี่ยมเซียร์พินสกี (Sierpinski Triangle)

Sierpinski triangle เป็นแฟร็กทัลที่สร้างจาก recursion — เริ่มจากสามเหลี่ยมใหญ่ แล้วตัดสามเหลี่ยมตรงกลางออก จากนั้นทำซ้ำกับสามเหลี่ยมย่อยทุกชิ้น ทุกระดับของ iteration จะเพิ่มรายละเอียดมากขึ้น

แฟร็กทัลในศิลปะ ธรรมชาติ และปรัชญาตะวันออก

เลโอนาร์โด ดา วินชี — ผู้สังเกตแฟร็กทัลก่อนยุคสมัย

ก่อนที่ Mandelbrot จะบัญญัติคำว่า "แฟร็กทัล" ราว 500 ปี เลโอนาร์โด ดา วินชี ได้บันทึกการสังเกตใน Paris Manuscript M (folio 78v) ซึ่งเป็นสมุดบันทึกขนาดเล็กที่นโปเลียนยึดจากมิลานไปปารีส ว่าเมื่อกิ่งไม้แตกสาขา พื้นที่หน้าตัดรวมของกิ่งย่อยจะเท่ากับพื้นที่หน้าตัดของกิ่งหลัก:

"กิ่งก้านทั้งหมดของต้นไม้ในทุกระดับความสูง เมื่อรวมกันจะมีความหนาเท่ากับลำต้น" — Leonardo da Vinci, Paris Manuscript M, folio 78v

กฎนี้เรียกว่า da Vinci rule — สร้างโครงสร้าง self-similar ที่รูปแบบการแตกสาขาซ้ำกันทุกระดับตั้งแต่ลำต้นจนถึงกิ่งเล็กที่สุด งานวิจัยของ Christophe Eloy (2011, Physical Review Letters) ยืนยันว่ากฎนี้ช่วยให้ต้นไม้ ต้านทานแรงลมได้ดีที่สุด ดา วินชีใช้เลขชี้กำลัง 2 (อนุรักษ์พื้นที่) แต่ต้นไม้จริงมีค่าระหว่าง 1.8 ถึง 2.3 ตามชนิดพันธุ์ — ใกล้เคียงมากสำหรับการสังเกตเมื่อ 500 ปีก่อน!

นอกจากนี้ ดา วินชียังวาดภาพ turbulence ของน้ำ (ค.ศ. 1506–1513, Royal Collection ที่ Windsor) แสดงกระแสน้ำวนซ้อนกระแสน้ำวน — สิ่งที่เราเข้าใจในปัจจุบันว่าเป็น turbulent cascade ซึ่งเป็นตัวอย่างสำคัญของโครงสร้างแฟร็กทัลในฟิสิกส์

ดอกไม้แห่งชีวิต (Flower of Life)

Flower of Life คือรูปแบบทางเรขาคณิตศักดิ์สิทธิ์ ที่ประกอบด้วยวงกลมซ้อนทับกันเป็นจำนวนมาก สร้างโครงสร้างหกเหลี่ยมที่ self-similar — ทุกจุดตัดสามารถเป็นศูนย์กลางของ Flower of Life ชุดใหม่ที่มีรูปแบบเดียวกัน

พบลวดลายนี้วาดด้วยสีแดงเข้ม (red ochre) บนผนังหิน Osireion ในวิหาร Seti I ที่ Abydos ประเทศอียิปต์ — มักอ้างว่าอายุ 6,000 ปี แต่นักวิชาการระบุว่าเป็นกราฟฟิตี้ จากยุคกรีก-โรมัน (ศตวรรษที่ 1 ก่อนคริสตกาลหรือหลังกว่านั้น) ตัวอาคาร Osireion เก่าแก่จริง (สร้างราว 1280 ปีก่อนคริสตกาล) แต่ลวดลายถูกเพิ่มทีหลัง — อย่าตัดสินอายุกราฟฟิตี้จากอายุกำแพง! อย่างไรก็ตาม รูปแบบนี้ปรากฏในศิลปะของจีน ญี่ปุ่น อินเดีย และยุโรปยุคกลาง ภายในรูปแบบนี้ซ่อนอยู่ด้วยรูปทรงทางคณิตศาสตร์สำคัญ เช่น Metatron's Cube ซึ่งบรรจุ Platonic solids ทั้ง 5 รูปทรง

ลำดับฟีโบนัชชีและอัตราส่วนทอง

Fibonacci sequence (1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...) ถูกอธิบายในโลกตะวันตกโดย Leonardo of Pisa ใน Liber Abaci (ค.ศ. 1202) แต่ Pingala นักฉันทศาสตร์อินเดีย (ราวศตวรรษที่ 3 ก่อนคริสตกาล) ได้ซ่อนแนวคิดนี้ไว้ในสูตรลึกลับ misrau cha ("ทั้งสองปะปนกัน") ในตำรา Chandahshastra เกี่ยวกับจังหวะในบทกวีสันสกฤต ต่อมา Virahanka (ราว ค.ศ. 700) เขียนสูตรอย่างชัดเจน และ Hemachandra (ค.ศ. 1150) อธิบายเพิ่มเติม — ทั้งหมดก่อน Fibonacci หลายศตวรรษ

อัตราส่วนของตัวเลขที่อยู่ติดกันลู่เข้าสู่ golden ratio φ ≈ 1.618 — ซึ่ง φ นิยามตัวเองแบบ recursive: φ = 1 + 1/φ เหมือนแฟร็กทัลที่นิยามตัวเองซ้ำไปไม่สิ้นสุด

ในธรรมชาติ จำนวนเกลียวในดอกทานตะวัน สับปะรด และลูกสน มักเป็นเลขฟีโบนัชชีติดกัน (เช่น 34 กับ 55) เพราะการจัดเรียงที่มุมทอง (≈137.5°) ให้ความหนาแน่นสูงสุด — ยืนยันโดยงานวิจัยของ Douady & Couder (1992, Physical Review Letters)

ฟีโบนัชชีในเซตแมนเดลบรอท: Robert Devaney (Boston University) แสดงให้เห็นว่า "หัว" (bulb) ที่ติดกับรูปหัวใจหลัก ของ Mandelbrot set มีคาบที่เรียงตามลำดับฟีโบนัชชี — เช่น ระหว่าง bulb 1/2 และ 1/3 จะพบ 2/5, 3/8, 5/13, 8/21 ซึ่งล้วนเป็นอัตราส่วนฟีโบนัชชี

ปรัชญาตะวันออกและแฟร็กทัล

แนวคิดเรื่อง self-similarity และ recursion ไม่ได้เป็นเรื่องใหม่ของคณิตศาสตร์สมัยใหม่ — ปรัชญาตะวันออกพูดถึงแนวคิดเหล่านี้มาหลายพันปีแล้ว:

ข่ายอินทรา (Indra's Net)

ตาข่ายอันไม่มีที่สิ้นสุดที่ทุกจุดตัดมีอัญมณีสะท้อนอัญมณีทุกดวงอื่น และในภาพสะท้อนแต่ละภาพก็มีภาพสะท้อนของทั้งหมดอีกที — ซ้ำไปไม่สิ้นสุด นี่คือคำอธิบาย self-similarity ที่สมบูรณ์แบบ: ส่วนหนึ่งบรรจุทั้งหมดไว้ในตัว

พระอาจารย์ Fazang (ค.ศ. 643–712) สาธิตแนวคิดนี้ให้จักรพรรดินีบูเช็กเทียน (สตรีเพียงคนเดียวที่ครองราชย์เป็นจักรพรรดิจีน) ทรงเห็นด้วยพระเนตร — สร้างห้องกระจกรอบด้านพร้อมพระพุทธรูปและเทียน ให้เกิดการสะท้อนซ้อนสะท้อนไม่มีที่สิ้นสุด นี่คือ "Infinity Room" แห่งแรกของโลก ก่อน Yayoi Kusama ราว 1,300 ปี!

อวตังสกสูตร (Avatamsaka Sutra) — สำนักหัวเหยียน, ศตวรรษที่ 5

มณฑลและศรียันตร์ (Mandala & Sri Yantra)

Sri Yantra ประกอบด้วย 9 สามเหลี่ยมซ้อนกัน สร้างสามเหลี่ยมย่อย 43 รูปในโครงสร้างที่ self-similar — คล้ายกับ Sierpinski triangle ทางคณิตศาสตร์ มณฑลทิเบตก็มีโครงสร้างซ้ำตัวเองจากขอบนอกเข้าสู่ศูนย์กลาง — เหมือนการ zoom เข้าไปในแฟร็กทัล

สันทรรยลหรี (Saundarya Lahari) — อทิ ศังกร, ศตวรรษที่ 8

จักรวาลวิทยาฮินดู — เวลาที่เป็นแฟร็กทัล

วัฏจักรเวลาของฮินดูซ้อนกันเป็นชั้น ๆ: ยุค (432,000 ปี) → มหายุค (4 ยุค = 4.32 ล้านปี) → มนวันตร (71 มหายุค) → กัลป์ (14 มนวันตร = 4.32 พันล้านปี) → อายุของพรหม (311.04 ล้านล้านปี) — วงจรซ้อนวงจร เหมือน fractal ในมิติเวลา

วิษณุปุราณะ, สุริยสิทธานตะ

เต๋าและการสร้างจากกฎง่าย ๆ

เต๋าเต๋อจิง บทที่ 42: "เต๋าให้กำเนิดหนึ่ง หนึ่งให้กำเนิดสอง สองให้กำเนิดสาม สามให้กำเนิดสรรพสิ่ง" — นี่คือคำอธิบาย recursive generation จากกฎง่าย ๆ ซึ่งเป็นหัวใจของแฟร็กทัล: สมการ z → z² + c ก็สร้างความซับซ้อนอนันต์จากกฎสั้น ๆ เพียงบรรทัดเดียว

เต๋าเต๋อจิง (道德經) — เล่าจื่อ, ศตวรรษที่ 6–4 ก่อนคริสตกาล

ลายไทย — แฟร็กทัลในศิลปะไทย

ลายกระหนก ลวดลายเปลวไฟอันเป็นเอกลักษณ์ของศิลปะไทย มีโครงสร้างที่เป็นแฟร็กทัลอย่างแท้จริง — ลายกระหนกใหญ่ประกอบด้วยลายกระหนกเล็กซ้อนอยู่ภายใน ซ้ำหลายระดับ ดูได้จากลวดลายที่วัดพระแก้วและพระบรมมหาราชวัง รูปแบบ self-similar นี้ซ้อนกันอย่างน้อย 3–4 ระดับในงานประดับเดียว

ลายกระหนก, ลายประจำยาม — ศิลปกรรมรัตนโกสินทร์

วาบิ-ซาบิ — ความงามของรูปทรงธรรมชาติ

สุนทรียศาสตร์ wabi-sabi ของญี่ปุ่น (รากฐานจากเซนพุทธ) ให้คุณค่ากับความไม่สมบูรณ์แบบตามธรรมชาติ — ผิวหยาบ รูปทรงอินทรีย์ รอยแตกร้าว — ซึ่งล้วนเป็นรูปทรงที่อธิบายด้วยเรขาคณิตแฟร็กทัล Kintsugi (金継ぎ) การซ่อมเครื่องปั้นด้วยทอง ก็เน้นย้ำรอยแตกที่เป็นแฟร็กทัล — การแตกร้าวในวัสดุเปราะสร้างกิ่งก้านที่มี fractal dimension วัดได้จริง ทองไม่เพียงซ่อมแซม แต่ลากเส้นตามเรขาคณิตของการทำลาย

ผู้ก่อตั้งสุนทรียศาสตร์นี้คือ เซน โนะ ริคิว ปรมาจารย์พิธีชา ผู้ถูกโชกุน Toyotomi Hideyoshi สั่งให้ทำเซ็ปปุกุ (ค.ศ. 1591) เพราะวางรูปปั้นตัวเองสวมรองเท้าไว้เหนือประตูที่โชกุนต้องเดินลอด — ก่อนตาย ท่านจัดพิธีชาครั้งสุดท้าย แจกอุปกรณ์ให้แขกทุกชิ้น ยกเว้นถ้วยชาที่ท่านทุบทำลายพร้อมสาปว่า "ถ้วยที่แตะริมฝีปากแห่งเคราะห์ร้ายจะไม่มีใครใช้อีก"

ประเพณีชาโนยุ — เซน โนะ ริคิว, ค.ศ. 1522–1591

เรขาคณิตศักดิ์สิทธิ์ — สะพานเชื่อมคณิตศาสตร์กับจิตวิญญาณ

Sacred geometry คือการศึกษารูปแบบเรขาคณิตที่ปรากฏในธรรมชาติและถูกนำมาใช้ในสถาปัตยกรรมทางศาสนาทั่วโลก แฟร็กทัลให้กรอบทางคณิตศาสตร์ที่อธิบายว่าทำไมรูปแบบเหล่านี้จึงปรากฏซ้ำแล้วซ้ำเล่า:

ศิลปะเรขาคณิตอิสลาม — งานวิจัยของ Peter J. Lu และ Paul J. Steinhardt (2007, Science) แสดงให้เห็นว่าสถาปนิกมุสลิมยุคกลางสร้างลวดลาย quasi-crystalline (คล้าย Penrose tilings) ที่ self-similar ในมัสยิด Darb-i Imam (Isfahan, ค.ศ. 1453) — จากกระเบื้อง 3,700 ชิ้นที่วิเคราะห์ พบข้อผิดพลาดเพียง 11 จุด! สถาปนิกเหล่านี้สร้างลวดลาย quasi-crystal 500 ปีก่อนที่ Dan Shechtman จะค้นพบ quasi-crystal ในห้องปฏิบัติการ (ได้รับรางวัลโนเบลสาขาเคมี ปี 2011)

สถาปัตยกรรมวัดฮินดู — หอ shikhara ของวัด Kandariya Mahadeva (Khajuraho, ค.ศ. 1030) ล้อมรอบด้วยจำลองตัวเองขนาดเล็กกว่า ซ้ำ 3–4 ระดับ — งานวิจัยโดย Kirti Trivedi (1989, The Visual Computer)

แฟร็กทัลในแอฟริกา — Ron Eglash บันทึกใน African Fractals: Modern Computing and Indigenous Design (1999) ว่าหมู่บ้าน Ba-ila ในแซมเบียมีผังเป็นวงแหวนของบ้าน ที่แต่ละครอบครัวเป็นวงแหวนเล็กกว่าของโครงสร้าง — แฟร็กทัลซ้อนแฟร็กทัล

ตัวอย่างโค้ด Python และคณิตศาสตร์เบื้องหลัง

ขั้นตอนที่ 1: ทำความเข้าใจจำนวนเชิงซ้อน

ก่อนเขียนโค้ด ต้องเข้าใจ complex number ก่อน — จำนวนที่มีรูปแบบ a + bi โดย a คือส่วนจริง (แกน x), b คือส่วนจินตภาพ (แกน y), และ i คือ √(-1) ทุกจุดบนภาพแฟร็กทัลคือจุดหนึ่งบนระนาบเชิงซ้อน

ค่าสัมบูรณ์ (absolute value) ของจำนวนเชิงซ้อน z = a + bi คือ |z| = √(a² + b²) — คือระยะห่างจากจุดกำเนิด เราใช้ค่านี้ตัดสินว่าจุดใด diverge: ถ้า |z| > 2 จุดนั้นจะหนีออกไปสู่อนันต์แน่นอน

สาธิต: ดูการ iteration ทีละขั้น
+ i

ขั้นตอนที่ 2: สมการหลัก z → z² + c

คณิตศาสตร์: การยกกำลังสองของจำนวนเชิงซ้อน z = a + bi:

z² = (a + bi)² = a² + 2abi + (bi)² = (a² - b²) + (2ab)i ใช้สูตร (a+b)² = a² + 2ab + b² และ i² = -1

แล้วบวก c: znew = z² + c = (a² - b² + creal) + (2ab + cimag)i — นี่คือทั้งหมดที่โค้ดทำในแต่ละรอบ เพียงสองบรรทัดเลขคณิต แต่เมื่อทำซ้ำหลายล้านครั้ง ก็สร้างความซับซ้อนอนันต์ เหมือนที่เล่าจื่อกล่าว: "สามให้กำเนิดสรรพสิ่ง"

ขั้นตอนที่ 3: สร้างเซตแมนเดลบรอทด้วย Python

ด้านล่างคือโค้ดเต็มโดยใช้ NumPy และ Matplotlib — สร้างระนาบ 800×600 จุด แต่ละจุดคือค่า c หนึ่งค่า:

Python
import numpy as np
import matplotlib.pyplot as plt

def mandelbrot(c, max_iter):
    """คำนวณจำนวนรอบก่อน diverge"""
    z = 0
    for n in range(max_iter):
        if abs(z) > 2:
            return n
        z = z*z + c
    return max_iter

# สร้างระนาบจำนวนเชิงซ้อน
x = np.linspace(-2.5, 1.0, 800)
y = np.linspace(-1.25, 1.25, 600)
X, Y = np.meshgrid(x, y)
C = X + 1j * Y

# คำนวณแต่ละจุด
max_iter = 100
img = np.vectorize(mandelbrot)(C, max_iter)

# แสดงผล
plt.figure(figsize=(12, 9))
plt.imshow(img, cmap='inferno', extent=[-2.5,1,-1.25,1.25])
plt.colorbar(label='จำนวนรอบ (iterations)')
plt.title('Mandelbrot Set')
plt.show()

ขั้นตอนที่ 4: จาก Mandelbrot สู่ Julia

ความแตกต่างทางคณิตศาสตร์: สมการ zn+1 = zn² + c เหมือนกันทุกประการ แต่:

ใน Mandelbrot: เริ่มจาก z₀ = 0 เสมอ และ c เปลี่ยนตามตำแหน่ง (คำถาม: "ค่า c ใดไม่ทำให้ลู่ออก?")
ใน Julia: c คงที่ค่าเดียว และ z₀ เปลี่ยนตามตำแหน่ง (คำถาม: "จุดเริ่มต้นใดไม่ทำให้ลู่ออก?")

ดังนั้นค่า c แต่ละค่าจะให้ Julia set รูปร่างต่างกัน — ถ้า c อยู่ ใน Mandelbrot set, Julia set จะเป็นผืนเดียวต่อเนื่อง แต่ถ้า c อยู่ นอก Mandelbrot set, Julia set จะแตกเป็น "ฝุ่น" (Fatou dust)

Python
def julia(z, c, max_iter):
    """คำนวณ Julia set สำหรับค่า c ที่กำหนด"""
    for n in range(max_iter):
        if abs(z) > 2:
            return n
        z = z*z + c
    return max_iter

# ค่า c ที่น่าสนใจ:
# c = -0.7 + 0.27015j   (รูปแบบคลาสสิก)
# c = -0.8 + 0.156j     (เกลียวงดงาม)
# c = 0.355 + 0.355j    (ดอกไม้)
c = -0.7 + 0.27015j
img = np.vectorize(lambda z: julia(z, c, 200))(C)

ขั้นตอนที่ 5: สามเหลี่ยมเซียร์พินสกี — คณิตศาสตร์ของ Recursion

คณิตศาสตร์: ในแต่ละรอบ จำนวนสามเหลี่ยมเพิ่มเป็น 3 เท่า: 1 → 3 → 9 → 27 → 81 ... (3ⁿ ชิ้น ที่ความลึก n) ขณะที่พื้นที่แต่ละชิ้นลดลงเป็น ¼: พื้นที่รวม = (3/4)ⁿ ของสามเหลี่ยมเดิม เมื่อ n → ∞ พื้นที่ลู่เข้า 0 แต่จำนวนชิ้นเป็นอนันต์ — fractal dimension ของ Sierpinski triangle คือ log(3)/log(2) ≈ 1.585 ซึ่งอยู่ระหว่างเส้น (1 มิติ) กับพื้นผิว (2 มิติ)

Python
import matplotlib.pyplot as plt
import matplotlib.patches as patches

def sierpinski(ax, vertices, depth):
    """วาดสามเหลี่ยมเซียร์พินสกีด้วย recursion"""
    if depth == 0:
        triangle = patches.Polygon(vertices, fill=True)
        ax.add_patch(triangle)
        return
    # หาจุดกึ่งกลางแต่ละด้าน
    mid = lambda a, b: ((a[0]+b[0])/2, (a[1]+b[1])/2)
    v0, v1, v2 = vertices
    sierpinski(ax, [v0, mid(v0,v1), mid(v0,v2)], depth-1)
    sierpinski(ax, [v1, mid(v0,v1), mid(v1,v2)], depth-1)
    sierpinski(ax, [v2, mid(v0,v2), mid(v1,v2)], depth-1)

ขั้นตอนที่ 6: เทคนิคสีแบบ Smooth Coloring

ปัญหา: ถ้าใช้จำนวนรอบเป็นจำนวนเต็ม สีจะเป็นแถบ ๆ ชัดเจน แก้ไข: ใช้สูตร smooth coloring:

nsmooth = n + 1 − log(log|z|) / log(2) ใช้ logarithm สองชั้น — log ด้านนอกชดเชยการเติบโตแบบเอ็กซ์โพเนนเชียลของ |z|

ทำไมต้อง log สองชั้น? เมื่อ z หนีออก |z| เติบโตแบบ |z|² ต่อรอบ ดังนั้น log|z| เติบโตแบบ 2ⁿ และ log(log|z|) เติบโตแบบ n — ให้ค่าต่อเนื่อง ที่เชื่อมระหว่างจำนวนเต็ม n และ n+1 ตามที่ |z| อยู่ระหว่างรอบ

Python — Smooth Coloring
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

def mandelbrot_smooth(c, max_iter):
    """Smooth coloring — เทคนิคเดียวกับ JS canvas"""
    z = 0
    for n in range(max_iter):
        if abs(z) > 2:
            # ค่า smooth แทนจำนวนเต็ม
            smooth = n + 1 - np.log(np.log(abs(z))) / np.log(2)
            return smooth
        z = z*z + c
    return max_iter

# สร้างชุดสีแบบ "cosmic" เหมือนใน JS
def cosmic_palette(t):
    r = 9 * (1-t) * t**3
    g = 15 * (1-t)**2 * t**2
    b = 8.5 * (1-t)**3 * t
    return (r, g, b)

# สร้าง colormap จากฟังก์ชัน
colors = [cosmic_palette(t/255) for t in range(256)]
cmap = LinearSegmentedColormap.from_list('cosmic', colors)

img = np.vectorize(mandelbrot_smooth)(C, 200)
plt.imshow(img, cmap=cmap)

Web Worker แบบ Python — ใช้ Multiprocessing

หน้าเว็บนี้ใช้ Web Worker เพื่อเรนเดอร์แฟร็กทัล โดยไม่บล็อก UI ใน Python ทำได้เช่นกันด้วย multiprocessing — แบ่งภาพเป็นแถวแล้วคำนวณแต่ละส่วนพร้อมกัน:

Python — Parallel Rendering
from multiprocessing import Pool
import numpy as np

def compute_row(args):
    """คำนวณแถวเดียว — ส่งไปทำงานใน process แยก"""
    y_val, x_arr, max_iter = args
    row = np.empty(len(x_arr))
    for i, x_val in enumerate(x_arr):
        c = complex(x_val, y_val)
        z = 0
        for n in range(max_iter):
            if abs(z) > 2:
                row[i] = n + 1 - np.log(np.log(abs(z))) / np.log(2)
                break
            z = z*z + c
        else:
            row[i] = max_iter
    return row

x = np.linspace(-2.5, 1.0, 800)
y = np.linspace(-1.25, 1.25, 600)

# แบ่งงานเป็น 600 แถว ส่งไปคำนวณพร้อมกัน
with Pool() as pool:
    args = [(yi, x, 200) for yi in y]
    rows = pool.map(compute_row, args)

img = np.array(rows)

อนุภาคลอยตัว — Particle Animation ด้วย Pygame

พื้นหลังของหน้านี้มีอนุภาคลอยตัวที่เชื่อมต่อกันเมื่ออยู่ใกล้ — สร้างเอฟเฟกต์เดียวกันใน Python ได้ด้วย Pygame:

Python — Particle System
import pygame, random, math

pygame.init()
W, H = 800, 600
screen = pygame.display.set_mode((W, H))

class Particle:
    def __init__(self):
        self.x = random.uniform(0, W)
        self.y = random.uniform(0, H)
        self.vx = random.uniform(-0.3, 0.3)
        self.vy = random.uniform(-0.3, 0.3)
        self.r = random.uniform(1, 3)
        self.color = random.choice([
            (108,92,231), (0,206,201)
        ])

particles = [Particle() for _ in range(60)]

running = True
while running:
    for e in pygame.event.get():
        if e.type == pygame.QUIT: running = False

    screen.fill((6, 6, 14))
    for p in particles:
        p.x = (p.x + p.vx) % W
        p.y = (p.y + p.vy) % H
        pygame.draw.circle(screen, p.color,
            (int(p.x), int(p.y)), int(p.r))

    # เส้นเชื่อมอนุภาคที่อยู่ใกล้กัน
    for i, a in enumerate(particles):
        for b in particles[i+1:]:
            d = math.hypot(a.x-b.x, a.y-b.y)
            if d < 120:
                alpha = int(15 * (1 - d/120))
                s = pygame.Surface((W,H), pygame.SRCALPHA)
                pygame.draw.line(s, (108,92,231,alpha),
                    (int(a.x),int(a.y)),
                    (int(b.x),int(b.y)))
                screen.blit(s, (0,0))

    pygame.display.flip()
    pygame.time.Clock().tick(60)

📖 อภิธานศัพท์ (คลิกคำศัพท์เพื่อฟังเสียงอ่านภาษาอังกฤษ)