{ "cells": [ { "cell_type": "markdown", "id": "anticipated-minority", "metadata": { "tags": [] }, "source": [ "# Visualizing Sorting Algorithms\n", "\n", "Sorting algorithms vary, for example, in their time and space complexity.\n", "From an artistic viewpoint, they also vary in the way they transiently reposition the elements of the array as the algorithm progresses.\n", "Here, we will animate this transient behavior." ] }, { "cell_type": "markdown", "id": "lucky-health", "metadata": { "tags": [] }, "source": [ "## Initial setup" ] }, { "cell_type": "code", "execution_count": 1, "id": "prerequisite-heaven", "metadata": { "tags": [] }, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from matplotlib import animation\n", "from IPython.display import HTML" ] }, { "cell_type": "markdown", "id": "structural-masters", "metadata": { "tags": [] }, "source": [ "## Implement sorting algorithms\n", "\n", "Each sorting function will be implemented as a generator.\n", "This way, each algorithm will track its state implicitly and the resolution can be adjusted freely by positioning the `yield` calls.\n", "In addition, the raw data is mutable and will be passed by reference. Thus no unnecessary copies are made." ] }, { "cell_type": "code", "execution_count": 2, "id": "fiscal-fifth", "metadata": { "tags": [] }, "outputs": [], "source": [ "def bubblesort(array):\n", " n = len(array)\n", " for i in range(n - 1):\n", " for j in range(0, n - i - 1):\n", " if array[j] > array[j + 1]:\n", " array[j], array[j + 1] = array[j + 1], array[j]\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 3, "id": "excessive-freedom", "metadata": { "tags": [] }, "outputs": [], "source": [ "def insertionsort(array):\n", " for i in range(1, len(array)):\n", " key = array[i]\n", "\n", " j = i - 1\n", " while j >= 0 and key < array[j]:\n", " array[j + 1] = array[j]\n", " j -= 1\n", "\n", " yield array\n", "\n", " array[j + 1] = key\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 4, "id": "mysterious-tampa", "metadata": { "tags": [] }, "outputs": [], "source": [ "def selectionsort(array):\n", " n = len(array)\n", " for i in range(n):\n", " min_idx = i\n", " for j in range(i + 1, n):\n", " if array[min_idx] > array[j]:\n", " min_idx = j\n", "\n", " yield array\n", "\n", " array[i], array[min_idx] = array[min_idx], array[i]\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 5, "id": "advance-benjamin", "metadata": { "tags": [] }, "outputs": [], "source": [ "def cocktailsort(array):\n", " n = len(array)\n", " swapped = True\n", " start = 0\n", " end = n - 1\n", " while swapped:\n", " swapped = False\n", "\n", " for i in range(start, end):\n", " if array[i] > array[i + 1]:\n", " array[i], array[i + 1] = array[i + 1], array[i]\n", " swapped = True\n", "\n", " yield array\n", "\n", " if not swapped:\n", " break\n", "\n", " swapped = False\n", " end = end - 1\n", "\n", " for i in range(end - 1, start - 1, -1):\n", " if array[i] > array[i + 1]:\n", " array[i], array[i + 1] = array[i + 1], array[i]\n", " swapped = True\n", "\n", " yield array\n", "\n", " start = start + 1\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 6, "id": "patient-basics", "metadata": { "tags": [] }, "outputs": [], "source": [ "def partition(array, low, high):\n", " i = low - 1\n", " pivot = array[high]\n", "\n", " array_list = []\n", " for j in range(low, high):\n", " if array[j] < pivot:\n", " i = i + 1\n", " array[i], array[j] = array[j], array[i]\n", "\n", " array_list.append(array.copy())\n", "\n", " array[i + 1], array[high] = array[high], array[i + 1]\n", " array_list.append(array.copy())\n", "\n", " return i + 1, array_list\n", "\n", "\n", "def quicksort(array, low=0, high=None):\n", " if high is None:\n", " high = len(array) - 1\n", "\n", " if low < high:\n", " pi, array_list = partition(array, low, high)\n", " yield from array_list\n", "\n", " yield from quicksort(array, low, pi - 1)\n", " yield from quicksort(array, pi + 1, high)\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 7, "id": "committed-filter", "metadata": { "tags": [] }, "outputs": [], "source": [ "def mergesort(array, left_index=0, right_index=None):\n", " if right_index is None:\n", " right_index = len(array) - 1\n", "\n", " if left_index >= right_index:\n", " return\n", "\n", " mid = (left_index + right_index) // 2\n", " yield from mergesort(array, left_index=left_index, right_index=mid)\n", " yield from mergesort(array, left_index=mid + 1, right_index=right_index)\n", "\n", " left_copy = array[left_index : mid + 1].copy()\n", " right_copy = array[mid + 1 : right_index + 1].copy()\n", "\n", " left_copy_index = 0\n", " right_copy_index = 0\n", " sorted_index = left_index\n", "\n", " while left_copy_index < len(left_copy) and right_copy_index < len(right_copy):\n", " if left_copy[left_copy_index] <= right_copy[right_copy_index]:\n", " array[sorted_index] = left_copy[left_copy_index]\n", " left_copy_index = left_copy_index + 1\n", " else:\n", " array[sorted_index] = right_copy[right_copy_index]\n", " right_copy_index = right_copy_index + 1\n", " sorted_index = sorted_index + 1\n", "\n", " yield array\n", "\n", " while left_copy_index < len(left_copy):\n", " array[sorted_index] = left_copy[left_copy_index]\n", " left_copy_index = left_copy_index + 1\n", " sorted_index = sorted_index + 1\n", "\n", " yield array\n", "\n", " while right_copy_index < len(right_copy):\n", " array[sorted_index] = right_copy[right_copy_index]\n", " right_copy_index = right_copy_index + 1\n", " sorted_index = sorted_index + 1\n", "\n", " yield array\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 8, "id": "hollywood-banana", "metadata": { "tags": [] }, "outputs": [], "source": [ "def heapify(array, n, i):\n", " largest = i\n", " l = 2 * i + 1\n", " r = 2 * i + 2\n", "\n", " if l < n and array[largest] < array[l]:\n", " largest = l\n", " if r < n and array[largest] < array[r]:\n", " largest = r\n", "\n", " if largest != i:\n", " array[i], array[largest] = array[largest], array[i]\n", " yield array\n", "\n", " yield from heapify(array, n, largest)\n", "\n", "\n", "def heapsort(array):\n", " n = len(array)\n", "\n", " for i in range(n // 2 - 1, -1, -1):\n", " yield from heapify(array, n, i)\n", "\n", " yield array\n", "\n", " for i in range(n - 1, 0, -1):\n", " array[i], array[0] = array[0], array[i]\n", " yield from heapify(array, i, 0)\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 9, "id": "material-administrator", "metadata": { "tags": [] }, "outputs": [], "source": [ "def shellsort(array):\n", " n = len(array)\n", " gap = n // 2\n", "\n", " while gap > 0:\n", " for i in range(gap, n):\n", " temp = array[i]\n", "\n", " j = i\n", " while j >= gap and array[j - gap] > temp:\n", " array[j] = array[j - gap]\n", " j -= gap\n", "\n", " yield array\n", "\n", " array[j] = temp\n", "\n", " yield array\n", " gap //= 2" ] }, { "cell_type": "code", "execution_count": 10, "id": "severe-accounting", "metadata": { "tags": [] }, "outputs": [], "source": [ "def stoogesort(array, low=0, high=None):\n", " if high is None:\n", " high = len(array) - 1\n", "\n", " if low >= high:\n", " return\n", "\n", " if array[low] > array[high]:\n", " array[low], array[high] = array[high], array[low]\n", "\n", " yield array\n", "\n", " if high - low + 1 > 2:\n", " sep = (high - low + 1) // 3\n", "\n", " yield from stoogesort(array, low, high - sep)\n", " yield from stoogesort(array, low + sep, high)\n", " yield from stoogesort(array, low, high - sep)\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 11, "id": "dependent-composition", "metadata": { "tags": [] }, "outputs": [], "source": [ "def combsort(array):\n", " n = len(array)\n", " shrink_factor = 1.3\n", " _gap = n\n", " sorted_ = False\n", "\n", " while not sorted_:\n", " _gap /= shrink_factor\n", " gap = int(_gap)\n", "\n", " if gap <= 1:\n", " sorted_ = True\n", " gap = 1\n", "\n", " for i in range(n - gap):\n", " if array[i] > array[i + gap]:\n", " array[i], array[i + gap] = array[i + gap], array[i]\n", " sorted_ = False\n", "\n", " yield array" ] }, { "cell_type": "code", "execution_count": 12, "id": "moral-encyclopedia", "metadata": { "tags": [] }, "outputs": [], "source": [ "algorithm_list = [\n", " bubblesort,\n", " cocktailsort,\n", " combsort,\n", " heapsort,\n", " insertionsort,\n", " mergesort,\n", " quicksort,\n", " selectionsort,\n", " shellsort,\n", " # stoogesort, # takes a long time\n", "]" ] }, { "cell_type": "markdown", "id": "exact-description", "metadata": { "tags": [] }, "source": [ "## Try out one of the algorithms\n", "\n", "Let's see how each generator yields intermediate arrays until it finally sorts it completely." ] }, { "cell_type": "code", "execution_count": 13, "id": "senior-oasis", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initial array: [3 2 1 0]\n", "At iteration 0: [2 3 1 0]\n", "At iteration 1: [2 1 3 0]\n", "At iteration 2: [2 1 0 3]\n", "At iteration 3: [1 2 0 3]\n", "At iteration 4: [1 0 2 3]\n", "At iteration 5: [0 1 2 3]\n", "Final array: [0 1 2 3]\n" ] } ], "source": [ "array = np.arange(4)[::-1]\n", "\n", "print('Initial array:', array)\n", "for i, arr in enumerate(bubblesort(array)):\n", " print(f'At iteration {i}:', arr)\n", "print('Final array:', array)" ] }, { "cell_type": "markdown", "id": "settled-george", "metadata": { "tags": [] }, "source": [ "## Create animations\n", "\n", "The `animate` function is straight-forward, it simply updates the data in each plot.\n", "The data is updated in the `step` function.\n", "Here, a generator is created for each sorting instance (row of each data block) and then executed until all generators are exhausted." ] }, { "cell_type": "code", "execution_count": 14, "id": "naughty-science", "metadata": { "tags": [] }, "outputs": [], "source": [ "def animate(data_list, im_list):\n", " \"\"\"Put each data block in appropriate image.\"\"\"\n", " for data, im in zip(data_list, im_list):\n", " im.set_data(data)\n", " return im_list\n", "\n", "\n", "def step():\n", " \"\"\"Run algorithms.\"\"\"\n", " # initialize sorting functions\n", " generator_list = [\n", " sort_function(row)\n", " for block, sort_function in zip(data_list, algorithm_list)\n", " for row in block\n", " ]\n", "\n", " while True:\n", " has_stopped = 0\n", " for gen in generator_list:\n", " try:\n", " # advance each sorting algorithm by one step\n", " next(gen)\n", " except StopIteration:\n", " has_stopped += 1\n", "\n", " if len(generator_list) == has_stopped:\n", " # all lists are sorted when all generators are exhausted\n", " break\n", "\n", " # yield intermediate result for plotting of animation frame\n", " yield data_list\n", "\n", "\n", "def create_animation(array_length, repetition_num, max_iter_num=50_000, block_size=3):\n", " # TODO: get rid of evil global variables\n", " global data_list\n", "\n", " # generate unsorted data\n", " block = np.repeat([np.arange(array_length).astype(float)], repetition_num, axis=0)\n", " [np.random.shuffle(row) for row in block]\n", "\n", " data_list = [block.copy() for _ in algorithm_list]\n", "\n", " # setup figure\n", " size = np.ceil(np.sqrt(len(algorithm_list))).astype(int)\n", "\n", " fig, ax_grid = plt.subplots(\n", " nrows=size,\n", " ncols=size,\n", " figsize=(size * block_size, size * block_size),\n", " constrained_layout=True,\n", " )\n", " ax_list = ax_grid.ravel()\n", "\n", " im_list = [\n", " ax.imshow(data, interpolation='nearest') for ax, data in zip(ax_list, data_list)\n", " ]\n", " [ax.axis('off') for ax in ax_list]\n", " [ax.set_title(alg.__name__) for ax, alg in zip(ax_list, algorithm_list)]\n", "\n", " # create animation\n", " return animation.FuncAnimation(\n", " fig=fig,\n", " func=animate,\n", " frames=step, # init_func=init_func, # TODO: use init_func to start with appropriate frame\n", " save_count=max_iter_num,\n", " interval=10,\n", " repeat=True,\n", " fargs=(im_list,),\n", " blit=True,\n", " )" ] }, { "cell_type": "code", "execution_count": 15, "id": "cellular-fortune", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 6.16 s, sys: 232 ms, total: 6.39 s\n", "Wall time: 6.17 s\n" ] }, { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "
\n", " \n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%%time\n", "small_animation = create_animation(10, 10, block_size=2)\n", "HTML(small_animation.to_jshtml(default_mode=None))" ] }, { "cell_type": "code", "execution_count": 16, "id": "adjacent-front", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 17min 41s, sys: 2min 4s, total: 19min 46s\n", "Wall time: 6min 48s\n" ] }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%%time\n", "big_animation = create_animation(80, 80)\n", "HTML(big_animation.to_html5_video())" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.6" } }, "nbformat": 4, "nbformat_minor": 5 }