Previous Up Next
Retour à la page personnelle de Bernard Parisse.

Chapitre 8  Quelques exemples de récursivité

Les programmes qui suivent sont dans le répertoire examples/recur.

8.1  Récursivité ayant un seul appel récursif

Ces programmes se trouvent dans examples/recur/carre.cxx.
On commence par des exemples simples.

8.1.1  Les carrés

On trace un carré puis le carré qui joint les milieux des cotés etc... on s’arrête quand les segments à dessiner deviennent trop petits ou quand on a un dessin de profondeur n (n est le nombre d’étapes necessaires pour réaliser le dessin).
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK :

 
carres(A,B):={
DispG();
carre(A,B);
if (longueur2(A,B)>0.01) {
  carres(A+(B-A)/2,B+(B-A)*i/2);
}
};

On tape :
carres(-1.0,1.0)
On obtient le dessin des carrés du plus grand au plus petit : le dessin du carré (-1,1,1+2*i,-1+2*i) puis du carré (0,1+i,i,-1+i)....
bf Remarque si on tape

 
carres2(A,B):={
DispG();
if (longueur2(A,B)>0.01) {
  carres2(A+(B-A)/2,B+(B-A)*i/2);
}
carre(A,B);
};

puis :
carres2(-1.0,1.0)
le dessin des carrés ne se fera pas dans le même ordre et se fera du plus petit au plus grand.
Autre test d’arrêt On peut avoir besion de connaitre le nombre de n de fois que l’on fait le ou les appels récursifs pour avoir un dessin de "profondeur" n. On rajoute pour cela un paramètre qui sera la profondeur.
Dans l’exemple ci-dessus, on tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK le programme :

 
carrep(A,B,n):={
DispG();
carre(A,B);
 if (n==0) return 0;
 carrep(A+(B-A)/2,B+(B-A)*i/2,n-1);
};

On tape :
carres(-1.0,1.0,5)
On obtient le dessin des carrés du plus grand au plus petit :le dessin du carré (-1,1,1+2*i,-1+2*i) puis du carré (0,1+i,i,-1+i)...
Généralisation
On trace un carré ABCD, puis le carré MNPQ avec :
AM=a*AB,
BN=a*BC,
CO=a*CD,
DP=a*DA,
a est un nombre réel entre 0 et 1.

 
carresp(A,B,a):={
DispG();
carre(A,B);
if (longueur2(A,B)>0.01) {
carresp(A+(B-A)*a,B+(B-A)*i*a,a);
}
};

On tape par exemple :
carresp(-1.0,1.0,0.2)

8.1.2  Les triangles

Ces programmes se trouvent dans examples/recur/triangle.cxx.
On trace un triangle puis le triangle qui joint les milieux des cotés etc...on s’arrête quand les segments à dessiner deviennent trop petits.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK :

 
triangles(A,B,C):={
DispG();
triangle(A,B,C);
if (longueur2(A,B)>0.01) {
   triangles(A+(B-A)/2,B+(C-B)/2,C+(A-C)/2);
}
};

On tape :
triangles(-2.0,1,2*i)
On obtient le dessin des triangles du plus grand au plus petit :le dessin du triangle (-2,1,2*i) puis du triangle (-0.5,0.5+i,-1+i,)....

Remarque si on tape

 
trianglesp(A,B,C):={
DispG();
if (longueur2(A,B)>0.01) {
  trianglesp(A+(B-A)/2,B+(C-B)/2,C+(A-C)/2);
}
triangle(A,B,C);
};

On tape :
trianglesp(-2.0,1,2*i)
le dessin des triangles ne se fera pas dans le même ordre et se fera du plus petit au plus grand.
Généralisation
On trace un triangle ABC, puis le triangle MNP avec :
AM=a*AB,
BN=a*BC,
CO=a*CD,
a est un nombre réel entre 0 et 1.

 
trianglep(A,B,C,a):={
DispG();
triangle(A,B,C);
if (longueur2(A,B)>0.01) {
trianglep(A+(B-A)*a,B+(C-B)*a,C+(A-C)*a,a);
}
};

On tape par exemple :
trianglep(-2.0,1.0,2*i,0.2)

8.2  Récursivité ayant plusieurs appels récursifs

Voici des exemples encore assez simples.

8.2.1  Les triangles

Ces programmes se trouvent dans examples/recur/triangle.cxx.
On trace un triangle puis on joint les milieux des cotés.
On obtient ainsi 4 petits triangles semblables au précédent.
On recommence le même processus avec les trois triangles qui ont un angle commun avec le grand triangle et ainsi de suite.....on s’arrête quand les segments à dessiner deviennent trop petits.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK :

 
triangle3(A,B,C):={
DispG();
triangle(A,B,C);
 if (longueur2(A,B)<0.01) return 0;
 triangle3(A,A+(B-A)/2,C+(A-C)/2);
 triangle3(A+(B-A)/2,B,B+(C-B)/2);
 triangle3(C+(A-C)/2,B+(C-B)/2,C);
};

On tape par exemple :
triangle3(-2.0,1.0,2*i)
Remarque
Le tracé du triangle ne peut se faire qu’a la fin car il suffit de tracer les derniers petits triangles on écrit donc dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK :

 
trianglep(A,B,C):={
DispG();
if (longueur2(A,B)<0.01) {triangle(A,B,C);return 0;}
trianglep(A,A+(B-A)/2,C+(A-C)/2);
trianglep(A+(B-A)/2,B,B+(C-B)/2);
trianglep(C+(A-C)/2,B+(C-B)/2,C);
};

