#カントール集合とは
幾何学的には、線分を3等分し、得られた3つの線分の真ん中のものを取り除くという操作を、再帰的に繰り返すことで作られる集合である。ここで、取り除く線分は開区間である。すなわち、単位区間 I = [0, 1] から、1回目の操作では (1/3, 2/3) を取り除き、2回目の操作では (1/9, 2/9) と (7/9, 8/9) を取り除き……といった具合に操作を無限に繰り返し、残った部分集合がカントール集合である。

#カントール集合の図示
カントール集合をProcessingを用いて図示する。
もととなる線分を表示し、取り除く部分を白で重ねる方法を取った。
単位区間 I = [0,1] からn回目の操作で取り除く部分の座標は、
(\frac{3k+1}{3^n},\frac{3k+2}{3^n}) (k=0,1,2,...,n)
と表せる。
##取り除く部分の表示
取り除く部分は、下のプログラムで表示できる。
iでは操作の回数、jではi回目の操作ででてくる線分のうち左から何番目かを指定している。
:interbal3_1
size(1200,500);
strokeWeight(1.5);
for (int i=0; i<10; i+=1){
for (int j=0; j < (pow(3, i)-2)/2 ; j += 1) {
line( 100 + 1000*(2*j+1)/pow(3,i), 30*i+100, 100 + 1000*(2*j+2)/pow(3,i), 30*i+100);
}
}
6行目では、1,3番目の数値で先に示した一般項を指定している。
2,4番目の数値では見やすさのために、線分のy座標を30ピクセルずつ下方向にずらしている。(5行目30*i+100の部分)
4行目のjの範囲j < (pow(3, i)-2)/2の'-2'の部分は'-1'でも成り立つ。
これはjを整数として扱い、2で割っているためである。
例えばi=2のとき、
j<(3^2-2)/2=3.5\quad j<(3^2-1)/2=4
はどちらも、jは0から3までの整数値を取る。
size(1200, 500) ;
strokeWeight(1.5) ;
for (int i=0; i < 10; i += 1) {
for (int j=0; j < (pow(3, i)-1)/2+1 ; j += 1) {
line( 100 + 1000*(2*j)/pow(3,i), 30*i+100, 100 + 1000*(2*j+1)/pow(3,i), 30*i+100);
}
}
##カントール集合を長方形で表示する
上のinterval3_1より、線分から長方形での表示に変えて見やすくする。
void setup(){
size(1200, 300) ;
background(255);
fill(0);
rect(100,100,1000,50);
}
void draw(){
stroke(255); //線を白色で表示
for (int i=0; i < 3; i += 1) {
for (int j=0; j < (pow(3, i)-2)/2 ; j += 1) {
fill(255); //白く塗りつぶす
rect( 100 + 1000*(2*j+1)/pow(3,i), 100, 1000*(2*j+2)/pow(3,i)-1000*(2*j+1)/pow(3,i), 50);
}
}
}
1~6行目で背景の設定と、もととなる黒い長方形を表示する。
8~16行目で白く抜きたい部分を表示する。
13行目の3番目の数値1000*(2*j+2)/pow(3,i)-1000*(2*j+1)/pow(3,i)は
interval3_1の(線分の右端のx座標)-(左端のx座標)とし、表示する長方形の幅としている。
#シェルピンスキーのギャスケット
シェルピンスキーのギャスケットとは、フラクタル図形の一種であり、自己相似的な無数の三角形からなる図形である。
正三角形を用意し、各辺の中点を結んだ三角形を取り除くという操作を、残った三角形に対して無限に繰り返すことで得られる。

このシェルピンスキーのギャスケットを、上で用いた「もとの図形から取り除く部分を白で表示する」方法を使ってProcessingを用いて表示したい。
正三角形は、円弧を等分して内接する多角形を描く方法をとる。この方法では、円の中心点と半径を指定すれば内接する正三角形を描画できる。
void drawTriangle(float x,float y,float r) {
pushMatrix();
translate(x, y); // 中心となる座標
rotate(radians(-90));
// 円を均等に3分割する点を結び、三角形をつくる
beginShape();
for (int i = 0; i < 3; i++) {
vertex(r*cos(radians(360*i/3)), r*sin(radians(360*i/3)));
}
endShape(CLOSE);
popMatrix();
}
そのため、まず中心点を表示できるプログラムを考える。
##中心点を表示する
ここで表示したい円の中心点が正三角形の重心(・内心・外心)と一致することから、白く塗りつぶしたい三角形の重心の座標を表す。

