Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Python. Учебное пособие.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆

Оператор деления по модулю

Если бы сначала проверяли, делится ли число только на 3 или только на 5,
Оператор деления по модулю обозначается символом «%» и возвращает
остаток от деления двух натуральных чисел. Например, «2 % 2» возвращает 0, тогда как «3 % 2» возвращает 1. Иначе говоря, это алгоритм нахождения остат­ка от деления первого натурального числа на второе. Он полезен при решении множества проблем, например таких, как FizzBuzz. Это известный тест, исполь­зуемый на собеседованиях для выбора или отсеивания кандидатов. Вопрос был введен Имраном Гори в его сообщении в блоге «Использование FizzBuzz для поиска разработчиков, которые занимаются кодированием на Grok». Если вы знаете правильный подход к проблеме, ее легко решить, а если нет, то она мо­жет показаться сложной. Задача обычно ставится в следующем виде:
Напишите программу, которая выводит числа от 1 до 100. Но вместо чи-
сел, кратных трем, выводите «Fizz», вместо кратных пяти выводите «Buzz», вместо кратных и трем, и пяти – «FizzBuzz».
Ключом к решению этой задачи является использование деления по моду-
лю. Чтобы решить задачу, просто нужно выполнить итерацию от 1 до 100 и проверить, делится ли каждое число на 3, 5 или на то и другое. Вот решение:
def fizz_buzz():
for i in range(0, 101):
if i % 3 == 0 and i % 5 == 0:
print 'FizzBuzz'
elif i % 3 == 0:
print 'Fizz'
elif i % 5 == 0:
print 'Buzz'
else:
print(i)
fizz_buzz()
Сначала перебираете числа от 1 до 100, затем проверяете, делится ли число
и на 3, и на 5. Это важно сделать в первую очередь, так как если число делится и на 3, и на 5, нужно вывести FizzBuzz и перейти к следующей итерации цикла.
151
и обнаружили бы такое число, вы бы не могли вывести Fizz или Buzz и перейти к следующей итерации цикла, поскольку это число все еще могло бы делиться и на 3, и на 5. А в этом случае вывод Fizz или Buzz был бы неверен, ведь нужно было бы выводить FizzBuzz.
После проверки порядок этих двух тестов больше не важен, так как из­вестно, что число не делится на оба этих числа. Если число делится либо на 3, либо на 5, вы останавливаете алгоритм и выводите Fizz или Buzz. Если число проходит мимо первых трех условий, значит, оно не делится ни на 3, ни на 5 (ни на оба этих числа одновременно), и его можно вывести.
Вот еще одна задача, где использование деления по модулю является клю­чом к получению ответа:
Выполните циклический сдвиг массива из n элементов вправо на k шагов.
Например, при n = 7 и k = 3 массив [1,2,3,4,5,6,7] вращается до [5,6,7,1,2,3,4], т.е. вы перемещаете каждый элемент списка на k позиций. Сдвиг также должен быть кольцевым, то есть элемент, вышедший за пределы списка, должен появ­ляться с другого его конца.
При решении этой задачи первым побуждением может быть простое до­бавление n к индексу каждого числа и перемещение числа на новую позицию. Проблема в том, что это не работает. Ключ к решению задачи заключается в использовании остатка от деления. Скажем, был список чисел от 1 до 12. Можно думать об этом списке как о часах. Если начать с 12 часов и пройти по часам двенадцать часов, можно вернуться к тому, с чего начали.
Можно добиться этого, взяв текущий индекс, добавив новый индекс и по­лучить остаток. Если возьмем список из 12 чисел и рассчитаем, где будет чис­ло 12 (начиная с индекса 11), добавим 12 к индексу, можно вычислить это с по­мощью 11 +.

Сортировка методом пузырька

