Язык Си в примерах/Факториал: различия между версиями
Оформление, уточнения; оставлен единственный «итеративный» вариант; →Использование хвостовой рекурсии: новый раздел. |
Нет описания правки |
||
Строка 97: | Строка 97: | ||
# Улучшите программы, добавив к ним диагностику ошибки во входных данных: если <var >n</var> < 0, то программа должна ясно сообщать о неопределенности факториала для отрицательных чисел. |
# Улучшите программы, добавив к ним диагностику ошибки во входных данных: если <var >n</var> < 0, то программа должна ясно сообщать о неопределенности факториала для отрицательных чисел. |
||
# Опытным путем определите максимальные значения аргумента и результата для программ выше. Исследуйте влияние на эти значения замены используемого в программах [[Язык Си в примерах/Скалярные типы#Числовые типы |числового типа]] <code >int</code> на <code >short</code>, <code >long</code> и <code >long long</code>. <strong >Обратите внимание</strong>, что при этом также потребуется соответственно изменить ''строку формата'' (первый аргумент) функции <code >printf</code>. |
# Опытным путем определите максимальные значения аргумента и результата для программ выше. Исследуйте влияние на эти значения замены используемого в программах [[Язык Си в примерах/Скалярные типы#Числовые типы |числового типа]] <code >int</code> на <code >short</code>, <code >long</code> и <code >long long</code>. <strong >Обратите внимание</strong>, что при этом также потребуется соответственно изменить ''строку формата'' (первый аргумент) функции <code >printf</code>. |
||
# После выполнения первых двух заданий Вы поймете, что максимальной натуральной число по которому возможно вычислить факториал с использованием 4-х байтовых беззнаковых чисел это 12 (всего навсего). Настоящий программист не будет использовать рекурсии и умножать в цикле, а использует предвычисленный статический массив готовых данных. Попробуйте и сравните быстродействие. |
|||
== См. также == |
== См. также == |
Версия от 09:56, 3 апреля 2015
- Компиляция программ
- Простейшая программа «Hello World»
- Учимся складывать
- Максимум
- Таблица умножения
- ASCII-коды символов
- Верхний регистр
- Скобочки
- Факториал
- Степень числа
- Треугольник Паскаля
- Корень уравнения
- Система счисления
- Сортировка
- Библиотека complex
- Сортировка на основе qsort
- RPN-калькулятор
- RPN-калькулятор на Bison
- Простая грамматика
- Задача «Расчёт сопротивления схемы»
- Простая реализация конечного автомата
- Использование аргументов командной строки
- Чтение и печать без использования stdio
- Декодирование звукозаписи в формате ADX
- Другие примеры
- XCC C
Факториалом числа n называют произведение первых n натуральных чисел:
Ниже мы рассмотрим примеры программ, содержащих вычисляющую факториал заданного числа функцию factorial
.
Реализация рекуррентной формулы
#include <stdio.h>
static int
factorial (int n)
{
return (n < 2) ? 1 : n * factorial (n - 1);
}
int
main (void)
{
int n;
while (scanf ("%d", &n) == 1) {
printf ("%d\n", factorial (n));
}
}
Это определение основано на следующей рекуррентной формуле:
Для вычисления факториала n! эта функция вызывает саму себя с аргументом n − 1.
Тернарный оператор (? :
) в выражении (n < 2) ? 1 : n * factorial (n - 1)
для возвращаемого значения (return
) «обрывает» рекурсию, когда аргумент функции становится меньшим 2. В противном случае,
функция factorial
постоянно бы вызывала саму себя, и во
время выполнения программы мы получили бы сообщение об ошибке
«переполнение стека» или «превышена глубина рекурсии». Для
того, чтобы этого не было, необходимо, чтобы при некоторых
значения аргумента, функция не вызывала саму себя, а вычисляла
свое значение «самостоятельно».
Идея рекурсии заключается в сведении задачи к этой же задаче, но для более простых случаев (например, для меньшего значения аргумента n). Процесс сведения задачи к предыдущей должен когда-нибудь заканчиваться, поэтому рекурсивные функции для простейших входных данных должны «знать», чему они равны и не делать рекурсивных вызовов.
Использование хвостовой рекурсии
Возможность рекурсии ограничена предельной глубиной стека возврата. В некоторых случаях, однако, реализация языка программирования может самостоятельно свести рекурсию к итерации.
В примере ниже, мы воспользуемся хвостовой рекурсией, сведение которой к итерации хотя и не требуется стандартом, но все же реализуется компилятором C, входящим в распространенный комплект GCC. Для этого, нам потребуется определить вспомогательную функцию factorial_1
, вычисляющую значение p n!, где p, n — аргументы функции.
Обратите внимание, что в этом примере мы не приводим определение функции main
— его следует заимствовать из предыдущего примера.
#include <stdio.h>
static int
factorial_1 (int n, int p)
{
return (n < 2) ? p : factorial (n - 1, p * n);
}
static int
factorial (int n)
{
return factorial_1 (n, 1);
}
Явно итеративный вариант
Наконец, в варианте ниже мы вовсе исключим рекурсию из определения функции factorial
, явно сведя ее к итерации.
#include <stdio.h>
static int
factorial (int n) {
int r;
for (r = 1; n > 1; r *= (n--))
;
return r;
}
В отличие от первого варианта (но аналогично варианту с хвостовой рекурсией) в данном примере нам потребовалась дополнительная локальная переменная. В случае «нехвостовой» рекурсии — роль такой переменной фактически играет растущий стек возврата. Отметим, впрочем, что в данной конкретной задаче это не столь важно.
Задания
- Улучшите программы, добавив к ним диагностику ошибки во входных данных: если n < 0, то программа должна ясно сообщать о неопределенности факториала для отрицательных чисел.
- Опытным путем определите максимальные значения аргумента и результата для программ выше. Исследуйте влияние на эти значения замены используемого в программах числового типа
int
наshort
,long
иlong long
. Обратите внимание, что при этом также потребуется соответственно изменить строку формата (первый аргумент) функцииprintf
. - После выполнения первых двух заданий Вы поймете, что максимальной натуральной число по которому возможно вычислить факториал с использованием 4-х байтовых беззнаковых чисел это 12 (всего навсего). Настоящий программист не будет использовать рекурсии и умножать в цикле, а использует предвычисленный статический массив готовых данных. Попробуйте и сравните быстродействие.
См. также
- Реализации алгоритмов/Факториал (на различных языках)