三角形の内部だけで考えると複雑になるため、三角形を四角形に拡張して考える。図中で点線で示した三角形は、先に表示した三角形や三角形の外側(背景)になり結果としては見えないため、考慮しないこととする。
上図のように考えると、白く塗りつぶす三角形の重心の座標は、
(x\times\frac{1}{2^n}+x\times\frac{j}{2^n}\,,\,y\times\frac{i}{2^n}-y\times\frac{1}{2^n}\times\frac{2}{3})\\
\begin{align}
(j&=0,1,2,...,2^n-1)\\
(i&=0,1,2,...,2^n)
\end{align}
と表せる。よって、まず重心を表示するプログラムは下のようである。
float x = 500; //底辺の長さ
float y = x*sqrt(3)/2; //正三角形の高さ
float r = y*2/3; //外接円の半径
void setup(){
size(800,800);
background(255);
drawTriangle(400,450,r); //もととなる三角形
}
void draw(){
strokeWeight(10);
points(2); //第何ステップの点を表示するか指定する
void points(int n){
for (int k=1;k<=n;k+=1){
for (int i=0 ;i<=pow(2,n);i+=1){
if (i%2==1){
for(int j=0;j<pow(2,n)-1;j+=1){
point(150+x*(1/pow(2,k))+x*(1/pow(2,k))*j,450-r+y*(i/pow(2,k))-y*(1/pow(2,k))*2/3+y*(1/pow(2,k)));
}
}
}
}
}
void drawTriangle(float x,float y,float r) {
pushMatrix();
translate(x, y); // 中心となる座標
rotate(radians(-90));
// 円を均等に3分割する点を結び、三角形をつくる
beginShape();
for (int i = 0; i < 3; i++) {
vertex(r*cos(radians(360*i/3)), r*sin(radians(360*i/3)));
}
endShape(CLOSE);
popMatrix();
}
実行結果は下のようである。

(右側に想像していなかった点が表示されたが、三角形の外側の点であるため無視できると考えた。)
##シェルピンスキーのギャスケットの表示
円の半径は第nステップでは1/(2^n)倍される。
これらのことから、シェルピンスキーのギャスケットは下のようにして描画できる。
float x = 500; //底辺の長さ
float y = x*sqrt(3)/2; //正三角形の高さ
float r = y*2/3; //外接円の半径
int a = 3; //第3ステップ
void setup(){
size(800,800);
background(255);
fill(0);
drawTriangle(400,450,r);
}
void draw(){
fill(255);
noStroke();
tri(a);
}
void tri(int n){
for (int k=1;k<=n;k+=1){
for (int i=0 ;i<=pow(2,n);i+=1){
if (i%2==1){
for(int j=0;j<pow(2,n)-1;j+=1){
float xx = 150+x*(1/pow(2,k))+x*(1/pow(2,k))*j;
\\中心点のx座標
float yy = 450-r+y*(i/pow(2,k))-y*(1/pow(2,k))*2/3+y*(1/pow(2,k));
\\中心点のy座標
drawTriangle(xx,yy,-r/pow(2,k)); \\半径は2のk乗分の1 (下向きの三角形のため、負の数にした)
}
}
}
}
}
void drawTriangle(float x,float y,float r) {
pushMatrix();
translate(x, y); // 中心となる座標
rotate(radians(-90));
// 円を均等に3分割する点を結び、三角形をつくる
beginShape();
for (int i = 0; i < 3; i++) {
vertex(r*cos(radians(360*i/3)), r*sin(radians(360*i/3)));
}
endShape(CLOSE);
popMatrix();
}
#参考文献
巴山竜来 『数学から創るジェネラティブアート』p.146課題7.1
Wikipedia カントール集合
[https://bit.ly/3grBQve]
シェルピンスキーのギャスケット
[https://bit.ly/3m9Oj8d]
@reona396 『Processingで描ける図形』
https://qiita.com/reona396/items/5fa4babc8243c4ed4914