Алгоритм сортировки – это алгоритм, который берет группу чисел и упо- рядочивает их в определенном порядке. Существует множество различных ал­горитмов, таких как сортировка выбором, сортировка вставками, сортировка оболочкой, сортировка слиянием и быстрая сортировка. Рассмотрим реализа-
152
цию пузырьковой сортировки, алгоритм которой не очень эффективен, но прост для понимания и полезен для понимания алгоритмов сортировки:
def bubble_sort (num_list):
”””
:param num_list: List of numbers :return : Sorted list of numbers
”””
for i in range(len (num_list)- 1 , 0 , - 1):
for j in range(i):
if num_list[j] > num_list[j + 1]:
temp = num_list[j] num_list[j] = num_list[j + 1]
num_list[j + 1] = temp my_list = [4, 266, 9, 24, 44, 54, 41, 89, 20] bubble_sort(my_list) print(my_list) >> [4, 9, 20, 24, 41, 44, 54, 89, 266]

Поисковый алгоритм

Поисковый алгоритм используется для поиска числа в списке чисел: по-
следовательного поска и бинарного поиска.
Последовательный поиск – это простой поисковый алгоритм, который
проверяет каждое число в списке одно за другим, чтобы увидеть, соответствует ли оно искомому числу. Последовательный поиск – это способ поиска опреде­ленной карты в колоде карт: вы проходите одну за другой через каждую карту, если это та карта, которую ищете, вы останавливаетесь; если прошли через всю колоду и не нашли карту, знаете, что карты там нет. Вот пример последова­тельного поиска:
def sequential_search(number_list, number):
””” :param number_list: List of integers. :param number: Integer to look for. :return: True if the number is found otherwise false. ”””
153
found = False
for i in number_list:
if i == number:
found = True break
return found
print sequential_search(range(0, 100), 2)
>> True print sequential_search(range(0, 100), 202) >> False
Первому “найденному” элементу присваивается значение False. Затем пе­ребирается каждое число в списке и проверяется, равно ли оно искомому числу. Если оно равно искомому числу, устанавливаете значение found равным True, выходите из цикла и возвращаете True. В противном случае переходите к сле­дующему номеру в списке. Если при прохождении по всему списку и для найденного элемента никогда не будет установлено значение True, возвращате found, для которого по-прежнему будет установлено значение False.
Последовательный поиск – это алгоритм, который принадлежит классу вычислительной сложности O(n). В лучшем случае, число, которое ищете, мо­жет быть первым числом в списке, и в этом случае ваш алгоритм будет выпол­нять только один шаг. Однако в худшем случае этого числа нет в списке, тогда нужно проверить каждое число в списке или n шагов. Если у вас есть миллион элементов в списке, в худшем случае придется искать миллион элементов. По мере роста списка наихудший сценарий для алгоритма увеличивается на размер списка, делая этот алгоритм класса сложности O(n).
Бинарный (двоичный) поиск – тип поискового алгоритма, который по- следовательно делит пополам заранее отсортированный массив данных, чтобы обнаружить нужный элемент. Другие его названия – двоичный поиск, метод половинного деления, дихотомия.
Заметим, что двоичный поиск – это алгоритм, работающий за логарифми­ческое время, используемый для поиска чисел в списке, но числа должны быть упорядочены. Бинарный поиск работает за счет постоянного сокращения спис­ка пополам. Алгоритм выбирает число в середине списка и проверяет, правиль­ное ли это число. Если это правильный номер, поиск завершен. Если это непра-
154
вильное число, алгоритм отбрасывает половину списка. Если число было слиш­ком большим, оно отбрасывает все, что находится выше выбранного им числа. Если число было слишком маленьким, оно отбрасывает все, что находится ни­же выбранного им числа.
Представьте, что имеется упорядоченный список от 1 до 10, в котором
нужно найти число 3. Этот список выглядит следующим образом:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Алгоритм сначала выберет число 5, потому что оно находится в середине
списка. Поскольку 5 – это не то число, которое ищите, а 3 меньше пяти, алго­ритм отбрасывает все, что выше 5. Теперь список выглядит так:
[1, 2, 3, 4, 5]
Затем в списке будет выбрано число 3, потому что оно находится в сере­дине списка. Поскольку 3 – это число, которое ищите, алгоритм остановится и вернет его так, как оно было найдено в списке. Обратите внимание, что ва­шему алгоритму потребовалось всего два шага, чтобы определить, что в списке было три. Если просматривать список линейно, один за другим, в поисках чис­ла 3, это заняло бы три шага. Вот пример алгоритма двоичного поиска, который ищет число 3:
def binary_search (number_list, number):
“““Logarithmic binary search algorithm. :param number_list: List of ordered integers.
:param number: Integer to search for in the passed in list.
:return: True if the number is found, otherwise False. ”””
first = 0 last = len(number_list)-1 number_found = False while first <= last and not number_found:
middle = (first + last)/2
if number_list[middle] == number:
number_found = True
else:
if number < number_list[middle]:
last = middle - 1
155
else:
first = middle + 1
return number_found
binary_search([1,2,3,4,5,6,7,8,9,10], 3)
Цикл while продолжается до тех пор, пока первая переменная не будет
больше или равна последней переменной, а переменная not_true имеет значение
False.
Вычисление среднего индекса списка заключается в сложении первого ин­декса списка с последним индексом и делении их на два. Причина, по которой вычитается единица из последнего, заключается в том, что длина списка вы­числяется, начиная с единицы, тогда как индексы начинаются с нуля.
Проверьте, является ли среднее число тем числом, которое вы ищете, про­сматривая среднее число в списке с помощью number_list[middle]. Если среднее число – это число, которое ищете, для not_true устанавливаете значение True и выходите из цикла. В противном случае проверяете, больше или меньше ис­комое число среднего числа. Если среднее число меньше, чем число, которое ищете, то меняете последний индекс на средний индекс –1, который в следую­щем цикле разделит список пополам со всем, что меньше текущего среднего числа; если среднее число больше, чем число, которое ищете, то меняете снача­ла на среднее +1, разделив список пополам, чтобы он содержал все, что больше вашего среднего числа.

