SE_BOKUのまとめノート的ブログ

SE_BOKUが知ってること・勉強したこと・考えたことetc

Pythonでフラクタル図形:「シェルピンスキーのギャスケット」を描く

f:id:arakan_no_boku:20210912183619p:plain

目次

Pythonフラクタル図形:「シェルピンスキーのギャスケット」を描く

Pythonで「シェルピンスキーのギャスケット」を描きます。

シェルピンスキーのギャスケットはフラクタル図形です。

フラクタルというのは、一部が全体と自己相似な構造(ようするに、小さくしても形は同じ)を持っている図形のことで、「シェルピンスキーのギャスケット」は三角形の組み合わせで三角形を描きます。

今回は、Pythonのmatplotlibのグラフの描画機能を使って描画します。

 

ソースコード

ソースコードです。

class_graphics.py

from matplotlib import pyplot as plt
import random


class Gasket():

    def __init__(self):
        self.__tx = [0]
        self.__ty = [0]
        self.__title = "シェルピンスキーのギャスケットを描く"

    def __tran_1(self, p):
        x = p[0]
        y = p[1]
        x1 = 0.5 * x
        y1 = 0.5 * y
        return x1, y1

    def __tran_2(self, p):
        x = p[0]
        y = p[1]
        x1 = 0.5 * x + 0.5
        y1 = 0.5 * y + 0.5
        return x1, y1

    def __tran_3(self, p):
        x = p[0]
        y = p[1]
        x1 = 0.5 * x + 1.0
        y1 = 0.5 * y
        return x1, y1

    def __get_index(self):
        prob = [0.333, 0.333, 0.333]
        r = random.random()
        c = 0
        sump = []
        for p in prob:
            c += p
            sump.append(c)
        for item, sp in enumerate(sump):
            if(r <= sp):
                return item
        return len(prob) - 1

    def __tran(self, p):
        trans = [self.__tran_1, self.__tran_2, self.__tran_3]
        tindex = self.__get_index()
        t = trans[tindex]
        x, y = t(p)
        return x, y

    def __draw(self, n):
        x1 = 0
        y1 = 0
        for i in range(n):
            x1, y1 = self.__tran((x1, y1))
            self.__tx.append(x1)
            self.__ty.append(y1)
        return self.__tx, self.__ty

    def draw(self, n=5000):
        x, y = self.__draw(n)
        plt.plot(x, y, '.')
        plt.title(self.__title)
        plt.show()

 

ソースコードの補足説明

メインの処理は「__draw()」です。

    def __draw(selfn):
        x1 = 0
        y1 = 0
        for i in range(n):
            x1, y1 = self.__tran*1
            self.__tx.append(x1)
            self.__ty.append(y1)

座標を示す「x1、y1」を更新しながら、「n」で指定した回数繰り返します。

デフォルトでは5000回です。

 

x1,y1を更新する処理は「__tran()」です。

    def __tran(selfp):
        trans = [self.__tran_1, self.__tran_2, self.__tran_3]
        tindex = self.__get_index()
        t = trans[tindex]
        x, y = t(p)
        return x, y

pというタプルで(x1,y1)を受け取ります。

それを引数に使って、「__tran_1」「__tran_2」「__tran_3」のいずれかのメソッドをよびだします。

 

どのメソッドを呼び出すかを決めるのは「__get_index()」です。

    def __get_index(self):
        prob = [0.3330.3330.333]
        r = random.random()
        c = 0
        sump = []
        for p in prob:
            c += p
            sump.append(c)
        for item, sp in enumerate(sump):
            if(r <= sp):
                return item
        return len(prob) - 1

 各1/3(=0.333)ずつを加算した値と、乱数を比較して、乱数が合計値よりも小さかったら、そのインデックスを返します。

 

あとは、入力された「x1とy1」の変換の仕方が3パターンあるだけです。

1パターン目

x1 = 0.5*x
y1 = 0.5*y

2パターン目

x1 = 0.5*x + 0.5

y1 = 0.5*y + 0.5

3パターン目

x1 = 0.5*x + 1.0

y1 = 0.5*y

このパターンをランダムに繰り返していくわけです。

 

実行してみます

上記のクラスを使って、図形を描くには以下のようにします。

from class_graphics import Gasket

Gasket().draw()

これで描けるのが以下です。

f:id:arakan_no_boku:20210918210319p:plain

 

散布図で点をうつことで描画しているので、繰り返し回数を減らすと。

from class_graphics import Gasket

Gasket().draw(1000)

こんな感じにぼにゃりした感じになります。

f:id:arakan_no_boku:20210918215654p:plain

ではでは。

*1:x1, y1