Un fractal este o figură în care același model se repetă la dimensiuni din
ce în ce mai mici. Dacă mărești o bucată din ea, vezi o formă asemănătoare
cu întregul. Această proprietate se numește
autosimilaritate.
Un exemplu din natură este copacul: o ramură seamănă cu un copac mic, iar
o rămurică de pe ea seamănă cu o ramură mică. În matematică, repetarea
poate continua la nesfârșit. Pe calculator ne oprim după un număr de pași,
deci desenăm o aproximare.
Pentru desene folosim noțiuni de la informatică: funcții, bucle,
recursivitate și coordonate.
↳
Recursivitate
O funcție se apelează pe ea însăși, de fiecare dată pentru o problemă mai
mică. Se oprește când problema devine suficient de simplă.
↻
Iterație
Aceeași operație se repetă de mai multe ori. Rezultatul unui pas este
punctul de plecare pentru pasul următor.
n
2
Complexitate
Cu cât cerem mai multe detalii, cu atât calculatorul face mai multe
operații și are nevoie de mai mult timp.
Teorie
De unde vine ideea de fractal și cum se măsoară un fractal.
T1
ISTORIE
Scurtă istorie
Primii fractali au apărut în matematică la începutul secolului XX. Abia
după 1970, cu ajutorul calculatoarelor, s-a văzut că ei descriu multe forme
din natură.
1904
Helge von Koch descrie curba Koch, obținută prin
împărțirea repetată a unui segment (demonstrația 02).
1915
Wacław Sierpiński descrie triunghiul care îi
poartă numele (demonstrația 01), iar în 1916, covorul.
1918
Gaston Julia studiază ce se întâmplă când aplici
aceeași formulă de multe ori la rând.
Felix Hausdorff definește o dimensiune care
poate fi și un număr cu zecimale (T2).
1975
Mandelbrot introduce cuvântul fractal, din
latinescul fractus, care înseamnă „rupt” sau
„fragmentat”.
1980
Mandelbrot studiază, pe calculatoarele firmei IBM, mulțimea care
îi poartă numele (demonstrația 03). Loren Carpenter prezintă
Vol Libre, un film scurt cu munți desenați pe calculator
cu ajutorul fractalilor.
T2
PUTERI & LOGARITMI
Dimensiunea fractală
Dimensiunea se poate afla cu o întrebare: câte copii micșorate ale unei
figuri sunt necesare ca să refaci figura întreagă?
Un segment împărțit în 2 părți egale: obții 2
bucăți. 2 = 2¹, deci dimensiunea este 1.
Un pătrat cu latura împărțită în 2: obții 4
pătrățele. 4 = 2², deci dimensiunea este 2.
Un cub cu latura împărțită în 2: obții 8 cubulețe.
8 = 2³, deci dimensiunea este 3.
Dimensiunea este deci exponentul. Dacă o figură este formată din
N copii ale ei, fiecare de k ori mai
mică, dimensiunea D respectă relația de mai jos. Exponentul
îl aflăm cu logaritmi.
N = kD⇒D = log N / log k
Triunghiul lui Sierpiński este format din 3 copii ale lui, fiecare de 2
ori mai mică. Avem 3 = 2D, deci D = log 3 / log 2 ≈ 1,585.
Rezultatul nu este un număr întreg: triunghiul ocupă mai mult decât o
linie, dar mai puțin decât o suprafață plină.
CALCULATOR
DIMENSIUNEA D
1,585
0 punct1 linie2 plan3 spațiu
Demonstrații
01
/03
RECURSIVITATE & CONDIȚIE DE OPRIRE
Triunghiul lui Sierpiński
Pornim de la un triunghi. Unim mijloacele laturilor și obținem 4 triunghiuri mai
mici. Îl eliminăm pe cel din mijloc și repetăm același lucru pentru celelalte 3.
Numărul de repetări este nivelul.
3 copii, fiecare de 2 ori mai micăNivelul 4
0 · un triunghi7 · 2.187 triunghiuri
81
triunghiuri desenate
3ⁿ
Algoritmul în C++
↴
Funcția primește n, numărul de repetări rămase, și coordonatele
x și y ale vârfurilor A, B și C. Rândurile evidențiate sunt cele care se
execută la nivelul ales.
void fractal(int n, double xA, double yA, double xB, double yB, double xC, double yC) { // La nivelul 0, desenam si ne oprim. if (n == 0) { deseneazaTriunghi(xA, yA, xB, yB, xC, yC); return; } // Calculam mijloacele celor trei laturi. double xAB = (xA + xB) / 2; double yAB = (yA + yB) / 2; double xBC = (xB + xC) / 2; double yBC = (yB + yC) / 2; double xCA = (xC + xA) / 2; double yCA = (yC + yA) / 2; // Continuam cu triunghiul din coltul A. fractal(n - 1, xA, yA, xAB, yAB, xCA, yCA); // Continuam cu triunghiul din coltul B. fractal(n - 1, xAB, yAB, xB, yB, xBC, yBC); // Continuam cu triunghiul din coltul C. fractal(n - 1, xCA, yCA, xBC, yBC, xC, yC);}
Programul complet în C++ (sierpinski.cpp)
↴
// Triunghiul lui Sierpinski, varianta recursiva.
// Programul scrie desenul in fisierul sierpinski.svg.
#include <fstream>
using namespace std;
ofstream fout("sierpinski.svg");
void deseneazaTriunghi(double xA, double yA, double xB, double yB, double xC, double yC) {
fout << "<polygon fill='#d34c2a' points='"
<< xA << "," << yA << " " << xB << "," << yB << " " << xC << "," << yC << "'/>\n";
}
void fractal(int n,
double xA, double yA,
double xB, double yB,
double xC, double yC) {
// La nivelul 0, desenam si ne oprim.
if (n == 0) {
deseneazaTriunghi(xA, yA, xB, yB, xC, yC);
return;
}
// Calculam mijloacele celor trei laturi.
double xAB = (xA + xB) / 2, yAB = (yA + yB) / 2;
double xBC = (xB + xC) / 2, yBC = (yB + yC) / 2;
double xCA = (xC + xA) / 2, yCA = (yC + yA) / 2;
// Continuam cu cele trei triunghiuri din colturi.
fractal(n - 1, xA, yA, xAB, yAB, xCA, yCA);
fractal(n - 1, xAB, yAB, xB, yB, xBC, yBC);
fractal(n - 1, xCA, yCA, xBC, yBC, xC, yC);
}
int main() {
int n = 5;
fout << "<svg xmlns='http://www.w3.org/2000/svg' width='600' height='530'>\n";
fout << "<rect width='600' height='530' fill='white'/>\n";
fractal(n, 300, 10, 10, 512, 590, 512);
fout << "</svg>\n";
return 0;
}
LEGĂTURA CU INFORMATICA
Metoda divide et impera. O problemă mare se împarte în
probleme mai mici de același fel, rezolvate cu aceeași funcție. Condiția
n == 0 oprește apelurile. Fără ea, funcția s-ar apela la
nesfârșit, până când programul ar rămâne fără memorie.
02
/03
ITERAȚIE & COMPLEXITATE
Curba Koch
Pornim de la un segment și îl împărțim în trei părți egale. Partea din mijloc o
înlocuim cu două segmente care formează un vârf, ca un triunghi fără bază.
Repetăm pentru fiecare segment nou. Dacă pornim de la un triunghi, obținem un
fulg de zăpadă.
4 segmente, fiecare de 3 ori mai scurtNivelul 3
06
64segmente
2,37×față de lungimea inițială
La fiecare repetare, fiecare segment devine 4 segmente. După
n repetări, o latură are 4ⁿ segmente. Numărul
crește foarte repede: la 6 repetări sunt deja 4.096.
Programul complet în C++ (koch.cpp)
↴
// Curba Koch, varianta recursiva.
// Programul scrie desenul in fisierul koch.svg.
#include <fstream>
#include <cmath>
using namespace std;
ofstream fout("koch.svg");
void segment(double x1, double y1, double x2, double y2) {
fout << "<line x1='" << x1 << "' y1='" << y1
<< "' x2='" << x2 << "' y2='" << y2 << "' stroke='#3e5b9c'/>\n";
}
void koch(int n, double x1, double y1, double x2, double y2) {
// La nivelul 0, desenam segmentul si ne oprim.
if (n == 0) {
segment(x1, y1, x2, y2);
return;
}
double dx = (x2 - x1) / 3, dy = (y2 - y1) / 3;
// P si Q impart segmentul in trei parti egale.
double xP = x1 + dx, yP = y1 + dy;
double xQ = x1 + 2 * dx, yQ = y1 + 2 * dy;
// V este varful: rotim segmentul PQ cu 60 de grade in jurul lui P.
double xV = xP + dx * 0.5 + dy * sqrt(3.0) / 2;
double yV = yP + dy * 0.5 - dx * sqrt(3.0) / 2;
koch(n - 1, x1, y1, xP, yP);
koch(n - 1, xP, yP, xV, yV);
koch(n - 1, xV, yV, xQ, yQ);
koch(n - 1, xQ, yQ, x2, y2);
}
int main() {
int n = 4;
fout << "<svg xmlns='http://www.w3.org/2000/svg' width='600' height='220'>\n";
fout << "<rect width='600' height='220' fill='white'/>\n";
koch(n, 10, 200, 590, 200);
fout << "</svg>\n";
return 0;
}
LUNGIME ȘI ARIE
Lungimea crește, aria nu. Fiecare repetare face curba de
4/3 ori mai lungă, deci lungimea crește fără limită. Totuși,
fulgul rămâne în aceeași zonă, iar aria lui nu depășește 8/5 din aria
triunghiului inițial. Pe ecran desenăm doar primele repetări.
03
/03
BUCLE, CONDIȚII & NUMERE COMPLEXE
Mulțimea Mandelbrot
Fiecare punct al imaginii corespunde unui număr c. Pornim de la
z = 0 și calculăm de multe ori z ← z² + c. Dacă
valorile rămân aproape de origine, punctul face parte din mulțime și îl colorăm
închis. Dacă se depărtează, punctul nu face parte din mulțime, iar culoarea arată
după câți pași s-a depărtat.
MANDELBROT
1,0×
Mai rapidMai multe detalii
REGULA CARE SE REPETĂ
z
n+1
= z
n
² + c
z₀ = 0 · c = a + bi
Închis: valorile au rămas aproape de origine pe toată
durata calculului, deci punctul aparține probabil mulțimii.
Colorat: valorile s-au depărtat, deci punctul nu
aparține mulțimii.
Cum se calculează z² + c ↴
Numărul c = a + bi corespunde punctului
(a, b). Litera i este un număr cu
proprietatea i² = −1. Dacă z = x + yi,
atunci z² + c se calculează separat pentru x și y:
x nou = x² − y² + a
y nou = 2xy + b
Ambele formule folosesc valorile vechi ale lui x și y. Ne oprim când
x² + y² > 4, adică atunci când punctul este la o
distanță mai mare de 2 față de origine. De acolo, se știe sigur că se
va depărta la nesfârșit.
Programul complet în C++ (mandelbrot.cpp)
↴
// Multimea Mandelbrot desenata cu caractere, in consola.
#include <iostream>
using namespace std;
int main() {
int latime = 78, inaltime = 32, maxPasi = 100;
char simboluri[] = " .:-=+*#%&"; // de la "evadeaza repede" la "evadeaza greu"
for (int linie = 0; linie < inaltime; linie++) {
for (int col = 0; col < latime; col++) {
// Punctul c = a + bi corespunzator caracterului de pe ecran.
double a = -2.2 + col * 3.0 / latime;
double b = 1.2 - linie * 2.4 / inaltime;
// Pornim de la z = 0 si repetam z = z*z + c.
double x = 0, y = 0;
int pas = 0;
while (x * x + y * y <= 4 && pas < maxPasi) {
double xNou = x * x - y * y + a;
y = 2 * x * y + b;
x = xNou;
pas++;
}
if (pas == maxPasi) cout << '@'; // nu a evadat: punct din multime
else {
int k = pas; // a evadat dupa "pas" pasi
if (k > 9) k = 9;
cout << simboluri[k];
}
}
cout << '\n';
}
return 0;
}
LEGĂTURA CU INFORMATICA
Două bucle și o condiție. O buclă parcurge pixelii imaginii.
Pentru fiecare pixel, a doua buclă repetă calculul până când punctul se
depărtează sau până la numărul maxim de pași. Mai mulți pași înseamnă mai
multe detalii, dar și mai mult timp de calcul.
Informatică
Algoritmii din spatele desenelor, explicați cu noțiuni din clasa a XI-a:
recursivitatea și stiva de apeluri.
I1
RECURSIVITATE & STIVĂ
Recursivitatea și stiva de apeluri
Când o funcție se apelează pe ea însăși, calculatorul trebuie să țină minte
unde a rămas cu fiecare apel neterminat. Pentru asta folosește o zonă de
memorie numită stiva de apeluri. Funcționează ca un teanc
de farfurii: o farfurie nouă se pune deasupra, iar prima luată este tot cea
de deasupra. Regula se numește LIFO (Last In, First Out):
ultimul intrat, primul ieșit.
Mai jos rulează, pas cu pas, funcția fractal din demonstrația
01. Fiecare apel fractal(n) se pune în vârful stivei, iar când
ajunge la return, iese din stivă și programul continuă apelul
de dedesubt.
apelul care ruleazăapeluri care așteaptădesenat
Stiva de apeluri · vârful este sus
Stiva este goală.
apeluri făcute
0
în stivă acum
0
maximul din stivă
0
triunghiuri desenate
0
DE REȚINUT
La nivelul n se fac în total 1 + 3 + 9 + … + 3ⁿ = (3ⁿ⁺¹ − 1) / 2 apeluri,
dar în stivă stau cel mult n + 1 în același timp: câte un
apel pentru fiecare nivel. Dacă funcția nu ar avea condiția de oprire
n == 0, stiva s-ar umple, iar programul s-ar opri cu eroarea
stack overflow (depășirea stivei).
Curiozități
Două locuri neașteptate în care apar fractalii și câteva exemple din jurul nostru.
C1
MULȚIMEA MANDELBROT
Numărul π în mulțimea Mandelbrot
În 1991, Dave Boll, student la informatică, a studiat punctele
c = −0,75 + εi, unde ε (epsilon) este un număr foarte mic.
Aceste puncte sunt chiar lângă locul unde corpul mare al mulțimii se
unește cu discul din stânga. Pentru fiecare punct, a numărat câți pași
durează până când valorile se depărtează.
A observat că
numărul de pași înmulțit cu ε se apropie de π. Cu
cât ε este mai mic, cu atât rezultatul este mai aproape de
3,14159… Aaron Klebanoff a demonstrat mai târziu un rezultat
asemănător pentru punctele c = 0,25 + ε.
ε
pași n
n · ε
Apasă „Calculează” ca să vezi rezultatele.
π = 3,1415926…
C2
COMBINĂRI & OPERAȚII PE BIȚI
Triunghiul lui Pascal și triunghiul lui Sierpiński
În triunghiul lui Pascal, fiecare număr este suma celor două numere de deasupra
lui: C(n, k) = C(n−1, k−1) + C(n−1, k). Dacă colorăm doar
numerele impare, apare triunghiul lui Sierpiński. Putem colora și după restul
împărțirii la 3 sau la 5.
64
de rânduri
colorat = nu se împarte exact
729numere impare
Pentru împărțirea la 2 obținem 3ᵏ numere impare, adică
exact câte triunghiuri are Sierpiński la nivelul k.
Pentru împărțirea la 3, cele două culori arată resturile 1 și 2. La
fiecare nivel apar 6 copii ale nivelului anterior.
Pentru împărțirea la 5, culorile arată resturile 1, 2, 3 și 4. La
fiecare nivel apar 15 copii ale nivelului anterior.
Același desen în C++, fără să calculăm combinările
↴
Teorema lui Lucas spune că C(i, j) este impar exact atunci
când fiecare bit 1 din j este 1 și în i. În C++
verificăm asta cu (i & j) == j, unde &
este operația „și” pe biți.
for (int i = 0; i < 32; i++) {
for (int j = 0; j <= i; j++)
cout << (((i & j) == j) ? "* " : " ");
cout << '\n';
}
Alte exemple
NATURĂ
Conopida romanesco
Fiecare buchețel are forma întregii conopide, la scară mai mică.
Buchețelele sunt așezate în spirale, iar numărul spiralelor este, de
obicei, un număr din șirul lui Fibonacci.
CORPUL UMAN
Plămânii
Căile respiratorii se ramifică de aproximativ 23 de ori, ca un arbore
fractal. Astfel, o suprafață de zeci de metri pătrați încape în piept.
TEHNOLOGIE
Antenele fractale
În 1987, radioastronomul Nathan Cohen a construit o antenă în formă de
fractal. O astfel de antenă poate fi mică și poate funcționa pe mai multe
frecvențe, de aceea ideea este folosită în dispozitive mobile.
FILM
Peisaje desenate pe calculator
Loren Carpenter a folosit fractali pentru munții din filmul scurt
Vol Libre (1980) și pentru o planetă din filmul
Star Trek II (1982). Jocurile video folosesc metode asemănătoare
pentru peisaje.