- •§0.1. Базовые знания
- •§0.2. Где достать интерпретатор языка Питон?
- •§0.3. Установка интерпретатора языка Питон в unix
- •§0.3.1. Установка/обновление Питона из грт-пакета
- •§0.3.2. Установка Питона из исходного кода
- •§0.4. Установка Питона в ос Windows
- •§0.5. Запуск программ, написанных на Питоне
- •§0.6. Среда разработки
- •§1.1. Алгоритмы и программы
- •§1.2. Языки программирования и уровни абстракции
- •Глава 1. Базовые понятия
- •§1.3. Формальные и естественные языки
- •§1.4. Интерпретаторы и компиляторы
- •§1.5. Первая программа
- •§1.6. Что такое отладка?
- •§1.6.1. Синтаксические ошибки (syntax errors)
- •§1.6.2. Ошибки выполнения (runtime errors)
- •§1.6.3. Семантические ошибки (semantic errors)
- •§1.6.4. Процесс отладки
- •Глава 2. Переменные, операции и выражения
- •§2.1. Значения и типы
- •§2.2. Преобразование типов
- •§2.3. Переменные
- •§2.4. Имена переменных и ключевые слова
- •§2.5. Выражения
- •§2.6. Выполнение выражений
- •§2.7. Операторы и операнды
- •§2.8. Порядок операций
- •§2.9. Простейшие операции над строками
- •§2.10. Композиция
- •Глава 3. Функции §3.1. Подпрограммы
- •§3.2. Вызовы функций
- •§3.3. Справочная система
- •§3.4. Импорт модулей и математические функции
- •§3.5. Композиция
- •§3.6. Создание функций
- •§3.7. Параметры и аргументы
- •§3.8. Локальные переменные
- •§3.9. Поток выполнения
- •§3.10. Стековые диаграммы
- •§3.11. Функции, возвращающие результат
- •Глава 4. Компьютерная графика
- •Глава 5. Логические выражения, условия и рекурсия §5.1. Комментарии в программах
- •§5.2. Простые логические выражения и логический тип данных
- •§5.3. Логические операторы
- •§5.4. Выполнение по условию и «пустота»
- •§5.5. Ввод данных с клавиатуры
- •§5.6. Альтернативные ветки программы (Chained conditionals)
- •§5.7. Пустые блоки
- •§5.8. Вложенные условные операторы (Nested conditionals)
- •§5.9. Рекурсия
- •§5.10. Стековые диаграммы рекурсивных вызовов
- •§5.11. Максимальная глубина рекурсии
- •§5.12. Числа Фибоначчи
- •Глава 6. Циклы
- •§6.1. Оператор цикла while
- •§6.2. Счетчики
- •§6.3. Бесконечные циклы
- •§6.4. Альтернативная ветка цикла while
- •§6.5. Табулирование функций
- •§6.6. Специальные и экранируемые символы
- •§6.7. Числа Фибоначчи и оператор цикла while
- •§6.8. Вложенные операторы цикла и двумерные таблицы
- •§6.9. Классификация операторов цикла
- •§6.10. Управляющие структуры
- •Глава 7. Строки
- •§7.1. Оператор индексирования
- •§7.2. Длина строки и отрицательные индексы
- •§7.3. Перебор и цикл for
- •§7.4. Срезы строк
- •§7.5. Сравнение строк
- •§7.6. Строки нельзя изменить
- •§7.7. Функция find
- •§7.8. Циклы и счётчики
- •§7.9. Модуль string
- •§7.10. Классификация символов
- •§10.2. Операции над словарями
- •§10.3. Методы словарей
- •§10.4. Использование псевдонимов и копирование
- •§10.5. Разряженные матрицы
- •§10.6. Подсказки
- •§10.7. Тип «длинное целое число»
- •§10.8. Подсчет букв
§10.6. Подсказки
Если вы забавлялись с рекурсивной функцией поиска чисел Фибоначчи из раздела 5.72, то, наверное, заметили, что чем большие аргументы вы передаете, тем дольше функция выполняется. Более того, время расчетов увеличивается очень быстро. На одной нашей машине, f ibonacci (2 0) завершается мгновенно, fibonacci (30) думает около секунды и f ibonacci (40) будет работать почти бесконечно.
Чтобы понять почему так происходит, рассмотрим диаграмму вызовов для функции
fibonacci() сn = 4:
[TODO: нарисовать в изображение http://www.ibiblio.org/obp/thinkCSpy/illustrations/fibonacci.png]
Диаграмма вызовов функций отображает совокупность прямоугольных блоков, обозначающих функции, с линиями, соединяющими каждый блок с блоками функций которые он вызывает. Вверху диаграммы, fibonacci () с n = 4 вызывает fibonacci () с n = Зип = 2. В свою очередь, f ibonacci () с n = 3 вызывает f ibonacci () с n = 2 и n = 1. Итак далее.
Таким образом, каждая ветка дерева, по сути, отображает изменение стека вызовов, т.е. Поток выполнения в процессе расчетов, должен пройти по каждой ветке этого дерева. Подсчитайте сколько раз были вызваны fibonacci (0) и fibonacci (1). Не самое эффективное решение задачи, с точки зрения производительности (хотя оно, бесспорно, красивое).
Хорошее решение - сохранять значения, которые были недавно вычислены, запоминая их в словаре. Предыдущее вычисленное значение, которое запоминается для последующего использования, называют подсказкой. Ниже приведена реализация fibonacci (), использующая подсказки:
previous = {0:1, 1:1} |
|
|
|
|
def fibonacci(n): |
|
|
|
|
if previous.has key(n): |
|
|
|
|
return previous[n] |
|
|
|
|
else: |
|
|
|
|
newValue = fibonacci(n- |
-1) |
+ |
fibonacci |
(n-2) |
previous[n] = newValue |
|
|
|
|
return newValue |
|
|
|
|
Словарь названный previous хранит числа Фибоначчи, которые мы уже знаем. Мы начинаем только с двух пар: 0 соответствует 1 и 1 соответствует 1.
Всякий раз, когда вызывается функция fibonacci (), она проверяет словарь, чтобы определить содержит ли он результат. Если это так, функция может немедленно возвратить результат, без выполнения дополнительных рекурсивных вызовов. Если нет, он должна рассчитать новое значение. Новое значение добавляется в словарь, перед тем как функция вернёт результат.
Используя эту реализацию, наша машина может вычислить функцию fibonacci() при п = 40 почти мгновенно. Но когда мы пытаемся вычислить fibonacci (50), мы сталкиваемся с другой проблемой:
90
Ревизия: 170
Словари
>» fibonacci (50)
OverflowError: integer addition
Ответ, как вы увидите через минуту 20365011074. Проблема в том, что это число слишком большое чтобы поместиться в тип int. Такую ситуацию называют переполнением. К счастью эта проблема имеет простое решение.