Рекурсия

Рекурсия известна как одна из самых сложных концепций для начинаю­щих программистов. Рекурсия – это метод решения проблемы путем разбиения ее на более мелкие части до тех пор, пока она не будет легко решена. Это до­стигается с помощью функции, которая вызывает сама себя. Любая проблема, которая может быть решена рекурсивно, также может быть решена итеративно, однако в некоторых случаях рекурсия предлагает более рациональное решение.
Рекурсивный алгоритм должен следовать трем правилам:
1. Рекурсивный алгоритм должен содержать базовый случай.
2. Рекурсивный алгоритм должен изменять свое состояние и переходить
к базовому случаю.
3. Рекурсивный алгоритм должен вызывать сам себя рекурсивно.
156
Рассмотрим пример рекурсивной функции, в которой есть все три правила. Функция для распечатки текста популярной детской песенки «99 Bottles of
Beer on the Wall»:
def bottles_of_beer (bob):
””” Use recursion to print the bottles of beer song. :param bob: Integer number of beers that arestart
on
the wall. ”””
if bob < 1:
print “No more bottles of beer on the wall. No more
bottles of beer.”
return
tmp = bob bob -= 1 print“{} bottles of beer on the wall. {} bottles of
beer.
Take one down, pass it around, {} bottles of beer on the wall.”.format(tmp, tmp, bob)
bottles_of_beer(bob) bottles_of_beer(99) >> 99 bottles of beer on the wall. 99 bottles of beer. Take one down, pass it around, 98 bottles of beer on the wall. >> 98 bottles of beer on the wall. 98 bottles of beer. Take one down, pass it around, 97 bottles of beer on the wall. … >> No more bottles of beer on the wall. No more bottles of beer.
В этом примере базовый случай:
if bob < 1:
print“No more bottles of beer on the wall. No more
bottles of beer”
return
157
Базовый случай рекурсивного алгоритма – это то, что в конечном итоге
останавливает алгоритм. Рекурсивный алгоритм без базового случая будет про­должать вызывать себя вечно, и можно получить ошибку времени выполнения, говорящую о том, что максимальная глубина рекурсии превышена. Строка bob -= 1 удовлетворяет второму правилу, согласно которому нужно двигать- ся к базовому варианту. В рассматриваемом примере передали число «99» в функцию в качестве параметра bob. Поскольку в базовом случае bob меньше 1
и поскольку bob начинается с 99, без этой строки мы никогда не достигнем нашего базового случая, что также даст нам ошибку времени выполнения с максимальной глубиной рекурсии.
Заключительное правило рекурсии реализуется с помощью конструкции
bottles_of_beer(bob)
С помощью этой строки осуществляется вызов функции. Функция будет вызвана снова, но с одним важным отличием: вместо передачи 99, как при пер­вом вызове функции, на этот раз будет передано 98 из-за правила номер два. Далее вызвана третья функция с именем 97. Это будет продолжаться до тех пор, пока bob не станет равным 0 и не будет достигнута базового случая. В этот момент вы печатаете «No more bottles of beer on the wall. No more bottles of beer» и возвращаетесь, сигнализируя алгоритму об остановке.
Рассмотрим еще один рекурсивный алгоритм.
Дано неотрицательное целое число, многократно складывайте все его циф­ры, пока в результате не останется только одна цифра.
Например:
Дано число num = 38, процесс выглядит следующим образом: 3 + 8 = 11, 1+1=2. Поскольку 2 содержит только одну цифру, верните эту цифру.
Одним из способов решения этой проблемы является использование ре­курсии:
def add_digits (number):
”””
:param number: Int :return : Single digit int
”””
158
number = str (number)
if len (number) == 1 :
return int (number)
the_sum = 0 for c in number:
the_sum += int (c)
return add_digits(the_sum)
print add_digits( 99 )
>> 9
В этом примере функция принимает число в качестве параметра, вычис-
ляет его сумму и вызывает себя до тех пор, пока не столкнется с базовым случаем, то есть с числом, состоящим только из одной цифры. Если пропусти­ли число 99, оно будет состоять из двух цифр, поэтому оно не соответствует базовому регистру. Затем вычисляем, что the_sum равно 18, для этого снова
вызывается add_digits и передается 18. И снова 18 не проходит базовый случай, поэтому вычисляется сумма цифр, которая на этот раз равна 9, и вызы­вается add_digits с 9 в качестве параметра. В третий раз число равно 9, и поскольку это всего одна цифра, оно удовлетворяет базовому случаю, и вы­даем ответ.

