RSS    

   Реферат: VB, MS Access, VC++, Delphi, Builder C++ принципы(технология), алгоритмы программирования

Этот факт можно использовать для быстрого вычисления наибольшего общего делителя. Например:

GCD(9, 12) = GCD(12 Mod 9, 9)

           = GCD(3, 9)

           = 3

========84

На каждом шаге числа становятся все меньше, так как 1<=B Mod A<A, если A не делится на B нацело. По мере уменьшения аргументов, в конце концов, A примет значение 1. Так как любое число делится на 1 нацело, на этом шаге рекурсия остановится. Таким образом, в какой то момент B разделится на A нацело, и работа процедуры завершится.

Открытие Эйлера закономерным образом приводит к рекурсивному алгоритму вычисления наибольшего общего делителя:

public Function GCD(A As Integer, B As Integer) As Integer

    If B Mod A = 0 Then            ' Делится ли B на A нацело?

        GCD = A                    ' Да. Процедура завершена.

    Else

        GCD = GCD(B Mod A, A)      ' Нет. Рекурсия.

    End If

End Function

Анализ времени выполнения программы

Чтобы проанализировать время выполнения этого алгоритма, необходимо определить, насколько быстро убывает переменная A. Так как функция останавливается, когда A доходит до значения 1, то скорость уменьшения A дает верхнюю границу оценки времени выполнения алгоритма. Оказывается, при каждом втором вызове функции GCD, параметр A уменьшается, по крайней мере, в 2 раза.

Допустим, A < B. Это условие всегда выполняется при первом вызове функции GCD. Если B Mod A <= A/2, то при следующем вызове функции GCD первый параметр уменьшится, по крайней мере, в 2 раза, и доказательство закончено.

Предположим обратное. Допустим, B Mod A > A / 2. Первым рекурсивным вызовом функции GCD будет GCD(B Mod A, A).

Подстановка в функцию значения B Mod A и A вместо A и B дает следующий рекурсивный вызов GCD(B Mod A, A).

Но мы предположили, что B Mod A > A / 2. Тогда B Mod A разделится на A только один раз, с остатком A – (B Mod A). Так как B Mod A больше, чем A / 2, то A – (B Mod A) должно быть меньше, чем A / 2. Значит, первый параметр второго рекурсивного вызова функции GCD меньше, чем A / 2, что и требовалось доказать.

Предположим теперь, что N — это исходное значение параметра A. После двух вызовов функции GCD, значение параметра A должно уменьшится, по крайней мере, до N / 2. После четырех вызовов, это значение будет не больше, чем (N / 2) / 2 = N / 4. После шести вызовов, значение не будет превосходить (N / 4) / 2 = N / 8. В общем случае, после 2 * K вызовов функции GCD, значение параметра A будет не больше, чем N / 2K.

Поскольку алгоритм должен остановиться, когда значение параметра A дойдет до 1, он может продолжать работу только до тех, пока не выполняется равенство N/2K=1. Это происходит, когда N=2K или когда K=log2(N). Так как алгоритм выполняется за 2*K шагов это означает, что алгоритм остановится не более, чем через 2*log2(N) шагов. С точностью до постоянного множителя, это означает, что алгоритм выполняется за время порядка O(log(N)).

=======85

Этот алгоритм — один из множества рекурсивных алгоритмов, которые выполняются за время порядка O(log(N)). При выполнении фиксированного числа шагов, в данном случае 2, размер задачи уменьшается вдвое. В общем случае, если размер задачи уменьшается, по меньшей мере, в D раз после каждых S шагов, то задача потребует S*logD(N) шагов.

Поскольку при оценке по порядку величины можно игнорировать постоянные множители и основания логарифмов, то любой алгоритм, который выполняется за время S*logD(N), будет алгоритмом порядка O(log(N)). Это не обязательно означает, что этими постоянными можно полностью пренебречь при реализации алгоритма. Алгоритм, который уменьшает размер задачи при каждом шаге в 10 раз, вероятно, будет быстрее, чем алгоритм, который уменьшает размер задачи вдвое через каждые 5 шагов. Тем не менее, оба эти алгоритма имеют время выполнения порядка O(log(N)).

Алгоритмы порядка O(log(N)) обычно выполняются очень быстро, и алгоритм нахождения наибольшего общего делителя не является исключением из этого правила. Например, чтобы найти, что наибольший общий делитель чисел 1.736.751.235 и 2.135.723.523 равен 71, функция вызывается всего 17 раз. Фактически, алгоритм практически мгновенно вычисляет значения, не превышающие максимального значения числа в формате long — 2.147.483.647. Функция Visual Basic Mod не может оперировать значениями, большими этого, поэтому это практический предел для данной реализации алгоритма.

Программа GCD использует этот алгоритм для рекурсивного вычисления наибольшего общего делителя. Введите значения для A и B, затем нажмите на кнопку Go, и программа вычислит наибольший общий делитель этих двух чисел.

Рекурсивное вычисление чисел Фибоначчи

Можно рекурсивно определить числа Фибоначчи (Fibonacci numbers) при помощи уравнений:

Fib(0) = 0

Fib(1) = 1

Fib(N) = Fib(N - 1) + Fib(N - 2)         для N > 1.

Третье уравнение рекурсивно дважды вызывает функцию Fib, один раз с входным значением N-1, а другой — со значением N-2. Это определяет необходимость 2 условий остановки рекурсии: Fib(0)=0 и Fib(1)=1. Если задать только одно из них, рекурсия может оказаться бесконечной. Например, если задать только Fib(0)=0, то значение Fib(2) могло бы вычисляться следующим образом:

Fib(2)  = Fib(1) + Fib(0)

        = [Fib(0) + Fib(-1)] + 0

        = 0 + [Fib(-2) + Fib(-3)]

        = [Fib(-3) + Fib(-4)] + [Fib(-4) + Fib(-5)]

        И т.д.

Это определение чисел Фибоначчи легко преобразовать в рекурсивную функцию:

Public Function Fib(num As Integer) As Integer

    If num <= 1 Then

        Fib = num

    Else

        Fib = Fib(num – 1) + Fib(num - 2)

    End If

End Function

=========86

Анализ времени выполнения программы

Анализ этого алгоритма достаточно сложен. Во‑первых, определим, сколько раз выполняется одно из условий остановки num <=1. Пусть G(N) — количество раз, которое алгоритм достигает условия остановки для входа N. Если N <= 1, то функция достигает условия остановки один раз и не требует рекурсии.

Если N > 1, то функция рекурсивно вычисляет Fib(N-1) и Fib(N-2), и завершает работу. При первом вызове функции, условие остановки не выполняется — оно достигается только в следующих, рекурсивных вызовах. Полное число выполнения условия остановки для входного значения N, складывается из числа раз, которое оно выполняется для значения N-1 и числа раз, которое оно выполнялось для значения N-2. Все это можно записать так:

G(0) = 1

G(1) = 1

G(N) = G(N - 1) + G(N - 2)               для N > 1.

Это рекурсивное определение очень похоже на определение чисел Фибоначчи. В табл. 5.2 приведены некоторые значения функций G(N) и Fib(N). Легко увидеть, что G(N) = Fib(N+1).

Теперь рассмотрим, сколько раз алгоритм достигает рекурсивного шага. Если N<=1, функция не достигает этого шага. При N>1, функция достигает этого шага 1 раз и затем рекурсивно вычисляет Fib(n-1) и Fib(N-2). Пусть H(N) — число раз, которое алгоритм достигает рекурсивного шага для входа N. Тогда H(N)=1+H(N-1)+H(N-2). Уравнения, определяющие H(N):

H(0) = 0

H(1) = 0

H(N) = 1 + H(N - 1) + H(N - 2)           для N > 1.

В табл. 5.3 показаны некоторые значения для функций Fib(N) и H(N). Можно увидеть, что H(N)=Fib(N+1)-1.

@Таблица 5.2. Значения чисел Фибоначчи и функции G(N)

======87

@Таблица 5.3. Значения чисел Фибоначчи и функции H(N)

Объединяя результаты для G(N) и H(N), получаем полное время выполнения для алгоритма:

Время выполнения   = G(N) + H(N)

                   = Fib(N + 1) + Fib(N + 1) - 1

                   = 2 * Fib(N + 1) - 1

Поскольку Fib(N + 1) >= Fib(N) для всех значений N, то:

Время выполнения   >= 2 * Fib(N) - 1

С точностью до порядка это составит O(Fib(N)). Интересно, что эта функция не только рекурсивная, но она также используется для оценки времени ее выполнения.

Чтобы помочь вам представить скорость роста функции Фибоначчи, можно показать, что Fib(M)>ÆM-2 где Æ — константа, примерно равная 1,6. Это означает, что время выполнения не меньше, чем значение экспоненциальной функции O(ÆM). Как и другие экспоненциальные функции, эта функция растет быстрее, чем полиномиальные функции, но медленнее, чем функция факториала.

Поскольку время выполнения растет очень быстро, этот алгоритм довольно медленно выполняется для больших входных значений. Фактически, настолько медленно, что на практике почти невозможно вычислить значения функции Fib(N) для N, которые намного больше 30. В табл. 5.4 показано время выполнения для этого алгоритма на компьютере с процессором Pentium с тактовой частотой 90 МГц при разных входных значениях.

Программа Fibo использует этот рекурсивный алгоритм для вычисления чисел Фибоначчи. Введите целое число и нажмите на кнопку Go для вычисления чисел Фибоначчи. Начните с небольших чисел, пока не оцените, насколько быстро ваш компьютер может выполнять эти вычисления.

Рекурсивное построение кривых Гильберта

Кривые Гильберта (Hilbert curves) — это самоподобные (self‑similar) кривые, которые обычно определяются при помощи рекурсии. На рис. 5.2. показаны кривые Гильберта с 1, 2 или 3 порядка.

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82


Новости


Быстрый поиск

Группа вКонтакте: новости

Пока нет

Новости в Twitter и Facebook

                   

Новости

© 2010.