Одним днем я решил поработать с различными алгоритмами, но как оказалось это не так просто. Дело в том, что проще визуально воспринимать информацию, нежели в виде кода. Тогда я поставил себе цель - попробовать написать небольшой, но полезный прототип библиотеки для визуализации алгоритмов на языке программирования Python.
Обоснование актуальности
При изучении алгоритмов лучше всего ознакомиться не только с их описанием и возможными реализациями в общем виде, но и то, как они работают на конкретных данных. Частично эту потребность покрывает отладчик (debugger), но значительно удобнее смотреть на gif картинку, на которой отображается только полезная информация, запрашиваемая пользователем. Визуализация алгоритмов часто оказывается более полезной, чем традиционный отладчик в определенных контекстах, благодаря своей способности обеспечивать высокоуровневое интуитивное представление всего алгоритмического процесса. Хотя отладчики превосходно справляются с выявлением и решением конкретных проблем в коде, им может не удаться понять общий ход и логику сложных алгоритмов. Визуализация алгоритма предлагает целостное представление, позволяя разработчикам наблюдать последовательное выполнение каждого шага и наблюдать за преобразованиями данных в графическом формате. Такая визуальная ясность особенно полезна для понимания сложных алгоритмов с многочисленными точками принятия решений и сложными ветвлениями. В отличие от отладчиков, которые сосредоточены на изоляции ошибок, визуализация алгоритма позволяет разработчикам понять поведение алгоритма в целом, помогая как выявлять неэффективности, так и оптимизировать общую структуру алгоритма. Эта более широкая перспектива может сыграть важную роль в создании более надежных и эффективных алгоритмов в различных вычислительных приложениях.
Поставленные цели и задачи
Цель моего мини-проекта заключается в создании прототипа библиотеки, с помощью которой было бы удобно создавать анимации алгоритмов. Для ее реализации решаются задачи:
Разобраться с ООП в python, освоить работу с классами и “магическими методами”;
Разобраться с библиотекой Pillow, понять, как создавать отдельные изображения и gif анимации;
Анимировать перестановку и присвоение элементов одномерного массива;
Проверить работу библиотеки на простых алгоритмах.
Дорожная карта
Чтобы наиболее точно разобраться в материале и не потеряться, я составил дорожную карту. Ее можно разделить на 3 части:
Магические методы (init, setitem, getitem)
Магические методы — это специальные методы Python, которые начинаются и заканчиваются двойным подчеркиванием. Эти методы позволяют определить, как объекты ведут себя в различных обстоятельствах. Метод init — это важнейший магический метод Python, служащий конструктором класса. Он автоматически вызывается при создании объекта из класса и используется для инициализации его атрибутов.
Методы setitem и getitem — это магические методы, связанные с индексацией и подпиской в Python. Они используются, когда экземпляры класса рассматриваются как контейнеры или сопоставления. Метод setitem вызывается, когда элементу присваивается определенный индекс, а метод getitem вызывается, когда доступ к элементу осуществляется с использованием индекса.
Библиотека Pillow
Прототип библиотеки разрабатывается с применением библиотеки Pillow [4]. В данном случае использовалась часть функций и методов для решения трех конкретных задач:
Создание нового изображения;
Рисование поверх изображения (в том числе – текста);
Сохранения массива изображений в виде .gif анимации. Ниже представлен пример скрипта, который создает новое изображение:
self.data[index] = value
width = (cellSize + space + 2) * len(self.data)
height = cellSize * 6
dx = cellSize
dy = cellSize / 2
img = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("arial.ttf", 15)
for i, n in enumerate(self.data):
draw.rectangle([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy,
i * cellSize + cellSize / 2 + i * space + dx,
50 + cellSize / 2 + dy],
fill = None, outline = (0, 0, 0))
if n != None:
draw.text([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy],
text = str(n), fill = (0, 0, 0), font = font)
Анимация: текстовое описание, вывод формул
На рисунке ниже представлена схема, согласно которой формируется изображение массива. space - это расстояние между квадратами справа и слева, cellSize - сторона квадрата. Высота gif картинки равна cellSize*5, а длина уже зависит от количества квадратов, то есть от количества чисел в массиве.
Этот макет позволяет составить следующие формулы:
draw.rectangle([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy,
i * cellSize + cellSize / 2 + i * space + dx,
50 + cellSize / 2 + dy],
fill = None, outline = (0, 0, 0))
draw.text([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy],
text = str(n), fill = (0, 0, 0), font = font)
На рисунке ниже представлен макет анимации, согласно которому происходит движение красного и зеленого квадратов. Так как число в красном квадрате меньше чем в зеленом, то происходит их замена. Красный квадрат сдвигается вверх, а зеленый вниз, и уже в дальнейшем они меняются местами.
На языке Python это можно описать следующим образом:
left_x = left * cellSize + space * left
right_x = right * cellSize + space * right
left_y = 50
right_y = 50
main_frame = 0
width = (cellSize + space + 2) * len(self.data)
height = cellSize * 6
dx = cellSize
dy = cellSize / 2
font = ImageFont.truetype("arial.ttf", 15)
for main_frame in range(30):
img = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(img)
for i, n in enumerate(self.data):
if i not in [left, right]:
draw.rectangle([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy,
i * cellSize + cellSize / 2 + i * space + dx,
50 + cellSize / 2 + dy],
fill = None, outline = (0, 0, 0))
draw.text([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy],
text = str(n), fill = (0, 0, 0), font = font)
draw.rectangle([left_x - cellSize / 2 + dx,
left_y - cellSize / 2 + dy,
left_x + cellSize / 2 + dx,
left_y + cellSize / 2 + dy], fill = None, outline = (0, 0, 0))
draw.text([left_x - cellSize / 2 + dx,
left_y - cellSize / 2 + dy],
text = str(self.data[left]),
fill = (0, 0, 0),
font = font)
draw.rectangle([right_x - cellSize / 2 + dx,
right_y - cellSize / 2 + dy,
right_x + cellSize / 2 + dx,
right_y + cellSize / 2 + dy], fill = None, outline = (0, 0, 0))
draw.text([right_x - cellSize / 2 + dx,
right_y - cellSize / 2 + dy],
text = str(self.data[right]),
fill = (0, 0, 0),
font = font)
main_frame += 1
if main_frame < 10:
left_y += 5
right_y -= 5
elif main_frame < 20:
left_x -= (left - right) * (cellSize + space) / 10
right_x += (left - right) * (cellSize + space) / 10
elif main_frame < 29:
left_y -= 5
right_y += 5
self.frames.append(img)
self.data[left], self.data[right] = self.data[right], self.data[left]
Мой результат
В рамках разработки была применена сортировка пузырьком:
arr = AnimatedList(size = 10)
for i in range(10):
arr[i] = randint(-10, 10)
for i in range(10):
for j in range(9):
if arr.data[j] > arr.data[j + 1]:
arr.swap(j, j + 1)
arr.save_animation('animation.gif')
Данный тип сортировки формирует gif анимацию. Gif анимация представляет собой перемещение элементов массива, который схематически показан на рисунке ниже.
Так как анимация выполняется без ошибок, то библиотека написана корректно. Следовательно, цель, поставленная в проекте, достигнута! Уррра!
Полный код
from random import randint
from PIL import Image, ImageDraw, ImageFont
class AnimatedList:
def __init__(self, data = [], size = 0):
self.frames = []
self.data = [None for i in range(size)]
if data:
self.data = data
def __setitem__(self, index, value):
self.data[index] = value
width = (cellSize + space + 2) * len(self.data)
height = cellSize * 6
dx = cellSize
dy = cellSize / 2
img = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("arial.ttf", 15)
for i, n in enumerate(self.data):
draw.rectangle([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy,
i * cellSize + cellSize / 2 + i * space + dx,
50 + cellSize / 2 + dy],
fill = None, outline = (0, 0, 0))
if n != None:
draw.text([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy],
text = str(n), fill = (0, 0, 0), font = font)
self.frames.append(img)
def __getitem__(self, *args):
pass
def swap(self, left, right):
left_x = left * cellSize + space * left
right_x = right * cellSize + space * right
left_y = 50
right_y = 50
main_frame = 0
width = (cellSize + space + 2) * len(self.data)
height = cellSize * 6
dx = cellSize
dy = cellSize / 2
font = ImageFont.truetype("arial.ttf", 15)
for main_frame in range(30):
img = Image.new('RGB', (width, height), (255, 255, 255))
draw = ImageDraw.Draw(img)
for i, n in enumerate(self.data):
if i not in [left, right]:
draw.rectangle([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy,
i * cellSize + cellSize / 2 + i * space + dx,
50 + cellSize / 2 + dy],
fill = None, outline = (0, 0, 0))
draw.text([i * cellSize - cellSize / 2 + i * space + dx,
50 - cellSize / 2 + dy],
text = str(n), fill = (0, 0, 0), font = font)
draw.rectangle([left_x - cellSize / 2 + dx,
left_y - cellSize / 2 + dy,
left_x + cellSize / 2 + dx,
left_y + cellSize / 2 + dy], fill = None, outline = (0, 0, 0))
draw.text([left_x - cellSize / 2 + dx,
left_y - cellSize / 2 + dy],
text = str(self.data[left]),
fill = (0, 0, 0),
font = font)
draw.rectangle([right_x - cellSize / 2 + dx,
right_y - cellSize / 2 + dy,
right_x + cellSize / 2 + dx,
right_y + cellSize / 2 + dy], fill = None, outline = (0, 0, 0))
draw.text([right_x - cellSize / 2 + dx,
right_y - cellSize / 2 + dy],
text = str(self.data[right]),
fill = (0, 0, 0),
font = font)
main_frame += 1
if main_frame < 10:
left_y += 5
right_y -= 5
elif main_frame < 20:
left_x -= (left - right) * (cellSize + space) / 10
right_x += (left - right) * (cellSize + space) / 10
elif main_frame < 29:
left_y -= 5
right_y += 5
self.frames.append(img)
self.data[left], self.data[right] = self.data[right], self.data[left]
def save_animation(self, name):
self.frames[0].save('/Users/egorkluychikov/Downloads'+name,
save_all = True,
append_images = self.frames[1:],
duration = 100,
loop = 0)
cellSize = 20
space = 5
arr = AnimatedList(size = 10)
for i in range(10):
arr[i] = randint(-10, 10)
for i in range(10):
for j in range(9):
if arr.data[j] > arr.data[j + 1]:
arr.swap(j, j + 1)
arr.save_animation('animation.gif')
Список источников:
Что такое объектно-ориентированное программирование (ООП)? - URL: https://itanddigital.ru/oop
Какая разница между объектом и классом. - URL: https://uchet-jkh.ru/i/kakaya-raznica-mezdu-obektom-i-klassom - Дата публикации: 19 августа 2023.
Что такое Self в Python — подробно на примерах. - URL: https://python-lab.ru/chto-takoe-self-v-python-podrobno-na-primerah
Pillow (PIL Fork) 10.2.0 documentation. - URL: https://pillow.readthedocs.io/en/stable/index.html
Буду рад услышать от вас вопросы и предложения, потому что сделан только прототип и его еще нужно доводить до ума.
Комментарии (4)
Vitaliy_dzen
07.10.2024 04:16Что же Вы не указываете на то, что используется шрифт, который например у меня на чистом Linux Mint отсутствует
font = ImageFont.truetype("arial.ttf", 15)
и дирректории
self.frames[0].save('/Users/egorkluychikov/Downloads'+name,
в линуксе как бы не существует...
Указывайте в следующий раз на такие моменты, а то новички могут и "поплыть"
Занудствовать не хотел.
Shaha272
07.10.2024 04:16Python-da algoritmlarni vizuallashtirish uchun prototip kutubxonasini yaratish juda qiziqarli va foydali loyiha bo'lishi mumkin. Bunday kutubxona orqali siz turli xil algoritmlarni, masalan, tartiblash, qidirish, graf algoritmlari va boshqalarni vizual ko‘rinishda taqdim etishingiz mumkin. Quyida oddiy bir prototip kutubxonasini yaratish uchun qadamlar keltirilgan:
### 1. Kutubxona Tuzilishi
Dastlabki tuzilishini belgilaymiz. Misol uchun, `visualizer.py` nomli faylda asosiy funksiyalarni kiritamiz.
### 2. Kutubxona uchun Asosiy Funktsiyalar
Keling, oddiy tartiblash algoritmini (masalan, Bubble Sort) vizuallashtirish uchun kod yozamiz.
```python
import matplotlib.pyplot as plt
import time
def bubble_sort_visualization(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
# Vizuallashtirish
plt.clf() # Oyna tozalash
plt.bar(range(len(arr)), arr)
plt.title("Bubble Sort Visualization")
plt.pause(0.1) # Vaqtni kutish
plt.show()
# Misol uchun foydalanish
if __name__ == "__main__":
data = [64, 34, 25, 12, 22, 11, 90]
plt.ion() # Interaktiv rejimga o'tish
bubble_sort_visualization(data)
```
### 3. Kutubxonani Kengaytirish
Bu kodda `matplotlib` kutubxonasi yordamida Bubble Sort algoritmini vizuallashtirdik. Siz ushbu asosiy kodga boshqa algoritmlarni qo'shishingiz, foydalanuvchilar uchun interfeys yaratishingiz yoki turli xil ko'rinishlar qo'shishingiz mumkin.
### 4. Qo'shimcha Funksiyalar
Siz kutubxonaning imkoniyatlarini kengaytirishingiz mumkin:
- **Turli algoritmlar**: Quick Sort, Merge Sort, Dijkstra algoritmi va boshqalar.
- **Interaktiv interfeys**: Foydalanuvchilarga o'z ma'lumotlarini kiritish va algoritmni tanlash imkoniyatini berish.
- **Ovoz qo'shish**: Algoritm harakatlari bilan birga ovoz effektlari qo'shish.
### 5. O'rnatish
Agar siz bu kutubxonani boshqa loyihalarda ishlatmoqchi bo'lsangiz, uni `setup.py` fayli orqali paketga aylantirishingiz mumkin.
### 6. Foydalanish
Yuqoridagi kodni `visualizer.py` faylida saqlang va terminalda quyidagi buyruqni bajarib ishga tushiring:
```bash
python visualizer.py
```
Bu oddiy prototip kutubxonasi sizga Python-da algoritmlarni vizuallashtirishni o’rganish va o’z g'oyalaringizni amalga oshirishda yordam beradi. Agar sizda qo'shimcha savollar yoki aniqroq talablar bo'lsa, iltimos, so'rang!
belonesox
07.10.2024 04:16Я при преподавании использую свою библиотеку «прозрачной визуализации отладки», https://github.com/belonesox/pyalgovisualizer (введение в разработку на ней тут → https://gitlab.ispras.ru/discopal/algo-visual/-/blob/master/contribution.md), вот короткий (10мин) доклад https://0x1.tv/20240629H (там идея, демо, и в частности — почему не manim не панацея), вот некоторые примеры → https://vimeo.com/showcase/10185384 (но смысл не в видеороликах, а в том, что студент в браузере может интерактивно разбираться в алгоритме).
zabanen2
pip install manim
https://www.google.com/search?q=manim+visualizing+algorithms