Абстрактные типы данных

При изучении структур данных встречается термин «абстрактный тип
данных». Понять, что это такое, можно, проводя аналогию связи между аб-
страктным типом данных и структурой данных, как о похожей (хотя и не со­всем такой же), как о связи между классом и объектом.
Если нужно смоделировать апельсин в объектно-ориентированном про-
граммировании, класс представляет собой идею апельсина, тогда как объект представляет собой фактический, реальный апельсин. Аналогично, абстракт­ный тип данных представляет собой идею определенного типа структуры дан­ных. Примером абстрактного типа данных является список. Список – это аб­страктный тип данных, который может быть реализован несколькими способа­ми с помощью структур данных, таких как массив или связанный список.
159

Узлы

Узел – это термин, часто используемый в компьютерных науках. Можно думать об узле как о точке на графике. Интернет состоит из маршрутизаторов, которые взаимодействуют друг с другом. Каждый маршрутизатор является уз­лом в сети. Узлы используются в нескольких структурах данных, включая свя­занные списки, деревья и графы.

Стеки

Стек – это структура данных по принципу «последним вошел – первым вышел» (LIFO, last-in-first-out). Лучше всего представить это как стопку посу-
ды. Скажем, вы ставите пять тарелок друг на друга. Чтобы добраться до по­следней тарелки в стопке, нужно убрать все остальные тарелки. Вот как работа­ет структура данных стека. Вы помещаете данные в стек. Каждая часть данных похожа на блюдо, и вы можете получить доступ к данным, только вытащив данные из верхней части стека. Вот пример стека, реализованного на Python:
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
Двумя наиболее важными методами в определении стека являются push и pop. Push позволяет помещать данные в верхнюю часть стека, а pop – из- влекать их из стека.
Стеки важны для понимания рекурсии. Рекурсия – фундаментальная часть программирования. Большинство начинающих программистов борются с ре-
160
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]