ใบเฟิร์น Barnsley
ความสวยงามอย่างหนึ่งของใบเฟิร์น คือ รูปทรงของส่วนย่อยๆ ภายในใบจะมีความคล้ายคลึงกับใบเฟิร์นทั้งใบ ภาษาคณิตศาสตร์มีชื่อเรียกพิเศษให้รูปทรงเหล่านี้เลยว่า fractal แต่ถ้าใครคุ้นเคยกับศัพท์สายคอมพิวเตอร์มากกว่า ก็อาจจะมองว่ามันเป็น recursion ก็ได้
การสร้าง fractal รูปใบเฟิร์นอย่างง่าย สามารถอธิบายคร่าวๆ ได้ด้วยขั้นตอนวิธีนี้
- ลากเส้นตรงตั้งต้นยาว $\ell$ แล้วเลือกปลายข้างหนึ่งไว้เป็นปลายที่จะต่อยอดออกไป
- ลากเส้นตรงเพิ่ม 3 เส้นที่มีปลายด้านหนึ่งอยู่บนเส้นตั้งต้น ดังนี้
- ที่ปลายยอดของเส้นตั้งต้น ลากเส้นตรงยาว $0.7\ell$ ต่อขนานขึ้นไปตรงๆ
- ที่ตรงกลางของเส้นตั้งต้น ลากเส้นตรงยาว $0.4\ell$ ตั้งฉากออกมาทั้งด้านซ้ายและขวา
- สำหรับแต่ละเส้นที่ลากต่อออกมาในข้อ 2 เลือกปลายด้านที่ว่างๆ ไว้เป็นปลายต่อยอด แล้ววนกลับไปทำขั้นตอนที่ 2 โดยเปลี่ยนมาใช้เส้นตั้งต้นเป็นเส้นที่ได้จากข้อ 2 แทน (คิดซะว่าเส้นใหม่นี้ยาว $\ell$) ทำไปเรื่อยๆ จนกว่าจะพอใจผลลัพธ์
ใบเฟิร์นอย่างง่ายจาก fractal
วิธีการข้างต้นจะให้ผลลัพธ์เป็นรูปใบเฟิร์นเรียบง่ายไม่หวือหวานัก (เอาจริงๆ ก็อาจจะดูไม่เหมือนใบเฟิร์นซักเท่าไหร่) จนกระทั่งมีนักคณิตศาสตร์นามว่า Michael Barnsley ได้เขียนสมการกำหนดมุมและจัดตำแหน่งของการต่อยอดให้สวยงามมีลูกเล่น เนื่องจากเขาเป็นคนแรกที่เขียนเรื่องนี้ไว้ตั้งแต่ปี 1993 ทุกคนเลยพากันเรียกใบเฟิร์นที่สวยงามเป็นเอกลักษณ์นี้ว่า ใบเฟิร์น Barnsley
ใบเฟิร์น Barnsley ที่ได้หลังจากการทำซ้ำ 23 รอบ
พูดให้รัดกุมเป็นภาษาคณิตศาสตร์ก็คือ การสร้างใบเฟิร์น Barnsley เริ่มจากลากเส้นแรกยาว $1.6$ หน่วยจากจุด $(0,0)$ ขึ้นไปตามแกน $y$ แล้วเอาเส้นนั้นมาวาดซ้ำๆ อีกครั้ง จากเลื่อนและปรับขนาดด้วยสมการดังนี้
\[\begin{align} f_1(x, y) &= \begin{bmatrix} 0.85 & 0.04 \\ -0.04 & 0.85 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix} \\ f_2(x, y) &= \begin{bmatrix} 0.20 & -0.26 \\ 0.23 & 0.22 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} 0 \\ 1.6 \end{bmatrix} \\ f_3(x, y) &= \begin{bmatrix} -0.15 & 0.28 \\ 0.26 & 0.24 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} 0 \\ 0.44 \end{bmatrix} \end{align}\]โดย $f_1, f_2, f_3$ คือสมการสำหรับคำนวณรูปร่างก้านที่ต่อยอดขึ้นข้างบน ก้านที่เอียงไปด้านซ้าย และก้านที่พลิกตัวแล้วเอียงไปด้านขวา ตามลำดับ
ผมแนบโค้ดในภาษา Python มาให้ด้วยสำหรับใครที่ต้องการนำไปเล่นต่อ ซึ่งวิธีการที่แนบนี้เป็นการสร้างแบบ recursive ต่อยอดออกไปเรื่อยๆ ตามแนวทางขั้นตอนวิธีที่ได้กล่าวไว้ข้างต้น หากไปค้นดูวิธีอื่นเพิ่มเติมจะพบว่าวิธีการสุ่มเลือกจุดจะให้ผลลัพธ์ที่รวดเร็วกว่า
from PIL import Image, ImageDraw
def transform(matrix, xy):
return [sum(e * p for e, p in zip(row, xy)) for row in matrix]
def translate(vector, xy):
return [e + p for e, p in zip(vector, xy)]
class BarnsleyFern(object):
affines = [([[0.85, 0.04], [-0.04, 0.85]], [0, 1.6]),
([[0.20, -0.26], [0.23, 0.22]], [0, 1.6]),
([[-0.15, 0.28], [0.26, 0.24]], [0, 0.44])]
def __init__(self, depth=10, size=(800,800)):
self.image = Image.new('RGB', size)
self.draw = ImageDraw.Draw(self.image)
self.iterate([0.0, 0.0], [0.0, 1.6], depth)
def canvas_coordinate(self, xy):
width, height = self.image.size
return [width/2 + xy[0]*width/10, height - xy[1]*height/10]
def linespec(self, xy0, xy1):
return self.canvas_coordinate(xy0) + self.canvas_coordinate(xy1)
def draw_too_small(self, xy0, xy1, pos=2):
return all(round(a, pos) == round(b, pos) for a, b in zip(xy0, xy1))
def iterate(self, xy0, xy1, depth, affine=None):
if depth == 0:
return
if affine is not None:
xy0 = translate(affine[1], transform(affine[0], xy0))
xy1 = translate(affine[1], transform(affine[0], xy1))
if self.draw_too_small(xy0, xy1):
return
self.draw.line(self.linespec(xy0, xy1), fill='#0C3')
for affine in self.affines:
self.iterate(xy0, xy1, depth-1, affine)
fractal ยังมีรูปร่างอื่นๆ อีกมากมาย ทั้งที่พบได้ในธรรมชาติ เช่น ดอกทานตะวัน บรอคโคลี เกล็ดหิมะ หรือมาจากการสังเคราะห์ขึ้นอย่าง สามเหลี่ยม Sierpinski หรือ เซต Mandelbrot
แต่ความมหัศจรรย์ของ fractal ที่แท้จริง คือเราไม่สามารถรู้ได้เลยว่าตอนนี้เรากำลังอยู่ตรงไหนของมันกันแน่ ลองคิดภาพว่าตัวเองเป็น Ant-Man ที่จะปรับขนาดตัวให้ไม่ใหญ่ไปกว่าก้านใบเฟิร์นที่ยืนอยู่ได้ แล้วก็ลองไปยืนอยู่บนใบเฟิร์นข้างต้นดู ณ ขณะหนึ่งเราอาจจะคิดว่าตัวเองอยู่บนก้านที่ใหญ่ที่สุดแล้ว แต่นอกจากขนาดที่แตกต่างกัน ก้านที่เราคิดว่าใหญ่ที่สุดนั้น ก็ไม่ได้มีอะไรที่แตกต่างจากก้านย่อยก้านอื่นๆ เลย หากเราเดินย้อนกลับไปเรื่อยๆ อาจพบว่าก้านที่เราคิดว่าใหญ่ที่สุดนั้น เป็นเพียงแค่ก้านย่อยที่แตกแขนงแยกออกมาก็เป็นได้
ในทางเดียวกัน เราก็ไม่สามารถแน่ใจได้ว่าตรงไหนคือจุดปลายของใบเฟิร์น เพราะยิ่งเดินค้นหาปลายใบเท่าไหร่ เราก็จะยิ่งตัวเล็กลงๆ และพบว่าใบเฟิร์นนั้นมีรูปร่างเหมือนเดิมไม่เปลี่ยนแปลง
ก็เปรียบเหมือนความรู้ เราจะรู้ได้ไงว่าอะไรคือพื้นฐานที่แท้จริงกันแน พันปีก่อนเราอาจคิดว่าถ้าได้รู้จักอะตอมก็คือรู้ถึงพื้นฐานแห่งทุกสรรพสิ่งแล้ว แต่ไม่กี่ร้อยปีก่อนเราเพิ่งพบว่าอะตอมยังแบ่งแยกย้อยลงไปได้อีกเป็นโปรตอน นิวตรอน อิเล็กตรอน และปฏิรูปพื้นฐานความรู้ด้านเคมีขึ้นใหม่ ส่วนปัจจุบันเรายังแบ่งย่อยสิ่งต่างๆ ลงไปได้อีกเป็นควาร์ก หรือแม้กระทั่งเสนอทฤษฎีสตริงซึ่งเป็นพื้นฐานของทุกอนุภาค
อีกนัยหนึ่ง พื้นฐานความรู้ที่ลึกเกินไปอาจเป็นสิ่งไม่จำเป็น หรือยิ่งไปกว่านั้น มันอาจเป็นตัวถ่วงให้เราไม่กล้าที่จะเดินหน้าสำรวจโลกใหม่ๆ ก็เป็นได้
author