On tape par exemple :
trianglep(-2.0,1.0,2*i)
Généralisation
On trace un triangle ABC, puis le triangle MNP avec :
AM=a*AB,
BN=a*BC,
CO=a*CD,
a est un nombre réel entre 0 et 1.

 
triangle3p(A,B,C,a):={
DispG();
if (longueur2(A,B)<0.02) {triangle(A,B,C);return 0;}
triangle3p(A,A+(B-A)*a,C+(A-C)*a,a);
triangle3p(A+(B-A)*a,B,B+(C-B)*a,a);
triangle3p(C+(A-C)*a,B+(C-B)*a,C,a);
};

On tape par exemple :
triangle3p(-2.0,1.0,2*i,0.6)
Et avec un autre test d’arrêt en utilisant la profondeur n du dessin :

 
triangle3an(A,B,C,a,n):={
DispG();
if (n==0) {triangle(A,B,C);return 0;}
triangle3an(A,A+(B-A)*a,C+(A-C)*a,a,n-1);
triangle3an(A+(B-A)*a,B,B+(C-B)*a,a,n-1);
triangle3an(C+(A-C)*a,B+(C-B)*a,C,a,n-1);
};

On tape par exemple :
triangle3an(-2.0,1.0,2*i,0.6,3)

8.2.2  Les hexagones

Ces programmes se trouvent dans examples/recur/polygone.cxx.
On considère un hexagone de coés de longueur l, on remplace cet hexagone par 7 hexagones de cotés de longueur l/3 qui sont :les 6 hexagones ayant un angle commun avec l’hexagone de départ et un septième hexagone se trouvant au centre.
On obtient ainsi 7 petits hexagones semblables au précédent et on recommence le même processus.
On tape la fonction hexagone :

 
// dessin d'un hexagone
hexagone(x,y):={
local a,b,c;
DispG();
a:=x;
b:=y;
for (j:=1;j<=6;j++) {
c:=a+(b-a)*exp(evalf(i*pi*2/3));
segment(a,c);
b:=a;
a:=c;
}
};

puis on fait un premier appel récursif correspondant à l’hexagone du centre et avec la même itération que dans la fonction hexagone, on fait un appel récursif au lieu de tracer un segment pour les hexagones des angles, on tape :

hexagones(x,y,n):={
local a,b,c;
a:=x;
b:=y;
DispG();
if (n==0) {hexagone(a,b);return 0;}
c:=a+(b-a)*2/3*exp(evalf(i*pi/3))
// dessin de l'hexagone central
hexagones(c,c+(b-a)/3, n-1);
//dessin des 6 hexagones dans les angles
for (j:=1;j<=6;j++) {
c:=a+(b-a)*exp(evalf(i*pi*2/3));
hexagones(c,c+(a-c)/3,n-1);
b:=a;
a:=c;
} 
};

8.2.3  Les polygones réguliers

Ces programmes se trouvent dans examples/recur/polygone.cxx.
On part d’un polygone régulier P à k sommets et on le remplace par k polygones réguliers à k sommets de façon à ce que ces k polygones aient chacun un angle commun avec P et de façon à ce qu’ils ne se chevauchent pas. Puis on contine le processus et on ne dessine que les derniers petits hexagones.
Par exemple un hexagone H de coté a est remplacé par 6 hexagones de côtés a/3 obtenus par homothétie de rapport 1/3 et de centre les sommets de l’hexagone H.
Pour écrire une procédure générale il faut faire un peu de trigonométrie.
Le calcul du coté h du petit polygone k-régulier doit vérifier :
- si k=3, 4 on a h=(ba)/2
- si k=5, 6, 7, 8 on a h+hcos(2π/k)=(ba)/2
- si k=9, 10, 11, 12, 13, 14, 15, 16 on a :
h+hcos(2π/k)+hcos(4π/k)=(ba)/2 - si p=iquo(k-1,4), on a :
hl=0p cos(2pπ/k)=(ba)/2
On a donc :
s=∑l=0p cos(2pπ/k)=(sin((2p+1)π/k)+sin(π/k))/(2sin(π/k))
donc h=(ba)/2/s
et on tape :

//napperon de Cantor ou de Sierpinski k=3,4...
//utilise isopolygone(a,b,k) k>0 
//ex polyserp(-1-2*i,1-2*i,5,3); polyserp(-2*i,1-2*i,9,2)
polyserp(x,y,k,n):={
local a,b,c,h,j,q,p,s;
DispG();
if (n==0) {isopolygone(x,y,k);return 0;}
a:=x;
b:=y;
//pour k=3 ou 4  h:=(b-a)/3;
//pour k=5,6,7,8 h:=(b-a)/2/(cos(evalf(2*pi/k))+1);
//pour autre k il faut calculer s avec la trigo ou avec
//s:=1;for (l:=1;l<=iquo(k-1,4);l++){s:=s+cos(2*l*evalf(pi)/k);} 
p:=iquo(k-1,4);
s:=(sin(evalf(pi)/k)+sin((2*p+1)*evalf(pi)/k))/
    2/sin(evalf(pi)/k);
for (j:=1;j<=k;j++) {
h:=(b-a)/2/s;
polyserp(a,a+h,k,n-1);
c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k));
b:=a;
a:=c;
}
};

Le programme du dessin d’un polygone régulier à k cotés peut vous aider á comprendre le programme précédent.

 
// dessin d'un polygone regulier de k cotes
polyreg(x,y,k):={
local a,b,c;
a:=x;
b:=y;
DispG();
for (j:=1;j<=k;j++) {
c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k));
segment(a,c);
b:=a;
a:=c;
}
};

