В этой теме рассматривается применение рекурсии в графике. Приводятся примеры построения кривых Гильберта и Серпинского. Использование рекурсии в графике. Рекурсия часто используется в графике. Рассмотрим некоторые примеры.
Задача. Составить рекурсивный алгоритм рисования окружностей следующего вида:
Центральная окружность радиуса R, на этой окружности симметрично располагаются 4 окружности радиуса R/2 каждая. На каждой из этих 4-х окружностях аналогичным образом строятся ещё 4 и т.д. Рисование прекращается, если радиус последних становится меньше некоторого числа k. Рисование окружностей будем производить относительно центральной окружности с центром (x,y). Центр первой окружности - (x+r,y), второй - (x,y+r), третьей - (x-r,y), четвертой - (x,y-r). |
uses crt,graph;
var
gd,gm,mx,my:integer;
ch :char;
procedure krug(x,y,r:integer);
begin
if r>k then begin krug(x+r,y,r div 2); krug(x,y+r,r div 2);
krug(x-r,y,r div 2); krug(x,y-r,r div 2); end;
circle(x,y,r);
end;
Procedure Init; {инициализация графического режима}
var err: integer;
begin
DetectGraph(gd,gm); InitGraph(gd,gm,' путь драйвера');
if GraphResult<>grok then
begin Writeln(GraphErrorMsg(err));Readln;Halt(1); end;
end;
BEGIN
Init;
krug(getmaxX div 2, getmaxY div 2, getmaxY div 4);
END.
Кривые Гильберта.
Этот узор состоит из суперпозиции пяти кривых. Три наложенные друг на друга кривые имеют форму Н1, Н2 и Н3.
|
||
|
|
|
Нi+1 получается соединением четырех экземпляров Нi вдвое меньшего размера, повернутых соответствующим образом и стянутых вместе тремя прямыми линиями. Н1 можно считать состоящей из четырех вхождений пустой Н0, соединенных этими же тремя линиями. Кривая Нi называется кривой Гильберта i-го порядка в честь её первооткрывателя Д. Гильберта.
Каждая кривая Каждая кривая Нi состоит из четырех вдвое меньших Нi-1, то процедура для рисования Нi будет включать четыре обращения для рисования Нi-1, соответствующим образом повернутых и уменьшенных вдвое. Обозначим эти четыре части через А, В, С и D.Это кривые Гильберта 1-го порядка.
Соединительные прямые будем обозначать стрелками, указывающими в соответствующем направлении. Будем предполагать, что направление задается целым параметром i как i·45 градусов.
Тогда получаем: вправо при i=0, вниз при i=2, влево при i=4, вверх при i=6. Для построения А, В, С, D получается следующая схема рекурсий:Кривые Гильберта 2-го порядка.Для рисования линии будем использовать процедуру :
Line( i, s: integer), где i - направление, s - длина отрезка.procedure Line( i, s: integer);Используя эту процедуру, с помощью рекурсивных обращений напишем процедуру, соответствующую схеме А:
BEGIN
x:=i*45/180*pi;
setlinestyle(0,0,1);
LineRel(Round(s*cos(x)),Round(s*sin(x)));
END;
Procedure A(i, s: integer);Аналогично составляются процедуры для В, С, D. Процедура А инициируется главной программой по одному разу для каждой из кривых Гильберта. Главная программа определяет начальную точку кривой, т.е. исходные координаты (x0,y0) и начальное значение длины отрезка u (желательно, чтобы u=2n).
BEGIN
if i>0 then
begin
D(i-1,s);
Line(4,s);
A(i-1,s);
Line(6,s);
A(i-1,s);
Line(0,s);
B(i-1,s);
end;
Эта программа рисует кривые Гильберта 5-го порядка.
Uses Crt, Graph;
Var
x: real;
GrD, GrM :integer;
i,x0,y0,u :integer;procedure Line( i, s: integer);
BEGIN
x:=i*45/180*pi;
setlinestyle(0,0,1);
LineRel(Round(s*cos(x)),Round(s*sin(x)));
END;
Procedure B(i, s: integer);forward;
Procedure C(i, s: integer);forward;
Procedure D(i, s: integer);forward;
Procedure A(i, s: integer);
BEGIN
if i>0 then
begin
D(i-1,s); Line(4,s);
A(i-1,s); Line(6,s);
A(i-1,s); Line(0,s);
B(i-1,s);
end;
END;
Procedure B;
BEGIN
if i>0 then
begin
C(i-1,s); Line(2,s);
B(i-1,s); Line(0,s);
B(i-1,s); Line(6,s);
A(i-1,s);
end;
END;
Procedure C;
BEGIN
if i>0 then
begin
B(i-1,s); Line(0,s);
C(i-1,s); Line(2,s);
C(i-1,s); Line(4,s);
D(i-1,s);
end;
END;
Procedure D;
BEGIN
if i>0 then
begin
A(i-1,s); Line(6,s);
D(i-1,s); Line(4,s);
D(i-1,s); Line(2,s);
C(i-1,s);
end;
END;
BEGIN
Grd := Detect;
InitGraph(GrD,GrM, ' путь драйвера ');
if GraphResult=GrOk
then
begin
x0:=450;y0:=350;u:=128;i:=1;
while i<=5 do
begin
MoveTo(x0,y0);
A(i,u); i:=i+1;u:=u div 2;
x0:=x0+(u div 2);y0:=y0+(u div 2);
Readln;
end;
CloseGraph;
end;
END.Кривые Серпинского. Аналогично, путем наложения друг на друга нескольких кривых, получается рисунок из кривых Серпинского. Первые две из них С1 и С2 показаны ниже:
|
|
Главное отличие кривой Серпинского от кривой Гильберта в том, что первая кривая замкнута. Значит, основная рекурсивная схема должна давать разомкнутую кривую, четыре части которой соединяются линиями, не принадлежащими самому рекурсивному образу. Четыре составляющих образа обозначим через A, B, C, D.
Соединительные прямые будем обозначать стрелками, указывающими в соответствующем направлении. Будем предполагать, что направление задается целым параметром i как i·45 градусов. Кроме направлений, описанных в предыдущем примере, понадобятся ещё : Вниз-вправо i=1, вниз-влево i=3, вверх-влево i=5, вверх-вправо i=7Основной образ кривых Серпинского задается схемой:
S: A Вниз-вправо B вправо C вверх-влево D вверх-вправо
Рекурсивные составляющие по схемам:A: A вправо-вниз B вправо D вправо-вверх AЗаметим, что горизонтальные и вертикальные отрезки - двойной длины. Если использовать ту же процедуру рисования линии, что и в случае кривых Гильберта, то приведенные рекурсивные схемы записываются в рекурсивный алгоритм:
B: B влево-вниз C вниз A вправо-вниз B
C: C влево-вверх D влево B влево-вниз C
D: D вправо-вверх A вверх C влево-вверх Dprocedure A(i,s: integer);Аналогично получаются процедуры для B, C, D. Главная программа строится по образу S. Её задача - установить начальные значения для координат рисунка и задать единичную длину линий.
BEGIN
if i>0 then
begin A(i-1,s); Line(7,s);
B(i-1,s); Line(0,2*s);
D(i-1,s); Line(1,s);
A(i-1,s);
end;
END;Литература:
1. В.А.Дагене, Г.К.Григас, К.Ф.Аугутис. 100 задач по программированию.Упражнения:
2. Ж.Арсак. Программирование игр и головоломок.
3. Н.Вирт. Алгоритмы и структуры данных.
4. С.Барт. Россыпи головоломок.
5. Математический энциклопедический словарь.
6. М.Гарднер. Математические досуги.
1. Составить рекурсивный алгоритм рисования кривых Серпинского 4-го порядка.
2. Составить рекурсивный алгоритм рисования окружностей следующего вида: 3. Составить рекурсивный алгоритм рисования дерева следующего вида:
|
|