Питон для нормальных. Учебник Москва Базальт спо макс пресс 2018
Скачать 2.54 Mb.
|
благодаря чему каждый следующий рекурсивный вызов этой функции пользу- ется своим набором локальных переменных и за счёт этого работает корректно. Оборотной стороной этого довольно простого по структуре механизма является то, что на каждый рекурсивный вызов требуется некоторое количество опера- тивной памяти компьютера, и при чрезмерно большой глубине рекурсии может наступить переполнение стека вызовов. Вопрос о желательности использования рекурсивных функций в программи- ровании неоднозначен: с одной стороны, рекурсивная форма может быть струк- турно проще и нагляднее, в особенности, когда сам реализуемый алгоритм, по сути, рекурсивен. Кроме того, в некоторых декларативных или чисто функци- ональных языках (таких, как Пролог или Haskell) просто нет синтаксических средств для организации циклов, и рекурсия в них — единственный доступный механизм организации повторяющихся вычислений. С другой стороны, обычно рекомендуется избегать рекурсивных программ, которые приводят (или в некото- рых условиях могут приводить) к слишком большой глубине рекурсии. Так, ши- роко распространённый в учебной литературе пример рекурсивного вычисления факториала является, скорее, примером того, как не надо применять рекурсию, так как приводит к достаточно большой глубине рекурсии и имеет очевидную реализацию в виде обычного циклического алгоритма. 4.6 Примеры решения заданий Пример задачи 12 Напишите функцию, вычисляющую значения экспо- ненты по рекуррентной формуле e x = 1 + x + x 2 2! + x 3 3! + · · · = P ∞ n=0 x n n! . Ре- ализуйте контроль точности вычислений с помощью дополнительного параметра ε со значением по умолчанию (следует остановить вычисле- ния, когда очередное приближение будет отличаться от предыдущего менее, чем на 10 − 10 ). Реализуйте вызов функции различными способами: 4.6. Примеры решения заданий 107 • с одним позиционным параметром (при этом будет использовано значение по умолчанию); • с двумя позиционными параметрами (значение точности будет пе- редано как второй аргумент); • передав значение как именованный параметр. Решение задачи 12 d e f E X P O N E N T A (x , eps =10**( -10)): ex = 1 # будущий результат dx = x # приращение i = 2 # номер приращения w h i l e a b s ( dx ) > eps : ex = ex + dx dx = dx * x / i i = i + 1 r e t u r n ex # Основная программа A = f l o a t ( i n p u t ( ’Введите показатель экспоненты: ’ )) p r i n t ( E X P O N E N T A ( A )) p r i n t ( E X P O N E N T A (A , 10**( -4))) p r i n t ( E X P O N E N T A ( x = A ) Пример задачи 13 Сделайте из функции процедуру (вместо того, что- бы вернуть результат с помощью оператора return, выведите его внут- ри функции с помощью функции print). Решение задачи 13 d e f E X P O N E N T A (x , eps =10**( -10)): ex = 1 # будущий результат dx = x # приращение i = 2 # номер приращения w h i l e a b s ( dx ) > eps : ex = ex + dx dx = dx * x / i i = i + 1 p r i n t ( ex ) # Основная программа A = f l o a t ( i n p u t ( ’Введите показатель экспоненты: ’ )) E X P O N E N T A ( A ) 108 Глава 4. Функции 4.7 Задания на функции Задание 11 Выполнять одно задание в зависимости от номера в спис- ке: (n-1)%10+1 , где n — номер в списке. Напишите функцию, вычисляю- щую значения одной из следующих специальных функций по рекуррент- ной формуле. Реализуйте контроль точности вычислений с помощью до- полнительного параметра ε со значением по умолчанию (следует оста- новить вычисления, когда очередное приближение будет отличаться от предыдущего менее, чем на 10 − 10 ). Реализуйте вызов функции различны- ми способами: • с одним позиционным параметром (при этом будет использовано значение по умолчанию); • с двумя позиционными параметрами (значение точности будет пе- редано как второй аргумент); • передав значение как именованный параметр. Сделайте из функции процедуру (вместо того, чтобы вернуть резуль- тат с помощью оператора return, выведите его внутри функции с по- мощью функции print). 1. Косинус cos(x) = 1 − x 2 2! + x 4 4! − · · · = P ∞ n=0 (−1) n x 2n (2n)! . Формула хорошо ра- ботает для −2π 6 x 6 2π, поскольку получена разложением в ряд Тейлора возле ноля. Для прочих значений x следует воспользоваться свойствами периодичности косинуса: cos(x) = cos(2 + 2πn), где n есть любое целое число, тогда cos(x) = cos(x%(2*math.pi)). Для проверки использовать функцию math.cos(x). 2. Синус sin(x) = x − x 3 3! + x 5 5! + · · · = P ∞ n=0 (−1) n x 2n+1 (2n+1)! . Формула хорошо ра- ботает для −2π 6 x 6 2π, поскольку получена разложением в ряд Тейлора возле ноля. Для прочих значений x следует воспользоваться свойствами пе- риодичности косинуса: sin(x) = sin(2 + 2πn), где n есть любое целое число, тогда sin(x) = sin(x%(2*math.pi)) . Для проверки использовать функцию math.sin(x) 3. Гиперболический косинус ch(x) = 1 + x 2 2! + x 4 4! + · · · = P ∞ n=0 x 2n (2n)! . Для про- верки использовать функцию math.cosh(x). 4. Гиперболический косинус по формуле для экспоненты, оставляя только слагаемые с чётными n. Для проверки использовать функцию math.cosh(x). 5. Гиперболический синус sh(x) = x + x 3 3! + x 5 5! + · · · = P ∞ n=0 x 2n+1 (2n+1)! . Для про- верки использовать функцию math.sinh(x). 4.7. Задания на функции 109 6. Гиперболический синус по формуле для экспоненты, оставляя только сла- гаемые с нечётными n. Для проверки использовать функцию math.sinh(x). 7. Натуральный логарифм (формула работает при 0 < x 6 2): ln(x) = (x − 1) − (x − 1) 2 2 + (x − 1) 3 3 − (x − 1) 4 4 + · · · = ∞ X n=1 (−1) n−1 (x − 1) n n Чтобы найти логарифм для x > 2, необходимо представить его в виде ln(x) = ln(y · 2 p ) = p ln(2) + ln(y), где y < 2, а p натуральное число. Чтобы найти p и y, нужно в цикле делить x на 2 до тех пор, пока результат больше 2. Когда очередной результат деления станет меньше 2, этот результат и есть y, а число делений, за которое он достигнут – это p. Для проверки использовать функцию math.log(x). 8. Гамма-функция Γ(x) по формуле Эйлера: Γ(x) = 1 x ∞ Y n=1 1 + 1 n x 1 + x n Формула справедлива для x / ∈ {0, −1, −2, . . . }. Для проверки можно исполь- зовать math.gamma(x). Также, поскольку Γ(x + 1) = x! для натуральных x, то для проверки можно использовать функцию math.factorial(x). 9. Функция ошибок, также известная как интеграл ошибок, интеграл вероят- ности, или функция Лапласа: erf(x) = 2 √ π Z x 0 e − t 2 dt = 2 √ π ∞ X n=0 x 2n + 1 n Y i=1 −x 2 i Для проверки использовать функцию scipy.special.erf(x). 10. Дополнительная функция ошибок: erfc(x) = 1 − erf(x) = 2 √ π Z ∞ x e − t 2 dt = e − x 2 x √ π ∞ X n=0 (−1) n (2n)! n!(2x) 2n Для проверки использовать функцию scipy.special.erf(x). Задание 12 (Танцы) Выполнять в каждом разделе по одному заданию в зависимости от номера в списке группы: (n-1)%5+1 , где n — номер в списке. 1. два списка имён: первый — мальчиков, второй — девочек; 2. один список имён и число мальчиков, все имена мальчиков стоят в начале списка; 110 Глава 4. Функции 3. один список имён и число девочек, все имена девочек стоят в начале списка; 4. словарь, в котором в качестве ключа используется имя, а в каче- стве значения символ «м» для мальчиков и символ «ж» для девочек; 5. словарь, в котором в качестве ключей выступают символы «м» и «ж», а в качестве соответствующих им значений — списки маль- чиков и девочек соответственно. Проверьте работу функции на разных примерах: • когда мальчиков и девочек поровну, • когда мальчиков больше, чем девочек, и наоборот, • когда есть ровно 1 мальчик или ровно 1 девочка, • когда либо мальчиков, либо девочек нет вовсе. Модифицируйте функцию так, чтобы она принимала второй необяза- тельный параметр — список уже составленных пар, участников кото- рых для составления пар использовать нельзя. В качестве значения по умолчанию для этого аргумента используйте пустой список. Проверьте работу функции, обратившись к ней: • как и ранее (с одним аргументом), в этом случае результат должен совпасть с ранее полученным; • передав все аргументы позиционно без имён; • передав последний аргумент (список уже составленных пар) по име- ни; • передав все аргументы по имени в произвольном порядке. Задание 13 (Создание списков) Напишите функцию, принимающую от 1 до 3 параметров — целых чисел (как стандартная функция range). Единственный обязательный аргумент — последнее число. Если поданы 2 аргумента, то первый интерпретируется как начальное число, второй — как конечное (не включительно). Если поданы 3 аргумента, то тре- тий аргумент интерпретируется как шаг. Функция должна выдавать один из следующих списков: 1. квадратов чисел; 2. кубов чисел; 3. квадратных корней чисел; 4.7. Задания на функции 111 4. логарифмов чисел; 5. чисел последовательности Фибоначчи с номерами в указанных пре- делах. Запускайте вашу функцию со всеми возможными вариантами по числу параметров: от 1 до 3. Подсказка: проблему переменного числа параметров, из которых необязательным является в том числе первый, можно решить 2-мя способами. Во-первых, можно сопоставить всем параметрам нечисло- вые значения по умолчанию, обычно для этого используют специальное значение None. Тогда используя условный оператор можно определить, сколько параметров реально заданы (не равны None). В зависимости от этого следует интерпретировать значение первого аргумента как: конец последовательности, если зада только 1 параметр; как начало, если за- даны 2 или 3. Во-вторых, можно проделать то же, используя синтаксис функции с произвольным числом параметров; в таком случае задавать значения по умолчанию не нужно, а полезно использовать стандартную функцию len, которая выдаст количество реально используемых пара- метров. Задание 14 (Интернет-магазин) Решите задачу об интернет-торговле. Несколько покупателей в течении года делали покупки в интернет- магазине. При каждой покупке фиксировались имя покупателя (строка) и потраченная сумма (действительное число). Напишите функцию, рас- считывающую для каждого покупателя и выдающую в виде словаря по всем покупателям (вида имя:значение) один из следующих параметров: 1. число покупок; 2. среднюю сумму покупки; 3. максимальную сумму покупки; 4. минимальную сумму покупки; 5. общую сумму всех покупок. На вход функции передаётся: • либо 2 списка, в первом из которых имена покупателей (могут по- вторяться), во втором – суммы покупок; • либо 1 список, состоящий из пар вида (имя, сумма) ; • либо словарь, в котором в качестве ключей используются имена, а в качестве значений — списки с суммами. Глава 5 Массивы. Модуль numpy Сам по себе «чистый» Python пригоден только для несложных вычислений. Ключевая особенность Python — его расширяемость. Это, пожалуй, самый рас- ширяемый язык из получивших широкое распространение. Как следствие этого для Python не только написаны и приспособлены многочисленные библиотеки алгоритмов на C и Fortran, но и имеются возможности использования других программных средств и математических пакетов, в частности, R и SciLab, а так- же графопостроителей, например, Gnuplot и PLPlot. Ключевыми модулями для превращения Python в математический пакет яв- ляются numpy и matplotlib. numpy — это модуль (в действительности, набор модулей) языка Python, до- бавляющая поддержку больших многомерных массивов и матриц, вместе с боль- шим набором высокоуровневых (и очень быстрых) математических функций для операций с этими массивами. matplotlib — модуль (в действительности, набор модулей) на языке про- граммирования Python для визуализации данных двумерной (2D) графикой (3D графика также поддерживается). Получаемые изображения могут быть исполь- зованы в качестве иллюстраций в публикациях. Кроме numpy и matplotlib популярностью пользуются scipy для специализи- рованных математических вычислений (поиск минимума и корней функции мно- гих переменных, аппроксимация сплайнами, вейвлет-преобразования), sympy для символьных вычислений (аналитическое взятие интегралов, упрощение матема- тических выражений), ffnet для построения искусственных нейронных сетей, pyopencl /pycuda для вычисления на видеокартах и некоторые другие. Возмож- ности numpy и scipy покрывают практически все потребности в математических алгоритмах. 5.1. Создание и индексация массивов 113 (a) (b) Рис. 5.1. Одномерный — (a) и двумерный — (b) массивы. 5.1 Создание и индексация массивов Массив — упорядоченный набор значений одного типа, расположенных в па- мяти непосредственно друг за другом. При этом доступ к отдельным элемен- там массива осуществляется с помощью индексации, то есть через ссылку на массив с указанием номера (индекса) нужного элемента. Это возможно потому, что все значения имеют один и тот же тип, занимая одно и то же количество байт памяти; таким образом, зная ссылку и номер элемента, можно вычислить, где он расположен в памяти. Количество используемых индексов массива может быть различным: массивы с одним индексом называют одномерными, с двумя — двумерными, и т. д. Одномерный массив («колонка», «столбец») примерно соот- ветствует вектору в математике (на рис. 5.1(а) a[4] == 56, т.е. четвёртый эле- мент массива а равен 56); двумерный — матрице (на рис. 5.1(b) можно писать A[1][6] == 22 , можно A[1, 6] == 22). Чаще всего применяются массивы с од- ним или двумя индексами; реже — с тремя; ещё большее количество индексов встречается крайне редко. Как правило, в программировании массивы делятся на статические и ди- намические. Статический массив — массив, размер которого определяется на момент компиляции программы. В языках с динамической типизацией таких, как Python, они не применяются. Динамический массив — массив, размер кото- рого задаётся во время работы программы. То есть при запуске программы этот массив не занимает нисколько памяти компьютера (может быть, за исключе- нием памяти, необходимой для хранения ссылки). Динамические массивы могут поддерживать и не поддерживать изменение размера в процессе исполнения про- граммы. Массивы в Python не поддерживают явное изменение размера: у них, в отличие от списков, нет методов append и extend, позволяющих добавлять эле- менты, и методов pop и remove, позволяющих их удалять. Если нужно изменить размер массива, это можно сделать путём переприсваивания имени переменной, 114 Глава 5. Массивы. Модуль numpy обозначающей массив, нового значения, соответствующего новой области памя- ти, больше или меньше прежней разными способами. Базовый оператор создания массива называется array. С его помощью можно создать новый массив с нуля или превратить в массив уже существующий список. Вот пример: f r o m numpy i m p o r t * A = array ([0.1 , 0.4 , 3 , -11.2 , 9]) p r i n t ( A ) p r i n t ( t y p e ( A )) В первой строке из модуля numpy импортируются все функции и константы. Вывод программы следующий: [0.1 0.4 3. -11.2 9. ] Функция array() трансформирует вложенные последовательности в много- мерные массивы. Тип элементов массива зависит от типа элементов исходной последовательности: B = array ([[1 , 2 , 3] , [4 , 5 , 6]]) p r i n t ( B ) Вывод: [[1 2 3] [4 5 6]] Тип элементов массива можно определить в момент создания с помощью име- нованного аргумента dtype. Модуль numpy предоставляет выбор из следующих встроенных типов: bool (логическое значение), character (символ), int8, int16, int32 , int64 (знаковые целые числа размеров в 8, 16, 32 и 64 бита соответствен- но), uint8, uint16, uint32, uint64 (беззнаковые целые числа размеров в 8, 16, 32 и 64 бита соответственно), float32 и float64 (действительные числа одинарной и двойной точности), complex64 и complex128 (комплексные числа одинарной и двойной точности), а также возможность определить собственные типы данных, в том числе и составные. C = array ([[1 , 2 , 3] , [4 , 5 , 6]] , dtype = f l o a t ) p r i n t ( C ) Вывод: [[ 1. 2. 3.] [ 4. 5. 6.]] Можно создать массив из диапазона: 5.1. Создание и индексация массивов 115 f r o m numpy i m p o r t * L = r a n g e (5) A = array ( L ) p r i n t ( A ) Вывод: [0 1 2 3 4] Отсюда видно, что в первом случае массив состоит из действительных чисел, так как при определении часть его значений задана в виде десятичных дробей. В numpy есть несколько действительнозначных типов, базовый тип соответствует действительным числам Python и называется float64. Второй массив состоит из целых чисел, потому что создан из диапазона range, в который входят толь- ко целые числа. На 64-битных операционных системах элементы такого списка будут автоматически приведены к целому типу int64, на 32-битных — к типу int32 В numpy есть функция arange, позволяющая сразу создать массив-диапазон, причём можно использовать дробный шаг. Вот программа, рассчитывающая зна- чения синуса на одном периоде с шагом π/6: f r o m numpy i m p o r t * a1 = arange (0 , 2* pi , pi /6) s1 = sin ( a1 ) f o r j i n r a n g e ( l e n ( a1 )): p r i n t ( a1 [ j ] , s1 [ j ]) А вот её вывод: 0.0 0.0 0.523598775598 0.5 1.0471975512 0.866025403784 1.57079632679 1.0 2.09439510239 0.866025403784 2.61799387799 0.5 3.14159265359 1.22464679915e-16 3.66519142919 -0.5 4.18879020479 -0.866025403784 4.71238898038 -1.0 5.23598775598 -0.866025403784 5.75958653158 -0.5 Кроме arange есть ещё функция linspace, тоже создающая массив-диапазон. |