Remarque
Une faute de signe peut vous faire voir de jolis dessins pour k=7,8...et n=2

//utilise polyreg(a,b,k) k>0 k=nb de cotes
//ex polyserr(-2*i,1-2*i,8,2); polyserr(-2*i,1-2*i,9,2)
polyserr(x,y,k,n):={
local a,b,c,h,j,q,p,s;
//if (n==0) return isopolygone(x,y,k);
DispG();
if (n==0) {polyreg(x,y,k);return 0;}
a:=x;
b:=y;
p:=iquo(k-1,4);
s:=(sin(evalf(pi)/k)-sin((2*p+1)*evalf(pi)/k))/
    2/sin(evalf(pi)/k);
for (j:=1;j<=k;j++) {
if ( s!=0) h:=(b-a)/2/s; else h:=(b-a)/3;
polyserr(a,a+h,k,n-1);
c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k));
b:=a;
a:=c;
}
};

Un autre dessin avec des octogones en tracant un octogone au centre et 8 octogones dans les angles :

polyserp8(x,y,n):={
local a,b,c,h,j,q,p,s,k;
//if (n==0) return isopolygone(x,y,8);
k:=8;
DispG();
if (n==0) {polyreg(x,y,k);return 0;}
a:=x;
b:=y;
p:=iquo(k-1,4);
s:=(sin(evalf(pi)/k)+sin((2*p+1)*evalf(pi)/k))/
    2/sin(evalf(pi)/k);
h:=(b-a)/2/s;
polyserp8(a+h+i*h*(sqrt(2)+1),a+h*(sqrt(2)+1)+i*h*(sqrt(2)+1),n-1);
for (j:=1;j<=k;j++) {
polyserp8(a,a+h,n-1);
c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k));
b:=a;
a:=c;
 h:=(b-a)/2/s;}
};

8.2.4  Le flocon

Ces programmes se trouvent dans examples/recur/flocon.cxx.
Tous les programmes qui sont dans cette section se trouve dans le fichier :
flocon.cas On considère un segment AB et on place las points PQR tels que :
3*AP=AB
3*BQ=BA
et le triangle PQR est équilatèral direct.
On remplace alors le tracé du segment AB par le tracé APRQB.
On continue en faisant subir le même traitement aux 4 segments AP,PR,RQ,QB...on s’arrête quand la longueur des segments devient trop petite ou quand la profondeur est nulle.
Il y a donc 4 appels récursifs.

 
flocon(A,B):={
DispG();
if (longueur2(A,B)<0.01) {segment(A,B);return 0;}
flocon(A,A+(B-A)/3);
flocon(A+(B-A)/3,A+(B-A)/3*(1+exp(i*pi/3)));
flocon(A+(B-A)/3*(1+exp(i*pi/3)),A+2*(B-A)/3);
flocon(A+2*(B-A)/3,B);
};

On tape par exemple :
flocon(-1.0,1.0)
ou avec la profondeur :

 
floconp(A,B,n):={
local h;
DispG();
if (n==0) {segment(A,B);return 0;}
h:=(B-A)/3;
floconp(A,A+h,n-1);
floconp(A+h,A+h*(1+exp(i*pi/3)),n-1);
floconp(A+h*(1+exp(i*pi/3)),A+2*h,n-1);
floconp(A+2*h,B,n-1);
};

On tape par exemple :
floconp(-2.0,2.0,4)
Généralisation
On coupe le segment initial AB en trois en placant P et Q de façon à avoir :
AP=a*AB
BQ=a*BA
avec 0.25< a <0.5. puis on construit un triangle isocéle PQR tel que PR=QR=AP . On remplace le segment AB par les segments APRQB et on recommence le processus. Il y a donc 4 appels récursifs.

 
flocong(A,B,a,n):={
local h,t;
DispG();
if (n==0) {segment(A,B);return 0;}
t:=acos((0.5-a)/a);
h:=(B-A)*a;
flocong(A,A+h,a,n-1);
flocong(A+h,A+h*(1+exp(i*t)),a,n-1);
flocong(A+h*(1+exp(i*t)),B-h,a,n-1);
flocong(B-h,B,a,n-1);
};

On tape par exemple :
flocong(-2.0,2.0,0.4,4)

8.2.5  Le tapis carré

Ces programmes se trouvent dans examples/recur/carre.cxx.
On trace un carré puis puis on partage les cotés de ce carré en trois partie égales. On obtient ainsi 9 carrés.
On recommence le même processus avec les 8 carrés qui ont un coté commun avec le grand carré et ainsi de suite.....on s’arrête quand les segments à dessiner deviennent trop petits. Il y a donc 8 appels récursifs.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK :

 
carre8(A,B):={
local h;
DispG();
carre(A,B);
if (longueur2(A,B)<0.01) return 0;
h:=(B-A)/3;
carre8(A,A+h);
carre8(A+h,A+2*h);
carre8(A+2*h,B);
carre8(A+i*h,A+i*h+h);
carre8(A+i*h+2*h,B+i*h);
carre8(A+2*i*h,A+2*i*h+h);
carre8(A+2*i*h+h,A+2*i*h+2*h);
carre8(A+2*i*h+2*h,B+2*i*h);
};

