import os.path

class prog:
    def list_():
        text = '''
90 БИЛЕТОВ
1)Опишите класс Book, заданный названием, автором, издательством и годом издания. Включите в описание класса методы: вывода информации о книге на экран, проверки, является ли книга новой (изданной в последние 5 лет), и свойство, позволяющее установить жанр книги.
2)Опишите класс Car, заданный маркой, моделью, годом выпуска и пробегом. Включите в описание класса методы: вывода информации о машине на экран, проверки, нужно ли произвести техническое обслуживание (пробег больше 10 000 км с последнего ТО), и свойство, позволяющее установить тип топлива (бензин, дизель и т. п.).
3)Опишите класс Employee, заданный фамилией, именем, должностью и зарплатой. Включите в описание класса методы: вывода информации о сотруднике на экран, проверки, является ли зарплата высокой (больше 100 000 рублей), и свойство, позволяющее установить стаж работы.
4)Опишите класс Product, заданный названием, ценой и количеством. Включите в описание класса методы: вывода информации о товаре на экран, проверки, есть ли товар в наличии (количество больше 0), и свойство, позволяющее установить категорию товара.
5)Опишите класс Circle, заданный радиусом. Включите в описание класса методы: вывода информации о круге на экран, расчета длины окружности и площади круга, и свойство, позволяющее установить цвет круга.
6)Создайте класс АВТОМОБИЛЬ с методами, позволяющими вывести на экран информацию об автомобиле, а также определить, подходит ли данный автомобиль для заданных условий. Создайте дочерние классы ЛЕГКОВОЙ АВТОМОБИЛЬ (марка, модель, год выпуска, тип кузова), ГРУЗОВОЙ АВТОМОБИЛЬ (марка, модель, год выпуска, грузоподъемность), АВТОБУС (марка, модель, год выпуска, количество мест) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список автомобилей, выведите полную информацию из базы на экран, а также организуйте поиск автомобилей с заданной маркой или годом выпуска.
7)Создайте класс СТУДЕНТ с методами, позволяющими вывести на экран информацию о студенте, а также определить, подходит ли данный студент для заданных условий. Создайте дочерние классы БАКАЛАВР (имя, фамилия, возраст, курс), МАГИСТР (имя, фамилия, возраст, специализация), АСПИРАНТ (имя, фамилия, возраст, тема диссертации) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список студентов, выведите полную информацию из базы на экран, а также организуйте поиск студентов с заданным именем или курсом.
8)Создайте класс РЕСТОРАН с методами, позволяющими вывести на экран информацию о ресторане, а также определить, подходит ли данный ресторан для заданных условий. Создайте дочерние классы ИТАЛЬЯНСКИЙ РЕСТОРАН (название, адрес, тип кухни, рейтинг), ЯПОНСКИЙ РЕСТОРАН (название, адрес, тип кухни, рейтинг), ФРАНЦУЗСКИЙ РЕСТОРАН (название, адрес, тип кухни, рейтинг) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список ресторанов, выведите полную информацию из базы на экран, а также организуйте поиск ресторанов с заданным типом кухни или рейтингом.
9)Создайте класс СПОРТСМЕН с методами, позволяющими вывести на экран информацию о спортсмене, а также определить, подходит ли данный спортсмен для заданных условий. Создайте дочерние классы ЛЕГКОАТЛЕТ (имя, фамилия, возраст, дисциплина), ПЛОВЕЦ (имя, фамилия, возраст, дистанция), БОКСЕР (имя, фамилия, возраст, весовая категория) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список спортсменов, выведите полную информацию из базы на экран, а также организуйте поиск спортсменов с заданным именем или возрастом.
10)Создайте класс ТОВАР с методами, позволяющими вывести на экран информацию о товаре, а также определить, подходит ли данный товар для заданных условий. Создайте дочерние классы ЭЛЕКТРОНИКА (название, производитель, цена, тип устройства), ОДЕЖДА (название, производитель, цена, размер), ПРОДУКТЫ ПИТАНИЯ (название, производитель, цена, срок годности) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список товаров, выведите полную информацию из базы на экран, а также организуйте поиск товаров с заданным названием или ценой.
11)Создайте класс «Магазин» с атрибутами название и список товаров. Каждый товар представлен классом «Товар» с атрибутами название, цена и количество. Напишите методы для добавления товара в магазин, удаления товара из магазина и вычисления общей стоимости товаров в магазине. Используйте магический метод __len__ для определения количества товаров в магазине.
12)Создайте класс «Задача» с атрибутами название, описание и статус (выполнена или нет). Напишите методы для изменения статуса задачи на выполненную и вывода информации о задаче в виде «Задача '{название}': {описание}, статус - {статус}". Используйте магический метод __str__ для вывода информации о задаче.
13)Создайте класс «Банк» с атрибутами название и список счетов. Каждый счет представлен классом «Счет» с атрибутами номер и баланс. Напишите методы для добавления счета в банк, удаления счета из банка и вычисления общего баланса всех счетов в банке. Используйте магический метод __len__ для определения количества счетов в банке.
14)Создайте класс «Студент» с атрибутами имя, фамилия, возраст и список оценок. Напишите методы для добавления оценки, вычисления среднего балла и вывода информации о студенте в виде «Студент {имя} {фамилия}, возраст - {возраст}, средний балл - {средний балл}". Используйте магический метод __len__ для определения количества оценок у студента.
15)Создайте класс «Автомобиль» с атрибутами марка, модель, год выпуска и скорость. Напишите методы для увеличения/уменьшения скорости автомобиля, вывода информации о автомобиле в виде «Автомобиль {марка} {модель}, год выпуска - {год}, скорость - {скорость}". Используйте магический метод __eq__ для сравнения скорости двух автомобилей.
16)Создайте класс фрукты Fruit, хранящий информацию о форме фрукта, его цвете и вкусе. Для класса фрукты Fruit создать: - метод подсчета общего веса текущего экземпляра класса; - метод сравнения общего веса данного экземпляра класса с любым другим; - метод вывода полной информации об экземпляре класса в виде таблицы.
17)Реализовать класс - простейший калькулятор, который в качестве методов реализует арифметические операции. Программа на вход принимает комбинацию чисел и операций, разделенных пробелами. Например, 6 - 7 + 4. В качестве операций для работы с числами использовать сложение и вычитание.
18)Создайте класс окружность и класс прямоугольник, а также методы подсчета периметра для этих фигур. Выведите информацию по периметру фигур, используя принцип полиморфизма.
19)Создать класс Дома и класс Рабочие. Класс дома должен содержать атрибуты: этажность, количество подъездов, район, рабочие, которые строят этот объект. Класс Рабочие должен содержать: название строительной компании, квалификацию рабочего. Создать объекты класса Дома с указанием, в какие сроки планируется осуществить постройку и каким коллективом рабочих. Определить в скольких постройках рабочий задействован в один год одновременно. Данные по датам оформить в виде формата вида 01.01.2022
20)Создать родительский класс Сотрудник магазина, который содержит метод вывода информации о сотруднике (имя, зарплата, стаж). Создать дочерний класс Кассир и дочерний класс Мерчендайзер, которые также имеют возможность вывода информации (имя, зарплата, стаж).
21)Создать родительский класс "Прямоугольник", который содержит методы вычисления площади и периметра прямоугольника. Реализовать дочерний класс "Квадрат" с методами вычисления площади и периметра квадрата.
22)Создать класс Лифт, объекты которого содержат атрибут текущей грузоподъемности. Разработать метод, который сравнивает текущую грузоподъемность лифта с максимальной грузоподъемностью лифта и дает рекомендации по переполнению или недобору грузоподъемности. 
23)Создать класс Квадрат, который будет рисовать закрашенный квадрат, зная координаты точек левого верхнего угла квадрата и длину его стороны. Координаты точки левого верхнего угла и длину стороны квадрата сделать приватными.
24)Создать класс Book, который описывает следующие данные о книге: author (автор), title (название), year (год), price (цена). Найти самую дорогую книгу, используя магические методы сравнения.
25)Создайте класс с методами, формирующими вложенную последовательность. Пользователю должна быть предоставлена возможность заполнить ее либо случайными числами в интервале [-10; 10], либо осуществить ввод данных с клавиатуры.
26)Создайте класс Person с методами, позволяющими вывести на экран информацию о персоне, а также определить ее возраст (в текущем году). Создайте дочерние классы: АБИТУРИЕНТ (фамилия, дата рождения, факультет), СТУДЕНТ (фамилия, дата рождения, факультет, курс), ПРЕПОДАВАТЕЛЬ (фамилия, дата рождения, факультет, должность, стаж), со своими методами вывода информации на экран и определения возраста. Создайте список из n персон, выведите полную информацию из базы на экран, а также организуйте поиск персон, чей возраст попадает в заданный диапазон.
27)Создайте класс ТРЕУГОЛЬНИК, заданный длинами двух сторон и угла между ними, с методами вычисления площади и периметра треугольника, а также методом, выводящим информацию о фигуре на экран. Создайте дочерние классы ПРЯМОУГОЛЬНЫЙ, РАВНОБЕДРЕННЫЙ, РАВНОСТОРОННИЙ со своими методами вычисления площади и периметра. Создайте список n треугольников и выведите полную информацию о треугольниках на экран.
28)Создайте класс ТРАНСПОРТ с методами, позволяющими вывести на экран информацию о транспортном средстве, а также определить грузоподъемность транспортного средства. Создайте дочерние классы АВТОМОБИЛЬ (марка, номер, скорость, грузоподъемность), МОТОЦИКЛ (марка, номер, скорость, грузоподъемность, наличие коляски, при этом если коляска отсутствует, то грузоподъемность равна нулю), ГРУЗОВИК (марка, номер, скорость, грузоподъемность, наличие прицепа, при этом если есть прицеп, то грузоподъемность увеличивается в два раза) со своими методами вывода информации на экран и определения грузоподъемности. Создайте список из n машин, выведите полную информацию на экран, а также организуйте поиск машин, удовлетворяющих требованиям грузоподъемности.
29)Создайте класс ТОВАР с методами, позволяющими вывести на экран информацию о товаре, а также определить, может ли приобрести товар покупатель, имеющий заданную сумму денег. Создайте дочерние классы ПРОДУКТ (название, цена, дата производства, срок годности), ПАРТИЯ (название, цена за штуку, количество штук, дата производства, срок годности), ТЕЛЕФОН (название, цена) со своими методами вывода информации на экран и определения соответствия заданной цене. Создайте список из n товаров, выведите полную информацию из базы на экран, а также организуйте поиск товара, который может приобрести покупатель, имеющий заданную сумму денег.
30)Создайте класс ТОВАР с методами, позволяющими вывести на экран информацию о товаре, а также определить, предназначен ли он для заданного возраста потребителя. Создайте дочерние классы ИГРУШКА (название, цена, производитель, материал, возраст, на который рассчитана), КНИГА (название, автор, цена, издательство, возраст, на который рассчитана), СПОРТИНВЕНТАРЬ (название, цена, производитель, возраст, на который рассчитан) со своими методами вывода информации на экран и определения соответствия возрасту потребителя. Создайте список из n товаров, выведите полную информацию из базы на экран, а также организуйте поиск товаров для потребителя в заданном возрастном диапазоне.
31)Создайте класс ТЕЛЕФОННЫЙ_СПРАВОЧНИК с методами, позволяющими вывести на экран информацию о записях в телефонном справочнике, а также определить соответствие записи критерию поиска. Создайте дочерние классы ПЕРСОНА (фамилия, адрес, номер телефона), ОРГАНИЗАЦИЯ (название, адрес, телефон, факс, контактное лицо), ДРУГ (фамилия, адрес, номер телефона, дата рождения) со своими методами вывода информации на экран и определения соответствия заданной фамилии. Создайте список из n записей, выведите полную информацию из базы на экран, а также организуйте поиск в базе по фамилии.
32)Создайте класс КЛИЕНТ с методами, позволяющими вывести на экран информацию о клиентах банка, а также определить соответствие клиента критерию поиска. Создайте дочерние классы ВКЛАДЧИК (фамилия, дата 269 открытия вклада, размер вклада, процент по вкладу), КРЕДИТОР (фамилия, дата выдачи кредита, размер кредита, процент по кредиту, остаток долга), ОРГАНИЗАЦИЯ (название, дата открытия счета, номер счета, сумма на счету) со своими методами вывода информации на экран и определения соответствия дате (открытия вклада, выдаче кредита, открытия счета). Создайте список из n клиентов, выведите полную информацию из базы на экран, а также организуйте поиск клиентов, начавших сотрудничать с банком в заданную дату.
33)Создайте класс ПРОГРАММНОЕ_ОБЕСПЕЧЕНИЕ с методами, позволяющими вывести на экран информацию о программном обеспечении, а также определить соответствие возможности использования (на текущую дату). Создайте дочерние классы СВОБОДНОЕ (название, производитель), УСЛОВНО_БЕСПЛАТНОЕ (название, производитель, дата установки, срок бесплатного использования), КОММЕРЧЕСКОЕ (название, производитель, цена, дата установки, срок использования) со своими методами вывода информации на экран и определения возможности использования на текущую дату. Создайте список из n видов программного обеспечения, выведите полную информацию из базы на экран, а также организуйте поиск программного обеспечения, которое допустимо использовать на текущую дату.
34)В функцию func() передаются данные о школьнике и его оценке по ИТ. Функция func() пропечатывает на экране эти данные в виде таблицы. Составить декоратор для функции func(), который выводил бы на экран только школьников, чья оценка по ИТ выше 4.5 баллов.
35)Декорируемая функция выводит число. Реализовать декоратор, который возводит число в степень двойки, если данное число четное. И возводит число в куб, если число нечетное.
36)Напишите функцию summa(start, end), которая суммирует все целые числа от значения «start» до величины «end» включительно. Если пользователь задаст первое число большее чем второе, то срабатывает декоратор, который выведет разность чисел.
37)Создайте функцию three_args(), которая принимает 1, 2 или 3 строго ключевых параметра. В результате ее работы на печать выводится сумма значений переданных переменных, но только если они не равны None. Создать декоратор к функции three_args(), который выводит значение 0, если одно из значений ключевых параметров равно None.
38)Реализовать декоратор с именем not_null, который генерирует исключительную ситуацию если декорируемая функция вернула значения 0.
39)Создать декоратор tol(dlina, fill) с параметрами dlina и fill. Декоратор превращает результат декорируемой функции в словарь состоящий из dlina элементов. Если исходная функция возвращает меньше заданного количества элементов, то оставшиеся места заполняются значениями {порядковый номер элемента : fill}, в случае, если количество возвращаемых элементов больше dlina, то хвост последовательности отбрасывается.
40)Создать декоратор dec(a, b) с параметрами a и b. Декоратор увеличивает результат декорируемой функции, которая вычисляет сумму произвольного количества чисел, на «a» элементов при условии положительного значения суммы. Если исходная функция возвращает отрицательное значение суммы, то декоратор уменьшает результат декорируемой функции на значение «b».
41)Создать декоратор decor(a) с параметром a. Декоратор приводит в верхний регистр все значения словаря с ключами, кратными параметру a. Словарь вида {1:’Group’, 2:’is’, 3:’in’, 4:’Exam’} является результатом выполнения декорируемой функции.
42)Создать функцию func c параметром name, которая в качестве результата работы выводит ваше имя и количество букв в нем. Создать декоратор decor_func к функции func, который будет возвращать ваше имя в обратном порядке столько раз, сколько в нем букв. Например, Иван-->навИнавИнавИнавИ
43)Создать функцию func без параметров, которая в качестве результата работы выводит ваше ФИО. Создать декоратор decor_func к функции func, который будет возвращать ваше ФИО в обратном порядке. Например, Иван-->навИ
44)Создать класс стек. Необходимо найти количество элементов, которые больше среднего значения всех элементов стека.
45)Создать класс стек. Дан стек. Необходимо удалить из него все элементы, которые не являются квадратами целых чисел.
46)Создать класс стек. Необходимо развернуть каждый второй элемент в обратном порядке.
47)Создать класс стек. Необходимо найти количество элементов, которые больше среднего значения всех элементов стека.
48)Создать класс стек. Необходимо проверить, является ли его содержимое упорядоченным по возрастанию.
49)Создать класс стек. Использовать способ реализации стека через list. Поменять местами первый и последний элементы стека.
50)Создать класс стек. Удалить элемент, который находится в середине стека, если нечетное число элементов, а если четное, то два средних.
51)Создать класс стек. Удалить каждый второй элемент стека.
52)Создать класс стек. Найти минимальный элемент стека и вставить после него «0».
53)Создать класс стек, который будет хранить только элементы, удовлетворяющие заданному условию. Условие задается при инициализации объекта класса очереди. При добавлении элемента, если он не удовлетворяет заданному условию, то он не должен добавляться.
54)Создать класс стек, который будет хранить только элементы определенной длины. Длина элементов задается при инициализации объекта класса стек. При добавлении элемента, если его длина не соответствует заданной, то он не должен добавляться.
55)Создать класс стек, который будет хранить только элементы типа str и отсортированные по алфавиту. При добавлении элемента, если он не является строкой, то он не должен добавляться. При получении элементов из стека они должны быть отсортированы по алфавиту.
56)Создать два стека и поменять информацию местами.
57)Сформировать стек с элементами – строками. Прочитать три нижних элемента стека и поменять местами верхний и нижний элементы.
58)Реализовать функцию, которая находит минимальный элемент в однонаправленном связном списке.
59)Реализовать функцию, которая добавляет элемент в однонаправленный связный список справа.
60)Реализовать функцию, которая добавляет элемент в однонаправленный связный список слева.
61)Реализовать функцию, которая вставляет элемент в однонаправленный связный список после заданного элемента (по номеру).
62)Реализовать функцию, которая вставляет элемент в однонаправленный связный список после заданного элемента (по значению).
63)Реализовать функцию, которая находит заданный элемент в однонаправленном связном списке и удаляет его из списка.
64)Реализовать функцию, которая находит сумму всех элементов, кратных 3, в однонаправленном связном списке.
65)Дан двунаправленный связный список. Вставить элемент после n-го элемента списка.
66)Дан двунаправленный связный список. Удалить n-ый элемент списка.
67)Соединить два однонаправленных связных списка.
68)Даны 2 кольцевых списка с фамилиями шахматистов 2-х команд. Произвести жеребьевку. В первой команде выбирается каждый n-й игрок, а во второй - каждый k-й.
69)Даны 2 кольцевых списка: фамилии участников розыгрыша и названия призов. Выиграет n человек (каждый k-й). Число для пересчета призов - t.
70)Дан однонаправленный связный список с перечнем товаров. Выбрать все товары, изготовленные фирмой Bosh и создать из них новый список.
71)Даны 2 однонаправленных связных списка: перечень товаров, производимых Концернами BORK и PHILIPS. Создать список товаров, выпускаемых как одной, так и другой фирмой.
72)Даны 2 однонаправленных связных списка с фамилиями студентов 2-х групп. Перевести n студентов из 1-й группы во 2-ю.
73)Даны 2 кольцевых списка: перечень товаров и фамилии покупателей. Каждый n-й покупатель покупает m-й товар. Вывести список покупок.
74)Дан массив целых чисел размерности n, заданных случайным образом из интервала от -20 до 20. Если сумма отрицательных элементов по модулю превышает сумму положительных, то отсортировать массив по возрастанию, иначе – по убыванию.
75)Дан массив целых чисел размерности n, заданных случайным образом из интервала от -30 до 30. Если в массиве есть отрицательные элементы, то отсортировать массив по возрастанию, иначе - по убыванию.
76)Дан массив целых чисел размерности n, заданных случайным образом. Рассчитать остаток от деления на 7 для каждого из чисел списка и получить произведение тех остатков, величина которых больше 4.
77)Дан массив целых чисел размерности n, заданных случайным образом из интервала от 0 до 100. Если количество четных элементов, стоящих на нечетных местах, превышает количество нечетных элементов, стоящих на четных местах, то отсортировать массив по возрастанию, иначе по убыванию.
78)Дано два массива. Создать массив, содержащий элементы общие для двух исходных массивов.
79)В массиве целых чисел найти количество пар элементов разного знака. (пара — это два рядом стоящих элемента).
80)Дано предложение без знаков препинания. Превратить предложение в список слов. При помощи механизма map/filter/reduce отбросить у каждого слова первую и последнюю букву и склеить в одну строку те обрезанные слова, длина которых больше 5.
81)При помощи механизма map/filter/reduce удалить заданный элемент из списка и вывести итоговый список.
82)Дан список слов. При помощи механизма map/filter/reduce рассчитать остаток от деления на 7 для каждой длины слова из списка и получить произведение тех остатков, величина которых больше 4.
83)Дан список целых чисел.  При помощи механизма map/filter/reduce рассчитать остаток от деления на 17 для каждого из чисел списка и получить произведение тех остатков, величина которых меньше 7.
84)Дан список целых чисел.  При помощи механизма map/filter/reduce рассчитать произведение чисел списка и получить сумму тех значений, величина которых меньше 0.
85)Реализовать двоичное дерево в виде связанных объектов (реализовать класс для элементов двоичного дерева) и реализовать симметричную процедуру обхода двоичного дерева в виде рекурсивной функции
86)Описать функцию, которая:
a) присваивает параметру Е запись из самого левого листа непустого дерева Т (лист-вершина, из которого не выходит ни одной ветви);
b) определяет число вхождений записи Е в дерево Т.
с) Вершины дерева вещественные числа. Описать функцию, которая вычисляет среднее арифметическое всех вершин
дерева.
87)Стек Долгов(Для Камилы)

27 БИЛЕТОВ
1)
    2.
    а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Фибоначчи. 
    б) Напишите программу для сортировки заданного списка кортежей по разности между максимальным и минимальным элементами каждого кортежа.
    в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые содержат только согласные буквы.
    3.Реализовать функцию, которая находит максимальный элемент в двусвязном списке и удаляет его из списка. 
2)
    2. 
    а) Отфильтровать список целых чисел на простые и составные числа с помощью лямбда-функции. 
    б) Для удаления определённых символов из заданной строки используйте лямбда-функцию. Пример: дана строка 'hello world', удалить символы 'l' и 'o' → 'he wrd'. 
    в) Используя лямбда-функцию, проверить, является ли указанный список палиндромом или нет.
    3. Реализовать функцию, которая проверяет, является ли двусвязный список палиндромом (элементы списка читаются одинаково как слева направо, так и справа налево).
3)
    2.Создайте класс «Банк» с атрибутами название, адрес и список клиентов. Каждый клиент представлен классом «Клиент» с атрибутами имя, фамилия, номер счета и баланс. Напишите методы для добавления клиента в банк, удаления клиента из банка и вывода информации о банке в виде «Банк '{название}', адрес - {адрес}, клиенты - {список клиентов}». Используйте магический метод __str__ для вывода информации о клиенте в удобном формате.
    3.Дан стек. Необходимо проверить, содержит ли он хотя бы один элемент, который является квадратом другого элемента стека.
4)
    2. Создайте класс АВТОМОБИЛЬ с методами, позволяющими вывести на экран информацию об автомобиле, а также определить, подходит ли данный автомобиль для заданных условий. Создайте дочерние классы ЛЕГКОВОЙ (марка, модель, год выпуска, объем двигателя, тип топлива), ГРУЗОВОЙ (марка, модель, год выпуска, грузоподъемность), ПАССАЖИРСКИЙ (марка, модель, год выпуска, количество мест) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список автомобилей, выведите полную информацию из базы на экран, а также организуйте поиск автомобилей с заданными характеристиками.
    3.Реализовать функцию, которая находит произведение квадратов всех элементов в двусвязном списке.
5)
    2. Создайте класс ФИЛЬМ с методами, позволяющими вывести на экран информацию о фильме, а также определить, подходит ли данный фильм для заданных условий. Создайте дочерние классы КОМЕДИЯ (название, год выпуска, режиссер, актеры), ДРАМА (название, год выпуска, режиссер, актеры), ФАНТАСТИКА (название, год выпуска, режиссер, актеры) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список фильмов, выведите полную информацию из базы на экран, а также организуйте поиск фильмов с заданным годом выпуска или режиссером.
    3. Найти высоту бинарного дерева поиска.
6)
    2.
    а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются палиндромами. 
    б) Напишите программу для сортировки заданного списка строк по количеству гласных букв в каждой строке.
    в) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются совершенными числами (сумма делителей числа равна самому числу).
    3. Найти наибольший элемент, меньший заданного значения, в бинарном дереве поиска.
7)
    2. Создайте класс «Круг» с атрибутами радиус и цвет. Напишите методы для вычисления площади и длины окружности круга. Используйте магический метод __str__ для вывода информации о круге в виде «Круг радиуса {радиус} и цвета {цвет}». 
    3. Реализовать функцию, которая находит минимальный элемент в двусвязном списке.
8)
    2. Создайте класс «Студент» с атрибутами имя, возраст и список оценок. Напишите методы для вычисления среднего балла и определения успеваемости студента (средний балл выше 4). Используйте магический метод __repr__ для вывода информации о студенте в виде «Студент {имя}, возраст {возраст}».
    3. Создать класс очереди, который будет хранить только элементы, большие заданного значения. Значение задается при инициализации объекта класса очереди. При добавлении элемента, если он меньше или равен заданному значению, то он не должен добавляться.
9)
    2.
    а) Используя лямбда-функцию, отсортировать список строковых значений в алфавитном порядке. 
    б) Найдите все анаграммы в заданном списке строк с помощью лямбдафункции.
    в) Найти индекс и значение максимального и минимального значений в заданном списке чисел с помощью лямбда-функции.
    3. Дан стек. Необходимо удалить из него все элементы, которые не являются делителями последнего элемента стека.
10)
    2.
    а) Используя лямбда-функцию, отсортировать список кортежей по второму элементу.
    б) Найти все числа, которые делятся на три или пять, из заданного списка чисел, используя лямбда-функцию.
    в) Напишите программу для сортировки заданного смешанного списка целых чисел и строк с помощью лямбда-функции. Строки должны быть отсортированы перед числами.
    3. Написать функцию, которая принимает на вход двусвязный список и значение элемента, который нужно найти. Функция должна вернуть индекс первого вхождения элемента в список или -1, если элемент не найден.
11)
    2.
    а) С помощью лямбда-функции проверьте, является ли данное число простым или нет.
    б) Напишите программу для поиска чисел в заданном диапазоне, которые являются суммой двух квадратов. Пример: задан диапазон от 1 до 50 → [1, 2, 4, 5, 8, 9, 10, 13, 16, 17, 18, 20, 25, 26, 29, 32, 34, 36, 37, 40, 41, 45, 49, 50].
    в) Удалите все элементы из заданного списка, не присутствующие в другом списке. Пример: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8] → [2, 4, 6, 8].
    3. Создать класс хеш-таблицы для хранения объектов класса «Сотрудник». Хеш-функция должна основываться на поле «должность» сотрудника. Если два сотрудника имеют одну и ту же должность, они должны храниться в одной ячейке таблицы.
12)
    2. Опишите класс Recipe, заданный названием, списком ингредиентов и шагами приготовления. Включите в описание класса методы: вывода информации о рецепте на экран, проверки, есть ли все ингредиенты для приготовления блюда, и свойство, позволяющее установить тип кухни (например, итальянская, японская и т. д.).
    3. Написать метод класса «Заказ», который сортирует список заказов по дате с помощью алгоритма быстрой сортировки. Метод должен изменять исходный список.
13)
    2.
    а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются простыми числами. 
    б) Напишите программу для сортировки заданного списка строк по убыванию длины каждой строки. 
    в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые содержат только буквы верхнего регистра.
    3.Реализовать класс хеш-таблицы для хранения объектов класса «Книга». Хеш-функция должна основываться на поле «автор книги». Если две книги имеют одного и того же автора, они должны храниться в одной ячейке таблицы.
14)
    2.
    а) Используя лямбда-функцию, найдите все числа в заданном списке, которые делятся на 3 и не делятся на 5. 
    б) Напишите программу для сортировки заданного списка кортежей по возрастанию второго элемента каждого кортежа. 
    в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые содержат только цифры.
    3. Создать класс очереди, который будет хранить только уникальные элементы. При добавлении элемента, если он уже есть в очереди, то он не должен добавляться
15)
    2. Создайте класс ПРОДУКТ с методами, позволяющими вывести на экран информацию о продукте, а также определить, подходит ли данный продукт для заданных условий. Создайте дочерние классы МЯСО (название, цена, производитель, срок годности), ОВОЩИ (название, цена, производитель, сезонность), ФРУКТЫ (название, цена, производитель, сезонность) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список продуктов, выведите полную информацию из базы на экран, а также организуйте поиск продуктов с заданной ценой или сезонностью.
    3. Дан стек. Необходимо удалить из него все элементы, которые не являются четными числами.
16)
    2. Создайте класс ЖИВОТНОЕ с методами, позволяющими вывести на экран информацию о животном, а также определить, подходит ли данное животное для заданных условий. Создайте дочерние классы КОШКА (кличка, порода, возраст, окрас), СОБАКА (кличка, порода, возраст, размер), ПТИЦА (вид, возраст, окрас) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список животных, выведите полную информацию из базы на экран, а также организуйте поиск животных с заданной породой или окрасом.
    3. Написать функцию, которая принимает на вход двусвязный список и индекс элемента, который нужно удалить. Функция должна удалить элемент с указанным индексом и вернуть измененный список.
17)
    2. Создайте класс СТУДЕНТ с методами, позволяющими вывести на экран информацию о студенте, а также определить, соответствует ли данный студент заданным критериям. Создайте дочерние классы БАКАЛАВР (фамилия, имя, отчество, группа, курс, средний балл), МАГИСТР (фамилия, имя, отчество, группа, курс, средний балл, тема диссертации) со своими методами вывода информации на экран и определения соответствия заданным критериям. Создайте список студентов, выведите полную информацию из базы на экран, а также организуйте поиск студентов с заданным средним баллом или темой диссертации.
    3. Написать метод класса «Клиент», который сортирует список клиентов по возрасту с помощью алгоритма сортировки пузырьком. Метод должен изменять исходный список. 
18)
    2.
    а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Кармайкла (для любого числа a, взаимно простого с числом n, a^(n-1) mod n = 1). 
    б) Напишите программу для сортировки заданного списка строк по количеству символов-разделителей (пробелов или запятых) в каждой строке.
    в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые заканчиваются на точку.
    3. Создать класс очереди, который будет хранить только элементы типа int и отсортированные по убыванию. При добавлении элемента, если он не является целым числом, то он не должен добавляться. При получении элементов из очереди они должны быть отсортированы по убыванию.
19)
    2. Опишите класс Animal, заданный видом, возрастом и весом. Включите в описание класса методы: вывода информации о животном на экран, проверки, является ли животное взрослым (возраст больше 3 лет), и свойство, позволяющее установить цвет животного.
    3. Написать функцию, которая принимает на вход список слов и сортирует его по алфавиту с помощью алгоритма сортировки вставками. Функция должна возвращать отсортированный список.
20)
    2.
    а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Капрекара. 
    б) Напишите программу для сортировки заданного списка строк в лексикографическом порядке (по алфавиту). 
    в) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Армстронга.
    3. Реализовать функцию, которая находит количество элементов в двусвязном списке.
21)
    2. Опишите класс Student, заданный фамилией, именем, возрастом и средним баллом. Включите в описание класса методы: вывода информации о студенте на экран, проверки, является ли студент отличником (средний балл больше 4.5), и свойство, позволяющее установить факультет, на котором учится студент.
    3. Написать функцию, которая принимает на вход список чисел и сортирует его по убыванию с помощью алгоритма сортировки вставками. Функция должна возвращать отсортированный список.
22)
    2.
    а) Напишите лямбда-функцию, которая умножает заданное число на 7 и вычитает из него 3, переданное в качестве аргумента.
    б) Найдите сумму всех чисел в заданном списке целых чисел с помощью лямбда-функции. 
    в) Извлечь элементы списка, которые больше заданного числа, с помощью лямбда-функции.
    3. Дан стек. Необходимо найти сумму всех элементов, которые являются простыми числами.
23)
    2. Создайте класс «Книга» с атрибутами название, автор и год издания. Напишите методы для вывода информации о книге в виде «Книга '{название}' автора {автор}, издана в {году}». Используйте магический метод __eq__ для сравнения двух книг по году издания.
    3. Написать функцию, которая принимает на вход список строк и сортирует его по длине строк с помощью алгоритма сортировки выбором. Функция должна возвращать отсортированный список.
24)
    2. Опишите класс BankAccount, заданный номером счета, балансом и владельцем. Включите в описание класса методы: вывода информации о банковском счете на экран, проверки, достаточно ли денег на счете для выполнения операции, и свойство, позволяющее установить тип валюты, в которой открыт счет.
    3. Написать функцию, которая принимает на вход список дат и сортирует его по возрастанию с помощью алгоритма сортировки пузырьком. Функция должна возвращать отсортированный список.
25)
    2. Опишите класс Movie, заданный названием, режиссером, годом выпуска и продолжительностью. Включите в описание класса методы: вывода информации о фильме на экран, проверки, является ли фильм длинным (продолжительность больше 2 часов), и свойство, позволяющее установить жанр фильма.
    3. Реализовать класс хеш-таблицы для хранения объектов класса «Товар». Хеш-функция должна основываться на поле «название товара». Если два товара имеют одинаковое название, они должны храниться в одной ячейке таблицы. 
26)
    2.
    а) Напишите лямбда-функцию, которая принимает два аргумента и возвращает их произведение. 
    б) Найдите числа, которые являются квадратами целых чисел, из заданного списка чисел, используя лямбда-функцию. 
    в) Напишите программу для подсчёта целых чисел в заданном смешанном списке с помощью лямбда-функции.
    3. Написать метод класса «Товар», который сортирует список товаров по цене с помощью алгоритма быстрой сортировки. Метод должен изменять исходный список.
27)
    2. Создайте класс «Прямоугольник» с атрибутами длины и ширины. Напишите методы для вычисления площади и периметра прямоугольника. Используйте магический метод __eq__ для сравнения двух прямоугольников по площади.
    3.------


        '''
        print(text)
    def teor():
        text = '''
Объектно-ориентированное программирование

1.Концепция класса и объекта. Принципы и механизмы ООП. 
Концепция класса и объекта:
Класс - это абстрактный шаблон, описывающий свойства (поля) и поведение (методы) объектов определенного типа. Класс определяет структуру и поведение объектов, которые будут созданы на его основе.

Объект - это экземпляр класса. Он представляет собой конкретное воплощение класса, имеющее конкретные значения своих полей и способность выполнять определенные действия с помощью своих методов.

Принципы ООП (объектно-ориентированного программирования):
1. Инкапсуляция - принцип, согласно которому классы скрывают внутреннюю реализацию и предоставляют интерфейс для взаимодействия с внешним миром. Это позволяет обеспечить контролируемый доступ к данным и методам объекта.

2. Наследование - принцип, позволяющий создавать новые классы на основе существующих. Наследование позволяет переиспользовать код, расширять функциональность и создавать иерархии классов.

3. Полиморфизм - принцип, позволяющий использовать один и тот же код для работы с объектами разных классов. Полиморфизм позволяет обращаться к объектам разных классов через общий интерфейс, что упрощает разработку и обеспечивает гибкость системы.

Механизмы ООП:
1. Классы и объекты - классы предоставляют шаблон для создания объектов определенного типа, а объекты являются конкретными экземплярами класса.

2. Наследование - классы могут наследовать свойства и методы от других классов, что позволяет переиспользовать код и создавать иерархии классов.

3. Полиморфизм - объекты разных классов могут быть обращены через общий интерфейс, что позволяет использовать один и тот же код для работы с разными объектами.

4. Инкапсуляция - классы могут скрывать внутренние детали реализации и предоставлять интерфейс для взаимодействия с внешним миром.


2.Объявление класса, конструктор, создание объектов и одиночное наследование в Python. Управление доступом к атрибутам класса в Python. Полиморфизм и утиная типизация и проверка принадлежности объекта к классу в языке Python.
Объявление класса, конструктор, создание объектов и одиночное наследование в Python:
- Объявление класса: Классы в Python объявляются с использованием ключевого слова "class" и именем класса.
- Конструктор: Конструктор класса в Python определяется с помощью метода "__init__", который инициализирует объекты класса и устанавливает начальные значения его атрибутов.
- Создание объектов: Объекты класса создаются с использованием вызова класса с передачей аргументов конструктору.
- Одиночное наследование: Одиночное наследование в Python выполняется путем указания родительского класса в определении дочернего класса с использованием ключевого слова "class".

Управление доступом к атрибутам класса в Python:
- Публичные атрибуты: Публичные атрибуты класса доступны для чтения и записи из любого места программы.
- Приватные атрибуты: Приватные атрибуты класса доступны только внутри класса и не могут быть прямо доступны извне.
- Защищенные атрибуты: Защищенные атрибуты класса доступны внутри класса и его подклассов, но не рекомендуется обращаться к ним извне.

Полиморфизм и утиная типизация, проверка принадлежности объекта к классу в языке Python:
- Полиморфизм: Полиморфизм в Python означает использование одного и того же кода для работы с объектами разных классов, при условии, что они реализуют одинаковые методы или операторы.
- Утиная типизация: Утиная типизация в Python означает определение типа объекта не на основе его класса, а на основе наличия определенных методов или атрибутов.
- Проверка принадлежности объекта к классу: В Python можно проверить принадлежность объекта к определенному классу с помощью функции "isinstance".




3.Методы классов и статические переменные и методы в Python. Специальные методы для использования пользовательских классов со стандартными операторами и функциями.
Методы классов - это функции, определенные внутри класса, которые позволяют работать с объектами этого класса. Они определяются с использованием ключевого слова "def" и имеют доступ к атрибутам класса и объектам этого класса. Методы классов могут быть вызваны на объектах этого класса, используя точечную нотацию.

Статические переменные и методы - это переменные и методы, которые принадлежат классу, а не его объектам. Они общие для всех экземпляров класса и могут быть вызваны без создания экземпляра класса. Статические переменные объявляются внутри класса, но вне методов, и используются с помощью имени класса. Статические методы объявляются с использованием декоратора `@staticmethod` и не требуют доступа к экземпляру класса.

Специальные методы (также известные как магические методы или методы перегрузки операторов) позволяют пользовательским классам работать со стандартными операторами и функциями в Python. Эти методы начинаются и заканчиваются двумя подчеркиваниями. Некоторые из часто используемых специальных методов включают `__init__` (инициализация объекта), `__str__` (строковое представление объекта), `__len__` (длина объекта), `__add__` (сложение объектов) и `__eq__` (сравнение объектов на равенство).

Надеюсь, это объяснение поможет лучше понять методы классов, статические переменные и методы, а также специальные методы для работы с операторами и функциями в Python. Если у тебя возникнут еще вопросы, не стесняйся задавать их!


Функциональное программирование

4.Основные возможности, поддерживаемые функциональными языками программирования. Поддержка элементов функционального программирования в Python.
Основные возможности, поддерживаемые функциональными языками программирования, включают:

1. Функции первого класса: Функции в функциональных языках программирования могут быть переданы как аргументы другим функциям, присвоены переменным и возвращены из функций. Функции первого класса позволяют работать с функциями как с любыми другими объектами данных.

2. Замыкания: Замыкания позволяют функции сохранять ссылки на переменные из окружающей их области видимости даже после того, как эта область видимости закрыта. Это позволяет функциям иметь состояние и сохранять контекст.

3. Рекурсия: Функциональные языки программирования обычно поддерживают рекурсию, что позволяет функциям вызывать сами себя. Рекурсия является важным инструментом в функциональном программировании для решения задач, особенно тех, которые требуют итеративного или повторяющегося подхода.

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

5. Функции высших порядков: Функции высших порядков позволяют передавать функции в качестве аргументов и возвращать их из других функций. Они позволяют абстрагировать общие операции и повторно использовать код.

В Python также есть поддержка элементов функционального программирования, хотя он не является строго функциональным языком. Некоторые из элементов функционального программирования, поддерживаемых в Python, включают:

1. Функции первого класса: В Python функции являются объектами первого класса и могут быть переданы в качестве аргументов, присвоены переменным и возвращены из функций.

2. Замыкания: Python поддерживает замыкания, что позволяет функциям сохранять состояние и ссылки на переменные из окружающей области видимости.

3. Рекурсия: Python поддерживает рекурсию и позволяет функциям вызывать сами себя.

4. Анонимные функции (lambda-функции

): В Python можно определить анонимные функции с помощью ключевого слова `lambda`, которые могут использоваться вместе с функциями высших порядков.

5. Встроенные функции высшего порядка: В Python встроены функции высшего порядка, такие как `map`, `filter` и `reduce`, которые позволяют применять функции к коллекциям данных.

Хотя Python не является чисто функциональным языком, эти функциональные возможности делают его более гибким и позволяют использовать функциональные подходы в разработке программ.


5.Концепция «функции – граждане первого класса» в языке программирования, поддержка этой концепции в Python. Специфика лямбда-функций в Python их возможности и ограничения. Типичные сценарии использования лямбда-функций в Python.
Концепция «функции – граждане первого класса» в языке программирования означает, что функции могут быть рассматриваемыми как любые другие элементы языка, такие как переменные, объекты или данные. Это означает, что функции могут быть переданы в качестве аргументов другим функциям, присвоены переменным, возвращены из функций и храниться в структурах данных.

Python является языком программирования, который полностью поддерживает концепцию "функций - граждан первого класса". В Python функции являются объектами первого класса и могут быть переданы в качестве аргументов, присвоены переменным, возвращены из функций и храниться в структурах данных.

Лямбда-функции в Python - это анонимные функции, которые могут быть определены в одной строке кода с использованием ключевого слова `lambda`. Они обычно используются в местах, где требуется краткость и не требуется определение отдельной функции.

Лямбда-функции в Python имеют следующие особенности и ограничения:

- Они могут содержать только одно выражение и не могут содержать блоки кода.
- Они возвращают результат вычисления выражения, но не имеют явного оператора `return`.
- Они могут принимать любое количество аргументов, но должны быть записаны в виде одного выражения.
- Лямбда-функции могут использовать переменные из окружающей области видимости.

Типичные сценарии использования лямбда-функций в Python:

- Как аргументы функций высшего порядка, таких как `map`, `filter` и `reduce`, для определения логики преобразования или фильтрации элементов коллекций.
- В качестве ключей сортировки при использовании функции `sorted` или метода `sort`.
- В функциях, требующих краткого определения функции без необходимости создания отдельной именованной функции.
- Вместо создания функций-оберток или декораторов в случаях, когда нужно изменить поведение существующей функции.
- В функциях обратного вызова (callback), где требуется передать функцию для выполнения определенной операции.

Лямбда-функции предоставляют компактный и удобный способ определения простых функций на лету. Однако их использование следует ограничивать ситуациями, когда необходима краткая и одноразовая функциональность. Для более сложных и многофункциональных функций рекомендуется использовать обычные именованные функции в Python.


6.Глобальные и локальные переменные в функциях на примере Python. Побочные эффекты вызова функций и их последствия.
Глобальные переменные в Python - это переменные, которые определены за пределами функций и доступны в любой части программы. Они могут быть использованы как внутри функций, так и вне их. Глобальные переменные должны быть объявлены перед использованием в функциях с помощью ключевого слова `global`.

Локальные переменные в Python - это переменные, которые определены внутри функций и доступны только внутри этих функций. Они существуют только во время выполнения функции и уничтожаются после ее завершения. Локальные переменные не видны за пределами функций и не могут быть доступны другим функциям или коду.

Побочные эффекты вызова функций - это изменения, которые происходят при вызове функции и могут влиять на состояние программы или другие переменные. Побочные эффекты могут включать изменение значений переменных, вывод на экран, ввод данных, изменение файлов и т. д. Побочные эффекты могут быть полезными, но также могут приводить к нежелательным последствиям, особенно в сложных программах. Поэтому важно учитывать и управлять побочными эффектами вызова функций, чтобы избегать непредсказуемых изменений и обеспечивать модульность и предсказуемость кода.


7.Вложенные функции и замыкания, специфика реализации в Python.
Вложенные функции - это концепция в языке программирования, которая позволяет определять функции внутри других функций. Вложенная функция имеет доступ к переменным своей внешней функции и может использовать их в своем теле. Однако вложенная функция не может быть вызвана извне своей внешней функции.

Замыкание - это функция, которая сохраняет состояние окружающего контекста, включая переменные и параметры, в которых она была определена. Замыкание может получить доступ и использовать эти сохраненные значения, даже после того, как внешняя функция завершила свое выполнение.

В Python вложенные функции и замыкания реализуются следующим образом:

1. Вложенные функции: Внутренняя функция определяется внутри тела внешней функции. Она может обращаться к переменным и параметрам внешней функции, а также иметь свои собственные переменные и параметры.

2. Замыкания: Внутренняя функция, определенная внутри внешней функции, может захватывать (и сохранять) значения переменных и параметров внешней функции. Это происходит путем присвоения ссылки на внешнюю переменную внутри вложенной функции. Таким образом, даже после завершения внешней функции, замыкание может продолжать использовать сохраненные значения.

Вложенные функции и замыкания могут быть полезными в различных сценариях программирования, включая обработку состояния, создание функций-оберток, реализацию функций-генераторов и других ситуаций, когда требуется сохранение контекста или доступ к значениям внешних переменных.



8.Функции высшего порядка и декораторы в Python.
Функции высшего порядка (higher-order functions) в Python - это функции, которые могут принимать другие функции в качестве аргументов и/или возвращать функции в качестве результата. Они позволяют использовать функции как данные и манипулировать ими в программе.

Декораторы в Python - это специальные функции, которые позволяют изменять поведение других функций без изменения их исходного кода. Они позволяют добавлять дополнительную функциональность к функциям, оборачивая их вокруг других функций.

Функции высшего порядка и декораторы являются мощными концепциями в Python, которые позволяют создавать гибкий и модульный код, улучшать переиспользуемость функций и добавлять новые возможности без изменения их исходного кода.



9.Концепция map/filter/reduce. Реализация map/filter/reduce в Python и пример их использования.
Концепция `map`, `filter` и `reduce` - это функциональные операторы, часто используемые в функциональном программировании, которые позволяют применять операции к элементам коллекций или последовательностей.

В Python эти операторы представлены в виде встроенных функций:

1. `map(function, iterable)` - применяет функцию `function` к каждому элементу в `iterable` (например, списку) и возвращает итератор с результатами.

2. `filter(function, iterable)` - фильтрует элементы из `iterable` (например, списка), оставляя только те, для которых функция `function` возвращает `True`.

3. `reduce(function, iterable)` - сводит последовательность в единственное значение, применяя функцию `function` к парам элементов до тех пор, пока не останется только одно значение.

Примеры использования:

1. Пример использования `map`:
   - Применение функции `str.upper()` ко всем элементам списка для преобразования их в верхний регистр.
   - Применение функции `lambda x: x * 2` ко всем элементам списка для умножения их на 2.

2. Пример использования `filter`:
   - Фильтрация только нечетных чисел из списка с помощью функции `lambda x: x % 2 != 0`.
   - Фильтрация строк, содержащих определенное слово, с помощью функции `lambda x: 'word' in x`.

3. Пример использования `reduce`:
   - Сложение всех чисел из списка с помощью функции `lambda x, y: x + y`.
   - Вычисление произведения всех чисел из списка с помощью функции `lambda x, y: x * y`.

Операторы `map`, `filter` и `reduce` в Python предоставляют удобные способы преобразования, фильтрации и свертывания данных. Они могут использоваться для различных задач обработки данных и повышения эффективности кода.


10.Итераторы в Python: встроенные итераторы, создание собственных итераторов, типичные способы обхода итераторов и принцип их работы. Встроенные функции для работы с итераторами и возможности модуля itertools. Функции генераторы и выражения генераторы: создание и применение в Python.
Итераторы - это объекты в Python, которые предоставляют последовательный доступ к элементам коллекций или последовательностей. Они позволяют обходить элементы по одному, не загружая все элементы в память сразу.

В Python существуют встроенные итераторы, такие как списки, кортежи, строки и словари, которые могут быть обходимыми с помощью цикла `for`. Кроме того, можно создавать собственные итераторы, реализуя методы `__iter__()` и `__next__()`.

Создание собственных итераторов в Python требует определения класса, который имеет методы `__iter__()` и `__next__()`. Метод `__iter__()` возвращает сам итератор, а метод `__next__()` возвращает следующий элемент последовательности или вызывает исключение `StopIteration`, если больше элементов нет.

Существуют различные способы обхода итераторов, например, с использованием цикла `for` или функции `next()`.

Модуль `itertools` предоставляет дополнительные функции для работы с итераторами, такие как комбинирование, фильтрация, повторение и перестановки.

Функции генераторы и выражения генераторы - это специальные конструкции в Python, которые позволяют создавать итераторы более простым и компактным способом. Они позволяют создавать итераторы без явного определения класса и методов `__iter__()` и `__next__()`. Вместо этого они используют ключевое слово `yield` для возврата элементов последовательности по одному.

Это позволяет сократить объем кода и упростить процесс создания итераторов.


Оценка сложности. Массивы, стеки, очереди, списки

11.Специфика массивов, как структур данных. Динамические массивы – специфика работы, сложность операций. Специфика работа с array в Python.
Массивы являются структурами данных, предназначенными для хранения и организации элементов в памяти. Они позволяют обращаться к элементам по индексу и выполнять операции над ними.

Специфика массивов:
- Фиксированный размер: Массивы имеют фиксированный размер при создании, и этот размер не может быть изменен без создания нового массива.
- Способ хранения: Элементы массива хранятся в памяти последовательно, что обеспечивает прямой доступ к элементам по индексу.
- Однородность: Массивы содержат элементы одного типа данных.
- Быстрый доступ к элементам: Благодаря прямому доступу по индексу, получение элемента из массива имеет постоянную сложность O(1).

Динамические массивы - это расширяемая версия обычных массивов, которая позволяет автоматически увеличивать размер массива при необходимости. Они представляют собой более гибкую альтернативу фиксированным массивам.

Специфика работы динамических массивов:
- Динамическое изменение размера: Динамические массивы могут изменять свой размер по мере необходимости, что позволяет добавлять и удалять элементы.
- Резервирование памяти: При расширении массива дополнительная память резервируется для хранения большего количества элементов. Это может привести к временной сложности O(n), где n - текущий размер массива.
- Амортизированная сложность: В среднем, операции расширения массива выполняются за константное время O(1), поскольку резервирование памяти происходит редко.

В Python для работы с массивами доступен модуль `array`. Он предоставляет эффективное хранение и манипулирование массивами однородных элементов. Модуль `array` позволяет создавать массивы различных типов данных, таких как целые числа, числа с плавающей запятой и другие. Он предоставляет быстрый доступ к элементам и поддерживает различные операции над массивами, такие как срезы, добавление и удаление элементов.

Использование массивов из модуля `array` может быть полезным, когда требуется эффективное хранение и манипулирование большими объемами данных одного типа.


12.Абстрактная структура данных стек и очередь: базовые и расширенные операции, их сложность. 
Стек и очередь - это абстрактные структуры данных, которые организуют элементы в определенном порядке и поддерживают определенные операции.

Стек:
- Базовые операции:
  - Push: добавляет элемент на верхушку стека.
  - Pop: удаляет и возвращает элемент с верхушки стека.
  - Peek (или Top): возвращает элемент с верхушки стека без его удаления.
  - IsEmpty: проверяет, является ли стек пустым.
- Расширенные операции:
  - Size: возвращает количество элементов в стеке.
- Сложность операций:
  - Push, Pop, Peek и IsEmpty выполняются за константное время O(1).
  - Size операция также выполняется за константное время O(1).

Очередь:
- Базовые операции:
  - Enqueue: добавляет элемент в конец очереди.
  - Dequeue: удаляет и возвращает элемент из начала очереди.
  - IsEmpty: проверяет, является ли очередь пустой.
- Расширенные операции:
  - Size: возвращает количество элементов в очереди.
- Сложность операций:
  - Enqueue, Dequeue и IsEmpty выполняются за константное время O(1).
  - Size операция также выполняется за константное время O(1).

Базовые операции стека и очереди выполняются за константное время, независимо от размера структуры данных. Это делает их очень эффективными при работе с ограниченным набором операций.

Расширенные операции, такие как получение размера стека или очереди, также выполняются за константное время, поскольку дополнительная информация об элементах обычно хранится отдельно.

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



13.Специфика реализации и скорости основных операций в очереди на базе массива и связанного списка.
Очередь на базе массива и очередь на базе связанного списка - это две различные реализации абстрактной структуры данных "очередь", каждая со своими особенностями и скоростью операций.

Очередь на базе массива:
- Реализация: В очереди на базе массива элементы хранятся внутри фиксированного массива, и для добавления и удаления элементов используются операции сдвига.
- Основные операции:
  - Enqueue: Добавление элемента в конец очереди требует проверки свободного места в массиве. В случае, если массив полон, может потребоваться расширение массива и копирование элементов в новый массив с увеличенным размером.
  - Dequeue: Удаление элемента из начала очереди также требует сдвига оставшихся элементов в массиве, чтобы заполнить пустое место.
- Скорость операций:
  - Enqueue и Dequeue выполняются в среднем за константное время O(1), но при необходимости расширения массива время выполнения может быть O(n), где n - текущий размер массива.
  - Размер массива ограничен, поэтому добавление большого количества элементов может потребовать множественных операций расширения и копирования.

Очередь на базе связанного списка:
- Реализация: В очереди на базе связанного списка элементы хранятся в виде узлов, каждый из которых содержит ссылку на следующий элемент.
- Основные операции:
  - Enqueue: Добавление элемента в конец очереди требует создания нового узла и обновления ссылок.
  - Dequeue: Удаление элемента из начала очереди также требует обновления ссылок на следующий элемент.
- Скорость операций:
  - Enqueue и Dequeue выполняются за константное время O(1), поскольку добавление и удаление элементов в связанном списке требует только обновления ссылок.
  - Размер связанного списка не ограничен, поэтому он может расти динамически и не требует дополнительных операций расширения и копирования.

В целом, скорость операций в очереди на базе связанного списка обычно более предсказуемая и эффективная, особенно при добавлении и удалении элементов. Однако, очередь на базе массива может быть удобной, если известно, что размер очереди ограничен и заранее известен. Выбор конкретной реализации очереди зависит от контекста использования и требований к производительности.



14.Связанные списки: однонаправленные и двунаправленные – принцип реализации. Сравнение скорости выполнения основных операций в связанных списках и в динамическом массиве.
Связанный список - это структура данных, состоящая из узлов, каждый из которых содержит значение и ссылку на следующий (или предыдущий в случае двунаправленного связанного списка) узел.

Однонаправленный связанный список:
- Реализация: Каждый узел в однонаправленном связанном списке содержит значение и ссылку только на следующий узел.
- Основные операции:
  - Вставка элемента в начало списка: Создание нового узла и обновление ссылок требуют константного времени O(1).
  - Вставка элемента в конец списка: При использовании указателя на последний узел, вставка нового узла требует времени O(1).
  - Удаление элемента: Обновление ссылок узлов требует константного времени O(1).
- Скорость операций:
  - Вставка и удаление элементов в начало и конец списка выполняются за константное время O(1).
  - Поиск элемента в списке может потребовать времени O(n), где n - размер списка.

Двунаправленный связанный список:
- Реализация: Каждый узел в двунаправленном связанном списке содержит значение и ссылки на предыдущий и следующий узлы.
- Основные операции:
  - Вставка элемента в начало или конец списка: Создание нового узла и обновление ссылок требуют константного времени O(1).
  - Удаление элемента: Обновление ссылок узлов требует константного времени O(1).
- Скорость операций:
  - Вставка и удаление элементов в начало и конец списка выполняются за константное время O(1).
  - Поиск элемента в списке может потребовать времени O(n), где n - размер списка.

Сравнение с динамическим массивом:
- Вставка и удаление элементов в середину динамического массива требуют сдвига остальных элементов, что может занимать время O(n).
- Динамический массив имеет преимущество при доступе к элементу по индексу за константное время O(1).
- Связанный список позволяет эффективно вставлять и удалять элементы в начале и конце списка.

В целом, скорость выполнения операций в связанных списках зависит от типа операции и расположения элементов. Они обеспечивают эффективные операции вставки и удаления в начале и конце списка, но могут быть медленнее при доступе к элементу по индексу. Динамический массив пред

оставляет более эффективный доступ к элементам по индексу, но может быть менее эффективным для вставки и удаления элементов в середине. Выбор между ними зависит от конкретных потребностей и характеристик задачи.


Сортировки

15.Алгоритм обменной сортировки, сложность сортировки и возможности по ее улучшению.
Алгоритм обменной сортировки (также известный как сортировка пузырьком) - это простой алгоритм сортировки, который работает путем многократного прохода по списку и сравнения соседних элементов. Если два соседних элемента находятся в неправильном порядке, они меняются местами. Этот процесс повторяется до тех пор, пока все элементы не будут отсортированы.

Сложность сортировки пузырьком:
- В худшем и среднем случаях время выполнения алгоритма обменной сортировки составляет O(n^2), где n - количество элементов в списке.
- Лучший случай, когда список уже отсортирован, имеет время выполнения O(n), но это редкий случай.
- Сортировка пузырьком является неэффективной для больших массивов данных из-за квадратичной сложности.

Улучшение сортировки пузырьком:
- Один из способов улучшить сортировку пузырьком - это добавить флаг, указывающий, были ли произведены обмены на текущем проходе по списку. Если на каком-то проходе не было произведено ни одного обмена, это означает, что список уже отсортирован, и алгоритм может быть остановлен.
- Другим улучшением является сокращение области сравнения на каждом проходе. После каждого прохода максимальный элемент "всплывает" на правильную позицию в конце списка, поэтому на следующем проходе его можно исключить из области сравнения.
- Более эффективные алгоритмы сортировки, такие как быстрая сортировка или сортировка слиянием, могут быть предпочтительнее для сортировки больших массивов данных, так как имеют лучшую сложность и производительность.

Обменная сортировка (сортировка пузырьком) является простым и интуитивно понятным алгоритмом, но из-за своей квадратичной сложности она может быть неэффективной для больших объемов данных.



16.Алгоритм сортировки выбором, сложность сортировки и возможности по ее улучшению.
Алгоритм сортировки выбором (Selection Sort) - это простой алгоритм сортировки, который работает путем многократного нахождения минимального (или максимального) элемента в неотсортированной части списка и перемещения его в начало (или конец) отсортированной части. Этот процесс повторяется до тех пор, пока весь список не будет отсортирован.

Сложность сортировки выбором:
- Наихудший, наилучший и средний случаи имеют одинаковую сложность.
- Время выполнения алгоритма сортировки выбором составляет O(n^2), где n - количество элементов в списке.
- Независимо от порядка элементов в списке, алгоритм всегда выполняет одинаковое количество операций сравнения и обмена.

Улучшение сортировки выбором:
- В основной реализации сортировки выбором каждый раз находится минимальный (или максимальный) элемент и меняется местами с элементом на первой (или последней) позиции отсортированной части списка. Это требует O(n) операций обмена на каждом проходе. Однако можно улучшить алгоритм, чтобы он минимизировал количество операций обмена.
- Улучшенная версия алгоритма называется "Улучшенная сортировка выбором". Вместо непосредственного обмена элементов на каждом шаге, алгоритм сохраняет индекс минимального (или максимального) элемента и производит обмены только после завершения прохода по списку. Таким образом, улучшенная сортировка выбором требует только O(n) операций обмена на всем протяжении сортировки, что может улучшить производительность.

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



17.Алгоритм сортировки вставками, его сложность. Алгоритм быстрого поиска в отсортированном массиве. Сложность поиска в отсортированном и не отсортированном массиве.
Алгоритм сортировки вставками (Insertion Sort) - это простой алгоритм сортировки, который сравнивает каждый элемент со всеми предыдущими элементами в отсортированной части списка и вставляет его в правильную позицию. Этот процесс повторяется для каждого элемента, пока весь список не будет отсортирован.

Сложность сортировки вставками:
- Время выполнения алгоритма сортировки вставками составляет O(n^2), где n - количество элементов в списке.
- Лучший случай происходит, когда список уже отсортирован. В этом случае, алгоритм требует O(n) операций для проверки каждого элемента.
- Худший случай происходит, когда список отсортирован в обратном порядке. В этом случае, алгоритм требует O(n^2) операций, так как для каждого элемента нужно выполнить O(n) сравнений.

Алгоритм быстрого поиска в отсортированном массиве (Binary Search) - это эффективный алгоритм поиска значения в отсортированном массиве. Он работает путем сравнения искомого значения с элементом в середине массива и последующим сужением области поиска до половины размера предыдущей области. Процесс повторяется, пока искомое значение не будет найдено или не будет определено его отсутствие в массиве.

Сложность поиска в отсортированном массиве:
- Время выполнения алгоритма быстрого поиска в отсортированном массиве составляет O(log n), где n - количество элементов в массиве.
- Поиск в отсортированном массиве значительно быстрее, чем в неотсортированном массиве, так как поиск может быть выполнен за меньшее количество шагов.
- В худшем случае, алгоритм быстрого поиска может выполнить до log n сравнений для нахождения значения.

Сложность поиска в неотсортированном массиве:
- Время выполнения поиска в неотсортированном массиве составляет O(n), где n - количество элементов в массиве.
- Поскольку нет предварительной сортировки, каждый элемент должен быть проверен по очереди, чтобы найти искомое значение.

Сортировка вставками и быстрый поиск в отсортированном массиве - это два разных алгоритма, каждый из которых имеет свою сложность и специфику использования.



18.Алгоритм сортировки Шелла, сложность сортировки и возможности по ее улучшению.
Алгоритм сортировки Шелла (Shell Sort) - это усовершенствованный алгоритм сортировки вставками. Он использует идею "сортировки с большим шагом", чтобы предварительно упорядочить элементы списка до выполнения окончательной сортировки. Алгоритм сортировки Шелла последовательно уменьшает шаг (интервал) и выполняет сортировку вставками на каждом шаге до тех пор, пока шаг не станет равным 1.

Сложность сортировки Шелла:
- Время выполнения алгоритма сортировки Шелла зависит от выбранной последовательности шагов.
- В наихудшем случае сложность сортировки Шелла составляет примерно O(n^2), где n - количество элементов в списке.
- Однако, в среднем и на практике, сортировка Шелла имеет лучшую производительность по сравнению с другими квадратичными алгоритмами сортировки, такими как сортировка пузырьком и сортировка вставками.
- Сортировка Шелла имеет адаптивную природу и может эффективно сортировать списки, частично отсортированные или имеющие небольшое количество обратных элементов.

Улучшение сортировки Шелла:
- Выбор оптимальной последовательности шагов является ключевым для достижения хорошей производительности сортировки Шелла.
- Существует множество различных последовательностей шагов, которые могут быть использованы, например, последовательность Шелла, последовательность Пратта и др.
- Выбор оптимальной последовательности шагов может значительно повлиять на скорость сортировки.
- Другие улучшения могут включать использование более эффективных алгоритмов сортировки на последних шагах или комбинирование сортировки Шелла с другими алгоритмами сортировки для улучшения производительности.

Сортировка Шелла является одним из классических алгоритмов сортировки, и ее эффективность зависит от выбора последовательности шагов. Сортировка Шелла может быть хорошим выбором для сортировки небольших списков или списков, частично отсортированных. Однако для больших объемов данных и строгих требований к времени выполнения, могут быть предпочтительны другие алгоритмы сортировки с лучшей сложностью, такие как быстрая сортировка или сортировка слиянием.


19.Алгоритм быстрой сортировки, сложность сортировки и возможности по ее улучшению.
Алгоритм быстрой сортировки (QuickSort) - это рекурсивный алгоритм сортировки, который работает по принципу разделяй и властвуй. Он выбирает опорный элемент из списка и разделяет список на две подгруппы: одну с элементами, меньшими опорного, и другую с элементами, большими опорного. Затем алгоритм рекурсивно применяется к каждой подгруппе до тех пор, пока подгруппы не станут достаточно маленькими для сортировки.

Сложность сортировки QuickSort:
- В среднем случае алгоритм QuickSort имеет сложность O(n log n), где n - количество элементов в списке.
- В наихудшем случае, когда опорный элемент выбирается неудачно и деление списка происходит на две подгруппы неравного размера, сложность алгоритма может стать квадратичной - O(n^2).
- Однако, на практике, алгоритм QuickSort обычно выполняется эффективно и в среднем быстрее других алгоритмов сортировки сравнимой сложности, таких как сортировка слиянием.

Улучшение сортировки QuickSort:
- Выбор опорного элемента может существенно повлиять на производительность сортировки QuickSort. Хороший выбор опорного элемента, такого как медиана трех случайно выбранных элементов, может уменьшить вероятность плохого разделения списка.
- Другим способом улучшения алгоритма QuickSort является использование других алгоритмов сортировки, таких как вставки, для малых подгрупп, когда их размер становится достаточно маленьким. Это позволяет избежать лишних рекурсивных вызовов и улучшить производительность для небольших списков.
- Можно также применить оптимизации для работы с повторяющимися элементами или уже отсортированными списками, чтобы избежать лишних операций.

Быстрая сортировка - это один из наиболее эффективных алгоритмов сортировки на практике, особенно для больших объемов данных. Однако, при некоторых условиях, таких как плохой выбор опорного элемента или большое количество повторяющихся элементов, алгоритм может работать неэффективно. Правильный выбор опорного элемента и применение оп

тимизаций позволяют улучшить производительность алгоритма QuickSort и сделать его еще более эффективным.



20.Алгоритм сортировки слиянием, сложность сортировки.
Алгоритм сортировки слиянием (Merge Sort) - это рекурсивный алгоритм сортировки, основанный на принципе разделения и слияния. Он разделяет список на две равные (или примерно равные) части, рекурсивно сортирует каждую из них, а затем объединяет отсортированные подсписки в один отсортированный список.

Сложность сортировки слиянием:
- Временная сложность сортировки слиянием в худшем, среднем и лучшем случае составляет O(n log n), где n - количество элементов в списке.
- Сортировка слиянием обеспечивает стабильность сортировки, то есть порядок элементов с одинаковыми значениями сохраняется после сортировки.
- Однако, сортировка слиянием требует дополнительной памяти для создания временных массивов при объединении подсписков. Это может быть недостатком при работе с большими объемами данных или ограниченными ресурсами памяти.

Преимущества сортировки слиянием:
- Главным преимуществом сортировки слиянием является ее стабильность и гарантированная производительность. Вне зависимости от исходного порядка элементов, сортировка слиянием всегда выполняется за время O(n log n).
- Сортировка слиянием хорошо подходит для сортировки связанных списков или файлов, когда доступ к элементам происходит последовательно.

Сортировка слиянием является одним из классических алгоритмов сортировки. Она обладает хорошей производительностью и гарантирует стабильность сортировки. Однако, требуется дополнительная память для создания временных массивов при слиянии подсписков.



Деревья

21.Реализация двоичных деревьев в виде связанных объектов. Различные реализации рекурсивного обхода двоичных деревьев.
Разумеется, извините за предыдущее недоразумение. Вот описание концепции рекурсивного обхода двоичных деревьев без приведения кода.

Рекурсивный обход двоичного дерева - это метод обхода структуры данных, который использует принцип рекурсии. Он основан на обходе корневого узла, его левого поддерева и правого поддерева.

Существуют три основных типа обхода двоичных деревьев: прямой обход (pre-order), центрированный обход (in-order) и обратный обход (post-order).

- Прямой обход (pre-order): В прямом обходе мы сначала посещаем корневой узел, затем левое поддерево и, наконец, правое поддерево.

- Центрированный обход (in-order): В центрированном обходе мы сначала посещаем левое поддерево, затем корневой узел и, наконец, правое поддерево. При использовании центрированного обхода для двоичного дерева, содержащего числа, результат будет отсортированной последовательностью чисел.

- Обратный обход (post-order): В обратном обходе мы сначала посещаем левое поддерево, затем правое поддерево и, наконец, корневой узел.

Рекурсивный обход позволяет легко реализовывать алгоритмы обхода двоичных деревьев. Каждая рекурсивная функция вызывает саму себя для обработки левого и правого поддерева.

Обходы двоичных деревьев широко используются в алгоритмах обработки и анализа данных, таких как поиск, вставка, удаление элементов, а также для вывода содержимого дерева в заданном порядке.

Важно отметить, что рекурсивный обход может потребовать дополнительной памяти из-за вызовов функций, поэтому для очень глубоких деревьев может потребоваться использование итеративных алгоритмов обхода.



22.Двоичное дерево поиска – принципы реализации и логика реализации основных операций.
Двоичное дерево поиска (Binary Search Tree, BST) - это особый вид двоичного дерева, где значения в каждом узле удовлетворяют следующему условию: значение в любом узле больше всех значений в его левом поддереве и меньше всех значений в его правом поддереве.

Основные принципы реализации двоичного дерева поиска:

1. Каждый узел дерева содержит значение и ссылки на левого и правого потомков.
2. Значение в левом поддереве должно быть меньше значения корневого узла, а значение в правом поддереве - больше.
3. Для пустого поддерева ссылки на левого и правого потомков будут равны None.

Основные операции в двоичном дереве поиска:

1. Вставка (Insertion): Эта операция используется для добавления нового значения в дерево. Новое значение сравнивается с каждым узлом, начиная с корневого. Если значение меньше текущего узла, оно вставляется в левое поддерево, иначе - в правое. Процесс повторяется до достижения пустого места, где новый узел будет вставлен.
   
2. Поиск (Search): Поиск значения в дереве осуществляется путем сравнения искомого значения с каждым узлом, начиная с корневого. Если значение равно текущему узлу, поиск завершается. Если искомое значение меньше текущего узла, поиск продолжается в левом поддереве. Если оно больше, поиск продолжается в правом поддереве. Если искомого значения нет в дереве, поиск достигнет пустого узла.

3. Удаление (Deletion): Удаление значения из дерева требует выполнения нескольких шагов. Если удаляемый узел является листовым узлом (не имеет потомков), он просто удаляется. Если у удаляемого узла есть только один потомок, этот потомок заменяет удаляемый узел. Если у удаляемого узла есть оба потомка, нужно найти наименьший узел в правом поддереве (или наибольший в левом поддереве) и заменить удаляемый узел этим наименьшим (наибольшим) узлом. Затем необходимо удалить этот наименьший (наибольший) узел из правого (левого) поддерева.

4. Обход (Traversal): Обход дерева

 используется для перечисления всех узлов в определенном порядке. Существуют различные методы обхода: прямой (pre-order), центрированный (in-order) и обратный (post-order). Каждый метод определяет порядок посещения узлов.

Операции в двоичном дереве поиска обычно имеют временную сложность O(log n), но в худшем случае (если дерево несбалансировано) она может достигать O(n). Для обеспечения эффективности и улучшения временной сложности существуют различные методы балансировки деревьев, такие как АВЛ-деревья и красно-черные деревья.



23.Двоичная куча – принципы реализации и логика реализации основных операций.
Двоичная куча (Binary Heap) - это структура данных, представляющая собой полное двоичное дерево, в котором значение каждого узла больше (или меньше) значений его потомков. В зависимости от порядка упорядоченности значений, двоичная куча может быть максимальной (max heap) или минимальной (min heap).

Принципы реализации двоичной кучи:

1. Представление в виде массива: Двоичная куча может быть эффективно представлена в виде одномерного массива, где индексы элементов соответствуют их положению в дереве. Используя формулы, можно определить индексы родительских и потомков узлов.

2. Свойство кучи: Каждый узел кучи должен удовлетворять свойству кучи, т.е. значение в родительском узле должно быть больше (или меньше) значений его потомков в случае максимальной (или минимальной) кучи соответственно.

Основные операции в двоичной куче:

1. Вставка (Insertion): При вставке нового элемента в кучу, он сначала добавляется в конец массива. Затем происходит перестроение кучи, чтобы восстановить свойство кучи. Это достигается путем сравнения нового элемента с его родительским узлом и, при необходимости, меняя их местами до тех пор, пока свойство кучи не будет выполнено.

2. Извлечение (Extraction): При извлечении элемента из кучи, обычно извлекается корневой элемент (наибольший или наименьший элемент в зависимости от типа кучи). Затем последний элемент массива перемещается на место корневого элемента. Происходит перестроение кучи, чтобы восстановить свойство кучи. Это достигается путем сравнения нового корневого элемента с его потомками и, при необходимости, меняя их местами до тех пор, пока свойство кучи не будет выполнено.

Основное преимущество двоичной кучи заключается в том, что операции вставки и извлечения имеют временную сложность O(log n), где n - количество элементов в куче. Это делает ее эффективной для решения определенных задач, таких как поиск k-го наименьшего/наибольшего элемента или реализация приоритетной очереди.


Хэш-таблицы
24.Абстрактный тип данных - ассоциативный массив и принцип его реализации на основе хэш-таблиц и хэш-функций.
Абстрактный тип данных (АТД) "ассоциативный массив" представляет собой коллекцию пар "ключ-значение", где каждый ключ уникален и связан с определенным значением. Он также известен как словарь, хеш-таблица или хеш-карта.

Принцип реализации ассоциативного массива на основе хэш-таблицы и хэш-функций включает следующие шаги:

1. Хэш-функция: Определите хэш-функцию, которая принимает ключ и преобразует его в числовое значение, называемое хэш-кодом. Хорошая хэш-функция должна равномерно распределять ключи по всему диапазону хэш-кодов.

2. Хэш-таблица: Создайте хэш-таблицу, которая представляет собой массив фиксированного размера, обычно большего, чем количество ожидаемых элементов. Каждый элемент хэш-таблицы называется ячейкой или бакетом.

3. Хэширование ключей: При добавлении пары "ключ-значение" вычислите хэш-код для ключа с помощью хэш-функции. Затем используйте хэш-код для определения индекса ячейки в хэш-таблице. Если два ключа имеют один и тот же хэш-код, это называется коллизией.

4. Разрешение коллизий: Если происходит коллизия (два ключа имеют одинаковый хэш-код), то используется метод разрешения коллизий для размещения пары "ключ-значение" в ячейке. Некоторые из распространенных методов включают метод цепочек (используя связанные списки для хранения коллизий) и метод открытой адресации (используя другие ячейки в хэш-таблице для поиска свободной ячейки).

5. Доступ и изменение значений: Для доступа к значению по ключу, вычислите хэш-код ключа, найдите соответствующую ячейку в хэш-таблице и верните значение, связанное с ключом. При изменении значения также следует применять тот же процесс.

Преимущество ассоциативного массива на основе хэш-таблицы состоит в том, что время выполнения операций (вставка, поиск, удаление) обычно является константным (O(1)), если хоро

шо распределены хэш-коды и размер хэш-таблицы достаточно большой. Однако, в случае плохо спроектированной хэш-функции или большого количества коллизий, производительность может снижаться до O(n), где n - количество элементов в ассоциативном массиве.


25.Общая схема построения хэш-функции и возможная роль в этой схеме хэш-функции multiply-add-and-divide. Принцип работы хэш-функции multiply-add-and-divide.
Общая схема построения хэш-функции включает в себя несколько шагов:

1. Инициализация: Начните с инициализации хэш-значения определенным стартовым значением.

2. Итерация по ключу: Разбейте ключ на более мелкие части и обработайте их по одной. Обычно используются операции, такие как умножение, сложение и деление, чтобы комбинировать значения частей ключа и текущего хэш-значения.

3. Финализация: После итерации по всем частям ключа, выполните финальные операции над хэш-значением, чтобы получить окончательный результат.

Хэш-функция "multiply-add-and-divide" (умножение, сложение и деление) является одной из возможных хэш-функций, которая может использоваться в этой схеме. Принцип работы этой хэш-функции заключается в следующих шагах:

1. Умножение: Умножьте текущее хэш-значение на определенное число (обычно простое число) и добавьте значение текущей части ключа.

2. Сложение: Добавьте к полученному результату значение текущей части ключа.

3. Деление: Поделите полученное значение на размер хэш-таблицы и возьмите остаток от деления. Это гарантирует, что хэш-значение будет попадать в диапазон индексов хэш-таблицы.

Этот процесс повторяется для каждой части ключа, а затем выполняется финализация, например, взятие остатка от деления на определенное число, чтобы получить окончательное хэш-значение.

Хэш-функция "multiply-add-and-divide" обладает хорошей равномерностью распределения хэш-значений и относительно низкой вероятностью коллизий при правильном выборе параметров (например, простых чисел для умножения и размера хэш-таблицы для деления). Однако, выбор правильных параметров является важным аспектом, чтобы обеспечить эффективность и качество хэш-функции.


26.Полиномиальная хэш-функция – принцип работы, специфика эффективной реализации и специфика применения хэш-функции.
Полиномиальная хэш-функция - это тип хэш-функции, который использует полиномиальную формулу для вычисления хэш-значения ключа. Принцип работы полиномиальной хэш-функции заключается в следующих шагах:

1. Инициализация: Начните с инициализации хэш-значения определенным стартовым значением.

2. Итерация по ключу: Разбейте ключ на отдельные символы или байты и обработайте их по одному. Каждый символ или байт рассматривается как коэффициент в полиномиальной формуле.

3. Полиномиальная формула: Каждый символ или байт умножается на некоторую степень основания и складывается с предыдущими значениями. Обычно используется простое число в качестве основания, чтобы обеспечить равномерное распределение хэш-значений.

4. Финализация: После обработки всех символов или байтов выполняется финализация, например, взятие остатка от деления на размер хэш-таблицы.

Специфика эффективной реализации полиномиальной хэш-функции включает выбор подходящего основания (обычно простого числа) и правильное подбор коэффициентов в полиномиальной формуле. Это позволяет достичь хорошей равномерности распределения хэш-значений и уменьшить вероятность коллизий.

Полиномиальные хэш-функции широко применяются для хэширования текстовых данных, таких как строки. Они обладают высокой производительностью и хорошей равномерностью распределения хэш-значений, что делает их подходящими для многих задач, включая построение хэш-таблиц, поиск и сравнение данных.


27.Различные методы разрешения коллизий в хэш-таблицах.
При использовании хэш-таблиц для хранения данных возникает возможность коллизий, то есть ситуации, когда два или более ключей сопоставляются с одним и тем же хэш-значением. Существуют различные методы разрешения коллизий в хэш-таблицах, вот некоторые из них:

1. Открытое адресное разрешение коллизий: При использовании этого метода, если возникает коллизия, то новый элемент помещается в другую свободную ячейку хэш-таблицы. Методы открытого адресного разрешения включают линейное пробирование, квадратичное пробирование и двойное хэширование.

2. Цепочки: При использовании метода цепочек каждая ячейка хэш-таблицы содержит связанный список элементов. При возникновении коллизии новый элемент добавляется в соответствующий связанный список.

3. Коэффициенты перезаписи: Этот метод использует дополнительные коэффициенты, которые рассчитываются на основе хэш-значения ключа. При возникновении коллизии новый элемент помещается в ячейку с наименьшим коэффициентом перезаписи.

4. Идеальное хэширование: В случае, когда множество ключей известно заранее, можно использовать идеальное хэширование, при котором каждый ключ сопоставляется с уникальным хэш-значением. Это обеспечивает отсутствие коллизий, но требует предварительного анализа множества ключей.

Выбор метода разрешения коллизий зависит от конкретных требований и характеристик приложения. Каждый метод имеет свои преимущества и недостатки в плане производительности, использования памяти и устойчивости к коллизиям.

        '''
        print(text)

    def pract90(n):
        sklad = {
            1:'''
class Book:
    def __init__(self, название, автор, издательство, год_издания):
        self.название = название
        self.автор = автор
        self.издательство = издательство
        self.год_издания = год_издания
        self.жанр = None  # По умолчанию жанр не задан

    def вывести_информацию(self):
        """Метод для вывода информации о книге на экран"""
        print("Название книги:", self.название)
        print("Автор:", self.автор)
        print("Издательство:", self.издательство)
        print("Год издания:", self.год_издания)
        if self.жанр:
            print("Жанр книги:", self.жанр)
        else:
            print("Жанр книги не указан")

    def является_новой(self):
        """Метод для проверки, является ли книга новой"""
        текущий_год = 2023  # Замените текущий год на актуальный
        return текущий_год - self.год_издания <= 5

    @property
    def жанр(self):
        """Свойство для получения жанра книги"""
        return self._жанр

    @жанр.setter
    def жанр(self, значение):
        """Свойство для установки жанра книги"""
        self._жанр = значение

# Пример использования класса Book
книга = Book("Преступление и наказание", "Федор Достоевский", "Эксмо", 1866)
книга.вывести_информацию()
print("Книга новая:", книга.является_новой())
книга.жанр = "Роман"
print("Жанр книги:", книга.жанр)
''',
2:'''
class Car:
    def __init__(self, brand, model, year, mileage):
        self.brand = brand
        self.model = model
        self.year = year
        self.mileage = mileage
        self.fuel_type = None

    def display_info(self):
        print(f"Марка: {self.brand}")
        print(f"Модель: {self.model}")
        print(f"Год выпуска: {self.year}")
        print(f"Пробег: {self.mileage} км")

    def needs_servicing(self):
        if self.mileage > 10000:
            return True
        else:
            return False

    @property
    def fuel_type(self):
        return self._fuel_type

    @fuel_type.setter
    def fuel_type(self, value):
        self._fuel_type = value

# Создаем объект класса Car
my_car = Car("Toyota", "Camry", 2019, 15000)

# Выводим информацию о машине
my_car.display_info()

# Проверяем, нужно ли произвести техническое обслуживание
if my_car.needs_servicing():
    print("Необходимо произвести техническое обслуживание")
else:
    print("Техническое обслуживание не требуется")

# Устанавливаем тип топлива
my_car.fuel_type = "бензин"

# Получаем тип топлива
print("Тип топлива:", my_car.fuel_type)

''',
3:'''
class Employee:
    def __init__(self, last_name, first_name, position, salary):
        self.last_name = last_name
        self.first_name = first_name
        self.position = position
        self.salary = salary
        self.experience = None  # By default, experience is not specified

    def display_information(self):
        """Method to display employee information"""
        print("Last Name:", self.last_name)
        print("First Name:", self.first_name)
        print("Position:", self.position)
        print("Salary:", self.salary, "USD")
        if self.experience:
            print("Experience:", self.experience, "years")
        else:
            print("Experience is not specified")

    def is_high_salary(self):
        """Method to check if the salary is high"""
        return self.salary > 100000

    @property
    def experience(self):
        """Property to get the experience"""
        return self._experience

    @experience.setter
    def experience(self, value):
        """Property to set the experience"""
        self._experience = value

# Example usage of the Employee class
employee = Employee("Smith", "John", "Manager", 120000)
employee.display_information()
print("Is the salary high?", employee.is_high_salary())
employee.experience = 5
print("Experience:", employee.experience, "years")

''',
4:'''
class Product:
    def __init__(self, название, цена, количество):
        self.название = название
        self.цена = цена
        self.количество = количество
        self.категория = None  # По умолчанию категория не задана

    def вывести_информацию(self):
        """Метод для вывода информации о товаре на экран"""
        print("Название товара:", self.название)
        print("Цена товара:", self.цена)
        print("Количество товара:", self.количество)
        if self.категория:
            print("Категория товара:", self.категория)
        else:
            print("Категория товара не указана")

    def есть_в_наличии(self):
        """Метод для проверки наличия товара"""
        return self.количество > 0

    @property
    def категория(self):
        """Свойство для получения категории товара"""
        return self._категория

    @категория.setter
    def категория(self, значение):
        """Свойство для установки категории товара"""
        self._категория = значение

# Пример использования класса Product
товар = Product("Мобильный телефон", 10000, 5)
товар.вывести_информацию()
print("Товар в наличии:", товар.есть_в_наличии())
товар.категория = "Электроника"
print("Категория товара:", товар.категория)

''',
5:'''
import math

class Circle:
    def __init__(self, radius):
        self.radius = radius
        self.color = None  # By default, color is not specified

    def display_information(self):
        """Method to display information about the circle"""
        print("Circle Radius:", self.radius)
        if self.color:
            print("Circle Color:", self.color)
        else:
            print("Color is not specified")

    def calculate_circumference(self):
        """Method to calculate the circumference of the circle"""
        return 2 * math.pi * self.radius

    def calculate_area(self):
        """Method to calculate the area of the circle"""
        return math.pi * self.radius ** 2

    @property
    def color(self):
        """Property to get the color of the circle"""
        return self._color

    @color.setter
    def color(self, value):
        """Property to set the color of the circle"""
        self._color = value

# Example usage of the Circle class
circle = Circle(5)
circle.display_information()
print("Circumference:", circle.calculate_circumference())
print("Area:", circle.calculate_area())
circle.color = "Red"
print("Circle Color:", circle.color)
''',
6:'''
class Car:
    def __init__(self, brand, model, year):
        self.brand = brand
        self.model = model
        self.year = year

    def display_information(self):
        """Метод для вывода информации об автомобиле на экран"""
        print("Марка:", self.brand)
        print("Модель:", self.model)
        print("Год выпуска:", self.year)

    def is_suitable(self, conditions):
        """Метод для определения, подходит ли автомобиль для заданных условий"""
        return all(getattr(self, attr) == value for attr, value in conditions.items())


class PassengerCar(Car):
    def __init__(self, brand, model, year, body_type):
        super().__init__(brand, model, year)
        self.body_type = body_type

    def display_information(self):
        super().display_information()
        print("Тип кузова:", self.body_type)


class Truck(Car):
    def __init__(self, brand, model, year, carrying_capacity):
        super().__init__(brand, model, year)
        self.carrying_capacity = carrying_capacity

    def display_information(self):
        super().display_information()
        print("Грузоподъемность:", self.carrying_capacity)


class Bus(Car):
    def __init__(self, brand, model, year, seat_count):
        super().__init__(brand, model, year)
        self.seat_count = seat_count

    def display_information(self):
        super().display_information()
        print("Количество мест:", self.seat_count)


# Пример использования классов и поиска автомобилей
cars = [
    PassengerCar("BMW", "X5", 2019, "SUV"),
    PassengerCar("Toyota", "Camry", 2020, "Sedan"),
    Truck("Volvo", "FH16", 2018, 20000),
    Bus("Mercedes-Benz", "Tourismo", 2017, 50),
]

# Вывод полной информации об автомобилях
for car in cars:
    car.display_information()
    print()

# Поиск автомобилей с заданной маркой
search_brand = "BMW"
found_cars = [car for car in cars if car.brand == search_brand]
print(f"Найдены автомобили марки {search_brand}:")
for car in found_cars:
    car.display_information()
    print()

# Поиск автомобилей с заданным годом выпуска
search_year = 2019
found_cars = [car for car in cars if car.year == search_year]
print(f"Найдены автомобили года выпуска {search_year}:")
for car in found_cars:
    car.display_information()
    print()

''',
7:'''
class Student:
    def __init__(self, name, surname, age):
        self.name = name
        self.surname = surname
        self.age = age

    def display_information(self):
        """Метод для вывода информации о студенте на экран"""
        print("Имя:", self.name)
        print("Фамилия:", self.surname)
        print("Возраст:", self.age)

    def is_suitable(self, conditions):
        """Метод для определения, подходит ли студент для заданных условий"""
        return all(getattr(self, attr) == value for attr, value in conditions.items())


class Bachelor(Student):
    def __init__(self, name, surname, age, course):
        super().__init__(name, surname, age)
        self.course = course

    def display_information(self):
        super().display_information()
        print("Курс:", self.course)


class Master(Student):
    def __init__(self, name, surname, age, specialization):
        super().__init__(name, surname, age)
        self.specialization = specialization

    def display_information(self):
        super().display_information()
        print("Специализация:", self.specialization)


class PhDStudent(Student):
    def __init__(self, name, surname, age, dissertation_topic):
        super().__init__(name, surname, age)
        self.dissertation_topic = dissertation_topic

    def display_information(self):
        super().display_information()
        print("Тема диссертации:", self.dissertation_topic)


# Пример использования классов и поиска студентов
students = [
    Bachelor("Иван", "Иванов", 20, 2),
    Bachelor("Петр", "Петров", 21, 3),
    Master("Мария", "Сидорова", 25, "Информатика"),
    Master("Елена", "Козлова", 24, "Экономика"),
    PhDStudent("Алексей", "Смирнов", 30, "Искусственный интеллект"),
]

# Вывод полной информации о студентах
for student in students:
    student.display_information()
    print()

# Поиск студентов с заданным именем
search_name = "Мария"
found_students = [student for student in students if student.name == search_name]
print(f"Найдены студенты с именем {search_name}:")
for student in found_students:
    student.display_information()
    print()

# Поиск студентов с заданным курсом
search_course = 2
found_students = [student for student in students if isinstance(student, Bachelor) and student.course == search_course]
print(f"Найдены студенты на {search_course} курсе:")
for student in found_students:
    student.display_information()
    print()

''',
8:'''
class Restaurant:
    def __init__(self, name, address, cuisine_type, rating):
        self.name = name
        self.address = address
        self.cuisine_type = cuisine_type
        self.rating = rating

    def display_information(self):
        """Метод для вывода информации о ресторане на экран"""
        print("Название:", self.name)
        print("Адрес:", self.address)
        print("Тип кухни:", self.cuisine_type)
        print("Рейтинг:", self.rating)

    def is_suitable(self, conditions):
        """Метод для определения, подходит ли ресторан для заданных условий"""
        return all(getattr(self, attr) == value for attr, value in conditions.items())


class ItalianRestaurant(Restaurant):
    def __init__(self, name, address, cuisine_type, rating):
        super().__init__(name, address, cuisine_type, rating)


class JapaneseRestaurant(Restaurant):
    def __init__(self, name, address, cuisine_type, rating):
        super().__init__(name, address, cuisine_type, rating)


class FrenchRestaurant(Restaurant):
    def __init__(self, name, address, cuisine_type, rating):
        super().__init__(name, address, cuisine_type, rating)


# Пример использования классов и поиска ресторанов
restaurants = [
    ItalianRestaurant("Итальянский ресторан", "ул. Итальянская, 1", "Итальянская", 4.5),
    JapaneseRestaurant("Японский ресторан", "ул. Японская, 2", "Японская", 4.2),
    FrenchRestaurant("Французский ресторан", "ул. Французская, 3", "Французская", 4.8),
]

# Вывод полной информации о ресторанах
for restaurant in restaurants:
    restaurant.display_information()
    print()

# Поиск ресторанов с заданным типом кухни
search_cuisine_type = "Итальянская"
found_restaurants = [restaurant for restaurant in restaurants if restaurant.cuisine_type == search_cuisine_type]
print(f"Найдены рестораны с типом кухни {search_cuisine_type}:")
for restaurant in found_restaurants:
    restaurant.display_information()
    print()

# Поиск ресторанов с заданным рейтингом
search_rating = 4.5
found_restaurants = [restaurant for restaurant in restaurants if restaurant.rating >= search_rating]
print(f"Найдены рестораны с рейтингом {search_rating} и выше:")
for restaurant in found_restaurants:
    restaurant.display_information()
    print()

''',
9:'''
class Sportsman:
    def __init__(self, name, surname, age):
        self.name = name
        self.surname = surname
        self.age = age

    def display_information(self):
        """Метод для вывода информации о спортсмене на экран"""
        print("Имя:", self.name)
        print("Фамилия:", self.surname)
        print("Возраст:", self.age)

    def is_suitable(self, conditions):
        """Метод для определения, подходит ли спортсмен для заданных условий"""
        return all(getattr(self, attr) == value for attr, value in conditions.items())


class TrackAthlete(Sportsman):
    def __init__(self, name, surname, age, discipline):
        super().__init__(name, surname, age)
        self.discipline = discipline

    def display_information(self):
        super().display_information()
        print("Дисциплина:", self.discipline)


class Swimmer(Sportsman):
    def __init__(self, name, surname, age, distance):
        super().__init__(name, surname, age)
        self.distance = distance

    def display_information(self):
        super().display_information()
        print("Дистанция:", self.distance)


class Boxer(Sportsman):
    def __init__(self, name, surname, age, weight_category):
        super().__init__(name, surname, age)
        self.weight_category = weight_category

    def display_information(self):
        super().display_information()
        print("Весовая категория:", self.weight_category)


# Пример использования классов и поиска спортсменов
sportsmen = [
    TrackAthlete("Иван", "Иванов", 25, "100 метров"),
    TrackAthlete("Петр", "Петров", 30, "400 метров"),
    Swimmer("Мария", "Сидорова", 28, "200 метров"),
    Swimmer("Елена", "Козлова", 24, "500 метров"),
    Boxer("Алексей", "Смирнов", 32, "легчайшая"),
]

# Вывод полной информации о спортсменах
for sportsman in sportsmen:
    sportsman.display_information()
    print()

# Поиск спортсменов с заданным именем
search_name = "Мария"
found_sportsmen = [sportsman for sportsman in sportsmen if sportsman.name == search_name]
print(f"Найдены спортсмены с именем {search_name}:")
for sportsman in found_sportsmen:
    sportsman.display_information()
    print()

# Поиск спортсменов с заданным возрастом
search_age = 25
found_sportsmen = [sportsman for sportsman in sportsmen if sportsman.age == search_age]
print(f"Найдены спортсмены возрастом {search_age} лет:")
for sportsman in found_sportsmen:
    sportsman.display_information()
    print()

''',
10:'''
class Product:
    def __init__(self, name, manufacturer, price):
        self.name = name
        self.manufacturer = manufacturer
        self.price = price

    def display_information(self):
        """Метод для вывода информации о товаре на экран"""
        print("Название:", self.name)
        print("Производитель:", self.manufacturer)
        print("Цена:", self.price)

    def is_suitable(self, conditions):
        """Метод для определения, подходит ли товар для заданных условий"""
        return all(getattr(self, attr) == value for attr, value in conditions.items())


class Electronics(Product):
    def __init__(self, name, manufacturer, price, device_type):
        super().__init__(name, manufacturer, price)
        self.device_type = device_type

    def display_information(self):
        super().display_information()
        print("Тип устройства:", self.device_type)


class Clothing(Product):
    def __init__(self, name, manufacturer, price, size):
        super().__init__(name, manufacturer, price)
        self.size = size

    def display_information(self):
        super().display_information()
        print("Размер:", self.size)


class Food(Product):
    def __init__(self, name, manufacturer, price, expiration_date):
        super().__init__(name, manufacturer, price)
        self.expiration_date = expiration_date

    def display_information(self):
        super().display_information()
        print("Срок годности:", self.expiration_date)


# Пример использования классов и поиска товаров
products = [
    Electronics("Смартфон", "Apple", 50000, "Смартфон"),
    Electronics("Телевизор", "Samsung", 70000, "Телевизор"),
    Clothing("Футболка", "Nike", 2000, "M"),
    Clothing("Джинсы", "Levi's", 5000, "L"),
    Food("Молоко", "Простоквашино", 80, "2023-06-30"),
    Food("Хлеб", "Бородинский", 50, "2023-06-20"),
]

# Вывод полной информации о товарах
for product in products:
    product.display_information()
    print()

# Поиск товаров с заданным названием
search_name = "Молоко"
found_products = [product for product in products if product.name == search_name]
print(f"Найдены товары с названием '{search_name}':")
for product in found_products:
    product.display_information()
    print()

# Поиск товаров с заданной ценой
search_price = 5000
found_products = [product for product in products if product.price == search_price]
print(f"Найдены товары с ценой {search_price}:")
for product in found_products:
    product.display_information()
    print()

''',
11:'''
class Product:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

    def __str__(self):
        return f"Товар: {self.name}, Цена: {self.price}, Количество: {self.quantity}"


class Store:
    def __init__(self, name):
        self.name = name
        self.products = []

    def add_product(self, product):
        """Метод для добавления товара в магазин"""
        self.products.append(product)

    def remove_product(self, product):
        """Метод для удаления товара из магазина"""
        self.products.remove(product)

    def total_cost(self):
        """Метод для вычисления общей стоимости товаров в магазине"""
        total = sum(product.price * product.quantity for product in self.products)
        return total

    def __len__(self):
        """Магический метод для определения количества товаров в магазине"""
        return len(self.products)


# Создание объектов класса Product
product1 = Product("Футболка", 1000, 5)
product2 = Product("Джинсы", 2000, 3)
product3 = Product("Кроссовки", 3000, 2)

# Создание объекта класса Store
store = Store("Мой магазин")

# Добавление товаров в магазин
store.add_product(product1)
store.add_product(product2)
store.add_product(product3)

# Вывод информации о товарах в магазине
for product in store.products:
    print(product)

# Удаление товара из магазина
store.remove_product(product2)

# Вывод информации о товарах в магазине после удаления
print("После удаления товара:")
for product in store.products:
    print(product)

# Вычисление общей стоимости товаров в магазине
total_cost = store.total_cost()
print(f"Общая стоимость товаров в магазине: {total_cost}")

# Определение количества товаров в магазине
product_count = len(store)
print(f"Количество товаров в магазине: {product_count}")

''',
12:'''
class Task:
    def __init__(self, title, description, status=False):
        self.title = title
        self.description = description
        self.status = status

    def complete_task(self):
        """Метод для изменения статуса задачи на выполненную"""
        self.status = True

    def __str__(self):
        """Магический метод для вывода информации о задаче"""
        status = "выполнена" if self.status else "не выполнена"
        return f"Задача '{self.title}': {self.description}, статус - {status}"


# Создание объекта класса Task
task = Task("Покупки", "Купить продукты для обеда")

# Вывод информации о задаче
print(task)

# Изменение статуса задачи на выполненную
task.complete_task()

# Вывод информации о задаче после изменения статуса
print(task)

''',
13:'''
class Account:
    def __init__(self, number, balance):
        self.number = number
        self.balance = balance

class Bank:
    def __init__(self, name):
        self.name = name
        self.accounts = []

    def add_account(self, account):
        """Метод для добавления счета в банк"""
        self.accounts.append(account)

    def remove_account(self, account):
        """Метод для удаления счета из банка"""
        self.accounts.remove(account)

    def total_balance(self):
        """Метод для вычисления общего баланса всех счетов в банке"""
        total = 0
        for account in self.accounts:
            total += account.balance
        return total

    def __len__(self):
        """Магический метод для определения количества счетов в банке"""
        return len(self.accounts)


# Создание объекта класса Bank
bank = Bank("My Bank")

# Создание счетов
account1 = Account("12345", 1000)
account2 = Account("67890", 500)

# Добавление счетов в банк
bank.add_account(account1)
bank.add_account(account2)

# Вывод количества счетов в банке
print(len(bank))  # Выведет: 2

# Удаление одного из счетов
bank.remove_account(account1)

# Вывод общего баланса всех счетов в банке
print(bank.total_balance())  # Выведет: 500 (баланс только у счета account2)

''',
14:'''
class Student:
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.grades = []

    def add_grade(self, grade):
        """Метод для добавления оценки"""
        self.grades.append(grade)

    def average_grade(self):
        """Метод для вычисления среднего балла"""
        if len(self.grades) == 0:
            return 0
        total = sum(self.grades)
        return total / len(self.grades)

    def __str__(self):
        """Магический метод для вывода информации о студенте"""
        return f"Студент {self.first_name} {self.last_name}, возраст - {self.age}, средний балл - {self.average_grade()}"

    def __len__(self):
        """Магический метод для определения количества оценок у студента"""
        return len(self.grades)

''',
15:'''
class Car:
    def __init__(self, brand, model, year, speed):
        self.brand = brand
        self.model = model
        self.year = year
        self.speed = speed

    def increase_speed(self, increment):
        """Метод для увеличения скорости автомобиля"""
        self.speed += increment

    def decrease_speed(self, decrement):
        """Метод для уменьшения скорости автомобиля"""
        self.speed -= decrement

    def __str__(self):
        """Магический метод для вывода информации о автомобиле"""
        return f"Автомобиль {self.brand} {self.model}, год выпуска - {self.year}, скорость - {self.speed}"

    def __eq__(self, other):
        """Магический метод для сравнения скорости двух автомобилей"""
        return self.speed == other.speed

''',
16:'''
class Fruit:
    def __init__(self, shape, color, taste):
        self.shape = shape
        self.color = color
        self.taste = taste

    def calculate_total_weight(self):
        """Метод для подсчета общего веса фрукта"""
        # Здесь вы можете добавить код для расчета общего веса фрукта
        pass

    def compare_weight(self, other_fruit):
        """Метод для сравнения общего веса текущего фрукта с другим фруктом"""
        # Здесь вы можете добавить код для сравнения общего веса фруктов
        pass

    def display_info(self):
        """Метод для вывода полной информации о фрукте"""
        print("Fruit Information:")
        print("------------------")
        print(f"Shape: {self.shape}")
        print(f"Color: {self.color}")
        print(f"Taste: {self.taste}")
        print("------------------")

''',
17:'''
class Calculator:
    def add(self, a, b):
        """Метод для выполнения сложения"""
        return a + b

    def subtract(self, a, b):
        """Метод для выполнения вычитания"""
        return a - b

    def calculate(self, expression):
        """Метод для вычисления арифметического выражения"""
        tokens = expression.split()
        result = int(tokens[0])  # Инициализируем результат первым операндом

        for i in range(1, len(tokens), 2):
            operator = tokens[i]
            operand = int(tokens[i + 1])

            if operator == "+":
                result = self.add(result, operand)
            elif operator == "-":
                result = self.subtract(result, operand)

        return result

''',
18:'''
import math

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def perimeter(self):
        return 2 * math.pi * self.radius

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def perimeter(self):
        return 2 * (self.width + self.height)

# Создание экземпляров классов
circle = Circle(5)
rectangle = Rectangle(4, 6)

# Вычисление периметров
circle_perimeter = circle.perimeter()
rectangle_perimeter = rectangle.perimeter()

# Вывод информации о периметрах с использованием полиморфизма
print("Периметр окружности:", circle_perimeter)
print("Периметр прямоугольника:", rectangle_perimeter)

''',
19:'''
class House:
    def __init__(self, floors, entrances, district, construction_deadline, workers):
        self.floors = floors
        self.entrances = entrances
        self.district = district
        self.construction_deadline = construction_deadline
        self.workers = workers

class Worker:
    def __init__(self, construction_company, qualification):
        self.construction_company = construction_company
        self.qualification = qualification

# Создание объектов класса Рабочие
worker1 = Worker("Строительная компания А", "Высокая")
worker2 = Worker("Строительная компания Б", "Средняя")
worker3 = Worker("Строительная компания В", "Высокая")

# Создание объектов класса Дома
house1 = House(5, 3, "Центральный район", "01.01.2022", [worker1, worker2])
house2 = House(10, 2, "Западный район", "01.05.2022", [worker2, worker3])
house3 = House(8, 4, "Восточный район", "01.03.2022", [worker1, worker3])

# Определение, в скольких постройках рабочий задействован в один год одновременно
def count_projects(worker, houses):
    count = 0
    for house in houses:
        if worker in house.workers:
            if house.construction_deadline[-4:] == construction_year:
                count += 1
    return count

construction_year = "2022"
houses = [house1, house2, house3]
worker = worker1

projects_count = count_projects(worker, houses)

print(f"Рабочий задействован в {projects_count} постройках в {construction_year} году.")

''',
20:'''
class StoreEmployee:
    def __init__(self, name, salary, experience):
        self.name = name
        self.salary = salary
        self.experience = experience

    def display_info(self):
        print(f"Имя: {self.name}")
        print(f"Зарплата: {self.salary}")
        print(f"Стаж: {self.experience}")


class Cashier(StoreEmployee):
    pass


class Merchandiser(StoreEmployee):
    pass


# Создание объектов классов
employee1 = Cashier("Анна", 30000, 2)
employee2 = Merchandiser("Иван", 40000, 4)

# Вывод информации о сотрудниках
employee1.display_info()
print()
employee2.display_info()

''',
21:'''
class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * (self.length + self.width)

class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)


# Создание объектов классов
rectangle = Rectangle(4, 6)
square = Square(5)

# Вычисление и вывод площади и периметра прямоугольника
print("Прямоугольник:")
print("Площадь:", rectangle.area())
print("Периметр:", rectangle.perimeter())

# Вычисление и вывод площади и периметра квадрата
print("\nКвадрат:")
print("Площадь:", square.area())
print("Периметр:", square.perimeter())

''',
22:'''
class Lift:
    def __init__(self, max_capacity):
        self.max_capacity = max_capacity
        self.current_capacity = 0

    def compare_capacity(self):
        if self.current_capacity > self.max_capacity:
            print("Лифт переполнен!")
        elif self.current_capacity < self.max_capacity:
            print("Грузоподъемность лифта не полностью использована.")
        else:
            print("Лифт загружен полностью.")


# Создание объекта класса "Лифт" с максимальной грузоподъемностью 500 кг
lift = Lift(500)

# Установка текущей грузоподъемности
lift.current_capacity = 400

# Сравнение текущей грузоподъемности с максимальной грузоподъемностью
lift.compare_capacity()

''',
23:'''
class Square:
    def __init__(self, x, y, side_length):
        self.__x = x  # Приватный атрибут - координата x левого верхнего угла
        self.__y = y  # Приватный атрибут - координата y левого верхнего угла
        self.__side_length = side_length  # Приватный атрибут - длина стороны квадрата

    def draw(self):
        for i in range(self.__side_length):
            for j in range(self.__side_length):
                print("*", end=" ")
            print()

# Создание объекта класса "Квадрат" с координатами (3, 2) и длиной стороны 5
square = Square(3, 2, 5)

# Рисование квадрата
square.draw()

''',
24:'''
class Book:
    def __init__(self, author, title, year, price):
        self.author = author
        self.title = title
        self.year = year
        self.price = price

    def __gt__(self, other):
        return self.price > other.price

    def __eq__(self, other):
        return self.price == other.price

# Создание объектов класса "Book"
book1 = Book("Автор 1", "Книга 1", 2000, 50)
book2 = Book("Автор 2", "Книга 2", 2010, 100)
book3 = Book("Автор 3", "Книга 3", 1995, 80)

# Сравнение объектов для поиска самой дорогой книги
most_expensive_book = max(book1, book2, book3)

# Вывод информации о самой дорогой книге
print(f"Самая дорогая книга: {most_expensive_book.title} ({most_expensive_book.author}), Цена: {most_expensive_book.price} руб.")

''',
25:'''
import random

class NestedSequence:
    def __init__(self):
        self.sequence = []

    def generate_random(self, rows, cols):
        self.sequence = [[random.randint(-10, 10) for _ in range(cols)] for _ in range(rows)]

    def generate_input(self, rows, cols):
        self.sequence = [[int(input(f"Введите число для ячейки [{i}][{j}]: ")) for j in range(cols)] for i in range(rows)]

    def display_sequence(self):
        for row in self.sequence:
            print(row)

# Пример использования класса
ns = NestedSequence()

# Формирование вложенной последовательности с случайными числами
ns.generate_random(3, 4)

print("Случайно сгенерированная последовательность:")
ns.display_sequence()

# Формирование вложенной последовательности с вводом чисел с клавиатуры
ns.generate_input(2, 3)

print("Последовательность с вводом данных:")
ns.display_sequence()

''',
26:'''
import datetime

class Person:
    def __init__(self, last_name, birth_date, faculty):
        self.last_name = last_name
        self.birth_date = birth_date
        self.faculty = faculty

    def display_info(self):
        print("Фамилия:", self.last_name)
        print("Дата рождения:", self.birth_date.strftime("%d.%m.%Y"))
        print("Факультет:", self.faculty)

    def calculate_age(self):
        current_year = datetime.datetime.now().year
        return current_year - self.birth_date.year


class Applicant(Person):
    pass


class Student(Person):
    def __init__(self, last_name, birth_date, faculty, course):
        super().__init__(last_name, birth_date, faculty)
        self.course = course

    def display_info(self):
        super().display_info()
        print("Курс:", self.course)


class Teacher(Person):
    def __init__(self, last_name, birth_date, faculty, position, experience):
        super().__init__(last_name, birth_date, faculty)
        self.position = position
        self.experience = experience

    def display_info(self):
        super().display_info()
        print("Должность:", self.position)
        print("Стаж:", self.experience, "лет")


# Создание списка персон
persons = [
    Applicant("Иванов", datetime.datetime(1995, 3, 12), "Физика"),
    Student("Петров", datetime.datetime(2000, 5, 20), "Математика", 2),
    Teacher("Сидоров", datetime.datetime(1978, 10, 2), "Информатика", "Преподаватель", 15),
    Student("Смирнова", datetime.datetime(1999, 7, 8), "Химия", 3),
    Teacher("Козлова", datetime.datetime(1985, 12, 30), "Биология", "Доцент", 10)
]

# Вывод полной информации о персонах из базы
print("Полная информация о персонах:")
for person in persons:
    person.display_info()
    print()

# Поиск персон по заданному диапазону возрастов
min_age = 25
max_age = 40

print("Персоны в возрасте от", min_age, "до", max_age, "лет:")
for person in persons:
    age = person.calculate_age()
    if min_age <= age <= max_age:
        person.display_info()
        print()

''',
27:'''
import math

class Triangle:
    def __init__(self, side1, side2, angle):
        self.side1 = side1
        self.side2 = side2
        self.angle = angle

    def calculate_area(self):
        return 0.5 * self.side1 * self.side2 * math.sin(math.radians(self.angle))

    def calculate_perimeter(self):
        side3 = math.sqrt(self.side1 ** 2 + self.side2 ** 2 - 2 * self.side1 * self.side2 * math.cos(math.radians(self.angle)))
        return self.side1 + self.side2 + side3

    def display_info(self):
        print("Треугольник")
        print("Длина стороны 1:", self.side1)
        print("Длина стороны 2:", self.side2)
        print("Угол между сторонами (в градусах):", self.angle)
        print("Площадь:", self.calculate_area())
        print("Периметр:", self.calculate_perimeter())


class RightTriangle(Triangle):
    def calculate_area(self):
        return 0.5 * self.side1 * self.side2

    def calculate_perimeter(self):
        side3 = math.sqrt(self.side1 ** 2 + self.side2 ** 2)
        return self.side1 + self.side2 + side3


class IsoscelesTriangle(Triangle):
    def __init__(self, base, side, angle):
        super().__init__(base, side, angle)

    def calculate_area(self):
        return 0.5 * self.side1 * self.side2 * math.sin(math.radians(self.angle))

    def calculate_perimeter(self):
        side3 = 2 * self.side1 * math.sin(math.radians(0.5 * self.angle))
        return self.side1 + self.side2 + side3


class EquilateralTriangle(Triangle):
    def __init__(self, side):
        super().__init__(side, side, 60)

    def calculate_area(self):
        return (math.sqrt(3) / 4) * self.side1 ** 2

    def calculate_perimeter(self):
        return 3 * self.side1


# Создание списка треугольников
triangles = [
    Triangle(5, 7, 45),
    RightTriangle(3, 4, 90),
    IsoscelesTriangle(6, 5, 60),
    EquilateralTriangle(4)
]

# Вывод полной информации о треугольниках
for triangle in triangles:
    triangle.display_info()
    print()

''',
28:'''
class Transport:
    def __init__(self, brand, number, speed, payload_capacity):
        self.brand = brand
        self.number = number
        self.speed = speed
        self.payload_capacity = payload_capacity

    def display_info(self):
        print("Марка:", self.brand)
        print("Номер:", self.number)
        print("Скорость:", self.speed)
        print("Грузоподъемность:", self.payload_capacity)

    def calculate_payload_capacity(self):
        return self.payload_capacity


class Car(Transport):
    pass


class Motorcycle(Transport):
    def __init__(self, brand, number, speed, payload_capacity, sidecar):
        super().__init__(brand, number, speed, payload_capacity)
        self.sidecar = sidecar

    def display_info(self):
        super().display_info()
        if self.sidecar:
            print("Наличие коляски: Да")
        else:
            print("Наличие коляски: Нет")

    def calculate_payload_capacity(self):
        if self.sidecar:
            return self.payload_capacity
        else:
            return 0


class Truck(Transport):
    def __init__(self, brand, number, speed, payload_capacity, trailer):
        super().__init__(brand, number, speed, payload_capacity)
        self.trailer = trailer

    def display_info(self):
        super().display_info()
        if self.trailer:
            print("Наличие прицепа: Да")
        else:
            print("Наличие прицепа: Нет")

    def calculate_payload_capacity(self):
        if self.trailer:
            return self.payload_capacity * 2
        else:
            return self.payload_capacity


# Создание списка транспортных средств
transports = [
    Car("Toyota", "A123BC", 120, 500),
    Motorcycle("Honda", "B456DE", 80, 200, True),
    Truck("Volvo", "C789FG", 100, 3000, False),
    Car("Ford", "D012HI", 150, 800),
    Motorcycle("BMW", "E345JK", 100, 250, False),
    Truck("Mercedes", "F678LM", 90, 5000, True)
]

# Вывод полной информации о транспортных средствах
print("Полная информация о транспортных средствах:")
for transport in transports:
    transport.display_info()
    print()

# Поиск машин, удовлетворяющих требованиям грузоподъемности
min_payload_capacity = 500
max_payload_capacity = 2000

print("Транспортные средства с грузоподъемностью от", min_payload_capacity, "до", max_payload_capacity, ":")
for transport in transports:
    payload_capacity = transport.calculate_payload_capacity()
    if min_payload_capacity <= payload_capacity <= max_payload_capacity:
        transport.display_info()
        print()

''',
29:'''
from datetime import datetime

class Goods:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def display_info(self):
        print("Название:", self.name)
        print("Цена:", self.price)

    def can_afford(self, money):
        return money >= self.price


class Product(Goods):
    def __init__(self, name, price, production_date, expiration_date):
        super().__init__(name, price)
        self.production_date = production_date
        self.expiration_date = expiration_date

    def display_info(self):
        super().display_info()
        print("Дата производства:", self.production_date)
        print("Срок годности:", self.expiration_date)

    def is_expired(self):
        return datetime.now().date() > self.expiration_date


class Batch(Goods):
    def __init__(self, name, price_per_item, quantity, production_date, expiration_date):
        super().__init__(name, price_per_item * quantity)
        self.price_per_item = price_per_item
        self.quantity = quantity
        self.production_date = production_date
        self.expiration_date = expiration_date

    def display_info(self):
        super().display_info()
        print("Цена за штуку:", self.price_per_item)
        print("Количество штук:", self.quantity)
        print("Дата производства:", self.production_date)
        print("Срок годности:", self.expiration_date)

    def is_expired(self):
        return datetime.now().date() > self.expiration_date


class Phone(Goods):
    pass


class GoodsDatabase:
    def __init__(self):
        self.goods_list = []

    def add_goods(self, goods):
        self.goods_list.append(goods)

    def display_all_goods(self):
        print("Полная информация о товарах:")
        for goods in self.goods_list:
            goods.display_info()
            print()

    def find_goods_by_price(self, money):
        print("Товары, которые можно приобрести за", money, "рублей:")
        for goods in self.goods_list:
            if goods.can_afford(money):
                goods.display_info()
                print()


# Создание списка товаров
database = GoodsDatabase()
database.add_goods(Product("Молоко", 50, datetime(2023, 6, 30), datetime(2023, 7, 15)))
database.add_goods(Batch("Яблоки", 30, 10, datetime(2023, 6, 10), datetime(2023, 6, 30)))
database.add_goods(Phone("iPhone", 50000))
database.add_goods(Product("Хлеб", 20, datetime(2023, 6, 16), datetime(2023, 6, 20)))
database.add_goods(Batch("Мороженое", 70, 5, datetime(2023, 6, 1), datetime(2023, 7, 10)))
database.add_goods(Phone("Samsung", 40000))

# Вывод полной информации о товарах
database.display_all_goods()

# Поиск товара по заданной сумме денег
money = 100
database.find_goods_by_price(money)

''',
30:'''
class Goods:
    def __init__(self, name, price, age_range):
        self.name = name
        self.price = price
        self.age_range = age_range

    def display_info(self):
        print("Название:", self.name)
        print("Цена:", self.price)

    def is_age_appropriate(self, age):
        return self.age_range[0] <= age <= self.age_range[1]


class Toy(Goods):
    def __init__(self, name, price, manufacturer, material, age_range):
        super().__init__(name, price, age_range)
        self.manufacturer = manufacturer
        self.material = material

    def display_info(self):
        super().display_info()
        print("Производитель:", self.manufacturer)
        print("Материал:", self.material)


class Book(Goods):
    def __init__(self, name, author, price, publisher, age_range):
        super().__init__(name, price, age_range)
        self.author = author
        self.publisher = publisher

    def display_info(self):
        super().display_info()
        print("Автор:", self.author)
        print("Издательство:", self.publisher)


class SportsEquipment(Goods):
    def __init__(self, name, price, manufacturer, age_range):
        super().__init__(name, price, age_range)
        self.manufacturer = manufacturer

    def display_info(self):
        super().display_info()
        print("Производитель:", self.manufacturer)


class GoodsDatabase:
    def __init__(self):
        self.goods_list = []

    def add_goods(self, goods):
        self.goods_list.append(goods)

    def display_all_goods(self):
        print("Полная информация о товарах:")
        for goods in self.goods_list:
            goods.display_info()
            print()

    def find_goods_by_age_range(self, age):
        print("Товары, предназначенные для возраста", age, "лет:")
        for goods in self.goods_list:
            if goods.is_age_appropriate(age):
                goods.display_info()
                print()


# Создание списка товаров
database = GoodsDatabase()
database.add_goods(Toy("Мяч", 100, "ООО Игрушки", "Резина", (3, 10)))
database.add_goods(Book("Маленький принц", "Антуан де Сент-Экзюпери", 200, "Издательство А", (5, 12)))
database.add_goods(SportsEquipment("Велосипед", 5000, "ООО Спорт", (10, 99)))
database.add_goods(Toy("Кукла", 150, "ООО Игрушки", "Пластик", (3, 8)))
database.add_goods(Book("Гарри Поттер", "Джоан Роулинг", 300, "Издательство Б", (8, 15)))
database.add_goods(SportsEquipment("Теннисная ракетка", 1000, "ООО Спорт", (12, 99)))

# Вывод полной информации о товарах
database.display_all_goods()

# Поиск товаров для потребителя в заданном возрастном диапазоне
age = 7
database.find_goods_by_age_range(age)

''',
31:'''
class PhoneBook:
    def __init__(self, surname, address, phone_number):
        self.surname = surname
        self.address = address
        self.phone_number = phone_number

    def display_info(self):
        print("Фамилия:", self.surname)
        print("Адрес:", self.address)
        print("Номер телефона:", self.phone_number)

    def match_surname(self, surname):
        return self.surname.lower() == surname.lower()


class Person(PhoneBook):
    def __init__(self, surname, address, phone_number):
        super().__init__(surname, address, phone_number)


class Organization(PhoneBook):
    def __init__(self, name, address, phone_number, fax, contact_person):
        super().__init__(name, address, phone_number)
        self.fax = fax
        self.contact_person = contact_person

    def display_info(self):
        super().display_info()
        print("Факс:", self.fax)
        print("Контактное лицо:", self.contact_person)


class Friend(PhoneBook):
    def __init__(self, surname, address, phone_number, birth_date):
        super().__init__(surname, address, phone_number)
        self.birth_date = birth_date

    def display_info(self):
        super().display_info()
        print("Дата рождения:", self.birth_date)


class PhoneBookDatabase:
    def __init__(self):
        self.phonebook_list = []

    def add_entry(self, entry):
        self.phonebook_list.append(entry)

    def display_all_entries(self):
        print("Полная информация о записях в телефонном справочнике:")
        for entry in self.phonebook_list:
            entry.display_info()
            print()

    def find_entries_by_surname(self, surname):
        print("Записи в телефонном справочнике с фамилией", surname + ":")
        for entry in self.phonebook_list:
            if entry.match_surname(surname):
                entry.display_info()
                print()


# Создание списка записей в телефонном справочнике
database = PhoneBookDatabase()
database.add_entry(Person("Иванов", "ул. Ленина, 10", "123-456"))
database.add_entry(Organization("ООО Компания", "ул. Пушкина, 15", "789-012", "456-789", "Петров"))
database.add_entry(Friend("Сидоров", "ул. Гагарина, 20", "345-678", "01.01.1990"))
database.add_entry(Person("Петров", "ул. Советская, 5", "987-654"))

# Вывод полной информации о записях в телефонном справочнике
database.display_all_entries()

# Поиск записей по фамилии
surname = "Иванов"
database.find_entries_by_surname(surname)

''',
32:'''
class Client:
    def __init__(self, surname, start_date):
        self.surname = surname
        self.start_date = start_date

    def display_info(self):
        print("Фамилия:", self.surname)
        print("Дата начала сотрудничества:", self.start_date)

    def match_start_date(self, date):
        return self.start_date == date


class Depositor(Client):
    def __init__(self, surname, start_date, deposit_amount, deposit_interest):
        super().__init__(surname, start_date)
        self.deposit_amount = deposit_amount
        self.deposit_interest = deposit_interest

    def display_info(self):
        super().display_info()
        print("Размер вклада:", self.deposit_amount)
        print("Процент по вкладу:", self.deposit_interest)


class Creditor(Client):
    def __init__(self, surname, start_date, loan_amount, loan_interest, debt_balance):
        super().__init__(surname, start_date)
        self.loan_amount = loan_amount
        self.loan_interest = loan_interest
        self.debt_balance = debt_balance

    def display_info(self):
        super().display_info()
        print("Размер кредита:", self.loan_amount)
        print("Процент по кредиту:", self.loan_interest)
        print("Остаток долга:", self.debt_balance)


class Organization(Client):
    def __init__(self, name, start_date, account_number, account_balance):
        super().__init__(name, start_date)
        self.account_number = account_number
        self.account_balance = account_balance

    def display_info(self):
        super().display_info()
        print("Номер счета:", self.account_number)
        print("Сумма на счету:", self.account_balance)


class BankDatabase:
    def __init__(self):
        self.client_list = []

    def add_client(self, client):
        self.client_list.append(client)

    def display_all_clients(self):
        print("Полная информация о клиентах банка:")
        for client in self.client_list:
            client.display_info()
            print()

    def find_clients_by_start_date(self, date):
        print("Клиенты, начавшие сотрудничать с банком", date + ":")
        for client in self.client_list:
            if client.match_start_date(date):
                client.display_info()
                print()


# Создание списка клиентов банка
bank_db = BankDatabase()
bank_db.add_client(Depositor("Иванов", "01.01.2022", 10000, 5))
bank_db.add_client(Creditor("Петров", "01.03.2022", 5000, 10, 2000))
bank_db.add_client(Organization("ООО Компания", "01.02.2022", "123456", 50000))
bank_db.add_client(Depositor("Сидоров", "01.01.2022", 15000, 4))

# Вывод полной информации о клиентах банка
bank_db.display_all_clients()

# Поиск клиентов по дате начала сотрудничества
start_date = "01.01.2022"
bank_db.find_clients_by_start_date(start_date)

''',
33:'''
import datetime

class Software:
    def __init__(self, name, manufacturer):
        self.name = name
        self.manufacturer = manufacturer

    def display_info(self):
        print("Название:", self.name)
        print("Производитель:", self.manufacturer)

    def is_usable(self, current_date):
        return True


class FreeSoftware(Software):
    def __init__(self, name, manufacturer):
        super().__init__(name, manufacturer)

    def display_info(self):
        super().display_info()

    def is_usable(self, current_date):
        return True


class FreemiumSoftware(Software):
    def __init__(self, name, manufacturer, installation_date, free_usage_period):
        super().__init__(name, manufacturer)
        self.installation_date = installation_date
        self.free_usage_period = free_usage_period

    def display_info(self):
        super().display_info()
        print("Дата установки:", self.installation_date)
        print("Срок бесплатного использования:", self.free_usage_period)

    def is_usable(self, current_date):
        expiration_date = self.installation_date + self.free_usage_period
        return current_date <= expiration_date


class CommercialSoftware(Software):
    def __init__(self, name, manufacturer, price, installation_date, usage_period):
        super().__init__(name, manufacturer)
        self.price = price
        self.installation_date = installation_date
        self.usage_period = usage_period

    def display_info(self):
        super().display_info()
        print("Цена:", self.price)
        print("Дата установки:", self.installation_date)
        print("Срок использования:", self.usage_period)

    def is_usable(self, current_date):
        expiration_date = self.installation_date + self.usage_period
        return current_date <= expiration_date


# Создание списка программного обеспечения
software_list = []
software_list.append(FreeSoftware("Браузер", "Mozilla"))
software_list.append(FreemiumSoftware("Антивирус", "Kaspersky", datetime.date(2022, 1, 1), datetime.timedelta(days=30)))
software_list.append(CommercialSoftware("Офисный пакет", "Microsoft", 500, datetime.date(2022, 1, 1), datetime.timedelta(days=365)))

# Вывод полной информации о программном обеспечении
print("Полная информация о программном обеспечении:")
for software in software_list:
    software.display_info()
    print()

# Поиск программного обеспечения, которое допустимо использовать на текущую дату
current_date = datetime.date.today()
print("Доступное программное обеспечение на", current_date, ":")
for software in software_list:
    if software.is_usable(current_date):
        software.display_info()
        print()

''',
34:'''
def filter_students(func):
    def wrapper(student_data):
        # Фильтруем школьников по оценке по ИТ выше 4.5 баллов
        filtered_students = [student for student in student_data if student[1] > 4.5]
        # Вызываем функцию func() для вывода отфильтрованных данных
        func(filtered_students)
   
    return wrapper




@filter_students
def func(student_data):
    print("Student Data:")
    print("-----------------------------")
    print("|   Name   |   IT Grade   |")
    print("-----------------------------")
    for student in student_data:
        name, grade = student
        print(f"| {name:<9} | {grade:^11.2f} |")
    print("-----------------------------")




# Пример использования


# Данные о школьниках и их оценках по ИТ
student_data = [
    ("John", 4.7),
    ("Alice", 3.9),
    ("Michael", 4.2),
    ("Emma", 4.9),
    ("Daniel", 4.6)
]


# Вызываем функцию func(), декорированную декоратором filter_students
func(student_data)

''',
35:'''
def power_decorator(func):
    def wrapper(number):
        if number % 2 == 0:
            powered_number = number ** 2  # Возвести в степень двойки, если число четное
        else:
            powered_number = number ** 3  # Возвести в куб, если число нечетное
        func(powered_number)  # Вызываем исходную функцию с полученным результатом
    return wrapper




@power_decorator
def print_number(number):
    print(number)




# Пример использования


print_number(4)  # Выводит 16 (четное число, возводится в степень двойки)
print_number(7)  # Выводит 343 (нечетное число, возводится в куб)

''',
36:'''
def difference_decorator(func):
    def wrapper(start, end):
        if start > end:
            difference = start - end
            print(f"Разность чисел: {difference}")
        else:
            func(start, end)
    return wrapper




@difference_decorator
def summa(start, end):
    total_sum = sum(range(start, end + 1))
    print(f"Сумма чисел: {total_sum}")




# Пример использования


summa(1, 10)  # Выводит: Сумма чисел: 55
summa(10, 1)  # Выводит: Разность чисел: 9

''',
37:'''
def zero_if_none_decorator(func):
    def wrapper(*args, **kwargs):
        if any(arg is None for arg in kwargs.values()):
            print("Значение одного из ключевых параметров равно None. Выводится значение 0.")
            return 0
        else:
            return func(*args, **kwargs)
    return wrapper




@zero_if_none_decorator
def three_args(**kwargs):
    values = [value for value in kwargs.values() if value is not None]
    total_sum = sum(values)
    print(f"Сумма значений: {total_sum}")


# Пример использования


three_args(param1=5, param2=10, param3=15)  # Выводит: Сумма значений: 30
three_args(param1=5, param2=None, param3=15)  # Выводит: Значение одного из ключевых параметров равно None. Выводится значение 0.
''',
38:'''
def not_null(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        if result == 0:
            raise Exception("Декорируемая функция вернула значение 0")
        return result
    return wrapper




# Пример использования


@not_null
def calculate_sum(a, b):
    return a + b


@not_null
def calculate_product(a, b):
    return a * b




try:
    result1 = calculate_sum(3, 4)  # Возвращает 7
    print(result1)  # Выводит: 7


    result2 = calculate_sum(0, 5)  # Возвращает 5
    print(result2)  # Выводит: 5


    result3 = calculate_product(2, 6)  # Возвращает 12
    print(result3)  # Выводит: 12


    result4 = calculate_product(0, 4)  # Генерирует исключение Exception
    print(result4)  # Этот код не будет достигнут


except Exception as e:
    print("Произошла ошибка:", e)

''',
39:'''
def tol(dlina, fill):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            if len(result) < dlina:
                for i in range(len(result) + 1, dlina + 1):
                    result[i] = fill
            elif len(result) > dlina:
                result = {k: v for k, v in result.items() if k <= dlina}
            return result
        return wrapper
    return decorator




# Пример использования


@tol(dlina=5, fill='None')
def get_values():
    return {1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'F'}




result = get_values()
print(result)  # Выводит: {1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E'}
''',
40:'''
def dec(a, b):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            if result > 0:
                result += a
            else:
                result -= b
            return result
        return wrapper
    return decorator




# Пример использования


@dec(a=10, b=5)
def calculate_sum(*args):
    return sum(args)




result1 = calculate_sum(1, 2, 3)  # Сумма = 6, положительная
print(result1)  # Выводит: 16


result2 = calculate_sum(-1, -2, -3)  # Сумма = -6, отрицательная
print(result2)  # Выводит: -11
''',
41:'''
def decor(a):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            for key in result:
                if key % a == 0:
                    result[key] = result[key].upper()
            return result
        return wrapper
    return decorator




# Пример использования


@decor(a=2)
def create_dict():
    return {1: 'Group', 2: 'is', 3: 'in', 4: 'Exam'}




result = create_dict()
print(result)

''',
42:'''
def decor_func(func):
    def wrapper(name):
        result = func(name)
        reversed_name = result[::-1]
        return reversed_name * len(result)
    return wrapper


@decor_func
def func(name):
    print(f"Name: {name}")
    print(f"Length: {len(name)}")
    return name


# Пример использования
result = func("Иван")
print(result)

''',
43:'''
def decor_func(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        reversed_name = result[::-1]
        return reversed_name
    return wrapper


@decor_func
def func():
    return "Иван"


# Пример использования
print(func())

''',
44:'''
class Stack:
    def __init__(self):
        self.stack = []  # Инициализация пустого стека


    def push(self, item):
        self.stack.append(item)  # Добавление элемента в стек


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()  # Удаление и возврат верхнего элемента стека


    def is_empty(self):
        return len(self.stack) == 0  # Проверка, пуст ли стек


    def size(self):
        return len(self.stack)  # Возвращает текущий размер стека


    def average(self):
        if not self.is_empty():
            return sum(self.stack) / self.size()  # Вычисление среднего значения всех элементов стека


    def count_greater_than_average(self):
        average = self.average()
        if average is not None:
            count = 0
            for item in self.stack:
                if item > average:
                    count += 1
            return count  # Возвращает количество элементов, больших среднего значения




# Пример использования


stack = Stack()
stack.push(5)
stack.push(3)
stack.push(8)
stack.push(2)
stack.push(10)


count = stack.count_greater_than_average()
print("Количество элементов, больших среднего значения:", count)
''',
45:'''
import math


class Stack:
    def __init__(self):
        self.items = []


    def push(self, item):
        self.items.append(item)


    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None


    def is_empty(self):
        return len(self.items) == 0


    def remove_non_square_elements(self):
        i = 0
        while i < len(self.items):
            if not self.is_square(self.items[i]):
                self.items.pop(i)
            else:
                i += 1


    def is_square(self, num):
        return math.isqrt(num) ** 2 == num




# Создаем экземпляр класса Stack
stack = Stack()


# Добавляем элементы в стек
stack.push(4)
stack.push(9)
stack.push(7)
stack.push(16)
stack.push(5)


# Удаляем элементы, не являющиеся квадратами целых чисел
stack.remove_non_square_elements()


# Выводим содержимое стека после удаления
print(stack.items)
''',
46:'''
class Stack:
    def __init__(self):
        self.items = []


    def push(self, item):
        self.items.append(item)


    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None


    def is_empty(self):
        return len(self.items) == 0


    def reverse_every_second_element(self):
        for i in range(len(self.items)):
            if i % 2 == 1:
                self.items[i] = self.reverse_string(self.items[i])


    def reverse_string(self, string):
        return string[::-1]




# Создаем экземпляр класса Stack
stack = Stack()


# Добавляем элементы в стек
stack.push("apple")
stack.push("banana")
stack.push("carrot")
stack.push("date")
stack.push("eggplant")


# Разворачиваем каждый второй элемент
stack.reverse_every_second_element()


# Выводим содержимое стека после разворота
while not stack.is_empty():
    print(stack.pop())

''',
47:'''
class Stack:
    def __init__(self):
        self.items = []


    def push(self, item):
        self.items.append(item)


    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None


    def is_empty(self):
        return len(self.items) == 0


    def get_average(self):
        if not self.is_empty():
            total = sum(self.items)
            return total / len(self.items)
        return None


    def count_elements_above_average(self):
        count = 0
        average = self.get_average()
        if average is not None:
            for item in self.items:
                if item > average:
                    count += 1
        return count




# Создаем экземпляр класса Stack
stack = Stack()


# Добавляем элементы в стек
stack.push(5)
stack.push(10)
stack.push(15)
stack.push(20)
stack.push(25)


# Находим количество элементов, превышающих среднее значение
count = stack.count_elements_above_average()


# Выводим результат
print("Количество элементов, превышающих среднее значение:", count)

''',
48:'''
class Stack:
    def __init__(self):
        self.items = []


    def push(self, item):
        self.items.append(item)


    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None


    def is_empty(self):
        return len(self.items) == 0


    def is_ascending(self):
        if len(self.items) <= 1:
            return True


        for i in range(len(self.items) - 1):
            if self.items[i] > self.items[i + 1]:
                return False


        return True




# Создаем экземпляр класса Stack
stack = Stack()


# Добавляем элементы в стек
stack.push(5)
stack.push(10)
stack.push(15)
stack.push(20)
stack.push(25)


# Проверяем, является ли содержимое стека упорядоченным по возрастанию
is_ascending = stack.is_ascending()


# Выводим результат
if is_ascending:
    print("Содержимое стека упорядочено по возрастанию.")
else:
    print("Содержимое стека не упорядочено по возрастанию.")

''', 
49:'''
class Stack:
    def __init__(self):
        self.items = []


    def push(self, item):
        self.items.append(item)


    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None


    def is_empty(self):
        return len(self.items) == 0


    def swap_first_and_last(self):
        if len(self.items) >= 2:
            self.items[0], self.items[-1] = self.items[-1], self.items[0]




# Создаем экземпляр класса Stack
stack = Stack()


# Добавляем элементы в стек
stack.push(1)
stack.push(2)
stack.push(3)
stack.push(4)
stack.push(5)


# Меняем местами первый и последний элементы стека
stack.swap_first_and_last()


# Выводим содержимое стека
print("Содержимое стека:")
while not stack.is_empty():
    print(stack.pop())
''',
50:'''
class Stack:
    def __init__(self):
        self.stack = []


    def push(self, item):
        self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def remove_middle(self):
        stack_size = self.size()


        if stack_size == 0:
            return


        middle = stack_size // 2


        if stack_size % 2 == 0:  # Четное число элементов
            self.stack.pop(middle - 1)  # Удаляем первый средний элемент
            self.stack.pop(middle - 1)  # Удаляем второй средний элемент
        else:  # Нечетное число элементов
            self.stack.pop(middle)  # Удаляем средний элемент


    def print_stack(self):
        print(self.stack)




# Пример использования
my_stack = Stack()
my_stack.push(1)
my_stack.push(2)
my_stack.push(3)
my_stack.push(4)
my_stack.push(5)


print("Before removing middle element(s):")
my_stack.print_stack()


my_stack.remove_middle()


print("After removing middle element(s):")
my_stack.print_stack()

''', 
51:'''
class Stack:
    def __init__(self):
        self.stack = []


    def push(self, item):
        self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def remove_every_second(self):
        index = 1
        while index < self.size():
            self.stack.pop(index)
            index += 1


    def print_stack(self):
        print(self.stack)




# Пример использования
my_stack = Stack()
my_stack.push(1)
my_stack.push(2)
my_stack.push(3)
my_stack.push(4)
my_stack.push(5)


print("Before removing every second element:")
my_stack.print_stack()


my_stack.remove_every_second()


print("After removing every second element:")
my_stack.print_stack()

''',
52:'''
class Stack:
    def __init__(self):
        self.stack = []


    def push(self, item):
        self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def find_min_and_insert(self):
        if self.is_empty():
            return


        min_val = float('inf')
        min_index = -1


        # Находим минимальный элемент и его индекс в стеке
        for i, item in enumerate(self.stack):
            if item < min_val:
                min_val = item
                min_index = i


        # Вставляем "0" после минимального элемента
        self.stack.insert(min_index + 1, 0)


    def print_stack(self):
        print(self.stack)




# Пример использования
my_stack = Stack()
my_stack.push(3)
my_stack.push(6)
my_stack.push(2)
my_stack.push(8)
my_stack.push(4)


print("Before finding the minimum and inserting '0':")
my_stack.print_stack()


my_stack.find_min_and_insert()


print("After finding the minimum and inserting '0':")
my_stack.print_stack()

''', 
53:'''
class ConditionStack:
    def __init__(self, condition):
        self.stack = []
        self.condition = condition


    def push(self, item):
        if self.condition(item):
            self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def print_stack(self):
        print(self.stack)




# Пример использования
def is_positive(num):
    return num > 0




my_stack = ConditionStack(is_positive)
my_stack.push(3)
my_stack.push(-2)
my_stack.push(5)
my_stack.push(-1)


print("Stack content:")
my_stack.print_stack()

''',
54:'''
class LengthStack:
    def __init__(self, length):
        self.stack = []
        self.length = length


    def push(self, item):
        if len(item) == self.length:
            self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def print_stack(self):
        print(self.stack)




# Пример использования
my_stack = LengthStack(4)
my_stack.push("abcd")
my_stack.push("xy")
my_stack.push("wxyz")
my_stack.push("pqrs")


print("Stack content:")
my_stack.print_stack()

''',
55:'''
class SortedStringStack:
    def __init__(self):
        self.stack = []


    def push(self, item):
        if isinstance(item, str):
            self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def get_sorted_items(self):
        sorted_items = sorted(self.stack)
        return sorted_items


    def print_stack(self):
        print(self.stack)




# Пример использования
my_stack = SortedStringStack()
my_stack.push("apple")
my_stack.push("banana")
my_stack.push("cherry")
my_stack.push(123)
my_stack.push("orange")


print("Stack content:")
my_stack.print_stack()


sorted_items = my_stack.get_sorted_items()
print("Sorted items:")
print(sorted_items)
''',
56:'''
class Stack:
    def __init__(self):
        self.stack = []


    def push(self, item):
        self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return None


    def is_empty(self):
        return len(self.stack) == 0


    def size(self):
        return len(self.stack)


    def print_stack(self):
        print(self.stack)




# Создание двух стеков
stack1 = Stack()
stack2 = Stack()


# Заполнение первого стека
stack1.push(1)
stack1.push(2)
stack1.push(3)


# Заполнение второго стека
stack2.push(4)
stack2.push(5)
stack2.push(6)


# Вывод содержимого стеков перед обменом
print("Stack1 before swapping:")
stack1.print_stack()


print("Stack2 before swapping:")
stack2.print_stack()


# Обмен информацией между стеками
temp_stack = Stack()


# Перекладываем элементы из stack1 в temp_stack
while not stack1.is_empty():
    item = stack1.pop()
    temp_stack.push(item)


# Перекладываем элементы из stack2 в stack1
while not stack2.is_empty():
    item = stack2.pop()
    stack1.push(item)


# Перекладываем элементы из temp_stack в stack2
while not temp_stack.is_empty():
    item = temp_stack.pop()
    stack2.push(item)


# Вывод содержимого стеков после обмена
print("Stack1 after swapping:")
stack1.print_stack()


print("Stack2 after swapping:")
stack2.print_stack()

''',
57:'''
class Stack:
    def __init__(self):
        self.stack = []


    def push(self, item):
        self.stack.append(item)


    def pop(self):
        if not self.is_empty():
            return self.stack.pop()


    def is_empty(self):
        return len(self.stack) == 0


    def peek(self):
        if not self.is_empty():
            return self.stack[-1]




def swap_top_and_bottom(stack):
    if len(stack.stack) >= 3:
        bottom_1 = stack.pop()
        bottom_2 = stack.pop()
        bottom_3 = stack.pop()
        stack.push(bottom_1)
        stack.push(bottom_2)
        stack.push(bottom_3)




# Создаем стек и добавляем элементы
stack = Stack()
stack.push("5")
stack.push("4")
stack.push("3")
stack.push("2")
stack.push("1")


# Выводим исходное состояние стека
print("Исходный стек:", stack.stack)


# Меняем местами верхний и нижний элементы
swap_top_and_bottom(stack)


# Выводим измененное состояние стека
print("Измененный стек:", stack.stack)

''', 
58:'''
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None




def find_minimum(head):
    if head is None:
        return None


    minimum = head.data
    current = head.next


    while current is not None:
        if current.data < minimum:
            minimum = current.data
        current = current.next


    return minimum




# Пример создания связного списка: 7 -> 4 -> 9 -> 2 -> 5
head = Node(7)
node2 = Node(4)
node3 = Node(9)
node4 = Node(2)
node5 = Node(5)


head.next = node2
node2.next = node3
node3.next = node4
node4.next = node5


# Вызов функции для поиска минимального элемента
minimum_value = find_minimum(head)
print("Минимальный элемент:", minimum_value)
''',
59:'''
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None




def append_to_linked_list(head, data):
    new_node = Node(data)


    if head is None:
        return new_node


    current = head
    while current.next is not None:
        current = current.next


    current.next = new_node


    return head




# Пример создания связного списка: 7 -> 4 -> 9
head = Node(7)
node2 = Node(4)
node3 = Node(9)


head.next = node2
node2.next = node3


# Добавление элемента 2 в конец списка
head = append_to_linked_list(head, 2)


# Вывод элементов связного списка
current = head
while current is not None:
    print(current.data)
    current = current.next

''', 
60:'''
class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next


def add_left(head, data):
    new_node = Node(data)
    new_node.next = head
    return new_node


def display(head):
    current = head
    while current:
        print(current.data, end=" -> ")
        current = current.next
    print("None")


# Создание связного списка
head = Node(3)
head = Node(2, head)
head = Node(1, head)


# Добавление элемента слева
head = add_left(head, 4)


# Вывод содержимого списка
display(head)

''',
61:'''
class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next


def insert_after(head, position, data):
    current = head
    count = 0


    while current:
        if count == position:
            new_node = Node(data)
            new_node.next = current.next
            current.next = new_node
            break


        current = current.next
        count += 1


    return head


def display(head):
    current = head
    while current:
        print(current.data, end=" -> ")
        current = current.next
    print("None")


# Создание связного списка
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)


# Вставка элемента после заданного номера
head = insert_after(head, 0, 4)


# Вывод содержимого списка
display(head)

''',
62:'''
class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next


def insert_after_value(head, value, new_data):
    current = head


    while current:
        if current.data == value:
            new_node = Node(new_data)
            new_node.next = current.next
            current.next = new_node
            break


        current = current.next


    return head


def display(head):
    current = head
    while current:
        print(current.data, end=" -> ")
        current = current.next
    print("None")


# Создание связного списка
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)


# Вставка элемента после заданного значения
head = insert_after_value(head, 1, 4)


# Вывод содержимого списка
display(head)

''',
63:'''
class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next


def remove_element(head, value):
    if head is None:
        return head


    # Если заданный элемент находится в головном узле
    if head.data == value:
        return head.next


    current = head
    while current.next:
        if current.next.data == value:
            current.next = current.next.next
            break


        current = current.next


    return head


def display(head):
    current = head
    while current:
        print(current.data, end=" -> ")
        current = current.next
    print("None")


# Создание связного списка
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)


# Удаление заданного элемента из списка
head = remove_element(head, 2)


# Вывод содержимого списка
display(head)

''',
64:'''
class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next


def sum_multiples_of_three(head):
    current = head
    total = 0


    while current:
        if current.data % 3 == 0:
            total += current.data


        current = current.next


    return total


# Создание связного списка
head = Node(3)
head = Node(2, head)
head = Node(1, head)
head = Node(6, head)


# Вычисление суммы элементов, кратных 3
result = sum_multiples_of_three(head)
print("Сумма элементов, кратных 3:", result)
''',
65:'''
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

class DoublyLinkedList:
    def __init__(self):
        self.head = None

    def add_node(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node
            new_node.prev = current

    def delete_node(self, data):
        if self.head is None:
            return
        elif self.head.data == data:
            if self.head.next is not None:
                self.head = self.head.next
                self.head.prev = None
            else:
                self.head = None
        else:
            current = self.head
            while current.next is not None and current.next.data != data:
                current = current.next
            if current.next is None:
                return
            else:
                current.next = current.next.next
                if current.next is not None:
                    current.next.prev = current

    def __len__(self):
        count = 0
        current = self.head
        while current:
            count += 1
            current = current.next
        return count

    def __str__(self):
        if self.head == None:
            return f"Двусвязный список пустой"
        current = self.head
        dllist_str = ""
        while current:
            dllist_str += " ⇄ " + str(current.data)
            current = current.next
        return dllist_str.lstrip(" ⇄ ")
    
    def insert_after_n(self, n, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            return
        current = self.head
        count = 1
        while current.next is not None and count < n:
            current = current.next
            count += 1
        if count < n:
            print("Недостаточно элементов в списке.")
            return
        new_node.prev = current
        new_node.next = current.next
        if current.next is not None:
            current.next.prev = new_node
        current.next = new_node

dllist = DoublyLinkedList()
dllist.add_node(1)
dllist.add_node(2)
dllist.add_node(3)
dllist.add_node(4)

print("Исходный список:")
print(dllist)

n = 2
new_data = 5
dllist.insert_after_n(n, new_data)

print("Список после вставки:")
print(dllist)

''',
66:'''
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

class DoublyLinkedList:
    def __init__(self):
        self.head = None

    def add_node(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node
            new_node.prev = current

    def delete_node(self, data):
        if self.head is None:
            return
        elif self.head.data == data:
            if self.head.next is not None:
                self.head = self.head.next
                self.head.prev = None
            else:
                self.head = None
        else:
            current = self.head
            while current.next is not None and current.next.data != data:
                current = current.next
            if current.next is None:
                return
            else:
                current.next = current.next.next
                if current.next is not None:
                    current.next.prev = current

    def __len__(self):
        count = 0
        current = self.head
        while current:
            count += 1
            current = current.next
        return count

    def __str__(self):
        if self.head == None:
            return f"Двусвязный список пустой"
        current = self.head
        dllist_str = ""
        while current:
            dllist_str += " ⇄ " + str(current.data)
            current = current.next
        return dllist_str.lstrip(" ⇄ ")
    
    def delete_nth_node(self, n):
        if self.head is None:
            return
        current = self.head
        count = 1
        while current is not None and count < n:
            current = current.next
            count += 1
        if current is None:
            print("Недостаточно элементов в списке.")
            return
        if current.prev is not None:
            current.prev.next = current.next
        else:
            self.head = current.next
        if current.next is not None:
            current.next.prev = current.prev
        del current
        
        
    
dllist = DoublyLinkedList()
dllist.add_node(1)
dllist.add_node(2)
dllist.add_node(3)
dllist.add_node(4)

print("Исходный список:")
print(dllist)

n = 2
dllist.delete_nth_node(n)

print("Список после удаления:")
print(dllist)

''',
67:'''
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

class DoublyLinkedList:
    def __init__(self):
        self.head = None

    def add_node(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node
            new_node.prev = current

    def delete_node(self, data):
        if self.head is None:
            return
        elif self.head.data == data:
            if self.head.next is not None:
                self.head = self.head.next
                self.head.prev = None
            else:
                self.head = None
        else:
            current = self.head
            while current.next is not None and current.next.data != data:
                current = current.next
            if current.next is None:
                return
            else:
                current.next = current.next.next
                if current.next is not None:
                    current.next.prev = current

    def __len__(self):
        count = 0
        current = self.head
        while current:
            count += 1
            current = current.next
        return count

    def __str__(self):
        if self.head == None:
            return f"Двусвязный список пустой"
        current = self.head
        dllist_str = ""
        while current:
            dllist_str += " ⇄ " + str(current.data)
            current = current.next
        return dllist_str.lstrip(" ⇄ ")
    
    def merge_lists(self, other_list):
        if self.head is None:
            self.head = other_list.head
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = other_list.head
            if other_list.head is not None:
                other_list.head.prev = current
        
    
dllist1 = DoublyLinkedList()
dllist1.add_node(1)
dllist1.add_node(2)
dllist1.add_node(3)

dllist2 = DoublyLinkedList()
dllist2.add_node(4)
dllist2.add_node(5)
dllist2.add_node(6)

print("Первый список:")
print(dllist1)

print("Второй список:")
print(dllist2)

dllist1.merge_lists(dllist2)

print("Список после соединения:")
print(dllist1)

''',
68:'''
class Node:
    def __init__(self, data=None):
        self.data = data
        self.prev = None
        self.next = None

class CircularDoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
            new_node.prev = self.tail
            new_node.next = self.head
        else:
            new_node.prev = self.tail
            new_node.next = self.head
            self.tail.next = new_node
            self.head.prev = new_node
            self.tail = new_node

    def prepend(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
            new_node.prev = self.tail
            new_node.next = self.head
        else:
            new_node.prev = self.tail
            new_node.next = self.head
            self.head.prev = new_node
            self.tail.next = new_node
            self.head = new_node

    def delete(self, key):
        current_node = self.head
        while current_node:
            if current_node.data == key:
                if current_node == self.head:
                    self.head = current_node.next
                    self.tail.next = self.head
                    self.head.prev = self.tail
                elif current_node == self.tail:
                    self.tail = current_node.prev
                    self.head.prev = self.tail
                    self.tail.next = self.head
                else:
                    current_node.prev.next = current_node.next
                    current_node.next.prev = current_node.prev
                return
            current_node = current_node.next

    def __len__(self):
        count = 0
        current_node = self.head
        while current_node:
            count += 1
            current_node = current_node.next
            if current_node == self.head:
                break
        return count

    def __str__(self):
        cdllist_str = ""
        current_node = self.head
        while current_node:
            cdllist_str += str(current_node.data) + " ⇄ "
            current_node = current_node.next
            if current_node == self.head:
                break
        return " ⇄ " + cdllist_str
    
    def lottery_draw(self, n, k):
        result = []
        if n <= 0 or k <= 0:
            return result
        current_team1 = self.head
        current_team2 = self.head
        while len(result) < len(self):
            for _ in range(n-1):
                current_team1 = current_team1.next
            for _ in range(k-1):
                current_team2 = current_team2.next
            result.append((current_team1.data, current_team2.data))
            current_team1 = current_team1.next
            current_team2 = current_team2.next
        return result
    
    
    
    
    
team1 = CircularDoublyLinkedList()
team1.append("Ivanov")
team1.append("Petrov")
team1.append("Sidorov")
team1.append("Smirnov")

team2 = CircularDoublyLinkedList()
team2.append("Gonzalez")
team2.append("Lopez")
team2.append("Rodriguez")
team2.append("Martinez")

n = 2
k = 3

draw_results = team1.lottery_draw(n, k)
for team1_player, team2_player in draw_results:
    print(f"Team 1: {team1_player}, Team 2: {team2_player}")

''',
69:'''
class Node():
    def __init__(self,obj=None,nxt=None):
        self.obj=obj
        self.nxt=nxt
    def __str__(self):
        return f'{self.obj}'
    
class CircleLinkedList():
    def __init__(self):
        self.head=None
        self.tail=None
    def append_r(self,obj):
        if self.head==None and self.tail==None:
            new_obj=Node(obj)
            #print(new_obj.__dict__)
            self.head=new_obj
            self.tail=new_obj
            self.tail.nxt = self.head
            
        elif self.head==self.tail:  
            new_obj=Node(obj)
           # print(new_obj.__dict__)
            self.tail=new_obj  
            self.head.nxt=self.tail
            self.tail.nxt = self.head
            
        else:
            new_obj=Node(obj)
            #print(new_obj.__dict__)
            self.tail.nxt=new_obj
            self.tail=new_obj
            self.tail.nxt = self.head
            
    def append_l(self,obj):
        if self.head==None and self.tail==None:
            new_obj=Node(obj)
           # print(new_obj.__dict__)
            self.head=new_obj
            self.tail=new_obj          
            self.tail.nxt=self.head
        elif self.head==self.tail:  
            new_obj=Node(obj)
            #print(new_obj.__dict__)  
            self.head=new_obj
            self.head.nxt=self.tail    
            self.tail.nxt=self.head
        else:
            new_obj=Node(obj)
           # print(new_obj.__dict__)
            a=self.head
            self.head=new_obj 
            self.head.nxt=a
            self.tail.nxt=self.head
#     добавляем в середину списка

    def append_sr(self, obj, posle_chego):
        a = self.head
        print(a.__dict__)
        new_obj = Node(obj)
        for i in range(1, posle_chego):
            a = a.nxt
        b = a.nxt
        a.nxt = new_obj
        a.nxt.nxt = b
        a=self.head
        while a.nxt!=self.head:
            a=a.nxt
            self.tail=a
        self.tail=a
    

        
        
    def __str__(self):
        if self.head==None:
            return 'пуст'
        else:
            print('[')
            current=self.head
            while current is not None:
                print(current)
                current=current.nxt    
                if current==self.head:
                    break
            return ']'
    
    
    
s=CircleLinkedList()
s.append_r(5)
print(s.__dict__)
s.append_r(15)
print(s.__dict__)
s.append_r(25)
print(s.__dict__)
s.append_l(5)
print(s.__dict__)
s.append_l(15)
print(s.__dict__)
s.append_l(25)
print(s.__dict__)
print(s)
s.append_sr(77,2) # элемент 77 вставить после 2ого элемента

print(s)
''',
70:'''
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node

    def filter_by_manufacturer(self, manufacturer):
        new_list = LinkedList()
        current = self.head
        while current is not None:
            if current.data['manufacturer'] == manufacturer:
                new_list.append(current.data)
            current = current.next
        return new_list

    def __str__(self):
        if self.head is None:
            return "Список пуст"
        current = self.head
        linked_list_str = ""
        while current is not None:
            linked_list_str += str(current.data) + " -> "
            current = current.next
        return linked_list_str + "None"

# Пример использования
products = LinkedList()

# Добавляем товары в список
products.append({"name": "Телевизор", "manufacturer": "Samsung"})
products.append({"name": "Холодильник", "manufacturer": "Bosch"})
products.append({"name": "Стиральная машина", "manufacturer": "Bosch"})
products.append({"name": "Пылесос", "manufacturer": "Dyson"})
products.append({"name": "Микроволновка", "manufacturer": "Bosch"})

# Фильтруем товары по производителю Bosch
bosch_products = products.filter_by_manufacturer("Bosch")

# Выводим новый список товаров Bosch
print(bosch_products)

''', 
71:'''
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node

    def merge_lists(self, list1, list2):
        merged_list = LinkedList()

        current1 = list1.head
        while current1 is not None:
            current2 = list2.head
            while current2 is not None:
                if current1.data == current2.data:
                    merged_list.append(current1.data)
                    break
                current2 = current2.next
            current1 = current1.next

        return merged_list

    def __str__(self):
        if self.head is None:
            return "Список пуст"
        current = self.head
        linked_list_str = ""
        while current is not None:
            linked_list_str += str(current.data) + " -> "
            current = current.next
        return linked_list_str + "None"

# Пример использования
bork_products = LinkedList()
philips_products = LinkedList()

# Добавляем товары в список BORK
bork_products.append("Телевизор")
bork_products.append("Микроволновка")
bork_products.append("Пылесос")

# Добавляем товары в список PHILIPS
philips_products.append("Телевизор")
philips_products.append("Утюг")
philips_products.append("Миксер")

# Создаем новый список товаров, выпускаемых и BORK, и PHILIPS
merged_list = LinkedList().merge_lists(bork_products, philips_products)

# Выводим новый список товаров
print(merged_list)

''',
72:'''
class Node:
    def __init__(self, surname):
        self.surname = surname
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None

    def add_student(self, surname):
        new_node = Node(surname)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next is not None:
                current = current.next
            current.next = new_node

    def print_list(self):
        current = self.head
        while current is not None:
            print(current.surname)
            current = current.next

def transfer_students(list1, list2, n):
    if list1.head is None:
        print("Ошибка: Первый список пуст.")
        return

    if n <= 0:
        print("Ошибка: Некорректное значение n.")
        return

    # Находим последний узел первого списка
    last_node_list1 = list1.head
    while last_node_list1.next is not None:
        last_node_list1 = last_node_list1.next

    # Находим последний узел второго списка
    last_node_list2 = list2.head
    while last_node_list2.next is not None:
        last_node_list2 = last_node_list2.next

    # Проверяем, есть ли достаточно студентов в первом списке
    current_node = list1.head
    count = 0
    while current_node is not None and count < n:
        current_node = current_node.next
        count += 1

    if count < n:
        print("Ошибка: В первом списке недостаточно студентов.")
        return

    # Перемещаем n студентов из первого списка во второй
    current_node = list1.head
    for _ in range(n):
        next_node = current_node.next
        current_node.next = None
        last_node_list2.next = current_node
        last_node_list2 = current_node
        current_node = next_node

    # Обновляем ссылку последнего узла первого списка
    last_node_list1.next = current_node

    # Выводим сообщение об успешном переносе
    print(f"Перенесено {n} студентов из первого списка во второй список.")

# Создаем первый связный список
group1 = LinkedList()
group1.add_student("Иванов")
group1.add_student("Петров")
group1.add_student("Сидоров")

# Создаем второй связный список
group2 = LinkedList()
group2.add_student("Смирнов")
group2.add_student("Козлов")

# Переносим 2 студента из первого списка во второй
transfer_students(group1, group2, 2)

# Выводим оба списка
print("Список 1:")
group1.print_list()

print("Список 2:")
group2.print_list()

''',
73:'''
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None


class CircularLinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.head.next = self.head
        else:
            current = self.head
            while current.next != self.head:
                current = current.next
            current.next = new_node
            new_node.next = self.head

    def __len__(self):
        count = 0
        if self.head is None:
            return count
        current = self.head
        while True:
            count += 1
            current = current.next
            if current == self.head:
                break
        return count

    def __str__(self):
        if self.head is None:
            return "Кольцевой список пустой"
        current = self.head
        clist_str = ""
        while True:
            clist_str += str(current.data) + " ⇄ "
            current = current.next
            if current == self.head:
                break
        return clist_str.rstrip(" ⇄ ")

def generate_shopping_list(products, customers, n, m):
    shopping_list = CircularLinkedList()

    product_node = products.head
    customer_node = customers.head

    count = 1  # Счетчик покупателей
    while count <= len(customers):
        if count % n == 0:
            for _ in range(m):
                shopping_list.append((customer_node.data, product_node.data))
                product_node = product_node.next
        customer_node = customer_node.next
        count += 1

    return shopping_list

# Создание кольцевого списка с перечнем товаров
products = CircularLinkedList()
products.append("Товар 1")
products.append("Товар 2")
products.append("Товар 3")
products.append("Товар 4")
products.append("Товар 5")

# Создание кольцевого списка с фамилиями покупателей
customers = CircularLinkedList()
customers.append("Покупатель 1")
customers.append("Покупатель 2")
customers.append("Покупатель 3")
customers.append("Покупатель 4")
customers.append("Покупатель 5")

# Параметры n и m
n = 2
m = 3

# Генерация списка покупок
shopping_list = generate_shopping_list(products, customers, n, m)

# Вывод списка покупок
print("Список покупок:")
print(shopping_list)

''',
74:'''
import random

# Создание случайного массива размерности n
n = 10
array = [random.randint(-20, 20) for _ in range(n)]
print("Исходный массив:")
print(array)

# Вычисление суммы отрицательных и положительных элементов
negative_sum = sum([num for num in array if num < 0])
positive_sum = sum([num for num in array if num > 0])

# Сортировка массива в зависимости от суммы элементов
if abs(negative_sum) > positive_sum:
    sorted_array = sorted(array)
else:
    sorted_array = sorted(array, reverse=True)

# Вывод отсортированного массива
print("Отсортированный массив:")
print(sorted_array)

''',
75:'''
import random

# Создание случайного массива размерности n
n = 10
array = [random.randint(-30, 30) for _ in range(n)]
print("Исходный массив:")
print(array)

# Проверка наличия отрицательных элементов
has_negative = any(num < 0 for num in array)

# Сортировка массива в зависимости от наличия отрицательных элементов
if has_negative:
    sorted_array = sorted(array)
else:
    sorted_array = sorted(array, reverse=True)

# Вывод отсортированного массива
print("Отсортированный массив:")
print(sorted_array)

''',
76:'''
import random

# Создание случайного массива размерности n
n = 10
array = [random.randint(0, 100) for _ in range(n)]
print("Исходный массив:")
print(array)

# Рассчет остатка от деления на 7 и вычисление произведения остатков больше 4
product = 1
for num in array:
    remainder = num % 7
    if remainder > 4:
        product *= remainder

# Вывод произведения остатков
print("Произведение остатков больше 4:", product)

''',
77:'''
import random

# Создание случайного массива размерности n
n = 10
array = [random.randint(0, 100) for _ in range(n)]
print("Исходный массив:")
print(array)

# Рассчет количества четных и нечетных элементов на соответствующих позициях
even_on_odd_count = 0
odd_on_even_count = 0

for i in range(len(array)):
    if i % 2 != 0 and array[i] % 2 == 0:
        even_on_odd_count += 1
    elif i % 2 == 0 and array[i] % 2 != 0:
        odd_on_even_count += 1

# Сортировка массива в зависимости от результатов подсчета
if even_on_odd_count > odd_on_even_count:
    array.sort()
else:
    array.sort(reverse=True)

# Вывод отсортированного массива
print("Отсортированный массив:")
print(array)

''',
78:'''
# Исходные массивы
array1 = [1, 2, 3, 4, 5]
array2 = [3, 4, 5, 6, 7]

# Создание массива, содержащего общие элементы
common_elements = []

for element in array1:
    if element in array2 and element not in common_elements:
        common_elements.append(element)

# Вывод массива с общими элементами
print("Массив с общими элементами:")
print(common_elements)

''',
79:'''
def count_opposite_sign_pairs(arr):
    count = 0
    for i in range(1, len(arr)):
        if arr[i] * arr[i-1] < 0:
            count += 1
    return count

# Пример использования
array = [1, -2, 3, -4, 5, 6, -7]
result = count_opposite_sign_pairs(array)
print("Количество пар элементов разного знака:", result)

''',
80:'''
from functools import reduce

sentence = "Пример предложения без знаков препинания"

# Разделение предложения на список слов
word_list = sentence.split()

# Определение функции для обрезания первой и последней буквы у слова
def remove_first_and_last_letter(word):
    return word[1:-1]

# Отбрасывание первой и последней буквы у каждого слова
trimmed_words = list(map(remove_first_and_last_letter, word_list))

# Фильтрация слов длиной больше 5
filtered_words = list(filter(lambda x: len(x) > 5, trimmed_words))

# Склеивание обрезанных слов в одну строку
result = reduce(lambda x, y: x + y, filtered_words)

print(result)

''', 
81:'''
from functools import reduce

# Исходный список
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Заданный элемент для удаления
element_to_remove = 5

# Функция для фильтрации элементов, оставляем только те, которые не равны заданному элементу
def filter_element(element):
    return element != element_to_remove

# Функция для объединения элементов в список
def combine_elements(acc, element):
    acc.append(element)
    return acc

# Удаление заданного элемента из списка
filtered_numbers = list(filter(filter_element, numbers))

# Итоговый список после удаления элемента
result = reduce(combine_elements, filtered_numbers, [])

print(result)

''', 
82:'''
from functools import reduce

# Исходный список слов
words = ['apple', 'banana', 'orange', 'grapefruit', 'kiwi']

# Функция для рассчета остатка от деления на 7
def calculate_remainder(word):
    return len(word) % 7

# Функция для перемножения элементов
def multiply_elements(acc, element):
    return acc * element

# Рассчет остатков и фильтрация
remainders = list(filter(lambda x: x > 4, map(calculate_remainder, words)))

# Вычисление произведения остатков
result = reduce(multiply_elements, remainders, 1)

print(result)

''',
83:'''
from functools import reduce

# Исходный список целых чисел
numbers = [25, 36, 42, 57, 69, 73]

# Функция для рассчета остатка от деления на 17
def calculate_remainder(number):
    return number % 17

# Функция для перемножения элементов
def multiply_elements(acc, element):
    return acc * element

# Рассчет остатков и фильтрация
remainders = list(filter(lambda x: x < 7, map(calculate_remainder, numbers)))

# Вычисление произведения остатков
result = reduce(multiply_elements, remainders, 1)

print(result)

''',
84:'''
from functools import reduce

# Исходный список целых чисел
numbers = [-2, 3, 5, -7, 8, -10]

# Функция для рассчета произведения чисел
def multiply_numbers(acc, number):
    return acc * number

# Функция для суммирования значений
def sum_values(acc, value):
    return acc + value

# Рассчет произведения чисел
product = reduce(multiply_numbers, numbers, 1)

# Фильтрация и суммирование
filtered_sum = reduce(sum_values, filter(lambda x: x < 0, numbers), 0)

print("Произведение чисел:", product)
print("Сумма значений меньше 0:", filtered_sum)

''',
85:'''
class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

class BinaryTree:
    def __init__(self):
        self.root = None

    def insert(self, data):
        new_node = Node(data)
        if self.root is None:
            self.root = new_node
        else:
            current = self.root
            while True:
                if data < current.data:
                    if current.left is None:
                        current.left = new_node
                        break
                    else:
                        current = current.left
                else:
                    if current.right is None:
                        current.right = new_node
                        break
                    else:
                        current = current.right

    def search(self, data):
        current = self.root
        while current is not None:
            if data == current.data:
                return True
            elif data < current.data:
                current = current.left
            else:
                current = current.right
        return False

    def delete(self, data):
        if self.root is not None:
            self.root = self._delete(data, self.root)

    def _delete(self, data, node):
        if node is None:
            return node

        if data < node.data:
            node.left = self._delete(data, node.left)
        elif data > node.data:
            node.right = self._delete(data, node.right)
        else:
            if node.left is None:
                return node.right
            elif node.right is None:
                return node.left

            temp = self._find_min_node(node.right)
            node.data = temp.data
            node.right = self._delete(temp.data, node.right)

        return node

    def _find_min_node(self, node):
        while node.left is not None:
            node = node.left
        return node

    def __str__(self):
        return '\n'.join(self._display(self.root)[0])

    def _display(self, node):
        if node.right is None and node.left is None:
            line = str(node.data)
            width = len(line)
            height = 1
            middle = width // 2
            return [line], width, height, middle

        if node.right is None:
            lines, n, p, x = self._display(node.left)
            s = str(node.data)
            u = len(s)
            first_line = (x + 1)*' ' + (n - x - 1)*'_' + s
            second_line = x*' ' + '/' + (n - x - 1 + u)*' '
            shifted_lines = [line + u*' ' for line in lines]
            return [first_line, second_line] + shifted_lines, n + u, p + 2, n + u // 2

        if node.left is None:
            lines, n, p, x = self._display(node.right)
            s = str(node.data)
            u = len(s)
            first_line = s + x*'_' + (n - x)*' '
            second_line = (u + x)*' ' + '\\' + (n - x - 1)*' '
            shifted_lines = [u*' ' + line for line in lines]
            return [first_line, second_line] + shifted_lines, n + u, p + 2, u // 2

        left, n, p, x = self._display(node.left)
        right, m, q, y = self._display(node.right)
        s = str(node.data)
        u = len(s)
        first_line = (x + 1)*' ' + (n - x - 1)*'_' + s + y*'_' + (m - y)*' '
        second_line = x*' ' + '/' + (n - x - 1 + u + y)*' ' + '\\' + (m - y - 1)*' '
        if p < q:
            left += [n*' ']*(q - p)
        elif q < p:
            right += [m*' ']*(p - q)
        zipped_lines = zip(left, right)
        lines = [first_line, second_line] + [a + u*' ' + b for a, b in zipped_lines]
        return lines, n + m + u, max(p, q) + 2, n + u // 2
    def in_order_traversal_recursive(self, node):
        if node is not None:
            self.in_order_traversal_recursive(node.left)
            print(node.data)
            self.in_order_traversal_recursive(node.right)
            
tree = BinaryTree()
tree.insert(4)
tree.insert(2)
tree.insert(6)
tree.insert(1)
tree.insert(3)
tree.insert(5)
tree.insert(7)

tree.in_order_traversal_recursive(tree.root)

''',
86:'''
class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

class BinaryTree:
    def __init__(self):
        self.root = None

    def insert(self, data):
        new_node = Node(data)
        if self.root is None:
            self.root = new_node
        else:
            current = self.root
            while True:
                if data < current.data:
                    if current.left is None:
                        current.left = new_node
                        break
                    else:
                        current = current.left
                else:
                    if current.right is None:
                        current.right = new_node
                        break
                    else:
                        current = current.right

    def search(self, data):
        current = self.root
        while current is not None:
            if data == current.data:
                return True
            elif data < current.data:
                current = current.left
            else:
                current = current.right
        return False

    def delete(self, data):
        if self.root is not None:
            self.root = self._delete(data, self.root)

    def _delete(self, data, node):
        if node is None:
            return node

        if data < node.data:
            node.left = self._delete(data, node.left)
        elif data > node.data:
            node.right = self._delete(data, node.right)
        else:
            if node.left is None:
                return node.right
            elif node.right is None:
                return node.left

            temp = self._find_min_node(node.right)
            node.data = temp.data
            node.right = self._delete(temp.data, node.right)

        return node

    def _find_min_node(self, node):
        while node.left is not None:
            node = node.left
        return node

    def __str__(self):
        return '\n'.join(self._display(self.root)[0])

    def _display(self, node):
        if node.right is None and node.left is None:
            line = str(node.data)
            width = len(line)
            height = 1
            middle = width // 2
            return [line], width, height, middle

        if node.right is None:
            lines, n, p, x = self._display(node.left)
            s = str(node.data)
            u = len(s)
            first_line = (x + 1)*' ' + (n - x - 1)*'_' + s
            second_line = x*' ' + '/' + (n - x - 1 + u)*' '
            shifted_lines = [line + u*' ' for line in lines]
            return [first_line, second_line] + shifted_lines, n + u, p + 2, n + u // 2

        if node.left is None:
            lines, n, p, x = self._display(node.right)
            s = str(node.data)
            u = len(s)
            first_line = s + x*'_' + (n - x)*' '
            second_line = (u + x)*' ' + '\\' + (n - x - 1)*' '
            shifted_lines = [u*' ' + line for line in lines]
            return [first_line, second_line] + shifted_lines, n + u, p + 2, u // 2

        left, n, p, x = self._display(node.left)
        right, m, q, y = self._display(node.right)
        s = str(node.data)
        u = len(s)
        first_line = (x + 1)*' ' + (n - x - 1)*'_' + s + y*'_' + (m - y)*' '
        second_line = x*' ' + '/' + (n - x - 1 + u + y)*' ' + '\\' + (m - y - 1)*' '
        if p < q:
            left += [n*' ']*(q - p)
        elif q < p:
            right += [m*' ']*(p - q)
        zipped_lines = zip(left, right)
        lines = [first_line, second_line] + [a + u*' ' + b for a, b in zipped_lines]
        return lines, n + m + u, max(p, q) + 2, n + u // 2
    #a
    def get_leftmost_leaf(self, node):
        if node is None:
            return None

        current = node
        while current.left is not None:
            current = current.left

        return current.data
    
    #b
    def count_occurrences(self, node, target):
        if node is None:
            return 0

        count = 0
        if node.data == target:
            count += 1

        count += self.count_occurrences(node.left, target)
        count += self.count_occurrences(node.right, target)

        return count
    
    #c
    def calculate_average(self, node):
        if node is None:
            return 0, 0  # Возвращаем кортеж (сумма, количество)

        left_sum, left_count = self.calculate_average(node.left)
        right_sum, right_count = self.calculate_average(node.right)

        total_sum = left_sum + right_sum + node.data
        total_count = left_count + right_count + 1

        return total_sum, total_count

    def average_of_all_nodes(self):
        total_sum, total_count = self.calculate_average(self.root)
        if total_count > 0:
            return total_sum / total_count
        else:
            return 0
        
        
#a,b
tree = BinaryTree()
tree.insert(4)
tree.insert(2)
tree.insert(6)
tree.insert(2)
tree.insert(5)

leftmost_leaf = tree.get_leftmost_leaf(tree.root)
occurrences = tree.count_occurrences(tree.root, 2)

print("Leftmost Leaf:", leftmost_leaf)
print("Occurrences of 2:", occurrences)


#c
tree = BinaryTree()
tree.insert(4)
tree.insert(2)
tree.insert(6)
tree.insert(1)
tree.insert(3)
tree.insert(5)
tree.insert(7)

average = tree.average_of_all_nodes()
print("Average of all nodes:", average)

''',
87:'''
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class Stack:
    def __init__(self):
        self.head = None
        
    def is_empty(self):
        return self.head is None
    
    def push(self, item):
        new_node = Node(item)
        new_node.next = self.head
        self.head = new_node
    
    def pop(self):
        if self.is_empty():
            return None
        else: 
            popped_item = self.head.data
            self.head = self.head.next
            return popped_item
        
    def peek(self):
        if self.is_empty():
            return None
        else:
            return self.head.data
        
    def __str__(self):
        current = self.head
        stack_str = ""
        while current:
            stack_str += str(current.data) + " → "
            current = current.next
        return stack_str.rstrip(" → ")

'''

        }
        print(sklad[n])
    def pract27(n):
        sklad = {
1:'''
2. а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Фибоначчи. 
lst = [3, 5, 7, 8, 13, 21, 34, 42, 55, 89]
# определяем функцию для вычисления чисел Фибоначчи
fibonacci = lambda n: n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)
# используем лямбда-функцию для фильтрации списка
result = list(filter(lambda x: x in [fibonacci(n) for n in range(len(lst)+1)], lst))
print(result)
б) Напишите программу для сортировки заданного списка кортежей по разности между максимальным и минимальным элементами каждого кортежа.
lst = [(5, 10, 15), (2, 4, 6), (8, 16, 24), (3, 8, 11)]
# определяем функцию для вычисления разности между максимальным и минимальным элементами кортежа
key_function = lambda t: max(t) - min(t)
# сортируем список кортежей по разности между максимальным и минимальным элементами
lst.sort(key=key_function)
print(lst)
 в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые содержат только согласные буквы.
strings = ["hello", "world", "python", "xttnbz"]
consonants = lambda string: all(letter.isalpha() and letter.lower() not in "aeiouy" for letter in string)
result = list(filter(consonants, strings))
print(result)
3. Реализовать функцию, которая находит максимальный элемент в двусвязном списке и удаляет его из списка. 
class Node:
    def __init__(self, value, prev=None, next=None):
        self.value = value
        self.prev = prev
        self.next = next

class DoubleLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def add(self, value):
        new_node = Node(value)

        if self.head is None:
            self.head = new_node
            self.tail = new_node
        else:
            new_node.prev = self.tail
            self.tail.next = new_node
            self.tail = new_node

    def delete_max_node(self):
        if self.head is None:
            return None

        max_node = self.head
        current_node = self.head
        while current_node:
            if current_node.value > max_node.value:
                max_node = current_node
            current_node = current_node.next

        # Если удаляемый элемент является первым узлом
        if max_node.prev is None:
            self.head = max_node.next
            if self.head is not None:
                self.head.prev = None
        else:
            max_node.prev.next = max_node.next
            if max_node.next is not None:
                max_node.next.prev = max_node.prev

        return max_node.value

# создаём объект DoubleLinkedList
lst = DoubleLinkedList()
lst.add(5)
lst.add(10)
lst.add(15)
lst.add(20)

# удаляем максимальный элемент и выводим содержимое списка
max_value = lst.delete_max_node()
print("Максимальный элемент:", max_value)

current_node = lst.head
while current_node:
    print(current_node.value)
    current_node = current_node.next''',
2:'''
2. а) Отфильтровать список целых чисел на простые и составные числа с помощью лямбда-функции. 
numbers = [1,2, 3, 4, 5, 16, 19, 23, 24]
primes = lambda x: all(x % i != 0 for i in range(2, int(x ** 0.5) + 1)) and x >= 1
result_primes = list(filter(primes, numbers))
composites = lambda x: any(x % i == 0 for i in range(2, int(x ** 0.5) + 1)) and x > 1
result_composites = list(filter(composites, numbers))
print("Primes:", result_primes)  # Primes: [2, 3, 5, 19, 23]
print("Composites:", result_composites)  # Composites: [4, 16, 24]
б) Для удаления определённых символов из заданной строки используйте лямбда-функцию. Пример: дана строка 'hello world', удалить символы 'l' и 'o' → 'he wrd'. 
string = 'hello world'
to_remove = 'lo'
remove_chars = lambda s: ''.join(filter(lambda c: c not in to_remove, s))
result = remove_chars(string)
print(result) # "he wrd"
в) Используя лямбда-функцию, проверить, является ли указанный список палиндромом или нет.
lst = ["a", "b", "c", "b", "a"]
palindrome = lambda l: all(l[i] == l[-i-1] for i in range(len(l)//2))
result = palindrome(lst)
print(result)  # True
3. Реализовать функцию, которая проверяет, является ли двусвязный список палиндромом (элементы списка читаются одинаково как слева направо, так и справа налево).
class Node:
    def __init__(self, val=None, prev=None, next=None):
        self.val = val
        self.prev = prev
        self.next = next

class DoubleLinkedList:
    def __init__(self, *args):
        self.head = None
        self.tail = None
        self.length = 0

        for val in args:
            self.append(val)

    def append(self, val):
        'Добавление элемента в конец списка'
        node = Node(val, self.tail)
        if not self.head:
            self.head = node
        if self.tail:
            self.tail.next = node

        self.tail = node
        self.length += 1

    def __str__(self):
        'Строковое представление списка'
        node = self.head
        values = []
        while node:
            values.append(str(node.val))
            node = node.next
        return ' <-> '.join(values)

    def is_palindrome(self):
        'Проверка на палиндром'
        if not self.head:
            return True

        # Подход с двумя указателями: медленный и быстрый
        slow, fast = self.head, self.head
        while fast and fast.next:
            slow, fast = slow.next, fast.next.next
        
        # Реверсируем вторую половину
        prev, curr = None, slow
        while curr:
            next_node = curr.next
            curr.next = prev
            curr.prev, prev = prev, curr
            curr = next_node
        
        # Сравниваем элементы списка
        first, second = self.head, prev
        while first and second:
            if first.val != second.val:
                return False
            first, second = first.next, second.next
        
        return True

# Создаем DoubleLinkedList, который является палиндромом: 1 <-> 2 <-> 3 <-> 2 <-> 1
dllist = DoubleLinkedList(1, 2, 3, 2, 1)

print(dllist.is_palindrome()) # True

# Создаем DoubleLinkedList, который НЕ является палиндромом: 1 <-> 2 <-> 3 <-> 4 <-> 5
dllist = DoubleLinkedList(1, 2, 3, 5, 1)

print(dllist.is_palindrome()) # False''',
3:'''
2. Создайте класс «Банк» с атрибутами название, адрес и список клиентов. Каждый клиент представлен классом «Клиент» с атрибутами имя, фамилия, номер счета и баланс. Напишите методы для добавления клиента в банк, удаления клиента из банка и вывода информации о банке в виде «Банк '{название}', адрес - {адрес}, клиенты - {список клиентов}». Используйте магический метод __str__ для вывода информации о клиенте в удобном формате.
class Client:
    def __init__(self, first_name, last_name, account_number, balance):
        self.first_name = first_name
        self.last_name = last_name
        self.account_number = account_number
        self.balance = balance

    def __str__(self):
        return f"{self.first_name} {self.last_name}, account number - {self.account_number}, balance - {self.balance}"

class Bank:
    def __init__(self, name, address, clients = None):
        self.name = name
        self.address = address
        self.clients = []

    def add_client(self, client):
        'Добавление клиента в банк'
        self.clients.append(client)

    def remove_client(self, client):
        'Удаление клиента из банка'
        self.clients.remove(client)

    def __str__(self):
        client_info = "\n".join([str(client) for client in self.clients])
        return f"Bank '{self.name}', address - {self.address}, clients -\n{client_info}"

# Создаем банк и добавляем в него клиентов
bank = Bank("MyBank", "10 Main Street")
client1 = Client("John", "Doe", "111111", 1000)
client2 = Client("Jane", "Doe", "222222", 2000)
bank.add_client(client1)
bank.add_client(client2)

print(bank) 
# Вывод: Bank 'MyBank', address - 10 Main Street, clients -
# John Doe, account number - 111111, balance - 1000
# Jane Doe, account number - 222222, balance - 2000

# Удаляем клиента из банка и выводим информацию о банке ещё раз
bank.remove_client(client2)

print(bank)
# Вывод: Bank 'MyBank', address - 10 Main Street, clients -
# John Doe, account number - 111111, balance - 1000
3. Дан стек. Необходимо проверить, содержит ли он хотя бы один элемент, который является квадратом другого элемента стека.
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class Stack:
    def __init__(self):
        self.head = None

    def is_empty(self):
        return self.head is None

    def push(self, data):
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node

    def pop(self):
        if self.is_empty():
            return None
        popped = self.head
        self.head = self.head.next
        return popped.data

    def peek(self):
        if self.is_empty():
            return None
        return self.head.data

    def contains_square_pair(self):
        if self.is_empty():
            return False

        current = self.head
        values = set()

        while current:
            if current.data ** 2 in values:
                return True
            values.add(current.data)
            current = current.next

        return False

stack = Stack()
stack.push(1)
stack.push(3)
stack.push(0)
stack.push(4)
stack.push(5)

if stack.contains_square_pair():
    print("Содержит квадрат.")
else:
    print("Не содержит квадрат")
    
current = stack.head
while current:
    print(current.data)
    current = current.next
''',
4:'''2. Создайте класс АВТОМОБИЛЬ с методами, позволяющими вывести на экран информацию об автомобиле, а также определить, подходит ли данный автомобиль для заданных условий. Создайте дочерние классы ЛЕГКОВОЙ (марка, модель, год выпуска, объем двигателя, тип топлива), ГРУЗОВОЙ (марка, модель, год выпуска, грузоподъемность), ПАССАЖИРСКИЙ (марка, модель, год выпуска, количество мест) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список автомобилей, выведите полную информацию из базы на экран, а также организуйте поиск автомобилей с заданными характеристиками.
class Car:
    def __init__(self, brand, model, year):
        self.brand = brand
        self.model = model
        self.year = year

    def print_info(self):
        print(f'{self.brand} {self.model}, year {self.year}')

    def is_suitable(self, **kwargs):
        return True


class PassengerCar(Car):
    def __init__(self, brand, model, year, seats_count):
        super().__init__(brand, model, year)
        self.seats_count = seats_count

    def print_info(self):
        super().print_info()
        print(f'Seats count: {self.seats_count}')

    def is_suitable(self, **kwargs):
        if 'min_seats' in kwargs:
            return self.seats_count >= kwargs['min_seats']
        else:
            return True


class Truck(Car):
    def __init__(self, brand, model, year, carrying_capacity):
        super().__init__(brand, model, year)
        self.carrying_capacity = carrying_capacity

    def print_info(self):
        super().print_info()
        print(f'Carrying capacity: {self.carrying_capacity} kg')

    def is_suitable(self, **kwargs):
        if 'min_carrying_capacity' in kwargs:
            return self.carrying_capacity >= kwargs['min_carrying_capacity']
        else:
            return True


class Bus(Car):
    def __init__(self, brand, model, year, seats_count):
        super().__init__(brand, model, year)
        self.seats_count = seats_count

    def print_info(self):
        super().print_info()
        print(f'Seats count: {self.seats_count}')

    def is_suitable(self, **kwargs):
        if 'min_seats' in kwargs:
            return self.seats_count >= kwargs['min_seats']
        else:
            return True

passenger_cars = [
    PassengerCar('Brand1', 'Model1', 2010, 5),
    PassengerCar('Brand2', 'Model2', 2015, 3),
    PassengerCar('Brand3', 'Model3', 2018, 7),
]
for car in passenger_cars:
    car.print_info()
    print()
suitable_cars = list(filter(lambda car: car.is_suitable(min_seats=5), passenger_cars))

for car in suitable_cars:
    car.print_info()
truck_cars = [
    Truck('bmw', 'f5', 2018, 50),
    Truck('merc', 'a8', 2010, 33),
    Truck('audi', 'Mu5', 2003, 10),
]
for car in truck_cars:
    car.print_info()
    print()
suitable_cars = list(filter(lambda car: car.is_suitable(min_carrying_capacity=15), truck_cars))
for car in suitable_cars:
    car.print_info()
3. Реализовать функцию, которая находит произведение квадратов всех элементов в двусвязном списке.
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None
        self.prev = None

class DoubleLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.length = 0
        
    def add(self, value):
        node = Node(value)
        if self.length == 0:
            self.head = node
            self.tail = node
        else:
            node.prev = self.tail
            self.tail.next = node
            self.tail = node
        self.length += 1
        
    def multiply_squares(self):
        result = 1
        node = self.head
        while node is not None:
            result *= node.value ** 2
            node = node.next
        return result

# пример использования
lst = DoubleLinkedList()
lst.add(1)
lst.add(2)
lst.add(3)
lst.add(4)

print(lst.multiply_squares()) # выведет 576
''',
5:'''
2. Создайте класс ФИЛЬМ с методами, позволяющими вывести на экран информацию о фильме, а также определить, подходит ли данный фильм для заданных условий. Создайте дочерние классы КОМЕДИЯ (название, год выпуска, режиссер, актеры), ДРАМА (название, год выпуска, режиссер, актеры), ФАНТАСТИКА (название, год выпуска, режиссер, актеры) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список фильмов, выведите полную информацию из базы на экран, а также организуйте поиск фильмов с заданным годом выпуска или режиссером.
class Film:
    def __init__(self, title, year, director, actors):
        self.title = title
        self.year = year
        self.director = director
        self.actors = actors
    
    def info(self):
        print("Название фильма:", self.title)
        print("Год выпуска:", self.year)
        print("Режиссер:", self.director)
        print("Актеры:", self.actors)
    
    def check(self, year=None, director=None):
        if year is not None and self.year != year:
            return False
        if director is not None and self.director != director:
            return False
        return True

class Comedy(Film):
    def __init__(self, title, year, director, actors):
        super().__init__(title, year, director, actors)

    def info(self):
        print("Комедия")
        super().info()

class Drama(Film):
    def __init__(self, title, year, director, actors):
        super().__init__(title, year, director, actors)

    def info(self):
        print("Драма")
        super().info()

class Fantasy(Film):
    def __init__(self, title, year, director, actors):
        super().__init__(title, year, director, actors)

    def info(self):
        print("Фантастика")
        super().info()

films = [Comedy("Красотка", 1990, "Гэри Маршалл", ["Джулия Робертс", "Ричард Гир"]),
         Drama("Зеленая миля", 1999, "Гэри Маршалл", ["Том Хэнкс", "Майкл Кларк Дункан"]),
         Fantasy("Космический десант", 1997, "Пол Верховен", ["Каспер Ван Дин", "Дина Мейер"])]

for film in films:
    film.info()
    print()

year = 1990
director = "Гэри Маршалл"

print(f"Фильмы с годом выпуска {year}:")
for film in films:
    if film.check(year=year):
        film.info()
        print()

print(f"Фильмы режиссера {director}:")
for film in films:
    if film.check(director=director):
        film.info()
        print()


3. Найти высоту бинарного дерева поиска.
class Node:
    def __init__(self, key):
        self.left_child = None
        self.right_child = None
        self.value = key


class BinaryTree:
    def __init__(self, value):
        self.value = value
        self.left_child = None
        self.right_child = None
    
    def insert_left(self, value):
        if self.left_child == None:
            self.left_child = BinaryTree(value)
        else:
            new_node = BinaryTree(value)
            new_node.left_child = self.left_child
            self.left_child = new_node
    
    def insert_right(self, value):
        if self.right_child == None:
            self.right_child = BinaryTree(value)
        else:
            new_node = BinaryTree(value)
            new_node.right_child = self.right_child
            self.right_child = new_node
    
    def get_height(self):
        if self.left_child and self.right_child:
            return 1 + max(self.left_child.get_height(), self.right_child.get_height())
        elif self.left_child:
            return 1 + self.left_child.get_height()
        elif self.right_child:
            return 1 + self.right_child.get_height()
        else:
            return 1
    
root = BinaryTree(1)
root.insert_left(2)
root.insert_right(3)
root.left_child.insert_left(4)
root.left_child.insert_right(5)
root.right_child.insert_left(6)

print("Высота дерева:", root.get_height())
''',
6:'''
2. а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются палиндромами. 
def is_palindrome(num):
    return str(num) == str(num)[::-1]
numbers = [123, 4554, 987, 363, 555, 321, 1001, 4774]
palindromes = list(filter(lambda x: is_palindrome(x), numbers))
print("Палиндромы в списке:", palindromes)
б) Напишите программу для сортировки заданного списка строк по количеству гласных букв в каждой строке.
def count_vowels(s):
    vowels = 'aeiouyAEIOUY'
    count = 0
    for c in s:
        if c in vowels:
            count += 1
    return count

words = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']

sorted_words = sorted(words, key=lambda x: count_vowels(x))

print("Исходный список слов:", words)
print("Список слов, отсортированный по количеству гласных букв:", sorted_words)
 в) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются совершенными числами (сумма делителей числа равна самому числу).
def is_perfect(num):
    divisors = []
    for i in range(1, num):
        if num % i == 0:
            divisors.append(i)
    return sum(divisors) == num

numbers = [1,6, 12, 18, 24, 28, 30, 32, 36, 40,496]

perfect_numbers = list(filter(lambda x: is_perfect(x), numbers))

print("Совершенные числа в списке:", perfect_numbers)
3. Найти наибольший элемент, меньший заданного значения, в бинарном дереве поиска.
class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

class BinaryTree:
    def __init__(self):
        self.root = None

    def insert(self, value):
        new_node = Node(value)
        if self.root is None:
            self.root = new_node
        else:
            current = self.root
            while True:
                if value < current.value:
                    if current.left is None:
                        current.left = new_node
                        break
                    else:
                        current = current.left
                else:
                    if current.right is None:
                        current.right = new_node
                        break
                    else:
                        current = current.right

    def find_largest_smaller_than(self, value):
        largest_smaller = None
        current = self.root
        while current is not None:
            if current.value < value:
                largest_smaller = current.value
                current = current.right
            else:
                current = current.left
        return largest_smaller
tree = BinaryTree()
tree.insert(5)
tree.insert(3)
tree.insert(7)
tree.insert(1)
tree.insert(9)

largest_smaller = tree.find_largest_smaller_than(6)
print(largest_smaller)  # 5

largest_smaller = tree.find_largest_smaller_than(2)
print(largest_smaller)  # 1

largest_smaller = tree.find_largest_smaller_than(9)
print(largest_smaller)  # 9
''',
7:'''
2. Создайте класс «Круг» с атрибутами радиус и цвет. Напишите методы для вычисления площади и длины окружности круга. Используйте магический метод __str__ для вывода информации о круге в виде «Круг радиуса {радиус} и цвета {цвет}». 
import math

class Circle:
    def __init__(self, radius, color):
        self.radius = radius
        self.color = color

    def area(self):
        return math.pi * self.radius ** 2

    def circumference(self):
        return 2 * math.pi * self.radius

    def __str__(self):
        return f"Круг радиуса {self.radius} и цвета {self.color}"
circle = Circle(2, "красный")
print(circle)
print(f"Площадь круга: {circle.area()}")
print(f"Длина окружности: {circle.circumference()}")
3. Реализовать функцию, которая находит минимальный элемент в двусвязном списке.
class Node:
    def __init__(self, data=None, prev=None, next=None):
        self.data = data
        self.prev = prev
        self.next = next
        
class DoubleLinkedList:
    def __init__(self, *args):
        self.head = None
        self.tail = None
        self.count = 0
        for arg in args:
            self.append(arg)

    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
        else:
            new_node.prev = self.tail
            self.tail.next = new_node
            self.tail = new_node
        self.count += 1

    def get_min(self):
        if self.head is None:
            return None
        min_value = self.head.data
        current = self.head.next
        while current is not None:
            if current.data < min_value:
                min_value = current.data
            current = current.next
        return min_value
linked_list = DoubleLinkedList(5, 3, 33, 11, 9)

min_value = linked_list.get_min()
print(min_value)  # ''',
8:'''
2. Создайте класс «Студент» с атрибутами имя, возраст и список оценок. Напишите методы для вычисления среднего балла и определения успеваемости студента (средний балл выше 4). Используйте магический метод __repr__ для вывода информации о студенте в виде «Студент {имя}, возраст {возраст}».
class Student:
    def __init__(self, name, age, grades):
        self.name = name
        self.age = age
        self.grades = grades

    def average_grade(self):
        return sum(self.grades) / len(self.grades)

    def is_excellent_student(self):
        return self.average_grade() > 4

    def __repr__(self):
        return f"Студент {self.name}, возраст {self.age}"
student = Student("Иванов", 20, [4, 5, 3, 4, 5])
print(student)
print(f"Средний балл: {student.average_grade()}")
print(f"Студент {'не' if not student.is_excellent_student() else ''}успевает")
3. Создать класс очереди, который будет хранить только элементы, большие заданного значения. Значение задается при инициализации объекта класса очереди. При добавлении элемента, если он меньше или равен заданному значению, то он не должен добавляться.
class Node:
    def __init__(self, value=None):
        self.value = value
        self.next = None

class Queue:
    def __init__(self, threshold):
        self.head = None
        self.tail = None
        self.size = 0
        self.threshold = threshold         # заданное значение
    
    def is_empty(self):
        return self.size == 0
    
    def enqueue(self, value):
        if value > self.threshold:         # если значение больше заданного
            new_node = Node(value)
            if self.is_empty():
                self.head = new_node
                self.tail = new_node
            else:
                self.tail.next = new_node
                self.tail = new_node
            self.size += 1
    
    def dequeue(self):
        if self.is_empty():
            return None
        else:
            value = self.head.value
            self.head = self.head.next
            self.size -= 1
            return value
    
    def __str__(self):
        result = []
        current = self.head
        while current:
            result.append(str(current.value))
            current = current.next
        return '\n'.join(result)

# Пример использования
q = Queue(5)
q.enqueue(2)
q.enqueue(6)
q.enqueue(8)
q.enqueue(3)
q.enqueue(1)
q.enqueue(9)
q.enqueue(44)
print(q)
''',
9:'''
2. а) Используя лямбда-функцию, отсортировать список строковых значений в алфавитном порядке. 

lst = ['apple', 'banana', 'cherry', 'date', 'eggplant', 'fig']

lst.sort(key=lambda x: x)

print(lst)
б) Найдите все анаграммы в заданном списке строк с помощью лямбдафункции.
lst = ['race', 'care', 'nika','derf', 'tool', 'loot', 'doctor', 'octord']
anagrams = []
sorted_words = list(map(lambda x: ''.join(sorted(x)), lst))
for i in range(len(lst)):
    if sorted_words.count(sorted_words[i]) > 1:
        anagrams.append(lst[i])
print("Anagrams:", anagrams)
 в) Найти индекс и значение максимального и минимального значений в заданном списке чисел с помощью лямбда-функции.
lst = [2, 3, 4, 1, 7, 5, 6]
# Finding index and value of max and min values using lambda functions
max_index, max_value = max(enumerate(lst), key=lambda x: x[1])
min_index, min_value = min(enumerate(lst), key=lambda x: x[1])
print(f"Max value of {max_value} is at index {max_index}")
print(f"Min value of {min_value} is at index {min_index}")
3. Дан стек. Необходимо удалить из него все элементы, которые не являются делителями последнего элемента стека.
class Node:
    def __init__(self, value=None, next_node=None):
        self.value = value
        self.next_node = next_node

class Stack:
    def __init__(self):
        self.head = None
        self.length = 0

    def push(self, value):
        node = Node(value)
        if self.head is None:
            self.head = node
        else:
            node.next_node = self.head
            self.head = node
        self.length += 1

    def pop(self):
        if self.head is None:
            return None
        else:
            value = self.head.value
            self.head = self.head.next_node
            self.length -= 1
            return value

    def is_empty(self):
        return self.length == 0

    def peek(self):
        return self.head.value if self.head is not None else None
    def __str__(self):
        current_node = self.head
        result = ""
        while current_node:
            result += str(current_node.value) + " -> "
            current_node = current_node.next_node
        result += "None"
        return result
    def remove_non_divisors(self):
        if self.is_empty():
            return

        last_element = self.peek()
        current_node = self.head
        previous_node = None

        while current_node is not None:
            if last_element % current_node.value != 0:
                if previous_node is None:
                    self.head = current_node.next_node
                else:
                    previous_node.next_node = current_node.next_node
                self.length -= 1
            else:
                previous_node = current_node
            current_node = current_node.next_node
stack = Stack()
stack.push(4)
stack.push(9)
stack.push(3)
stack.push(15)
stack.push(9)
print("Исходный стек:")
current_node = stack.head
while current_node is not None:
    print(current_node.value)
    current_node = current_node.next_node
stack.remove_non_divisors()
print("Стек после удаления элементов-неделителей последнего элемента:")
current_node = stack.head
while current_node is not None:
    print(current_node.value)
    current_node = current_node.next_node
''',
10:'''
2. а) Используя лямбда-функцию, отсортировать список кортежей по второму элементу.
list1 = [(1, 4), (2, 9), (3, 2), (4, 8)]
list1.sort(key=lambda x: x[1])
print(list1) # [(3, 2), (1, 4), (4, 8), (2, 9)]
 б) Найти все числа, которые делятся на три или пять, из заданного списка чисел, используя лямбда-функцию.
numbers = [10, 15, 21, 22,30, 35, 40, 45, 50]
divisible_numbers = list(filter(lambda x: x % 3 == 0 or x % 5 == 0, numbers))
print(divisible_numbers)
 в) Напишите программу для сортировки заданного смешанного списка целых чисел и строк с помощью лямбда-функции. Строки должны быть отсортированы перед числами.
list1 = ['hello', 5, 'apple', 234, 'banana', 0]
print(sorted(list(filter(lambda x: type(x) is str, list1)))+sorted(list(filter(lambda x: type(x) is not str, list1))))
3. Написать функцию, которая принимает на вход двусвязный список и значение элемента, который нужно найти. Функция должна вернуть индекс первого вхождения элемента в список или -1, если элемент не найден.
def find_node_index(dllist, node_value):
    """ Функция для нахождения индекса первого вхождения указанного значения в двусвязном списке"""

    index = 0
    # Проверяем значения каждого узла в двусвязном списке
    current_node = dllist.head
    while current_node:
        # Если значение текущего узла равно искомому значению, возвращаем его индекс
        if current_node.data == node_value:
            return index
        index += 1
        current_node = current_node.next
    # Если значение не найдено, возвращаем -1
    return -1
class DoublyLinkedListNode:
    def __init__(self, value):
        self.data=value
        self.next=None
        self.prev=None


class DoublyLinkedList:
    def __init__(self):
        self.head=None
    
    def add(self, NumberList):
        for i in NumberList:
            if self.head == None:
                self.head = DoublyLinkedListNode(i)
                prev = self.head
            else:
                new_node = DoublyLinkedListNode(i)
                prev.next = new_node
                new_node.prev = prev
                prev = new_node
                
doubly_linked_list = DoublyLinkedList()
doubly_linked_list.add([1, 2, 3, 4, 3])

element_index = find_node_index(doubly_linked_list, 3)

print("Искомый элемент находится под индексом: ", element_index)
''',
11:'''
2. а) С помощью лямбда-функции проверьте, является ли данное число простым или нет.
is_prime = lambda number: all(number % i != 0 for i in range(2, int(number ** 0.5) + 1)) 
print(is_prime(1))  # True
print(is_prime(20))  # False
 б) Напишите программу для поиска чисел в заданном диапазоне, которые являются суммой двух квадратов. Пример: задан диапазон от 1 до 50 → [1, 2, 4, 5, 8, 9, 10, 13, 16, 17, 18, 20, 25, 26, 29, 32, 34, 36, 37, 40, 41, 45, 49, 50].
def is_sum_of_squares(n):
    # Функция проверяет, можно ли представить n как
    # сумму двух квадратов целых чисел
    for i in range(int(n**0.5)+1):
        j = int((n-i**2)**0.5)
        if i**2 + j**2 == n:
            return True
    return False

def find_sum_of_squares(start, end):
    # Функция ищет все числа в диапазоне [start, end],
    # которые можно представить как сумму двух квадратов
    result = []
    for n in range(start, end+1):
        if is_sum_of_squares(n):
            result.append(n)
    return result

# Пример использования
result = find_sum_of_squares(1, 50)
print(result)
 в) Удалите все элементы из заданного списка, не присутствующие в другом списке. Пример: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8] → [2, 4, 6, 8].
def filter_list(list1, list2):
    # Функция удаляет все элементы из list1,
    # которые не присутствуют в list2
    return list(filter(lambda x: x in list2, list1))
# Пример использования
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list2 = [2, 4, 6, 8]
result = filter_list(list1, list2)
print(result) # [2, 4, 6, 8]
3. Создать класс хеш-таблицы для хранения объектов класса «Сотрудник». Хеш-функция должна основываться на поле «должность» сотрудника. Если два сотрудника имеют одну и ту же должность, они должны храниться в одной ячейке таблицы.
class Employee():
    def __init__(self, name, post):
        self.name = name
        self.post = post
g = [Employee('Ника', "Программист"), Employee('Соня', "Дата-аналитик"), Employee('Ева', "Повар"), Employee('Кристина', "Программист"), Employee('Арина', "Няня"), Employee('Кира', "Бизнесвумен"),Employee('Адриана', "Уборщица")]
# реализация хеш-таблицы методом цепочек
class HashTable:
    def __init__(self, size):
        self.size = size
        self.table = [[] for _ in range(self.size)]

    def hash_function(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        slot = self.hash_function(key)
        for pair in self.table[slot]:
            if pair[0] == key:
                pair.append(value)
                return
        self.table[slot].append([key, value])

    def find(self, key):
        slot = self.hash_function(key)
        for pair in self.table[slot]:
            if pair[0] == key:
                return pair[1]
        return None
h = HashTable(5)
for i in g:
    h.insert(i.post, i)
print(h.table)''',
12:'''
2. Опишите класс Recipe, заданный названием, списком ингредиентов и шагами приготовления. Включите в описание класса методы: вывода информации о рецепте на экран, проверки, есть ли все ингредиенты для приготовления блюда, и свойство, позволяющее установить тип кухни (например, итальянская, японская и т. д.).
class Recipe:
    def __init__(self, title, ingredients, steps):
        self.title = title
        self.ingredients = ingredients
        self.steps = steps
        self.cuisine_type = ""

    def display_recipe(self):
        print(f"Recipe: {self.title}")
        print("Ingredients:")
        for i in self.ingredients:
            print(f"- {i}")
        print("Steps:")
        for i, step in enumerate(self.steps):
            print(f"{i+1}. {step}")

    def has_all_ingredients(self, available_ingredients):
        for i in self.ingredients:
            if i not in available_ingredients:
                return False
        return True

    @property
    def cuisine_type(self):
        return self._cuisine_type

    @cuisine_type.setter
    def cuisine_type(self, value):
        self._cuisine_type = value

# пример использования
r = Recipe("Spaghetti carbonara", ["spaghetti", "bacon", "eggs", "parmesan"], 
           ["Cook spaghetti al dente", "Fry bacon until crispy", 
            "Beat eggs and parmesan in a bowl", "Mix everything together and serve"])
r.cuisine_type = "Italian"
r.display_recipe()

available_ingredients = ["spaghetti", "bacon", "eggs"]
if r.has_all_ingredients(available_ingredients):
    print("Can cook the recipe!")
else:
    print("Cannot cook the recipe, missing ingredients.")
3. Написать метод класса «Заказ», который сортирует список заказов по дате с помощью алгоритма быстрой сортировки. Метод должен изменять исходный список.
class Order:
    def __init__(self, date, product):
        self.date = date
        self.product = product
    
    def __repr__(self):
        return f"Order(date={self.date}, product='{self.product}')"
    
    @staticmethod
    def quicksort_orders(orders):
        def partition(arr, low, high):
            i = (low - 1)
            pivot = arr[high].date

            for j in range(low, high):
                if arr[j].date <= pivot:
                    i = i + 1
                    arr[i], arr[j] = arr[j], arr[i]

            arr[i + 1], arr[high] = arr[high], arr[i + 1]
            return i + 1

        def quicksort_helper(arr, low, high):
            if low < high:
                pi = partition(arr, low, high)
                quicksort_helper(arr, low, pi - 1)
                quicksort_helper(arr, pi + 1, high)

        quicksort_helper(orders, 0, len(orders) - 1)


# Пример использования
orders = [
    Order("2023-08-15", "Product C"),
    Order("2023-07-10", "Product A"),
    Order("2023-06-12", "Product B")
]

print("Исходный список заказов:")
print(orders)

Order.quicksort_orders(orders)

print("\nОтсортированный список заказов:")
print(orders)''',
13:'''
2. а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются простыми числами. 
import math
# Исходный список чисел
numbers = [1,2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# Фильтрация простых чисел с помощью лямбда-выражения
prime_numbers = list(filter(lambda x: all(x % i != 0 for i in range(2, int(math.sqrt(x)) + 1)), numbers))
# Вывод отфильтрованного списка чисел
print(prime_numbers)
б) Напишите программу для сортировки заданного списка строк по убыванию длины каждой строки. 
# Заданный список строк
strings = ['apple', 'banana', 'pear', 'orange', 'kiwi', 'strawberry']
# Сортировка списка строк по убыванию длины
sorted_strings = sorted(strings, key=lambda x: -len(x))
# Вывод отсортированного списка строк
print(sorted_strings)
в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые содержат только буквы верхнего регистра.
# Исходный список строк
strings = ['abc', 'DEF', 'Ghi', 'jkl', 'MNO']
# Фильтрация строк, содержащих только буквы верхнего регистра с помощью лямбда-выражения
upper_strings = list(filter(lambda s: s.isupper(), strings))
# Вывод отфильтрованного списка строк
print(upper_strings)
3. Реализовать класс хеш-таблицы для хранения объектов класса «Книга». Хеш-функция должна основываться на поле «автор книги». Если две книги имеют одного и того же автора, они должны храниться в одной ячейке таблицы.
class Book():
    def __init__(self, author, title):
        self.author = author
        self.title = title
g = [Book('Роулинг', "Гарри Поттер"), Book('Есенин', "Стихи"), Book('Пушкин', "Капитанская дочка"), Book('Пушкин', "Дубровский"), Book('Тургенев', "Бежин луг"), Book('Роулинг', "Хоббит"),Book('Лермонтов', "Стихи")]
# реализация хеш-таблицы методом цепочек
class HashTable:
    def __init__(self, size):
        self.size = size
        self.table = [[] for _ in range(self.size)]

    def hash_function(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        slot = self.hash_function(key)
        for pair in self.table[slot]:
            if pair[0] == key:
                pair.append(value)
                return
        self.table[slot].append([key, value])

    def find(self, key):
        slot = self.hash_function(key)
        for pair in self.table[slot]:
            if pair[0] == key:
                return pair[1]
        return None
h = HashTable(5)
for i in g:
    h.insert(i.author, i)
print(h.table)
''',
14:'''
2. а) Используя лямбда-функцию, найдите все числа в заданном списке, которые делятся на 3 и не делятся на 5. 
lst = [1, 3, 5, 6, 9, 10, 12, 15, 18, 20]
result = list(filter(lambda x: x % 3 == 0 and x % 5 != 0, lst))
print(result) # ожидаемый результат: [3, 6, 9, 12]
б) Напишите программу для сортировки заданного списка кортежей по возрастанию второго элемента каждого кортежа. 
# Исходный список кортежей
tuples = [(1, 5), (2, 3), (4, 1), (3, 9), (6, 2)]
# Сортировка по возрастанию второго элемента каждого кортежа
tuples_sorted = sorted(tuples, key=lambda x: x[1])
# Вывод отсортированного списка кортежей
print(tuples_sorted)
в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые содержат только цифры.
# Исходный список строк
strings = ['abc', '123', '1a2b3c', '4567', '890d']
# Фильтрация строк, состоящих только из цифр с помощью лямбда-выражения
digit_strings = list(filter(lambda s: s.isdigit(), strings))
# Вывод отфильтрованного списка строк
print(digit_strings)
3. Создать класс очереди, который будет хранить только уникальные элементы. При добавлении элемента, если он уже есть в очереди, то он не должен добавляться
class UniqueQueue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        if item not in self.items:
            self.items.append(item)

    def dequeue(self):
        if not self.is_empty():
            return self.items.pop(0)

    def is_empty(self):
        return len(self.items) == 0

    def size(self):
        return len(self.items)

# Пример использования класса UniqueQueue
q = UniqueQueue()

q.enqueue(2)
q.enqueue(3)
q.enqueue(1)
q.enqueue(2)
print(q)
print(q.size()) # ожидаемый результат: 3

print(q.dequeue()) # ожидаемый результат: 1
print(q.dequeue()) # ожидаемый результат: 2
print(q.dequeue()) # ожидаемый результат: 3
print(q.dequeue()) # ожидаемый результат: None
''',
15:'''
2. Создайте класс ПРОДУКТ с методами, позволяющими вывести на экран информацию о продукте, а также определить, подходит ли данный продукт для заданных условий. Создайте дочерние классы МЯСО (название, цена, производитель, срок годности), ОВОЩИ (название, цена, производитель, сезонность), ФРУКТЫ (название, цена, производитель, сезонность) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список продуктов, выведите полную информацию из базы на экран, а также организуйте поиск продуктов с заданной ценой или сезонностью.
class Product:
    def __init__(self, name, price, producer):
        self.name = name
        self.price = price
        self.producer = producer

    def print_info(self):
        print("Название: ", self.name)
        print("Цена: ", self.price)
        print("Производитель: ", self.producer)

    def is_fresh(self):
        return True

class Meat(Product):
    def __init__(self, name, price, producer, expiry_date):
        super().__init__(name, price, producer)
        self.expiry_date = expiry_date

    def print_info(self):
        super().print_info()
        print("Срок годности: ", self.expiry_date)

    def is_fresh(self, current_date):
        return current_date <= self.expiry_date

class Vegetables(Product):
    def __init__(self, name, price, producer, seasonal):
        super().__init__(name, price, producer)
        self.seasonal = seasonal

    def print_info(self):
        super().print_info()
        print("Сезонность: ", self.seasonal)

    def is_fresh(self, current_season):
        return current_season == self.seasonal

class Fruits(Product):
    def __init__(self, name, price, producer, seasonal):
        super().__init__(name, price, producer)
        self.seasonal = seasonal

    def print_info(self):
        super().print_info()
        print("Сезонность: ", self.seasonal)

    def is_fresh(self, current_season):
        return current_season == self.seasonal

# Создание списка продуктов
products = [
    Meat("Свинина", 300, "ОАО Мясо", "2021-10-25"),
    Vegetables("Морковь", 50, "Частный Производитель", "Осень"),
    Fruits("Яблоки", 80, "ОАО Фрукты", "Осень"),
    Meat("Говядина", 500, "ОАО Мясо", "2022-02-01"),
    Vegetables("Огурцы", 70, "ООО Овощи", "Лето"),
    Fruits("Арбуз", 150, "ООО Фрукты", "Лето")
]

# Вывод полной информации о всех продуктах из базы
for product in products:
    product.print_info()
    print("-"*20)

# Поиск продуктов по заданной цене
price = int(input("Введите цену для поиска: "))
for product in products:
    if product.price == price:
        product.print_info()
        print("-"*20)

# Поиск продуктов по заданной сезонности
season = input("Введите сезонность для поиска: ")
for product in products:
    if isinstance(product, (Vegetables, Fruits)) and product.is_fresh(season):
        product.print_info()
        print("-"*20)
3. Дан стек. Необходимо удалить из него все элементы, которые не являются четными числами.
class Node:
    def __init__(self, value=None, next_node=None):
        self.value = value
        self.next_node = next_node

class Stack:
    def __init__(self):
        self.head = None
        self.length = 0

    def push(self, value):
        node = Node(value)
        if self.head is None:
            self.head = node
        else:
            node.next_node = self.head
            self.head = node
        self.length += 1

    def pop(self):
        if self.head is None:
            return None
        else:
            value = self.head.value
            self.head = self.head.next_node
            self.length -= 1
            return value

    def is_empty(self):
        return self.length == 0

    def peek(self):
        return self.head.value if self.head is not None else None

    def remove_non_evens(self):
        if self.is_empty():
            return

        current_node = self.head
        previous_node = None

        while current_node is not None:
            if current_node.value % 2 != 0:
                if previous_node is None:
                    self.head = current_node.next_node
                else:
                    previous_node.next_node = current_node.next_node
                self.length -= 1
            else:
                previous_node = current_node
            current_node = current_node.next_node

stack = Stack()
stack.push(4)
stack.push(9)
stack.push(3)
stack.push(15)
stack.push(8)

print("Исходный стек:")
current_node = stack.head
while current_node is not None:
    print(current_node.value)
    current_node = current_node.next_node

stack.remove_non_evens()

print("Стек после удаления нечетных элементов:")
current_node = stack.head
while current_node is not None:
    print(current_node.value)
    current_node = current_node.next_node''',
16:'''
2. Создайте класс ЖИВОТНОЕ с методами, позволяющими вывести на экран информацию о животном, а также определить, подходит ли данное животное для заданных условий. Создайте дочерние классы КОШКА (кличка, порода, возраст, окрас), СОБАКА (кличка, порода, возраст, размер), ПТИЦА (вид, возраст, окрас) со своими методами вывода информации на экран и определения соответствия заданным условиям. Создайте список животных, выведите полную информацию из базы на экран, а также организуйте поиск животных с заданной породой или окрасом.
class Animal:
    def __init__(self, name, breed, age, color):
        self.name = name
        self.breed = breed
        self.age = age
        self.color = color
        
    def print_info(self):
        print(f"Животное: {self.name}\nПорода: {self.breed}\nВозраст: {self.age}\nОкрас: {self.color}\n")
        
    def is_suitable(self, breed, color):
        if self.breed == breed and self.color == color:
            print(f"{self.name} подходит для условий: порода - {breed}, окрас - {color}\n")
            

class Cat(Animal):
    def __init__(self, name, breed, age, color):
        super().__init__(name, breed, age, color)
        
    def print_info(self):
        print(f"Кошка: {self.name}\nПорода: {self.breed}\nВозраст: {self.age}\nОкрас: {self.color}\n")
        
    def is_suitable(self, breed, color):
        if self.breed == breed and self.color == color:
            print(f"{self.name} подходит для условий: порода - {breed}, окрас - {color}\n")
            

class Dog(Animal):
    def __init__(self, name, breed, age, size):
        super().__init__(name, breed, age, "") # инициализируем окрас как пустую строку, т.к. у собак не указывается окрас
        self.size = size
        
    def print_info(self):
        print(f"Собака: {self.name}\nПорода: {self.breed}\nВозраст: {self.age}\nРазмер: {self.size}\n")
        
    def is_suitable(self, breed, color):
        if self.breed == breed:
            print(f"{self.name} подходит для условия: порода - {breed}\n")
            

class Bird(Animal):
    def __init__(self, name, kind, age, color):
        super().__init__(name, "", age, color) # окрас у птиц указывается в качестве цвета, а порода не нужна, поэтому передаем пустую строку в качестве аргумента для породы
        self.kind = kind
        
    def print_info(self):
        print(f"Птица: \nВид: {self.kind}\nВозраст: {self.age}\nОкрас: {self.color}\n")
        
    def is_suitable(self, breed, color):
        if self.color == color:
            print(f"{self.kind} подходит для условия: окрас - {color}\n")


animals_list = [
    Cat("Мурка", "Сиамская", 2, "коричневый"),
    Cat("Барсик", "Британская", 3, "серый"),
    Dog("Бобик", "Йоркширтерьер", 4, "маленький"),
    Dog("Рекс", "Овчарка", 5, "большой"),
    Bird("","Канарейка", 1, "желтый"),
    Bird("","Фламинго", 2, "розовый")
]

# выводим информацию о каждом животном из списка
for animal in animals_list:
    animal.print_info()

# ищем животных по породе или окрасу    
for animal in animals_list:
    animal.is_suitable("Сиамская", "коричневый")
    animal.is_suitable("", "розовый")
3. Написать функцию, которая принимает на вход двусвязный список и индекс элемента, который нужно удалить. Функция должна удалить элемент с указанным индексом и вернуть измененный список.
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

class DoubleLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
    
    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
        else:
            new_node.prev = self.tail
            self.tail.next = new_node
            self.tail = new_node
        
    def remove(self, index):
        current_node = self.head
        current_index = 0
        while current_node is not None:
            if current_index == index:
                if current_node.prev is None:
                    self.head = current_node.next
                    if self.head is not None:
                        self.head.prev = None
                elif current_node.next is None:
                    self.tail = current_node.prev
                    if self.tail is not None:
                        self.tail.next = None
                else:
                    current_node.prev.next = current_node.next
                    current_node.next.prev = current_node.prev
                return self
            current_index += 1
            current_node = current_node.next
        raise IndexError('list index out of range')

    def __repr__(self):
        nodes = []
        current_node = self.head
        while current_node is not None:
            nodes.append(str(current_node.data))
            current_node = current_node.next
        return "[" + "<->".join(nodes) + "]"

# Testing the function
dll = DoubleLinkedList()
dll.append(1)
dll.append(2)
dll.append(3)
dll.append(4)
dll.append(5)
dll.append(5)
print(dll) # [1<->2<->3<->4<->5]

dll.remove(5)

print(dll) # [1<->2<->4<->5]''',
17:'''
2. Создайте класс СТУДЕНТ с методами, позволяющими вывести на экран информацию о студенте, а также определить, соответствует ли данный студент заданным критериям. Создайте дочерние классы БАКАЛАВР (фамилия, имя, отчество, группа, курс, средний балл), МАГИСТР (фамилия, имя, отчество, группа, курс, средний балл, тема диссертации) со своими методами вывода информации на экран и определения соответствия заданным критериям. Создайте список студентов, выведите полную информацию из базы на экран, а также организуйте поиск студентов с заданным средним баллом или темой диссертации.
class Student:
    def __init__(self, surname, name, patronymic, group, course, avg_grade):
        self.surname = surname
        self.name = name
        self.patronymic = patronymic
        self.group = group
        self.course = course
        self.avg_grade = avg_grade
    
    def show_info(self):
        print("ФИО:", self.surname, self.name, self.patronymic)
        print("Группа:", self.group)
        print("Курс:", self.course)
        print("Средний балл:", self.avg_grade)
    def matches_criteria1(self, min_avg_grade):
        return self.avg_grade >= min_avg_grade

    
class Bachelor(Student):
    def __init__(self, surname, name, patronymic, group, course, avg_grade):
        super().__init__(surname, name, patronymic, group, course, avg_grade)
    
    def show_info(self):
        super().show_info()


    
class Master(Student):
    def __init__(self, surname, name, patronymic, group, course, avg_grade, thesis_topic):
        super().__init__(surname, name, patronymic, group, course, avg_grade)
        self.thesis_topic = thesis_topic
    
    def show_info(self):
        super().show_info()
        print("Тема диссертации:", self.thesis_topic)
    
    def matches_criteria2(self, min_avg_grade, thesis_topic):
        return super().matches_criteria1(min_avg_grade) and self.thesis_topic == thesis_topic


students = [Bachelor("Ivanov", "Ivan", "Ivanovich", "IT-21", 3, 4.5),
            Bachelor("Petrov", "Petr", "Petrovich", "IT-22", 2, 3.2),
            Master("Sidorov", "Sergey", "Nikolaevich", "IT-23", 1, 4.8, "ML алгоритмы"),
            Master("Semenov", "Andrey", "Vladimirovich", "IT-24", 2, 4.4, "Сетевые технологии")]

print("Список студентов:")
for student in students:
    student.show_info()

print("\nСтуденты с средним баллом выше 4.5:")
filtered_students = list(filter(lambda student: student.matches_criteria1(4.5), students))
for student in filtered_students:
    student.show_info()

print("\nСтуденты по теме диссертации 'ML алгоритмы':")
filtered_students = list(filter(lambda student: isinstance(student, Master) and student.matches_criteria2(0, "ML алгоритмы"), students))
for student in filtered_students:
    student.show_info()
3. Написать метод класса «Клиент», который сортирует список клиентов по возрасту с помощью алгоритма сортировки пузырьком. Метод должен изменять исходный список. 
class Client:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @staticmethod
    def sort_by_age(clients):
        n = len(clients)
        for i in range(n):
            for j in range(n-1):
                if clients[j].age > clients[j+1].age:
                    clients[j], clients[j+1] = clients[j+1], clients[j]

# Пример использования:
clients = [Client("Ann", 30), Client("Bob", 25), Client("Carl", 35)]
Client.sort_by_age(clients)
for client in clients:
    print(client.name, client.age)  # Bob 25 Ann 30 Carl 35
''',
18:'''
2. а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Кармайкла (для любого числа a, взаимно простого с числом n, a^(n-1) mod n = 1). 
import math
# функция для проверки числа на Кармайкла
def is_carmichael(num):  
# получаем список чисел, взаимно простых с num
    coprimes = [i for i in range(2, num) if num % i != 0 and math.gcd(i, num) == 1]
# проверяем условие Кармайкла для всех чисел из списка
    for coprime in coprimes:
    
        if pow(coprime, num-1, num) != 1:
        
            return False
    return True

# исходный список
lst = [561, 1105, 1729, 2465, 2821, 6601, 8911]
# находим числа Кармайкла в списке
carmichael_nums = list(filter(is_carmichael, lst))
# выводим на экран результат
print(carmichael_nums) # [561, 1105, 1729, 2465]
б) Напишите программу для сортировки заданного списка строк по количеству символов-разделителей (пробелов или запятых) в каждой строке.
lst = ["эта строка с двумя запятыми, разделительными символами", "эта строка одиноковая", "эта строка с тремя пробелами разделительными символами"]
def count_delimiters(s):
    return s.count(',') + s.count(' ')    
lst.sort(key=count_delimiters)
print(lst)
 в) Используя лямбда-функцию, найдите все строки в заданном списке строк, которые заканчиваются на точку.
lst = ["this is the first string.", "the second string", "here is the third string.", "fourth."]
filtered_lst = list(filter(lambda string: string.endswith('.'), lst))
print(filtered_lst)  # ['this is the first string.', 'here is the third string.']
3. Создать класс очереди, который будет хранить только элементы типа int и отсортированные по убыванию. При добавлении элемента, если он не является целым числом, то он не должен добавляться. При получении элементов из очереди они должны быть отсортированы по убыванию.
class Queue:
    
    def __init__(self):
        self.items = []
        
    def is_valid_element(self, element):
        return isinstance(element, int)
        
    def enqueue(self, element):
        if not self.is_valid_element(element):
            return
        self.items.append(element)
        self.items.sort()
        
    def dequeue(self):
        if self.is_empty():
            return None
        return self.items.pop()
    
    def is_empty(self):
        return len(self.items) == 0
q = Queue()

# добавление элементов в очередь
q.enqueue(4)
q.enqueue(2)
q.enqueue(7)
q.enqueue("apple")
q.enqueue(6)

# получение элементов в отсортированном порядке
while not q.is_empty():
        
    print(q.dequeue())
''',
19:'''
2. Опишите класс Animal, заданный видом, возрастом и весом. Включите в описание класса методы: вывода информации о животном на экран, проверки, является ли животное взрослым (возраст больше 3 лет), и свойство, позволяющее установить цвет животного.
class Animal:
    
    def __init__(self, species, age, weight):
        self.species = species
        self.age = age
        self.weight = weight
        self.color = ""
        
    def display_info(self):
        print(f"Species: {self.species}")
        print(f"Age: {self.age}")
        print(f"Weight: {self.weight} kg")
            
    def is_adult(self):
        return self.age > 3
    @property
    def color(self):
        return self._color
    @color.setter
    def color(self, value):
        self._color = value
# создание объекта класса Animal
lion = Animal(species="lion", age=5, weight=200)

# вывод информации о животном на экран
lion.display_info()

# проверка, является ли животное взрослым
print("Is adult:", lion.is_adult())

# установка цвета животного
lion.color = "yellow"
print(lion.color)
3. Написать функцию, которая принимает на вход список слов и сортирует его по алфавиту с помощью алгоритма сортировки вставками. Функция должна возвращать отсортированный список.
def insertion_sort(lst):
    for i in range(1, len(lst)):
        key = lst[i]
        j = i - 1
        while j >= 0 and key < lst[j]:
            lst[j + 1] = lst[j]
            j -= 1
        lst[j + 1] = key
    return lst

# основная программа
words = ['cat', 'apple', 'dog', 'orange', 'banana']
sorted_words = insertion_sort(words)
print(sorted_words)
''',
20:'''
2. а) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Капрекара. 
def is_kaprekar(n):
    square = str(n ** 2)
    for i in range(1, len(square)):
        left = int(square[:i])
        right = int(square[i:])
        if right != 0 and left + right == n:
            return True
    if n == 1:
        return True
    return False

# основная программа
lst = [1, 2, 3, 4, 9, 10, 45, 55, 100, 297, 703, 999, 2223, 2728, 4950, 5050, 7272, 7777, 9999]
kaprekars = list(filter(lambda x: is_kaprekar(x), lst))
print(kaprekars)
б) Напишите программу для сортировки заданного списка строк в лексикографическом порядке (по алфавиту). 
lst = ['apple', 'banana', 'orange', 'grape']
# сортировка списка по алфавиту
lst.sort()
print(lst)
в) Используя лямбда-функцию, найдите все числа в заданном списке, которые являются числами Армстронга.
def is_armstrong(n):
    num_str = str(n)
    k = len(num_str)
    sum = 0
    for d in num_str:
        sum += int(d) ** k
    return sum == n

# основная программа
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 153,456, 370, 371, 407, 1634, 8208, 9474]
armstrong_nums = list(filter(lambda x: is_armstrong(x), lst))
print(armstrong_nums)
3. Реализовать функцию, которая находит количество элементов в двусвязном списке.
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None
        self.prev = None

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def add_node(self, value):
        new_node = Node(value)
        if self.head is None:
            self.head = new_node
        else:
            self.tail.next = new_node
            new_node.prev = self.tail
        self.tail = new_node

def count_elements(dll):
    current = dll.head
    count = 0
    while current is not None:
        count += 1
        current = current.next
    return count

# Пример использования функции
my_list = DoublyLinkedList()
my_list.add_node(1)
my_list.add_node(2)
my_list.add_node(3)
my_list.add_node(9)

count = count_elements(my_list)
print(count) # 3
''',
21:'''
2. Опишите класс Student, заданный фамилией, именем, возрастом и средним баллом. Включите в описание класса методы: вывода информации о студенте на экран, проверки, является ли студент отличником (средний балл больше 4.5), и свойство, позволяющее установить факультет, на котором учится студент.
class Student:
    def __init__(self, last_name, first_name, age, average_grade):
        self.last_name = last_name
        self.first_name = first_name
        self.age = age
        self.average_grade = average_grade
        self.faculty = ""
        
    def __str__(self):
        return f"{self.first_name} {self.last_name}, возраст {self.age}, средний балл {self.average_grade}"
        
    def is_excellent_student(self):
        return self.average_grade > 4.5
    
    @property
    def faculty(self):
        return self._faculty
    
    @faculty.setter
    def faculty(self, value):
        self._faculty = value

# Пример использования класса

student1 = Student("Иванов", "Петр", 20, 4.8)
student2 = Student("Петров", "Иван", 21, 4.2)

print(student1) # Петр Иванов, возраст 20, средний балл 4.8
print(student2) # Иван Петров, возраст 21, средний балл 4.2

print(student1.is_excellent_student()) # True
print(student2.is_excellent_student()) # False

student1.faculty = "Математики"
print(student1.faculty) # Математики
3. Написать функцию, которая принимает на вход список чисел и сортирует его по убыванию с помощью алгоритма сортировки вставками. Функция должна возвращать отсортированный список.
def insertion_sort(arr):
    n = len(arr)
    for i in range(1, n):
        current = arr[i]
        j = i-1
        while j >= 0 and current >arr[j]:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = current
    return arr

# Пример использования функции
my_list = [8, 5, 3, 11, 9]
sorted_list = insertion_sort(my_list)
print(sorted_list) # [8, 7, 6, 5, 4, 3, 2, 1]
''',
22:'''
2. а) Напишите лямбда-функцию, которая умножает заданное число на 7 и вычитает из него 3, переданное в качестве аргумента.
multiply_and_subtract = lambda x, y: x*7 - y
# Пример использования функции
result = multiply_and_subtract(5, 2) # 5 * 7 - 2 = 33
print(result)
 б) Найдите сумму всех чисел в заданном списке целых чисел с помощью лямбда-функции. 
my_list = [1, 2, 3, 4, 5, 6]
sum_of_numbers = lambda lst: sum(lst)
# Пример использования функции
result = sum_of_numbers(my_list) # 1 + 2 + 3 + 4 + 5 + 6 = 21
print(result)
в) Извлечь элементы списка, которые больше заданного числа, с помощью лямбда-функции.
my_list = [1, 2, 3, 4, 5, 6]
greater_than = lambda lst, n: list(filter(lambda x: x > n, lst))
# Пример использования функции
result = greater_than(my_list, 3) # [4, 5, 6]
print(result)
3. Дан стек. Необходимо найти сумму всех элементов, которые являются простыми числами.
import math

def is_prime(n):
    if n < 2:
        return True
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True
class Node:
    def __init__(self, value=None, next_node=None):
        self.value = value
        self.next_node = next_node

class Stack:
    def __init__(self):
        self.head = None
        self.length = 0

    def push(self, value):
        node = Node(value)
        if self.head is None:
            self.head = node
        else:
            node.next_node = self.head
            self.head = node
        self.length += 1

    def pop(self):
        if self.head is None:
            return None
        else:
            value = self.head.value
            self.head = self.head.next_node
            self.length -= 1
            return value

    def is_empty(self):
        return self.length == 0

    def peek(self):
        return self.head.value if self.head is not None else None

    def sum_of_primes(self):
        current_node = self.head
        sum_of_primes = 0

        while current_node is not None:
            if is_prime(current_node.value):
                sum_of_primes += current_node.value
            current_node = current_node.next_node

        return sum_of_primes
stack = Stack()
stack.push(1)
stack.push(9)
stack.push(3)
stack.push(15)
stack.push(8)
stack.push(7)
print("Исходный стек:")
current_node = stack.head
while current_node is not None:
    print(current_node.value)
    current_node = current_node.next_node

sum_of_primes = stack.sum_of_primes()

print("Сумма всех простых чисел в стеке:", sum_of_primes)
''',
23:'''
2. Создайте класс «Книга» с атрибутами название, автор и год издания. Напишите методы для вывода информации о книге в виде «Книга '{название}' автора {автор}, издана в {году}». Используйте магический метод __eq__ для сравнения двух книг по году издания.
class Book:
    def __init__(self, title, author, year):
        self.title = title
        self.author = author
        self.year = year
        
    def __eq__(self, other):
        if isinstance(other, Book):
            return self.year == other.year
        return False

    def __str__(self):
        return f"Книга '{self.title}' автора {self.author}, издана в {self.year}"
        
# Пример использования класса

book1 = Book("Идиот", "Ф. М. Достоевский", 1869)
book2 = Book("Преступление и наказание", "Ф. М. Достоевский", 1866)
book3 = Book("Горе от ума", "А. С. Грибоедов", 1869)

print(book1) # Книга 'Идиот' автора Ф. М. Достоевский, издана в 1869
print(book2) # Книга 'Преступление и наказание' автора Ф. М. Достоевский, издана в 1866
print(book3) # Книга 'Горе от ума' автора А. С. Грибоедов, издана в 1825

print(book1 == book2) # False
print(book1 == book3) # True
3. Написать функцию, которая принимает на вход список строк и сортирует его по длине строк с помощью алгоритма сортировки выбором. Функция должна возвращать отсортированный список.
def selection_sort(arr):
    n = len(arr)
    for i in range(n):
        min_index = i
        for j in range(i+1, n):
            if len(arr[j]) < len(arr[min_index]):
                min_index = j
        arr[i], arr[min_index] = arr[min_index], arr[i]
    return arr

# Пример использования функции
my_list = ["apple", "banana", "kiwi", "orange"]
sorted_list = selection_sort(my_list)
print(sorted_list) # ['kiwi', 'apple', 'banana', 'orange']
''',
24:'''
2. Опишите класс BankAccount, заданный номером счета, балансом и владельцем. Включите в описание класса методы: вывода информации о банковском счете на экран, проверки, достаточно ли денег на счете для выполнения операции, и свойство, позволяющее установить тип валюты, в которой открыт счет.
class BankAccount:
    def __init__(self, account_number, balance, owner):
        self.account_number = account_number
        self.balance = balance
        self.owner = owner
        self.currency = "USD"

    def display_info(self):
        print(f"Account number: {self.account_number}")
        print(f"Balance: {self.balance} {self.currency}")
        print(f"Owner: {self.owner}")
        
    def enough_money(self, amount):
        if self.balance >= amount:
            return True
        else:
            print(f"Not enough money on the account. Current balance is {self.balance} {self.currency}.")
            return False
    
    @property
    def currency(self):
        return self._currency
    
    @currency.setter
    def currency(self, value):
        currencies = ["USD", "EUR", "GBP"]
        if value in currencies:
            self._currency = value
        else:
            print(f"Invalid currency {value}. Currency must be one of {currencies}.")
            self._currency = "USD"
account = BankAccount("123456789", 1000, "John Smith")
account.display_info() # Выводит на экран информацию о счете
if account.enough_money(500): # Проверка, достаточно ли денег на счете для выполнения операции
    account.balance -= 500
    print("Transaction successful")
account.currency = "EUR" # Установка типа валюты
account.display_info()
3. Написать функцию, которая принимает на вход список дат и сортирует его по возрастанию с помощью алгоритма сортировки пузырьком. Функция должна возвращать отсортированный список.
import datetime

def bubble_sort_dates(dates):
    """
    Функция принимает список дат и сортирует его по возрастанию с помощью алгоритма сортировки пузырьком.
    """
    n = len(dates)
    # проходим по списку
    for i in range(n):
        # последние i элементов уже отсортированы, поэтому можно не рассматривать их
        for j in range(n - i - 1):
            # переводим строки в объекты дат
            date1 = datetime.datetime.strptime(str(dates[j]), '%d.%m.%Y')
            date2 = datetime.datetime.strptime(str(dates[j+1]), '%d.%m.%Y')
            # выполняем сравнение и при необходимости меняем местами элементы
            if date1 > date2:
                dates[j], dates[j+1] = dates[j+1], dates[j]
    # возвращаем отсортированный список
    return dates

# вывод отсортированных дат
dates = ['24.03.2021', '12.11.2020', '31.01.2021', '07.12.2022', '30.05.2021']
print(bubble_sort_dates(dates))
''',
25:'''
2. Опишите класс Movie, заданный названием, режиссером, годом выпуска и продолжительностью. Включите в описание класса методы: вывода информации о фильме на экран, проверки, является ли фильм длинным (продолжительность больше 2 часов), и свойство, позволяющее установить жанр фильма.
class Movie:
    def __init__(self, title, director, year, duration):
        self.title = title
        self.director = director
        self.year = year
        self.duration = duration
        self.genre = None
    
    def show_info(self):
        print("Название: {}".format(self.title))
        print("Режиссер: {}".format(self.director))
        print("Год выпуска: {}".format(self.year))
        print("Продолжительность: {} минут".format(self.duration))
        print("Жанр: {}".format(self.genre))
    
    def is_long(self):
        if self.duration > 120:
            return True
        else:
            return False
    
    def set_genre(self, genre):
        self.genre = genre
# Создание экземпляра класса
movie1 = Movie("Побег из Шоушенка", "Фрэнк Дарабонт", 1994, 142)
movie2 = Movie("Хороший парень", "Кароль", 2017, 32)
# Изменение жанра фильма
movie1.set_genre("Драма")
movie2.set_genre("Мелодрама")
# Вывод информации о фильме
movie1.show_info()
movie2.show_info()
# Проверка продолжительности фильма
if movie1.is_long():
    print("Фильм длинный")
else:
    print("Фильм короткий")
3. Реализовать класс хеш-таблицы для хранения объектов класса «Товар». Хеш-функция должна основываться на поле «название товара». Если два товара имеют одинаковое название, они должны храниться в одной ячейке таблицы. 
class Good():
    def __init__(self, name, price):
        self.name = name
        self.price = price
g = [Good('Pork', 100), Good('Pork', 120), Good('Jam', 40), Good('Meat', 300), Good('Lemon', 30), Good('Kale', 25),Good('Meat', 780)]
# реализация хеш-таблицы методом цепочек
class HashTable:
    def __init__(self, size):
        self.size = size
        self.table = [[] for _ in range(self.size)]

    def hash_function(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        slot = self.hash_function(key)
        for pair in self.table[slot]:
            if pair[0] == key:
                pair.append(value)
                return
        self.table[slot].append([key, value])

    def find(self, key):
        slot = self.hash_function(key)
        for pair in self.table[slot]:
            if pair[0] == key:
                return pair[1]
        return None
h = HashTable(5)
for i in g:
    h.insert(i.name, i)
print(h.table)
''',
26:'''
2. а) Напишите лямбда-функцию, которая принимает два аргумента и возвращает их произведение. 
multiply = lambda x, y: x * y
result = multiply(8, 5)
print(result) # Output: 10
б) Найдите числа, которые являются квадратами целых чисел, из заданного списка чисел, используя лямбда-функцию. 
numbers = [4, 9, 16, 25, 36, 49, 64, 81]

square_numbers = list(filter(lambda x: int(x ** 0.5) ** 2 == x, numbers))

print(square_numbers) # Output: [4, 9, 16, 25, 36, 49, 64, 81]
в) Напишите программу для подсчёта целых чисел в заданном смешанном списке с помощью лямбда-функции.
mixed_list = [1, "hello", 3.5, True, 5, 9.0, "world"]
integer_count = len(list(filter(lambda x: type(x) == int, mixed_list)))
print("Количество целых чисел в списке: ", integer_count)
3. Написать метод класса «Товар», который сортирует список товаров по цене с помощью алгоритма быстрой сортировки. Метод должен изменять исходный список.
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __repr__(self):
        return f'{self.name} - {self.price}'

    @staticmethod
    def quick_sort(lst, start, end):
        if start >= end:
            return

        # выберем среднее значение для определения опорного элемента
        pivot_idx = (start + end) // 2
        pivot_value = lst[pivot_idx].price

        # разделим список на две группы: элементы до опорного и после
        i, j = start, end
        while i <= j:
            while lst[i].price < pivot_value:
                i += 1
            while lst[j].price > pivot_value:
                j -= 1
            if i <= j:
                lst[i], lst[j] = lst[j], lst[i]
                i += 1
                j -= 1

        # рекурсивно отсортируем подсписки
        Product.quick_sort(lst, start, j)
        Product.quick_sort(lst, i, end)

# создадим список товаров
products = [Product('Монитор', 350),
            Product('Клавиатура', 50),
            Product('Мышь', 25),
            Product('Ноутбук', 1000),
            Product('Принтер', 200)]

# отсортируем товары по цене
Product.quick_sort(products, 0, len(products)-1)

# выведем результат
for product in products:
    print(product)''',
27:'''
2. Создайте класс «Прямоугольник» с атрибутами длины и ширины. Напишите методы для вычисления площади и периметра прямоугольника. Используйте магический метод __eq__ для сравнения двух прямоугольников по площади.
class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width
        
    def area(self):
        return self.length * self.width
    
    def perimeter(self):
        return 2 * (self.length + self.width)
    
    def __eq__(self, other):
        return self.area() == other.area()

r1 = Rectangle(2, 4)
r2 = Rectangle(3, 5)
r3 = Rectangle(4, 2)

print(r1.area()) # 8
print(r1.perimeter()) # 12

print(r1 == r2) # False
print(r1 == r3) # True

'''
        }
        print(sklad[n])


prog.list_()
prog.pract27(3)