#c_sharp #алгоритм
Доброго времени суток.
Необходимо реализовать рекурсивную и нерекурсивную реализацию построения кривой Серпинского.
Рекурсивное построение не составило большого труда, а вот найти конечный алгоритм
для нерекурсивной реализации (понятный для имплементации) не получилось (Только в книге
Род Стивене - "Delphi Готовые алгоритмы").
Может ли кто-нибудь скинуть материалы где можно почитать про нерекурсивную имплементацию
построения кривых Серпинского?
Реализация Рекурсивного алгоритма:
static private void Sierpinski(int depth, float dx, float dy, Graphics gr = null){
float x = dx;
float y = dy;
drawA(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, dx, dy);
drawB(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -dx, dy);
drawC(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -dx, -dy);
drawD(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, dx, -dy);
}
static private void drawA(Graphics gr, int depth, ref float x, ref float y, float
dx, float dy)
{
if (depth > 0)
{
--depth;
drawA(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, dx, dy);
drawB(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, 2 * dx, 0);
drawD(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, dx, -dy);
drawA(gr, depth, ref x, ref y, dx, dy);
}
}
static private void drawB(Graphics gr, int depth, ref float x, ref float y, float
dx, float dy)
{
if (depth > 0)
{
--depth;
drawB(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -dx, dy);
drawC(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, 0, 2 * dy);
drawA(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, dx, dy);
drawB(gr, depth, ref x, ref y, dx, dy);
}
}
static private void drawC(Graphics gr, int depth, ref float x, ref float y, float
dx, float dy)
{
if (depth > 0)
{
--depth;
drawC(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -dx, -dy);
drawD(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -2 * dx, 0);
drawB(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -dx, dy);
drawC(gr, depth, ref x, ref y, dx, dy);
}
}
static private void drawD(Graphics gr, int depth, ref float x, ref float y, float
dx, float dy)
{
if (depth > 0)
{
--depth;
drawD(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, dx, -dy);
drawA(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, 0, -2 * dy);
drawC(gr, depth, ref x, ref y, dx, dy);
drawLine(gr, ref x, ref y, -dx, -dy);
drawD(gr, depth, ref x, ref y, dx, dy);
}
}
Реализация функции drawLine():
static private void drawLine(Graphics gr, ref float x, ref float y, float dx, float dy)
{
if (gr != null)
gr.DrawLine(Pens.Black, x, y, x + dx, y + dy);
x += dx;
y += dy;
}
Ответы
Ответ 1
Воспользуемся стандартным подходом: перепишем функцию в виде набора заданий и управляющего цикла, который выполняет задания по одному. Вместо рекурсивного вызова функций будем добавлять новые задания в стек этих самых заданий. Этим самым мы как бы заменяем стек вызовов стеком заданий. Для нашего случая, заведём структуру данных, описывающую одно задание. Одним заданием у нас будет вызов drawA, drawB, drawC, drawD или drawLine. Получаем следующую структуру: struct DrawTask { public DrawTask(char type, int depth, float dx = 0, float dy = 0) { Type = type; Depth = depth; Linedx = dx; Linedy = dy; } public char Type; // A, B, C, D или L public int Depth; public float Linedx; // эти параметры нужны лишь для задания типа L public float Linedy; } Теперь нам нужна очередь заданий и её интерпретатор: static void Sierpinski_NR(int depth, float dx, float dy, Graphics gr = null) { StacktaskStack = new Stack (); float x = dx; float y = dy; // загружаем задания в стек в обратном порядке taskStack.Push(new DrawTask('L', depth, dx, -dy)); taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, -dx, -dy)); taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, -dx, dy)); taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, dx, dy)); taskStack.Push(new DrawTask('A', depth)); // пока есть задание, достаём его и выполняем // в результате выполнения в стеке могут оказаться подзадания while (taskStack.Count > 0) { var currentTask = taskStack.Pop(); switch (currentTask.Type) { case 'A': drawA_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'B': drawB_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'C': drawC_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'D': drawD_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'L': drawLine(gr, ref x, ref y, currentTask.Linedx, currentTask.Linedy); break; } } } Процедуру A имплементируем таким же образом. Она сама не делает ничего, просто добавляет в стек подзадания: static void drawA_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; // помещаем в стек в обратном порядке taskStack.Push(new DrawTask('A', depth)); taskStack.Push(new DrawTask('L', depth, dx, -dy)); taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, 2 * dx, 0)); taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, dx, dy)); taskStack.Push(new DrawTask('A', depth)); } } Остальные процедуры реализуются аналогично: static void drawB_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, dx, dy)); taskStack.Push(new DrawTask('A', depth)); taskStack.Push(new DrawTask('L', depth, 0, 2 * dy)); taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, -dx, dy)); taskStack.Push(new DrawTask('B', depth)); } } static void drawC_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, -dx, dy)); taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, -2 * dx, 0)); taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, -dx, -dy)); taskStack.Push(new DrawTask('C', depth)); } } static void drawD_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, -dx, -dy)); taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, 0, -2 * dy)); taskStack.Push(new DrawTask('A', depth)); taskStack.Push(new DrawTask('L', depth, dx, -dy)); taskStack.Push(new DrawTask('D', depth)); } }
Комментариев нет:
Отправить комментарий