On tape par exemple :
carre8(-1.0,1.0)
Autre test d’arrêt
On peut avoir besion de connaitre le nombre de n de fois que l’on fait le ou les appels récursifs pour avoir un dessin de "profondeur" n. On rajoute pour cela un paramètre qui sera la profondeur.
Dans l’exemple ci-dessus, on tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK le programme :

 
carre8p(A,B,n):={
local h;
h:=(B-A)/3;
DispG();
carre(A,B);
if (n==0)  return 0;
h:=(B-A)/3;
carre8p(A,A+h,n-1);
carre8p(A+h,A+2*h,n-1);
carre8p(A+2*h,B,n-1);
carre8p(A+i*h,A+i*h+h,n-1);
carre8p(A+i*h+2*h,B+i*h,n-1);
carre8p(A+2*i*h,A+2*i*h+h,n-1);
carre8p(A+2*i*h+h,A+2*i*h+2*h,n-1);
carre8p(A+2*i*h+2*h,B+2*i*h,n-1);
};

On tape par exemple :
carre8p(-1.0,1.0,3)

8.2.6  Une courbe de Péano

Ces programmes se trouvent dans examples/recur/peano.cxx.
On trace la diagonale AC d’un carré ABCD.
Puis puis on partage les cotés de ce carrés en trois partie égales. On obtient ainsi 9 carrés.
On remplace alors la diagonale du carré précédent par les diagonales des 9 carrés de façon à avoir une ligne continue allant de A à C. On recommence le même processus avec les 9 carrés de façon à avoir une ligne continue allant de A à C, et ainsi de suite.....on s’arrête quand les segments à dessiner deviennent trop petits.
On a choisit comme paramètre les affixes des points A et B et d’utiliser la profondeur comme test d’arrêt.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p), puis on valide avec OK : ou on utilise la commande :
read("peano.cas") car ce progrmme se trouve dans le fichier peano.cas.

 
//arc qui remplit le carre de cote x,y 
peano(x,y,n):={
local u,v;
DispG();
if (n==0) {segment(x,y+3*v);return 0;}
u:=(y-x)/3;
v:=i*u;
peano(x,x+u,n-1);
peano(x+u+v,x+u,n-1);
peano(x+2*u,y,n-1);
peano(y+v,y+2*v,n-1);
peano(x+2*(u+v),x+u+2*v,n-1);
peano(x+(u+v),x+u+2*v,n-1);
peano(x+2*v,x+2*v+u,n-1);
peano(x+3*v+u,x+u+2*v,n-1);
peano(x+2*(u+v),y+2*v,n-1);
};

On tape par exemple :
peano(-i,3-i,3)
Vous pouvez voir les différentes étapes de la construction en faisant successivement n=1,2,3,4 en utilisant le bouton stop si le tracé est trop long.

8.3  Quelques dessins doublement récursifs

Dans ce chapitre on va faire des dessins qui obligent à écrire plusieurs procédures récursives qui s’appellent l’une l’autre.

8.3.1  Les sphinx

Ces programmes se trouvent dans examples/recur/sphinx.cxx.
Voici un sphinx droit :

sphinxd(x,y):={
local z,u,t;
DispG();
z:=x+2*(y-x)/3*exp(evalf(pi)*i/3);
t:= y+(x-y)/3*exp(-evalf(pi)*i/3);
u:=t+(x-y)/3;
segment(x,z);
segment(z,u);
segment(u,t);
segment(t,y);
segment(y,x);
};

Voici un sphinx gauche :

sphinxg(x,y):={
local z,u,t;
DispG();
z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3);
t:= x+(y-x)/3*exp(evalf(pi)*i/3);
u:=t+(y-x)/3;
segment(y,z);
segment(z,u);
segment(u,t);
segment(t,x);
segment(x,y);
};

Voici un sphinx droit et ses 4 petits composés de trois sphinx gauches et d’un sphinx droit.

sphinxd4(x,y):={
local z,u,t;
DispG();
z:=x+2*(y-x)/3*exp(3.14*i/3);
t:= y+(x-y)/3*exp(-3.14*i/3);
u:=t+(x-y)/3;
segment(x,z);
segment(z,u);
segment(u,t);
segment(t,y);
segment(y,x);
sphinxg(x,(x+y)/2);
sphinxg((x+y)/2,y);
sphinxg(t,t+(x-y)/2);
sphinxd(z,(3*x+z)/4);
};

Voici un sphinx gauche et ses 4 petits

sphinxg4(x,y):={
local z,u,t;
DispG();
z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3);
t:= x+(y-x)/3*exp(evalf(pi)*i/3);
u:=t+(y-x)/3;
segment(y,z);
segment(z,u);
segment(u,t);
segment(t,x);
segment(x,y);
sphinxd(x,(x+y)/2);
sphinxd((x+y)/2,y);
sphinxd(t+(y-x)/2,t);
sphinxg((3*y+z)/4,z);
};

et voici toute la famille des sphinx droits (sphinxds(x,y,n) est une fonction récursive qui utilise la fonction récursive sphinxgs(x,y,n)).

sphinxds(x,y,n):={
local z,u,t;
DispG();
if (n==0) return 0;
z:=x+2*(y-x)/3*exp(evalf(pi)*i/3);
t:= y+(x-y)/3*exp(-evalf(pi)*i/3);
u:=t+(x-y)/3;
segment(x,z);
segment(z,u);
segment(u,t);
segment(t,y);
segment(y,x);
sphinxgs(x,(x+y)/2,n-1);
sphinxgs((x+y)/2,y,n-1);
sphinxgs(t,t+(x-y)/2,n-1);
sphinxds(z,(3*x+z)/4,n-1);
};

et toute la famille des sphinx gauches

