和之前介紹的兩種圖形一樣,Sierpinski三角形也是一種分形圖形,它是遞歸地構造的。最常見的構造方法如上圖所示:把一個三角形分成四等份,挖掉中間那一份,然後繼續對另外三個三角形進行這樣的操作,並且無限地遞歸下去。每一次迭代後整個圖形的面積都會減小到原來的3/4,因此最終得到的圖形面積顯然爲0。這也就是說,Sierpinski三角形其實是一條曲線,它的Hausdorff維度介於1和2之間。
Sierpinski三角形的另一種構造方法如下圖所示。把正方形分成四等份,去掉右下角的那一份,並且對另外三個正方形遞歸地操作下去。挖個幾次後把腦袋一歪,你就可以看到一個等腰直角的Sierpinski三角形。
Sierpinski三角形有一個神奇的性質:如果某一個位置上有點(沒被挖去),那麼它與原三角形頂點的連線上的中點處也有點。這給出另一個詭異的Sierpinski三角形構造方法:給出三角形的三個頂點,然後從其中一個頂點出發,每次隨機向任意一個頂點移動1/2的距離(走到與那個頂點的連線的中點上),並在該位置作一個標記;無限次操作後所有的標記就組成了Sierpinski三角形。
Sierpinski三角形與楊輝三角
第一次發現Sierpinski三角形與楊輝三角的關係時,你會發現這玩意兒不是一般的牛。寫出8行或者16行的楊輝三角,然後把楊輝三角中的奇數和偶數用不同的顏色區別開來,你會發現楊輝三角模2與Sierpinski三角形是等價的。也就是說,二項式係數(組合數)的奇偶性竟然可以表現爲一個分形圖形!在感到詫異的同時,冷靜下來仔細想想,你會發現這並不難理解。
我們下面說明,如何通過楊輝三角奇偶表的前四行推出後四行來。可以看到楊輝三角的前四行是一個二階的Sierpinski三角形,它的第四行全是奇數。由於奇數加奇數等於偶數,那麼第五行中除了首尾兩項爲1外其餘項都是偶數。而偶數加偶數還是偶數,因此中間那一排連續的偶數不斷地兩兩相加必然得到一個全是偶數項的“倒三角”。同時,第五行首尾的兩個1將分別產生兩個和楊輝三角前四行一樣的二階Sierpinski三角形。這正好組成了一個三階的Sierpinski三角形。顯然它的最末行仍然均爲奇數,那麼對於更大規模的楊輝三角,結論將繼續成立。
Sierpinski三角形與Hanoi塔
有沒有想過,把Hanoi塔的所有狀態畫出來,可以轉移的狀態間連一條線,最後得到的是一個什麼樣的圖形?二階Hanoi塔反正也只有9個節點,你可以自己試着畫一下。不斷調整節點的位置後,得到的圖形大概就像這個樣子:
如果把三階的Hanoi塔表示成無向圖的話,得到的結果就是三階的Sierpinski三角形。下面的這張圖說明了這一點。把二階Hanoi塔對應的無向圖複製兩份放在下面,然後在不同的柱子上爲每個子圖的每個狀態添加一個更大的盤子。新的圖中原來可以互相轉移的狀態現在仍然可以轉移,同時還出現了三個新的轉移關係將三個子圖連接在了一起。重新調整一下各個節點的位置,我們可以得到一個三階的Sierpinski三角形。
顯然,對於更大規模的Hanoi塔問題,結論仍然成立。
Sierpinski三角形與位運算
編程畫出Sierpinski三角形比想象中的更簡單。下面的兩個代碼(實質相同,僅語言不同)可以打印出一個Sierpinski三角形來。const
n=1 shl 5-1;
var
i,j:integer;
begin
for i:=0 to n do
begin
for j:=0 to n do
if i and j = j then write('#')
else write(' ');
writeln;
end;
readln;
end.#include
<stdio.h>
int main()
{
const int n=(1<<5)-1;
int i,j;
for (i=0; i<=n; i++)
{
for (j=0; j<=n; j++)
printf( (i&j)==j ? "#" : " ");
printf("\n");
}
getchar();
return 0;
}
上面兩個程序是一樣的。程序將輸出:#
##
# #
####
# #
## ##
# # # #
########
# #
## ##
# # # #
#### ####
# # # #
## ## ## ##
# # # # # # # #
################
# #
## ##
# # # #
#### ####
# # # #
## ## ## ##
# # # # # # # #
######## ########
# # # #
## ## ## ##
# # # # # # # #
#### #### #### ####
# # # # # # # #
## ## ## ## ## ## ## ##
# # # # # # # # # # # # # # # #
################################
這個程序告訴我們:在第i行第j列上打一個點當且僅當i and j=j,這樣最後得到的圖形就是一個Sierpinski三角形。這是爲什麼呢?其實原因很簡單。把i和j寫成二進制(添加前導0使它們位數相同),由於j不能大於i,因此只有下面三種情況:
情況一:
i = 1?????
j = 1?????
問號部分i大於等於j
i的問號部分記作i',j的問號部分記作j'。此時i and j=j當且僅當i' and j'=j'
情況二:
i = 1?????
j = 0?????
問號部分i大於等於j
i的問號部分記作i',j的問號部分記作j'。此時i and j=j當且僅當i' and j'=j'
情況三:
i = 1?????
j = 0?????
問號部分i小於j
此時i and j永遠不可能等於j。i' < j'意味着i'和j'中首次出現數字不同的那一位上前者爲0,後者爲1,那麼i和j做and運算時這一位的結果是0,與j不等。
注意到,去掉一個二進制數最高位上的“1”,相當於從這個數中減去不超過它的最大的2的冪。觀察每一種情況中i,j和i',j'的實際位置,不難發現這三種情況遞歸地定義出了整個Sierpinski三角形。
嘿!發現沒有,我通過Sierpinski三角形證明了這個結論:組合數C(N,K)爲奇數當且僅當N and K=K。這篇文章很早之前就計劃在寫了,前幾天有人問到這個東西,今天順便也寫進來。
另外,把i and j=j 換成i or j=n也可以打印出Sierpinski三角形來。i and j=j表示j的二進制中有1的位置上i也有個1,那麼此時i or (not j)結果一定全爲1(相當於程序中的常量n),因此打印出來的結果與原來的輸出正好左右鏡像。