sphinxgs(x,y,n):={
local z,u,t,p;
DispG();
if (n==0) return 0;
z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3);
t:= x+(y-x)/3*exp(evalf(pi)*i/3);
u:=t+(y-x)/3;
segment(y,z);
segment(z,u);
segment(u,t);
segment(t,x);
segment(x,y);
sphinxds(x,(x+y)/2,n-1);
sphinxds((x+y)/2,y,n-1);
sphinxds(t+(y-x)/2,t,n-1);
sphinxgs((3*y+z)/4,z,n-1);
};

ou encore en ne dessinant que la dernière génération du sphinx droit :
sphindps(-2,2,4) met 0.52s alors que sphinds(-2,2,4) met 0.83s

sphinxdps(x,y,n):={
local z,u,t;
DispG();
if (n==1) {sphinxd(x,y);return 0;}
z:=x+2*(y-x)/3*exp(evalf(pi)*i/3);
t:= y+(x-y)/3*exp(-evalf(pi)*i/3);
u:=t+(x-y)/3;
sphinxgps(x,(x+y)/2,n-1);
sphinxgps((x+y)/2,y,n-1);
sphinxgps(t,t+(x-y)/2,n-1);
sphinxdps(z,(3*x+z)/4,n-1);
};

et en ne dessinant que la dernière génération du sphinx gauche

sphinxgps(x,y,n):={
local z,u,t,p;
DispG();
if (n==1) {sphinxg(x,y);return 0}
z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3);
t:= x+(y-x)/3*exp(evalf(pi)*i/3);
u:=t+(y-x)/3;
sphinxdps(x,(x+y)/2,n-1);
sphinxdps((x+y)/2,y,n-1);
sphinxdps(t+(y-x)/2,t,n-1);
sphinxgps((3*y+z)/4,z,n-1);
};

et en ne dessinant pas un sphinx gauche mais un segment

sphinxdpst(x,y,n):={
local z,u,t;
DispG();
if (n==1) return sphinxd(x,y);
z:=x+2*(y-x)/3*exp(evalf(pi)*i/3);
t:= y+(x-y)/3*exp(-evalf(pi)*i/3);
u:=t+(x-y)/3;
sphinxgpst(x,(x+y)/2,n-1);
sphinxgpst((x+y)/2,y,n-1);
segment(t,t+(x-y)/3);
sphinxdpst(z,(3*x+z)/4,n-1);
};
et en ne dessinant pas un sphinx droit mais un segment
\begin{verbatim}
sphinxgpst(x,y,n):={
local z,u,t,p;
DispG();
if (n==1) return sphinxg(x,y);
z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3);
t:= x+(y-x)/3*exp(evalf(pi)*i/3);
u:=t+(y-x)/3;
sphinxdpst(x,(x+y)/2,n-1);
sphinxdpst((x+y)/2,y,n-1);
segment(t+(y-x)/3,t);
sphinxgpst((3*y+z)/4,z,n-1);
};

8.3.2  Le dragon

Ces programmes se trouvent dans examples/recur/dragon.cxx.
On se donne deux points A et B (ou deux nombres complexes a et b qui sont l’affixe de ces points) et on considère le carré ACBD direct ayant pour diagonale AB.
Le segment AB peut donner naissance à un dragon gauche, pour cela, on remplace le segment AB par les deux côtés AD et DB du carré ACBD situé à gauche du vecteur AB ou,
le segment AB peut donner naissance à un dragon droit, pour cela, on remplace le segment AB par les deux côtés AC et CB du carré ACBD situé à droite du vecteur AB. Pour la fabrication du dragon gauche, ces deux segments sont considérés comme allant donner naissance à un dragon gauche (AD) et à un dragon droit (DB) et
pour la fabrication du dragon droit, ces deux segments sont considérés comme allant donner naissance à un dragon gauche (AC) et à un dragon droit (CB).
On a :
bc=i*(ac) et bd=−i*(ad) donc :
c=(bi*a)*(1+i)/2 et d=(b+i*a)*(1−i)/2.
On écrit donc en prenant comme test d’arrêt la profondeur n c’est à dire le nombre de générations.

// dessine un dragon dragong(-i,2+i,10)
//x=a,y=b et d=u
 dragong(x,y,n):={
local u;
DispG();
if (n==0){segment(x,y);return 0;}
u:=(y+i*x)*(1-i)/2;
dragong(x,u,n-1);
dragond(u,y,n-1);
};
// dessine un dragon dragond(-i,2+i,10)
//x=a,y=b et c=u
dragond(x,y,n):={
local u;
DispG();
if (n==0){segment(x,y);return 0;}
u:=(y-i*x)*(1+i)/2;
dragong(x,u,n-1);
dragond(u,y,n-1);
};

Remarque
Il est facile d’obtenir la courbe du dragon en prenant une longue bande de papier que l’on plie n fois sur elle même, toujours dans le même sens. Lorsqu’on a pris soin de bien marquer les plis, on obtient un dragon lorsqu’on déplie la bande en disposant les plis à angle droit.
Ce n’est pas tout à fait ce que l’on a programmer car dans le programme à chaque étape on multiplie la longueur du dragon par √2.
Sauriez vous programmer le dragon de la bande de papier ? Voici la solution : on remarquera que le dragon droit est réalisé par la deuxième moitié de la bande de papier et donc la fonction dragonpapierd est la fonction dragonpapierg en changeant gauche en droite, et en commençant par la dernière instruction.

dragonpapierg(x,y,n):={
local u,v,a,b;
DispG();
if (n==0){segment(x,y); return y;}
u:=x+(y-x)/2;
a:=dragonpapierg(x,u,n-1);
v:=a+(y-x)*i/2;
b:=dragonpapierd(a,v,n-1);
return b
};

dragonpapierd(x,y,n):={
local u,v,a,b;
DispG();
if (n==0){segment(x,y); return y;}
v:=x+(y-x)*i/2;
b:=dragonpapierg(x,v,n-1);
u:=a+(y-x)/2;
a:=dragonpapierd(b,u,n-1);
return a
};

On tape :
dragonpapierg(-3.0,13,8)
Voici une autre solution où on repère l’arrivée et la direction du dernier trait. Dans ce cas on connait le départ et la direction de départ du dragon droit....mais c’est nettement plus compliqué.

dragonpaperg(x,y,n):={
local u,v,a,b;
DispG();
if (n==0){segment(x,y); return (x,y);}
u:=x+(y-x)/2;
a:=dragonpaperg(x,u,n-1);
v:=a[1]+abs((y-x)/(a[1]-a[0]))*(a[1]-a[0])*i/2;
b:=dragonpaperd(a[1],v,n-1);
return b
};

dragonpaperd(x,y,n):={
local u,v,a,b;
DispG();
if (n==0){segment(x,y); return (x,y);}
u:=x+(y-x)/2;
a:=dragonpaperg(x,u,n-1);
v:=a[1]-abs((y-x)/(a[1]-a[0]))*(a[1]-a[0])*i/2;
b:=dragonpaperd(a[1],v,n-1);
return b
};

On tape :
dragonpaperg(-3,13,8)

8.3.3  Deux courbes de Péano formée d’arcs de cercle

Parmi les nombreuses courbes inventées par Péano on va en décrire deux qui sont des courbes rècursives formées par des arcs de cercle.
Ces programmes se trouvent dans examples/recur/peano.cxx

Une courbe de Péano formée par 13 arcs

On écrit la fonction arcg (resp arcd) qui dessine des arcs définit par le début de l’arc, la fin de l’arc, et de mesure π/3 (resp −π/3).

//arc x y  de mesure +pi/3
arcg(x,y):={
return arc(x,y,pi/3);
};
//arc x y de de mesure -pi/3
arcd(x,y):={
return arc(x,y,-pi/3);
};

Puis on écrit la fonction peanog (resp peanod) :
Soient deux points A d’affixe a et B d’affixe b.
Pour la fonction peanog, on débute par l’arc AB de mesure π/3, situé sur le cercle de centre Cg d’affixe cg=(ba*exp(i*π/3))*(1−exp(−i*π/3)) que l’on appellera arcg.
Pour la fonction peanod, on débute par l’arc AB de mesure −π/3, situé sur le cercle de centre Cd d’affixe cd=(ba*exp(−i*π/3))*(1−exp(i*π/3)) que l’on appellera arcd.
On remplace ensuite arcg (resp arcd) par 13 arcs de mesure π/3 ou de mesure −π/3 selon le dessin que l’on obtient en tapant :
peanog(-2-2*i,2-2*i,1) (resp peanod(-2-2*i,2-2*i,1)).
Ces deux figures sont symétriques.
Et on continue en appliquant le même traitement à chacun de ses 13 arcs en remplacant les arcg (resp arcd) par 13 arcs.

// courbe de peano avec 13 arcs
//par ex peanod(-2-2*i,2-2*i,3)
peanod(x,y,n):={
local c1,b,c,d,e1,f,g,h,i1,j,k,l,m;
DispG();
if (n==0) {arc(x,y,-pi/3);return 0;}
c1:=x+(y-x)*exp(evalf(pi)*2*i/3)/3;
b:=x+(y-x)/3*exp(evalf(pi)*i/3);
c:=x+(y-x)/3*2*exp(evalf(pi)*i/3);
d:=c+(y-x)/3;
e1:=b+2*(y-x)/3;
f:=c1+(y-x)*(15+i*sqrt(3))/18;
g:=c1+(y-x)*(6+i*sqrt(3))/9;
h:=f-(y-x)/3;
i1:=h-i*(y-x)/9*sqrt(3);
j:=i1+(y-x)/3;
k:=g-i*2*(y-x)/9*sqrt(3);
l:=x+(y-x)/3;
m:=x+2*(y-x)/3;
peanog(x,b,n-1);
peanod(b,c,n-1);
peanod(c,d,n-1);
peanod(d,e1,n-1);
peanod(e1,f,n-1);
peanog(f,g,n-1);
peanog(g,h,n-1);
peanog(h,i1,n-1);
peanod(i1,j,n-1);
peanod(j,k,n-1);
peanog(k,l,n-1);
peanog(l,m,n-1);
peanod(m,y,n-1);
};
peanog(x,y,n):={
local c1,b,c,d,e1,f,g,h,i1,j,k,l,m;
DispG();
if (n==0) {
arc(x,y,pi/3);return 0;}
c1:=x+(y-x)*exp(evalf(-2*pi)*i/3)/3;
b:=x+(y-x)/3*exp(evalf(-pi)*i/3);
c:=x+(y-x)/3*2*exp(evalf(-pi)*i/3);
d:=c+(y-x)/3;
e1:=b+2*(y-x)/3;
f:=c1+(y-x)*(15-i*sqrt(3))/18;
g:=c1+(y-x)*(6-i*sqrt(3))/9;
h:=f-(y-x)/3;
i1:=h+i*(y-x)/9*sqrt(3);
j:=i1+(y-x)/3;
k:=g+i*2*(y-x)/9*sqrt(3);
l:=x+(y-x)/3;
m:=x+2*(y-x)/3;
peanod(x,b,n-1);
peanog(b,c,n-1);
peanog(c,d,n-1);
peanog(d,e1,n-1);
peanog(e1,f,n-1);
peanod(f,g,n-1);
peanod(g,h,n-1);
peanod(h,i1,n-1);
peanog(i1,j,n-1);
peanog(j,k,n-1);
peanod(k,l,n-1);
peanod(l,m,n-1);
peanog(m,y,n-1);
};

Une courbe C1 de Péano qui remplit un carré

On va maintenant décrire la courbe de Péano ternaire qui remplit un carré direct de coté XY, en étant C1 .
Soient x est l’affixe de X et y est l’affixe de Y.
Le dessin de base est obtenu en tapant:
bases(x,y)
il est composé de 8 arcs de cercle de mesure π/2 ou de mesure −π/2 et débute au point A d’affixe a:=x+(yx)/6*1+i) et se termine au point K d’affixe k, symétrique de A par rapport au centre du carré.
Á partir de ce dessin de base on fait la figure un :
un(x,y)
qui est composée d’un arc de mesure π/2 commençant au point d’affixe :
a−(yx)/3 et se terminant au point A suivi du dessin de base.
Á partir de ce dessin de base on fait la figure deux :
deux(x,y)
qui est composée d’un arc de mesure π/2 commençant au point d’affixe :
ai*(yx)/3 et se terminant au point A suivi du dessin de base.
On partage le carré en 9 petits carrés et dans chacun des petits carrés on trace la figure de base ou la figure de baseun ou la figure de basedeux de façon à obtenir une ligne continue etc....
On écrit ensuite les procédures :
peano0 qui débute par la figure bases,
peano1 qui débute par la figure un,
peano2 qui débute par la figure de deux,
Taper par exemple peano0(-1,2,1) pour voir l’étape 1.

//motif de base
bases(x,y):={
local a,b,c,d,e1,f,g,h,i1,k;
DispG();
h:=(y-x)/3;
a:=x+h/2+i*h/2;
b:=a+i*h;
c:=b+i*h;
d:=c+h
e1:=b+h;
f:=a+h;
g:=f+h;
k:=e1+h;
i1:=d+h;
arc(a,b,pi/2);
arc(c,b,pi/2);
arc(d,c,pi/2);
arc(e1,d,pi/2);
arc(e1,f,pi/2);
arc(f,g,pi/2);
arc(g,k,pi/2);
arc(i1,k,pi/2);
};
//un arc et le motif de base
un(x,y):={
local h,a;
DispG();
h:=(y-x)/3;
a:=x+h/2+i*h/2;
arc(a-h,a,pi/2);
bases(x,y);
};
//un  autre arc et le motif de base
deux(x,y):={
local h,a;
DispG();
h:=(y-x)/3;
a:=x+h/2+i*h/2;
arc(a,a-h*i,pi/2);
bases(x,y);
};

//courbe qui remplit un carre debute par le motif bases
// ex peano0(-1,2,1) ou  peano0(-1,2,3)  
// utilise bases un deux peano1 peano2
peano0(x,y,n):={
local a,h;
DispG();
if (n==0) {bases(x,y);return 0;}
h:=(y-x)/3;
a:=x+h;
peano0(x,a,n-1);
peano2(a+i*h,a,n-1);
peano1(a+h,y,n-1);
peano1(y+i*h,y+2*i*h,n-1);
peano1(a+h+2*i*h,a+2*i*h,n-1);
peano2(a+i*h,a+2*i*h,n-1);
peano2(x+2*i*h,a+2*i*h,n-1);
peano2(a+3*i*h,a+2*i*h,n-1);
peano1(a+h+2*i*h,y+2*i*h,n-1);
};
//courbe qui remplit un carre debute par le motif un
// ex peano1(-1,2,1) 
// utilise bases un deux peano2
peano1(x,y,n):={
local a,h;
DispG();
if (n==0) {un(x,y);return 0;}
h:=(y-x)/3;
a:=x+h;
peano1(x,a,n-1);
peano2(a+i*h,a,n-1);
peano1(a+h,y,n-1);
peano1(y+i*h,y+2*i*h,n-1);
peano1(a+h+2*i*h,a+2*i*h,n-1);
peano2(a+i*h,a+2*i*h,n-1);
peano2(x+2*i*h,a+2*i*h,n-1);
peano2(a+3*i*h,a+2*i*h,n-1);
peano1(a+h+2*i*h,y+2*i*h,n-1);
};
//courbe qui remplit un carre debute par le motif deux
// ex peano2(-1,2,1) 
// utilise bases un deux peano1
peano2(x,y,n):={
local a,h;
DispG();
if (n==0) {deux(x,y);return 0;}
h:=(y-x)/3;
a:=x+h;
peano2(x,a,n-1);
peano2(a+i*h,a,n-1);
peano1(a+h,y,n-1);
peano1(y+i*h,y+2*i*h,n-1);
peano1(a+h+2*i*h,a+2*i*h,n-1);
peano2(a+i*h,a+2*i*h,n-1);
peano2(x+2*i*h,a+2*i*h,n-1);
peano2(a+3*i*h,a+2*i*h,n-1);
peano1(a+h+2*i*h,y+2*i*h,n-1);
};

Autres courbes : des progrmmes sans commentaires

Ces programmes se trouvent dans examples/recur/autres.cxx

La courbe de Hibert

//courbe de hilbert par exemple hilg(-2,0,4) est 
//compos\'e par 4 morceaux hilg hild berg et berd
//ou hild(0,2,4) ou berg(2,2-2*i,4) ou berd(2,2-2*i,4)
//le morceau 1
hilg(x,y,n):={
local u,v,a,b;
DispG();
if (n==0) {segment(x,y);return 0;}
u:=(y-x)/2;
v:=u*i;
hild(x,x+v,n-1);
hilg(x+v,x+v+u,n-1);
a:=berg(x+v+u,x+u,n-1);
b:=berd(a,a+u,n-1);
};
//le morceau 2
hild(x,y,n):={
local u,v,a,b;
DispG();
if (n==0) {segment(x,y);return 0;}
u:=(y-x)/2;
v:=u*i;
hilg(x,x-v,n-1);
hild(x-v,x-v+u,n-1);
b:=berd(x-v+u,x+u,n-1);
a:=berg(b,b+u,n-1);
};
//le morceau 3
berg(x,y,n):={
local u,v,b;
DispG();
if (n==0) {segment(x,y);return y;}
v:=(x-y)/2;
u:=-v*i;hild(x,x+v,n-1);
hilg(x+v,x+v+u,n-1);
b:=berg(x+v+u,x+u,n-1);
hild(b,b-v,n-1);
return(b-v);
};
//le morceau 4
berd(x,y,n):={
local u,v,a;
DispG();
if (n==0) {segment(x,y);return y;}
v:=(x-y)/2;
u:=-v*i;
hilg(x,x+v,n-1);
hild(x+v,x+v-u,n-1);
a:=berd(x+v-u,x-u,n-1);
hilg(a,a-v,n-1);
return a-v;
};

La courbe de Gosper

//gosper(-2-2*i,2-2*i,2)ou gosper(-2-2*i,2-2*i,3)
gosper(x,y,n):={
local a,b,c,d,f,g;
DispG();
if (n==0) return segment(x,y);
a:=x+(y-x)/sqrt(7)*exp(evalf(-i*acos(5*sqrt(7)/14)));
c:=x+(a-x)*exp(evalf(i*pi/3));
b:=c+a-x;
d:=c+(a-x)*exp(evalf(2*i*pi/3));
f:=d+2*(a-x);
g:=(d+f)/2;
gosper(x,a,n-1);
gosper(b,a,n-1);
gosper(c,b,n-1);
gosper(c,d,n-1);
gosper(d,g,n-1);
gosper(g,f,n-1);
gosper(y,f,n-1);
};

Des plantes et des arbres

Ces programmes se trouvent dans examples/recur/plantes.cxx

//Voici des sapins....sapin(0,2*i)
sapin(x,y):={
 DispG();
 if (abs(x-y)<0.5) {segment(x,y); return 0;}
 sapin(x,x+(y-x)*0.5*exp(i));
 sapin(x,x+(y-x)*0.5*exp(-i));
 segment(x,(3*x+y)/4);
 sapin((3*x+y)/4,y);
};
//Voici des fleurs....fleur(0,2*i)
fleur(x,y):={
 DispG();
 if (abs(x-y)<0.5) {segment(x,y);cercle(y,(y-x)*0.3); 
                    return 0;}
 segment(x,y);cercle(y,(y-x)*0.3);cercle(y,(y-x)*0.2);
 fleur(x,x+(y-x)*0.5*exp(i*0.5));
 fleur(x,x+(y-x)*0.5*exp(-i*0.5));
};
//Voici des arbres....arbre(0,2*i)
arbre(x,y):={
 DispG();
 if (abs(x-y)<0.2) {segment(x,y); return 0;}
 segment(x,(x+y)/2);
 arbre((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i*0.5));
 arbre((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i*0.5));
};
//et des arbres moins deplumes arbre2(0,2*i)
arbre2(x,y):={
 DispG();
 if (abs(x-y)<0.2) {segment(x,y); return 0;}
 segment(x,(x+y)/2);
 arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i*0.5));
 arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i*0.5));
 arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i));
 arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i));
};
//un epineux  arbre3(0,2*i)
arbre3(x,y):={
 DispG();
 if (abs(x-y)<0.2) {segment(x,y); return 0;}
 segment(x,(x+y)*0.5);
 arbre3((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(i*0.5));
 arbre3((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(-i*0.5));
 arbre3((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i));
 arbre3((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i));
};
//une fougere par ex fougeres(-2*i,2*i)
fougere(x,y):={
local a;
DispG();
 if (abs(x-y)<0.2) {segment(x,y); return 0;}
a:=x+(y-x)*0.15*exp(-i*0.2);
segment(x,a);
fougere(a,a+(y-x)*0.33*exp(i*1.2));
fougere(a,a+(y-x)*0.33*exp(-i*1.2));
fougere(a,a+(y-x)*0.85*exp(-i*0.2));
};
//par ex fougeres(-2*i,2*i,0.05,6)
fougeres(x,y,t,n):={
local a;
DispG();
if (n==0) {segment(x,(x+y)/2); return 0;}
//a:=x+(y-x)*0.15*exp(-i*t);
a:=x+(y-x)*0.15;
segment(x,a);
fougeres(a,a+(y-x)*0.33*exp(i*1.2),t,n-1);
fougeres(a,a+(y-x)*0.33*exp(-i*1.2),t,n-1);
fougeres(a,a+(y-x)*0.85*exp(-i*t),t,n-1);
};
//et le bouquet final bouquet(0,2*i)
bouquet(x,y):={
 DispG();
 if (abs(x-y)<0.2) {segment(x,y); return 0;}
 segment(x,(x+y)*0.5);
 bouquet((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(i*0.5));
 bouquet((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(-i*0.5));
 bouquet((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i));
 bouquet((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i));
 bouquet((x+y)/2,(x+y)/2+(y-x)*0.5);
};
Retour à la page personnelle de Bernard Parisse.
Previous Up Next