Skip to content

Instantly share code, notes, and snippets.

@Sarverott
Last active December 17, 2024 13:38
Show Gist options
  • Save Sarverott/e68e66ed0a2e1324ee16086170344517 to your computer and use it in GitHub Desktop.
Save Sarverott/e68e66ed0a2e1324ee16086170344517 to your computer and use it in GitHub Desktop.
kodowania-huffmana.ipynb
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"kernelspec": {
"name": "python",
"display_name": "Python (Pyodide)",
"language": "python"
},
"language_info": {
"codemirror_mode": {
"name": "python",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8"
},
"colab": {
"provenance": [],
"collapsed_sections": [
"719ab812-2824-4f77-8e63-3d363925deec",
"QQwn0bO93NSE"
],
"name": "kodowania-huffmana.ipynb",
"include_colab_link": true
}
},
"nbformat_minor": 5,
"nbformat": 4,
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/Sarverott/e68e66ed0a2e1324ee16086170344517/przyk-ad-kodowania-huffmana.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"> - ADNOTACJA TEGO MATERIAŁU ♎\n",
">\n",
"> Ten materiał publikowany jest na mocy licencji [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) przez __Setta Sarverotta A.A.B.__ `(Uznanie autorstwa-Użycie niekomercyjne-Bez utworów zależnych)`"
],
"metadata": {
"id": "dNAbEuDHm7Ae"
},
"id": "dNAbEuDHm7Ae"
},
{
"id": "719ab812-2824-4f77-8e63-3d363925deec",
"cell_type": "markdown",
"source": [
"# Kodowanie Huffmana 🔖\n",
"\n",
"\n",
"\n",
"#### skrypt obrazowy, demonstrujący proces wcielania teorii w praktykę\n",
"\n",
"aby nie smażyć sobie na zapas głowy i nie zgubić się podczas implementacji warto wypisać pseudokod algorytmu kodowania Huffmana:\n",
"\n",
"1. [ ] liczenie częstotliwości występowania symboli\n",
"2. [ ] sortuj utworzony zestaw powtórki-symbol\n",
"3. [ ] utwórz drzewo z posortowanych danych\n",
"4. [ ] przypisz kody binarne do każdego węzła drzewa\n",
"5. [ ] zapisz pierwotny tekst z użyciem nowych zastępczych kodów binarnych, zamiast kodów ASCII liter\n",
"\n",
"> UWAGA CIEKAWOSTKA! powyżej macie pseudokod, gdyby wypisane były wszystkie akcje do wykonania, zamiast uproszczonych podpunktów byłby to program w języku ludzkim... możecie go odpalić wykonując wszystko sami z użyciem kartki i długopisa `( ͡° ͜ʖ ͡°)` ktoś zgadnie o jakiej maszynie jest ciekawostka?\n",
"\n",
"import bibliotek"
],
"metadata": {
"id": "719ab812-2824-4f77-8e63-3d363925deec"
}
},
{
"id": "4b1107a3-5eb9-476e-8fe2-095fa822d0f5",
"cell_type": "code",
"source": [
"# ~~~ LIBRARIES ~~~ #\n",
"import pandas\n",
"import numpy\n",
"import IPython\n",
"import matplotlib\n",
"import json\n",
"# integracja z GDrive na bazie przykładów ćwiczebnych, zapewnionych przez google\n",
"# patrz: code snippetsy\n",
"#from pydrive.auth import GoogleAuth\n",
"#from pydrive.drive import GoogleDrive\n",
"#from google.colab import auth\n",
"#from oauth2client.client import GoogleCredentials\n",
"# API GDRIVE ZABLOKOWANE PRZEZ WSTI OD STRONY ADMINA Z NIEZNANEJ PRZYCZYNY\n",
"#\n",
"# NOTA AKTUALIZACYJNA DO PRZYSZŁEGO SIEBIE: sprawdzić czy już gdrive odblokował admin\n",
"#"
],
"metadata": {
"trusted": true,
"id": "4b1107a3-5eb9-476e-8fe2-095fa822d0f5"
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": [
"# przykładowe dane\n",
"Pobranie materiału testowego z pliku na GDrivie, w postaci charakterystycznego ciągu słów łacińskich, stosowanego przez średniowiecznych drukarzy w celu testowania wizualnej strony formatów z przykłądowym tekstem. Jest to praktyka stosowana po dziś dzień przez frontend developerów w tym samym celu co dawni drukarze."
],
"metadata": {
"id": "QQwn0bO93NSE"
},
"id": "QQwn0bO93NSE"
},
{
"cell_type": "code",
"source": [
"# łącze z dyskiem\n",
"# integracja z GDrive na bazie przykładów ćwiczebnych, zapewnionych przez google\n",
"# patrz: code snippetsy\n",
"#auth.authenticate_user()\n",
"#gauth = GoogleAuth()\n",
"#gauth.credentials = GoogleCredentials.get_application_default()\n",
"#drive = GoogleDrive(gauth)\n",
"# ZABLOKOWANE PRZEZ WSTI OD STRONY ADMINA Z NIEZNANEJ PRZYCZYNY\n",
"#\n",
"# NOTA AKTUALIZACYJNA DO PRZYSZŁEGO SIEBIE: sprawdzić czy już gdrive odblokował admin\n",
"#"
],
"metadata": {
"id": "p2vQJDIy7XjL"
},
"id": "p2vQJDIy7XjL",
"execution_count": null,
"outputs": []
},
{
"id": "9dc77e54-464e-4187-a1f8-969ff9abeb91",
"cell_type": "code",
"source": [
"#file_id = '1ZFIpTZLPlcW0HqlFjirTGoMv-cMLuG2c' # id pliku LOREM_IMPUS.txt (https://drive.google.com/file/d/1ZFIpTZLPlcW0HqlFjirTGoMv-cMLuG2c/view?usp=sharing)\n",
"#downloaded = drive.CreateFile({'id': file_id})\n",
"#lacinski_tekst_wypelniacz =downloaded.GetContentString()\n",
"# ZABLOKOWANE PRZEZ WSTI OD STRONY ADMINA Z NIEZNANEJ PRZYCZYNY\n",
"#\n",
"# NOTA AKTUALIZACYJNA DO PRZYSZŁEGO SIEBIE: sprawdzić czy już gdrive odblokował admin\n",
"#"
],
"metadata": {
"trusted": true,
"id": "9dc77e54-464e-4187-a1f8-969ff9abeb91"
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": [
"# DO CZASU ODBLOKOWANIA I AKTUALIZACJI KODU\n",
"# BĘDZIE TO PRZYPISANE JAKO ZWYKŁA DEFINICJA ZMIENNEJ\n",
"# Z WPISANĄ W KOD STAŁĄ WARTOŚCIĄ STRINGA BLOKOWEGO\n",
"# NIEZALEŻNĄ OD ŚWIATA ZEWNĘTRZNEGO\n",
"# polecam przewinąć, to długie\n",
"#\n",
"# NOTA AKTUALIZACYJNA DO PRZYSZŁEGO SIEBIE: jak gdrive działa ten segment kodu cały wypieprzyć\n",
"#\n",
"lacinski_tekst_wypelniacz = \"\"\"\n",
"\n",
"Adam Mickiewicz\n",
"Sonety krymskie\n",
"\n",
"Stepy akermańskie\n",
"\n",
"Wpłynąłem na suchego przestwór oceanu,\n",
"Wóz nurza się w zieloność i jak łódka brodzi,\n",
"Śród fali łąk szumiących, śród kwiatów powodzi,\n",
"Omijam koralowe ostrowy burzanu.\n",
"\n",
"\n",
"Już mrok zapada, nigdzie drogi ni kurhanu;\n",
"Patrzę w niebo, gwiazd szukam, przewodniczek łodzi;\n",
"Tam z dala błyszczy obłok - tam jutrzenka wschodzi;\n",
"To błyszczy Dniestr, to weszła lampa Akermanu.\n",
"\n",
"\n",
"Stójmy! - jak cicho! - słyszę ciągnące żurawie,\n",
"Których by nie dościgły źrenice sokoła;\n",
"Słyszę, kędy się motyl kołysa na trawie,\n",
"\n",
"\n",
"Kędy wąż śliską piersią dotyka się zioła.\n",
"W takiej ciszy - tak ucho natężam ciekawie,\n",
"Że słyszałbym głos z Litwy. - Jedźmy, nikt nie woła.\n",
"\n",
"\"\"\""
],
"metadata": {
"id": "U-PUZ_HXXMTk"
},
"id": "U-PUZ_HXXMTk",
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"#lacinski_tekst_wypelniacz=\"W CZASIE SUSZY SZOSA SUCHA\""
],
"metadata": {
"id": "oesoS2vs1UUX"
},
"id": "oesoS2vs1UUX",
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# DANE PODSTAWOWE"
],
"metadata": {
"id": "PLsj4vMRCCsl"
},
"id": "PLsj4vMRCCsl"
},
{
"id": "3a890e49-8f59-40b9-b0e0-b18b2503f380",
"cell_type": "code",
"source": [
"zdanie = lacinski_tekst_wypelniacz\n",
"\n",
"pociete_na_litery = [ litera for litera in zdanie ]\n",
"\n",
"unikatowy_zbior = set( pociete_na_litery )\n",
"\n",
"rejestr_powtorek = dict.fromkeys( unikatowy_zbior )\n",
"\n",
"for liczony_znak in rejestr_powtorek.keys():\n",
" #print( liczony_znak, \" \", zdanie.count( liczony_znak ) )\n",
" rejestr_powtorek[ liczony_znak ] = zdanie.count( liczony_znak )\n",
"\n",
"###\n",
"\n",
"print(\"###\")\n",
"print(\n",
" \"\\t\\tilość znaków w sumie: \",\n",
" len( lacinski_tekst_wypelniacz )\n",
")\n",
"print(\n",
" \"\\t\\tilość znaków wykluczając powtarzające się: \",\n",
" len( rejestr_powtorek.keys() )\n",
")\n",
"print(\"###\")"
],
"metadata": {
"trusted": true,
"id": "3a890e49-8f59-40b9-b0e0-b18b2503f380",
"outputId": "016a4cb0-4ed3-4cc4-d312-7fb554f20eeb",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"###\n",
"\t\tilość znaków w sumie: 692\n",
"\t\tilość znaków wykluczając powtarzające się: 52\n",
"###\n"
]
}
],
"execution_count": null
},
{
"cell_type": "markdown",
"source": [
"# dodatkowe biblioteki 📚\n",
"\n",
"**Pandas**\n",
"*to popularna biblioteka, zajmująca się danymi tabelarycznymi*\n",
"- więcej informacji: https://pandas.pydata.org/\n",
"- repozytorium PyPi: https://pypi.org/project/pandas/\n",
"\n",
"**IPython**\n",
"*to taka interaktywna powłoka z wieloma prezentacyjnymi bajerami*\n",
"- więcej informacji: https://ipython.org/\n",
"- repozytorium PyPi: https://pypi.org/project/ipython/\n",
"\n",
"\n",
"# cel ich użycia ⚛\n",
"nas obchodzi tylko konwerter do HTMLa wbudowany w tabelę Pandasową i wyświetlacz HTMLa IPythona"
],
"metadata": {
"id": "JVcRukrAeimH"
},
"id": "JVcRukrAeimH"
},
{
"id": "177478bc-32c1-4756-a9d1-0d4976c46fab",
"cell_type": "code",
"source": [
"\n",
"# Pandas\n",
"tabela = pandas.DataFrame(\n",
" {\n",
" \"symbol\": rejestr_powtorek.keys(),\n",
" \"powtórki\": rejestr_powtorek.values()\n",
" }\n",
")\n",
"\n",
"# IPython\n",
"IPython.display.HTML(\n",
" tabela.transpose().to_html(\n",
" header=False\n",
" )\n",
")\n",
"\n"
],
"metadata": {
"trusted": true,
"id": "177478bc-32c1-4756-a9d1-0d4976c46fab",
"outputId": "08a80e8b-aa7a-4111-d342-01cc5643c034",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 80
}
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<IPython.core.display.HTML object>"
],
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <tbody>\n",
" <tr>\n",
" <th>symbol</th>\n",
" <td>A</td>\n",
" <td>k</td>\n",
" <td>l</td>\n",
" <td>K</td>\n",
" <td>ó</td>\n",
" <td></td>\n",
" <td>h</td>\n",
" <td>.</td>\n",
" <td>S</td>\n",
" <td>i</td>\n",
" <td>T</td>\n",
" <td>\\n</td>\n",
" <td>c</td>\n",
" <td>ę</td>\n",
" <td>j</td>\n",
" <td>O</td>\n",
" <td>-</td>\n",
" <td>g</td>\n",
" <td>P</td>\n",
" <td>ą</td>\n",
" <td>ź</td>\n",
" <td>;</td>\n",
" <td>D</td>\n",
" <td>f</td>\n",
" <td>b</td>\n",
" <td>d</td>\n",
" <td>o</td>\n",
" <td>,</td>\n",
" <td>M</td>\n",
" <td>z</td>\n",
" <td>W</td>\n",
" <td>L</td>\n",
" <td>ć</td>\n",
" <td>y</td>\n",
" <td>ś</td>\n",
" <td>s</td>\n",
" <td>Ś</td>\n",
" <td>u</td>\n",
" <td>Ż</td>\n",
" <td>J</td>\n",
" <td>t</td>\n",
" <td>ń</td>\n",
" <td>!</td>\n",
" <td>m</td>\n",
" <td>p</td>\n",
" <td>a</td>\n",
" <td>e</td>\n",
" <td>r</td>\n",
" <td>w</td>\n",
" <td>n</td>\n",
" <td>ł</td>\n",
" <td>ż</td>\n",
" </tr>\n",
" <tr>\n",
" <th>powtórki</th>\n",
" <td>2</td>\n",
" <td>27</td>\n",
" <td>7</td>\n",
" <td>2</td>\n",
" <td>8</td>\n",
" <td>93</td>\n",
" <td>7</td>\n",
" <td>5</td>\n",
" <td>4</td>\n",
" <td>44</td>\n",
" <td>2</td>\n",
" <td>28</td>\n",
" <td>20</td>\n",
" <td>9</td>\n",
" <td>6</td>\n",
" <td>1</td>\n",
" <td>5</td>\n",
" <td>7</td>\n",
" <td>1</td>\n",
" <td>8</td>\n",
" <td>2</td>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>8</td>\n",
" <td>19</td>\n",
" <td>33</td>\n",
" <td>13</td>\n",
" <td>1</td>\n",
" <td>31</td>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>26</td>\n",
" <td>4</td>\n",
" <td>26</td>\n",
" <td>1</td>\n",
" <td>14</td>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>20</td>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>18</td>\n",
" <td>8</td>\n",
" <td>44</td>\n",
" <td>33</td>\n",
" <td>24</td>\n",
" <td>19</td>\n",
" <td>22</td>\n",
" <td>19</td>\n",
" <td>4</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
]
},
"metadata": {},
"execution_count": 11
}
],
"execution_count": null
},
{
"cell_type": "markdown",
"source": [
"# harmider 😖\n",
" ich ułożenie wynika teraz od mechanizmów stojących za trikami których\n",
" użyliśmy do zbioru znaków i zmiany zbioru w słownik...\n",
" nie obchodzi nas to teraz\n",
"\n",
"> przydałoby się posortować to według powtórek znaku, to nas obchodzi, takie ułożenie jest nam potrzebne\n"
],
"metadata": {
"id": "Piqbv0pagBbX"
},
"id": "Piqbv0pagBbX"
},
{
"id": "3baf3d2d-fd1f-4741-b1b7-b6acf3f55c83",
"cell_type": "code",
"source": [
"\n",
"\n",
"tabela = tabela.sort_values(\"powtórki\")\n",
"\n",
"IPython.display.HTML(\n",
" tabela.transpose().to_html(\n",
" header=False\n",
" )\n",
")\n",
"\n"
],
"metadata": {
"trusted": true,
"id": "3baf3d2d-fd1f-4741-b1b7-b6acf3f55c83",
"outputId": "f404aae5-37dc-4a4a-a3da-9cf39cab0ebd",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 80
}
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<IPython.core.display.HTML object>"
],
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <tbody>\n",
" <tr>\n",
" <th>symbol</th>\n",
" <td>L</td>\n",
" <td>O</td>\n",
" <td>D</td>\n",
" <td>f</td>\n",
" <td>M</td>\n",
" <td>ć</td>\n",
" <td>P</td>\n",
" <td>Ż</td>\n",
" <td>ń</td>\n",
" <td>Ś</td>\n",
" <td>ź</td>\n",
" <td>T</td>\n",
" <td>J</td>\n",
" <td>K</td>\n",
" <td>!</td>\n",
" <td>A</td>\n",
" <td>W</td>\n",
" <td>ś</td>\n",
" <td>;</td>\n",
" <td>ż</td>\n",
" <td>S</td>\n",
" <td>-</td>\n",
" <td>.</td>\n",
" <td>j</td>\n",
" <td>g</td>\n",
" <td>l</td>\n",
" <td>h</td>\n",
" <td>b</td>\n",
" <td>p</td>\n",
" <td>ó</td>\n",
" <td>ą</td>\n",
" <td>ę</td>\n",
" <td>,</td>\n",
" <td>u</td>\n",
" <td>m</td>\n",
" <td>w</td>\n",
" <td>d</td>\n",
" <td>ł</td>\n",
" <td>t</td>\n",
" <td>c</td>\n",
" <td>n</td>\n",
" <td>r</td>\n",
" <td>y</td>\n",
" <td>s</td>\n",
" <td>k</td>\n",
" <td>\\n</td>\n",
" <td>z</td>\n",
" <td>o</td>\n",
" <td>e</td>\n",
" <td>i</td>\n",
" <td>a</td>\n",
" <td></td>\n",
" </tr>\n",
" <tr>\n",
" <th>powtórki</th>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>3</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>6</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>9</td>\n",
" <td>13</td>\n",
" <td>14</td>\n",
" <td>18</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>20</td>\n",
" <td>20</td>\n",
" <td>22</td>\n",
" <td>24</td>\n",
" <td>26</td>\n",
" <td>26</td>\n",
" <td>27</td>\n",
" <td>28</td>\n",
" <td>31</td>\n",
" <td>33</td>\n",
" <td>33</td>\n",
" <td>44</td>\n",
" <td>44</td>\n",
" <td>93</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
]
},
"metadata": {},
"execution_count": 12
}
],
"execution_count": null
},
{
"cell_type": "markdown",
"source": [
"# tak lepiej, może jeszcze wizualizacja\n",
"\n",
"**MatPlotLib**\n",
"*powszechnie stosowana biblioteka do wizualizacji prezentowanych danych*\n",
"- więcej informacji: https://matplotlib.org/\n",
"- repozytorium PyPi: https://pypi.org/project/matplotlib/\n",
"\n",
"aktualnie od najrzadszej do najczęstrzej litery sortowane są nam potrzebne do algorytmu Huffmana, aby zobaczyć że tak faktycznie jest i by wzrokowo to odczuć walniemy sobie graf wystąpień"
],
"metadata": {
"id": "JgKMZVypEXPZ"
},
"id": "JgKMZVypEXPZ"
},
{
"cell_type": "code",
"source": [
"# UWAGA\n",
"# ten fragment w przeciwieństwie do reszty tego notesu przyspożył mi dużo trudności\n",
"# nie dosyć, że nasza pierwotna tabela jest zapisana w pamięci lekko obrócona pod względem logiki\n",
"# to dodatkowo zagadnienie działania plotera z matplotlib i konfiguracja jego\n",
"# parametrów to temat skomplikowany i szeroki. Rozważę napisanie o tym osobnego materiału.\n",
"\n",
"matplotlib.pyplot.cla()\n",
"matplotlib.pyplot.clf()\n",
"matplotlib.pyplot.close()\n",
"\n",
"plot_tmp_tab = pandas.DataFrame(\n",
" { \"liczebność\": tabela[\"powtórki\"].tolist() },\n",
" index=tabela[\"symbol\"].tolist()\n",
")\n",
"\n",
"matplotlib.pyplot.figure( dpi=10 )\n",
"matplotlib.pyplot.rcParams[ \"figure.figsize\" ] = ( 10, 10 )\n",
"\n",
"plot_box_tmp = plot_tmp_tab.plot( kind=\"barh\" )\n",
"\n",
"plot_box_tmp.tick_params( axis='y', pad=10, width=10, labelsize=10 )\n",
"\n",
"matplotlib.pyplot.title(\"Raport wystąpień znaków w tekście\")\n",
"matplotlib.pyplot.ylabel(\"Znaki występujące w tekście\")\n",
"matplotlib.pyplot.xlabel(\"Ilość wystąpień znaków\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 908
},
"id": "vfd1L71WFObn",
"outputId": "3dd6c6be-1801-4e17-a602-488aea390baa"
},
"id": "vfd1L71WFObn",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"Text(0.5, 0, 'Ilość wystąpień znaków')"
]
},
"metadata": {},
"execution_count": 13
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 64x48 with 0 Axes>"
]
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 1000x1000 with 1 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA08AAANYCAYAAAAc7dY5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACO2klEQVR4nOzdd3RUdf7/8dckJIGQzNBCNYQqxdCURWoSAQ0YkLaoKAIi2BBEFOtKCKIgKGXFsiJKVFT2C4osxVBMpKgUQRREekSRLkyhk9zfHx7mZ0iQSTLh3jDPxzlzjvczM5/7ymR2T968P/dzbYZhGAIAAAAA/K0gswMAAAAAQHFA8QQAAAAAPqB4AgAAAAAfUDwBAAAAgA8ongAAAADABxRPAAAAAOADiicAAAAA8AHFEwAAAAD4gOIJAHBV+/bbb5WSkqLjx4+bHcWvDMPQ5MmTNXv2bLOjWN6///1vffDBB2bHAHAVoHgCAFjOzJkzZbPZlJmZWah5Tpw4oTvvvFPHjh1TmTJl/JKtKIwePVo2m01Hjhzx+T2vvPKKJkyYoJYtWxZhMmtJSEhQbGxsvt4zbdo0jRkzRjfeeGO+z2ez2TR69Oh8vw/A1YviCUCxdOGP6wuPEiVKqFq1ahowYID27dtndjyfLFq0qFj9YVbc8krSv/71L0VFRWnChAlmR/Gr1atXa9y4cVq0aJFiYmLMjuOTr7/+WqNHj76iHcBNmzbp+eef1/z583XttddesfMCuHpRPAEo1saMGaMPPvhAb731ljp37qwPP/xQ8fHxOn36tNnRLmvRokVKSUkxO4bPrmTee+65R6dOnSpUYbBu3Tq9//77mj17tkJDQ/2Yznxbt27VvHnz1KxZM7Oj+Ozrr7++4ssnt2zZov/7v/9T69atC/T+U6dO6V//+pefUwEozkqYHQAACqNz585q3ry5JGnQoEGqUKGCXn75Zc2fP1+33367yenyduLECZUuXdrsGJYWHBys4ODgQs3xj3/8Q0ePHvVTImsZNGiQ2RGKhbvuuqtQ7y9ZsqSfkgC4WtB5AnBVadeunSRp165d3rGzZ89q1KhRuuGGG+RwOFS6dGm1a9dO6enpOd6bmZkpm82mV155RZMnT1ZMTIxKlSql+Ph4bd68Ode5vvzyS7Vr106lS5dWmTJl1K1bN23dujXHay5cy/LTTz/prrvuUtmyZdW2bVsNGDBAr7/+uiTlWH54KSNGjFD58uVlGIZ3bOjQobLZbPr3v//tHTt48KBsNpvefPNNeTwelS5dWo8++miu+X777TcFBwdr3LhxkqRz584pJSVFdevWVcmSJVW+fHm1bdtWS5culaTL5n3llVfUunVrlS9fXqVKldINN9ygOXPm5DqvzWbTI488olmzZqlevXoqWbKkbrjhBq1YsSLH6y51zdPixYu9n3lkZKSSkpK0ZcuWHK8ZMGCAIiIitG/fPnXv3l0RERGKiorSE088oaysrEt+xtL//33l9RgwYICknN+Tt99+W7Vr11ZYWJj+8Y9/aN26dTnm++GHHzRgwADVqlVLJUuWVOXKlTVw4ECfirpffvlFderUUWxsrA4ePChJ2r17t3r37q1y5copPDxcLVu21MKFC73vMQxDFSpU0IgRI7xj2dnZKlOmjIKDg3N0fV5++WWVKFFCHo8nz/MfP35cwcHBOb5fR44cUVBQUK7v4kMPPaTKlSv/7ec6cuRISVLNmjW9n+lff78ffvihbrjhBpUqVUrlypXTnXfeqV9//fWyn9OSJUsUHh6uPn366Pz585KkpUuXqm3btipTpowiIiJUr149Pfvsszned/r0aY0ePVrXXnutSpYsqSpVqqhnz545/r8jr2ue9u3bp4EDB6pSpUoKCwvTddddp3ffffeyOQFcHeg8AbiqXPhjrGzZst4xl8uld955R3369NHgwYPldrs1Y8YMJSYmau3atWratGmOOd5//3253W4NGTJEp0+f1tSpU9W+fXv9+OOPqlSpkiRp2bJl6ty5s2rVqqXRo0fr1KlTeu2119SmTRtt2LBBNWrUyDFn7969VbduXb300ksyDEPNmjXT77//rqVLl/q0C1i7du00efJkbdmyxXvB/MqVKxUUFKSVK1dq2LBh3jFJiouLU0REhHr06KHZs2dr0qRJOTo5H3/8sQzD0N133y3pzz9ux40bp0GDBqlFixZyuVxav369NmzYoJtvvlkPPPDA3+adOnWqbrvtNt199906e/asPvnkE/Xu3VsLFixQUlJSjtd+9dVXmj17toYNG6awsDC98cYb6tSpk9auXfu3mwF88MEH6t+/vxITE/Xyyy/r5MmTevPNN9W2bVtt3Lgxx2eelZWlxMRE3XjjjXrllVe0bNkyvfrqq6pdu7YeeuihS56j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gZbtlSlTJte4x+NReHi4Bg8erMGDBxcq1IgRI7R69WrNnz9flSpV0qhRo7RhwwY1bdq0UPMCAAAAQEHlu/PUrl07jRo1SmfOnJH0/7cAX7BggRISEgodyO12KzU1Va+88oo6dOig2NhYvffee8rKyir03AAAAABQUPnuPE2YMEEdOnRQzZo1FR8frzNnzmjYsGH6/vvv9dVXXxU60O7du3Xu3Dm1aNHCO+ZwOFSvXr1Czw0AAAAABZXvzlNsbKy2b9+uhx9+WFlZWerQoYOaNm2qdevWqXbt2kWREQAAAABMl+/O0969exUdHa1//etfeT53Yfe9gqpVq5ZCQkK0bt0671xOp1Pbt29XXFxcoebOy+aUxL+9izAAAAAASAUonmrWrKn9+/erYsWKOcaPHj2qmjVrFvrapMjISPXv318jR45UuXLlVLFiRSUnJysoKMh7fRUAAAAAXGn5XrZnGEaeRYzH41HJkiX9EmrSpElq1aqVunTpoo4dO6pNmzZq0KBBoeaPjIzM8YiIiPBLVgAAAACBwefO04gRIyT9ubve888/r/DwcO9zWVlZWrNmjd+2Eo+MjNSsWbO8xydOnFBKSoruv//+As/pcrlyHTscjgLPBwAAACCw+Fw8bdy4UdKfnacff/xRoaGh3udCQ0PVpEkTPfHEE34JtXHjRv38889q0aKFnE6nxowZI0nq1q1bgee8+LomwzAKlREAAABAYPG5eEpPT5ck3XvvvZo6dWqRb7LwyiuvaNu2bQoNDdUNN9yglStXqkKFCgWez+12+zEdAAAAgEBjMwrZgnG5XPryyy9Vv3591a9f31+5chkwYICOHz+uefPmFej9l9pswul0stseAAAAEMAuXNJzudog37vt3X777YqLi9MjjzyiU6dOqXnz5srMzJRhGPrkk0/Uq1evQgW/lKlTp7LUDgAAAIBp8l08rVixQs8995wk6bPPPpNhGDp+/LhSU1M1duzYIiueimpzh9jkNAWFhV/+hRaTOT7J7AgAAABAQMn3VuVOp1PlypWTJH3xxRfq1auXwsPDlZSUpB07dvg94AUDBgxQ9+7di2x+AAAAAPg7+S6eoqOj9c033+jEiRP64osvdMstt0iSjh075rf7PAEAAACA1eR72d7w4cN19913KyIiQjExMUpISJD053K+Ro0a+TsfAAAAAFhCvounhx9+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sUOGYahBgwZ5Pt+gQQMdO3ZMhw8fVsWKFa9wOgAAAKB4yM7OVufOneV2u/Xhhx+qdu3a+umnn7zbc3/66ac6e/as9/VDhgzRli1bVKlSJUl/dnpKlSqlxYsXy+Fw6D//+Y86dOig7du3ey+h2blzp/773//qf//7n1wul+677z49/PDDmjVrlnfe9PR0ValSRenp6dq5c6fuuOMONW3aVIMHD5b0Z4G1Y8cOzZ8/X3a7XU899ZRuvfVW/fTTTwoJCdGQIUN09uxZrVixQqVLl9ZPP/2kiIgISdK+ffsUFxenhIQEffnll7Lb7Vq9enWRbxhhmeLpAjpLAAAAQMEtW7ZMa9eu1datW3XttddKkmrVquV9/q97CEyePFlffvml1qxZo1KlSmnVqlVau3atDh06pLCwMEnSK6+8onnz5mnOnDm6//77Jf25+93777+vatWqSZJee+01JSUl6dVXX1XlypUlSWXLltW0adMUHBys+vXrKykpScuXL9fgwYO9RdPq1avVunVrSdKsWbMUHR2tefPmqXfv3tq7d6969eqlRo0a5foZXn/9dTkcDn3yyScKCQmRJO/PWpQss2yvTp06stls2rp1a57Pb926VWXLlr2q13MCAAAAhfX999/rmmuuuWwxsXjxYj399NOaPXu297WbNm2Sx+NR+fLlFRER4X3s2bMnx5K46tWrewsnSWrVqpWys7O1bds279h1112X42a0VapU0aFDhyT9+bd9iRIldOONN3qfL1++vOrVq+etB4YNG6axY8eqTZs2Sk5O1g8//JDjZ2zXrp23cLpSLFM8lS9fXjfffLPeeOMNnTp1KsdzBw4c0KxZs3THHXdwAzMAAADgb1y8f0BefvrpJ915550aP368brnlFu+4x+NRlSpV9P333+d4bNu2TSNHjsxXjosLG5vNpuzsbJ/fP2jQIO3evVv33HOPfvzxRzVv3lyvvfaaJN9+xqJgqWV706ZNU+vWrZWYmKixY8fm2Kq8WrVqevHFF/1+zs0pibLb7X6fFwAAADBD48aN9dtvv2n79u15dp+OHDmirl27qlevXnrsscdyPHf99dfrwIEDKlGihGrUqHHJc+zdu1e///67qlatKkn69ttvFRQUpHr16vmUsUGDBjp//rzWrFnjXbZ39OhRbdu2TQ0bNvS+Ljo6Wg8++KAefPBBPfPMM5o+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3O8X277++uvIysrCli1b0NTUhLq6OuTl5SEnJ0fHM9FbXl4e3nnnHWzbtg0ymQxtbW1oa2vTWhnwTlxdXWFsbCy91pKSEqSnp/c7ftOmTZg/fz6mTp0qrZa3YsUKZGdno6ioCI2NjVi1ahVqamqQmJgobRceHo6zZ8/izJkzePLJJ6W2goICBAQEwMLC4r7fAyKihw2LJyIiA5eXl4cJEyYgMjISU6ZMgRACn3zyiXRJmFqthkKhgJeXF5555hl4eHhIy3A7ODigtLQU3333HcLCwjBhwgRs3779jvdAhYSEQKPRaBVK4eHhUKvVfd4f4+7ujqCgIHh6emrdm2Vubo5z584hJiYGHh4eePnll6FQKPDb3/4WABATE4NnnnkGTz31FB555BHs3r0b/v7+yMnJQXZ2Nnx8fFBQUICsrKxexzQ3N8fKlSsxb948BAcHw9LSEkVFRf2+psWLF2PHjh3Iy8uDr68vwsLCkJ+f/0AzT0qlEmq1Gs899xycnZ2lx+1Lvd/JI488gvz8fBQXF2Ps2LFYv379Xbf905/+hDlz5mDq1KloamrC8uXLkZKSgtTUVPj6+mL//v0oKSmBu7u7tI2vry9sbW0xbtw4aRXGO51PIiJDJhNCCH0HQUREQ5cQAu7u7liyZAlSUlIGfP8pKSmYPHky5syZA+D771dKSkqCSqUa8GMREZFh48wTERHpzb///W+89dZbaGtrw6JFiwblGH/5y18wderUOy7cQEREpAu5vgMgIqKha+TIkRgxYgRyc3NhZ2c3KMeIjo6Gj48PFixYoPMlcURERH3hZXtEREREREQ64GV7REREREREOmDxREREREREpAMWT0RERERERDpg8URERERERKQDFk9EREREREQ6YPFERERERESkAxZPREREREREOmDxREREREREpIP/AyJsyw/MdgGiAAAAAElFTkSuQmCC\n"
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": [
"# ale jako ciekawostkę jeszcze `piruet` tablicami 💃\n",
"\n",
"Jako ciekawostkę jak tabele obracać i przewracać zrobię tu dla was taki mały **\"piruet\"**"
],
"metadata": {
"id": "KbkZKRU7iG0g"
},
"id": "KbkZKRU7iG0g"
},
{
"cell_type": "code",
"source": [
"revers_horyzontalny = tabela.iloc[::-1]\n",
"\n",
"revers_vertykalny = tabela.iloc[:, ::-1]"
],
"metadata": {
"trusted": true,
"id": "lL9IiRHki7YE"
},
"outputs": [],
"execution_count": null,
"id": "lL9IiRHki7YE"
},
{
"cell_type": "code",
"source": [
"IPython.display.HTML( revers_horyzontalny.transpose().to_html( header=False ) )"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 80
},
"id": "QazJU5yIs4cE",
"outputId": "fb892793-2978-456c-d834-adde990969c9"
},
"id": "QazJU5yIs4cE",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<IPython.core.display.HTML object>"
],
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <tbody>\n",
" <tr>\n",
" <th>symbol</th>\n",
" <td></td>\n",
" <td>a</td>\n",
" <td>i</td>\n",
" <td>e</td>\n",
" <td>o</td>\n",
" <td>z</td>\n",
" <td>\\n</td>\n",
" <td>k</td>\n",
" <td>s</td>\n",
" <td>y</td>\n",
" <td>r</td>\n",
" <td>n</td>\n",
" <td>c</td>\n",
" <td>t</td>\n",
" <td>ł</td>\n",
" <td>d</td>\n",
" <td>w</td>\n",
" <td>m</td>\n",
" <td>u</td>\n",
" <td>,</td>\n",
" <td>ę</td>\n",
" <td>ą</td>\n",
" <td>ó</td>\n",
" <td>p</td>\n",
" <td>b</td>\n",
" <td>h</td>\n",
" <td>l</td>\n",
" <td>g</td>\n",
" <td>j</td>\n",
" <td>.</td>\n",
" <td>-</td>\n",
" <td>S</td>\n",
" <td>ż</td>\n",
" <td>;</td>\n",
" <td>ś</td>\n",
" <td>W</td>\n",
" <td>A</td>\n",
" <td>!</td>\n",
" <td>K</td>\n",
" <td>J</td>\n",
" <td>T</td>\n",
" <td>ź</td>\n",
" <td>Ś</td>\n",
" <td>ń</td>\n",
" <td>Ż</td>\n",
" <td>P</td>\n",
" <td>ć</td>\n",
" <td>M</td>\n",
" <td>f</td>\n",
" <td>D</td>\n",
" <td>O</td>\n",
" <td>L</td>\n",
" </tr>\n",
" <tr>\n",
" <th>powtórki</th>\n",
" <td>93</td>\n",
" <td>44</td>\n",
" <td>44</td>\n",
" <td>33</td>\n",
" <td>33</td>\n",
" <td>31</td>\n",
" <td>28</td>\n",
" <td>27</td>\n",
" <td>26</td>\n",
" <td>26</td>\n",
" <td>24</td>\n",
" <td>22</td>\n",
" <td>20</td>\n",
" <td>20</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>18</td>\n",
" <td>14</td>\n",
" <td>13</td>\n",
" <td>9</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>6</td>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>3</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
]
},
"metadata": {},
"execution_count": 15
}
]
},
{
"cell_type": "code",
"source": [
"IPython.display.HTML( revers_vertykalny.transpose().to_html( header=False ) )"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 80
},
"id": "YUI80DiPtBD8",
"outputId": "2abfa780-6958-4853-b11b-fc60c6db4959"
},
"id": "YUI80DiPtBD8",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<IPython.core.display.HTML object>"
],
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <tbody>\n",
" <tr>\n",
" <th>powtórki</th>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>3</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>6</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>9</td>\n",
" <td>13</td>\n",
" <td>14</td>\n",
" <td>18</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>20</td>\n",
" <td>20</td>\n",
" <td>22</td>\n",
" <td>24</td>\n",
" <td>26</td>\n",
" <td>26</td>\n",
" <td>27</td>\n",
" <td>28</td>\n",
" <td>31</td>\n",
" <td>33</td>\n",
" <td>33</td>\n",
" <td>44</td>\n",
" <td>44</td>\n",
" <td>93</td>\n",
" </tr>\n",
" <tr>\n",
" <th>symbol</th>\n",
" <td>L</td>\n",
" <td>O</td>\n",
" <td>D</td>\n",
" <td>f</td>\n",
" <td>M</td>\n",
" <td>ć</td>\n",
" <td>P</td>\n",
" <td>Ż</td>\n",
" <td>ń</td>\n",
" <td>Ś</td>\n",
" <td>ź</td>\n",
" <td>T</td>\n",
" <td>J</td>\n",
" <td>K</td>\n",
" <td>!</td>\n",
" <td>A</td>\n",
" <td>W</td>\n",
" <td>ś</td>\n",
" <td>;</td>\n",
" <td>ż</td>\n",
" <td>S</td>\n",
" <td>-</td>\n",
" <td>.</td>\n",
" <td>j</td>\n",
" <td>g</td>\n",
" <td>l</td>\n",
" <td>h</td>\n",
" <td>b</td>\n",
" <td>p</td>\n",
" <td>ó</td>\n",
" <td>ą</td>\n",
" <td>ę</td>\n",
" <td>,</td>\n",
" <td>u</td>\n",
" <td>m</td>\n",
" <td>w</td>\n",
" <td>d</td>\n",
" <td>ł</td>\n",
" <td>t</td>\n",
" <td>c</td>\n",
" <td>n</td>\n",
" <td>r</td>\n",
" <td>y</td>\n",
" <td>s</td>\n",
" <td>k</td>\n",
" <td>\\n</td>\n",
" <td>z</td>\n",
" <td>o</td>\n",
" <td>e</td>\n",
" <td>i</td>\n",
" <td>a</td>\n",
" <td></td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
]
},
"metadata": {},
"execution_count": 16
}
]
},
{
"cell_type": "markdown",
"source": [
"# wracając do naszego zadania 🤔\n",
"\n",
"teraz pora użyć naszych narzędzi, zebranych danych i wyczarować kompresowaną wersję naszej informacji. wracamy do pseudokodu żeby sprawdzić jak tam nasze zadanie:\n",
"\n",
"1. [x] liczenie częstotliwości występowania symboli\n",
"2. [x] sortuj utworzony zestaw powtórki-symbol\n",
"3. [ ] utwórz drzewo z posortowanych danych\n",
"4. [ ] przypisz kody binarne do każdego węzła drzewa\n",
"5. [ ] zapisz pierwotny tekst z użyciem nowych zastępczych kodów binarnych, zamiast kodów ASCII liter\n"
],
"metadata": {
"id": "zgxLr2BpgVii"
},
"id": "zgxLr2BpgVii"
},
{
"cell_type": "markdown",
"source": [
"# konstrukcja drzewa 🌴\n",
"\n",
"nadeszła pora określenia drzewo... ale czym jest drzewo? to gałęzie z których wwychodzą gałęzie i mają liście... gałęzi trochę będzie, więc potrzeba nam fabryki gałęzi, szkieletowego konstruktora obiektu z którego będą wylatywać gotowe uzbrojone objekty - użyjemy klasy. Nazwę ją sobie `GrafoDrzew` i określę"
],
"metadata": {
"id": "W3D9IyRyjf5y"
},
"id": "W3D9IyRyjf5y"
},
{
"id": "fbb083a9-e621-4dc7-842c-4e00df4119c3",
"cell_type": "code",
"source": [
"\n",
"# przydasiek w postaci definicji specjalnego errora GrafoDrzewu\n",
"class G_D_Error(Exception):\n",
" pass\n",
"\n",
"############# FABRYKA BADYLI\n",
"\n",
"class GrafoDrzew:\n",
"\n",
" ygdrasil = None\n",
"\n",
" badyle = []\n",
"\n",
" def __init__(ten_badyl, wartosc=0):\n",
"\n",
" ten_badyl.wartosc = wartosc # to listek,\n",
" # ale aby nie przesadzać ze zbędnym nazywaniem wartość\n",
" # będzie wartością i tyle, nie chcę żebyście się zgubili\n",
" ten_badyl.przypisy = []\n",
"\n",
" ten_badyl.lewy = None # lewa gałąź\n",
" ten_badyl.prawy = None # prawa gałąź\n",
"\n",
" ten_badyl.korzen = None # gałąź-matka tej gałęzi\n",
" # jesli to najwyższy badyl korzen jest domyślnie pusty (None)\n",
"\n",
"\n",
" def przypis(thisStick, data):\n",
" thisStick.przypisy.append(data)\n",
"\n",
" # co rusz się zapominam i muszę poprawiać więc obrócę to w narrację\n",
" # \"tak naprawdę to wszystko zgodnie z moim planem\"\n",
"\n",
" # jako ciekawostka dobrym nawykiem programisty jest używać nazw angielskich\n",
" # w pewnym momencie od pewnego poziomu jest to obowiązkowy mus\n",
" # w środowisku opensource klarowność kodu to kwestia etykiety savoir-vivre\n",
"\n",
"\n",
"\n",
" def dodaj_badyl(ten_badyl, nazwa_strony, wartosc=0):\n",
" try:\n",
" ten_badyl.montuj_badyla(\n",
" nazwa_strony,\n",
" GrafoDrzew( wartosc )\n",
" )\n",
" except NameError:\n",
" raise G_D_Error(\"pączkowanie badyla\", \"strona nieistniejąca\")\n",
" return ten_badyl\n",
"\n",
" def montuj_badyla(ten_badyl, nazwa_strony, inny_badyl):\n",
"\n",
" inny_badyl.korzen = ten_badyl\n",
"\n",
" match nazwa_strony:\n",
" case \"lewy\":\n",
" ten_badyl.lewy = inny_badyl\n",
" case \"prawy\":\n",
" ten_badyl.prawy = inny_badyl\n",
" case _:\n",
" raise NameError( nazwa_strony )\n",
" return ten_badyl\n",
"\n",
" ##### to się przyda, ważna sprawa\n",
"\n",
" def __str__(ten_badyl):\n",
" return f'[ GrafoDrzew={str(ten_badyl.DEBUGPRINT())} ]'\n",
"\n",
" def __repr__(ten_badyl):\n",
" return f'[ GrafoDrzew={str(ten_badyl.DEBUGPRINT())} ]\\n'\n",
"\n",
" def __eq__(ten_badyl, inny_badyl):\n",
" return ten_badyl.wartosc == inny_badyl.wartosc\n",
"\n",
" def __gt__(ten_badyl, inny_badyl):\n",
" return ten_badyl.wartosc > inny_badyl.wartosc\n",
"\n",
" def __lt__(ten_badyl, inny_badyl):\n",
" return ten_badyl.wartosc < inny_badyl.wartosc\n",
"\n",
" def __ge__(ten_badyl, inny_badyl):\n",
" return ten_badyl.wartosc >= inny_badyl.wartosc\n",
"\n",
" def __le__(ten_badyl, inny_badyl):\n",
" return ten_badyl.wartosc <= inny_badyl.wartosc\n",
"\n",
" def __add__(ten_badyl, inny_badyl):\n",
" nowy_badyl = GrafoDrzew( ten_badyl.wartosc + inny_badyl.wartosc )\n",
" nowy_badyl.montuj_badyla(\"lewy\", ten_badyl)\n",
" nowy_badyl.montuj_badyla(\"prawy\", inny_badyl)\n",
" return nowy_badyl\n",
"\n",
"\n",
" ##### powyższe wytłumaczę potem\n",
"\n",
" def DEBUGPRINT(ten_badyl): # TEN_BADYL{ WARTOSC: [LEWY_BADYL, PRAWY_BADYL] }\n",
"\n",
" tmp_out = {}\n",
" tmp_out[ ten_badyl.wartosc ] = [None, None]\n",
"\n",
" if not ten_badyl.przypisy:\n",
" None\n",
" else:\n",
" tmp_out[ \"NOTA\" ] = ten_badyl.przypisy\n",
"\n",
" if ten_badyl.lewy is not None:\n",
" tmp_out[ ten_badyl.wartosc ][ 0 ] = ten_badyl.lewy.DEBUGPRINT()\n",
"\n",
" if ten_badyl.prawy is not None:\n",
" tmp_out[ ten_badyl.wartosc ][ 1 ] = ten_badyl.prawy.DEBUGPRINT()\n",
"\n",
" return tmp_out\n",
"\n",
"\n",
"\n",
"def DEBUG(dane):\n",
" print(\n",
" json.dumps(\n",
" dane,\n",
" sort_keys=False,\n",
" indent=4\n",
" )\n",
" )\n"
],
"metadata": {
"trusted": true,
"id": "fbb083a9-e621-4dc7-842c-4e00df4119c3"
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": [
"\n",
"> **Filozofia Kaizen** – metoda małych kroków\n",
"- `doskonalenie poprzez stopniowe wprowadzanie drobnych zmian`\n",
"- https://pl.wikipedia.org/wiki/Kaizen\n",
"\n",
"### test drzewa"
],
"metadata": {
"id": "FL74w7MF3Vlx"
},
"id": "FL74w7MF3Vlx"
},
{
"cell_type": "code",
"source": [
"test_swierk = GrafoDrzew(9)\n",
"test_swierk.wartosc = 27\n",
"test_swierk.dodaj_badyl(\"prawy\", 9)\n",
"tesy_wierzba = GrafoDrzew(3)\n",
"test_swierk.montuj_badyla(\"lewy\", tesy_wierzba).lewy.wartosc=81\n",
"tesy_wierzba.przypis(\"X\")\n",
"DEBUG(test_swierk.DEBUGPRINT())\n",
"print(test_swierk)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Q2gluszQiBd7",
"outputId": "f5591590-aaec-44a2-e1c3-a2f0bd75d8cf"
},
"id": "Q2gluszQiBd7",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"{\n",
" \"27\": [\n",
" {\n",
" \"81\": [\n",
" null,\n",
" null\n",
" ],\n",
" \"NOTA\": [\n",
" \"X\"\n",
" ]\n",
" },\n",
" {\n",
" \"9\": [\n",
" null,\n",
" null\n",
" ]\n",
" }\n",
" ]\n",
"}\n",
"[ GrafoDrzew={27: [{81: [None, None], 'NOTA': ['X']}, {9: [None, None]}]} ]\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"# odświeżenie w pamięci posiadanej wiedzy\n",
"\n",
"warto czasem zebrane dane powtórzyć aby się nie pogubić podczas pracy z tymi danymi mieć w głowie pełen obraz zagadnienia... ale aby zkompresować rutynę powtarzaną zamkniemy to w funkcji!"
],
"metadata": {
"id": "qohlh7XA--z4"
},
"id": "qohlh7XA--z4"
},
{
"cell_type": "code",
"source": [
"\n",
"# aby ułatwić sobie życie warto zamykać w funkcjach\n",
"# kompleksowe powtarzające się procedury\n",
"# można to interpretować jako kompresję zapisu wykonywania kroków\n",
"def poka_tabele(input_tab):\n",
" return IPython.display.HTML(\n",
" input_tab.transpose().to_html(\n",
" header=False\n",
" )\n",
" )\n"
],
"metadata": {
"id": "5p6m_7mG_KiY"
},
"id": "5p6m_7mG_KiY",
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"poka_tabele(tabela)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 80
},
"id": "rjJ2msvF_QPP",
"outputId": "0a6fc676-6f7e-4eb5-8de1-5738115c8dcb"
},
"id": "rjJ2msvF_QPP",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<IPython.core.display.HTML object>"
],
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <tbody>\n",
" <tr>\n",
" <th>symbol</th>\n",
" <td>L</td>\n",
" <td>O</td>\n",
" <td>D</td>\n",
" <td>f</td>\n",
" <td>M</td>\n",
" <td>ć</td>\n",
" <td>P</td>\n",
" <td>Ż</td>\n",
" <td>ń</td>\n",
" <td>Ś</td>\n",
" <td>ź</td>\n",
" <td>T</td>\n",
" <td>J</td>\n",
" <td>K</td>\n",
" <td>!</td>\n",
" <td>A</td>\n",
" <td>W</td>\n",
" <td>ś</td>\n",
" <td>;</td>\n",
" <td>ż</td>\n",
" <td>S</td>\n",
" <td>-</td>\n",
" <td>.</td>\n",
" <td>j</td>\n",
" <td>g</td>\n",
" <td>l</td>\n",
" <td>h</td>\n",
" <td>b</td>\n",
" <td>p</td>\n",
" <td>ó</td>\n",
" <td>ą</td>\n",
" <td>ę</td>\n",
" <td>,</td>\n",
" <td>u</td>\n",
" <td>m</td>\n",
" <td>w</td>\n",
" <td>d</td>\n",
" <td>ł</td>\n",
" <td>t</td>\n",
" <td>c</td>\n",
" <td>n</td>\n",
" <td>r</td>\n",
" <td>y</td>\n",
" <td>s</td>\n",
" <td>k</td>\n",
" <td>\\n</td>\n",
" <td>z</td>\n",
" <td>o</td>\n",
" <td>e</td>\n",
" <td>i</td>\n",
" <td>a</td>\n",
" <td></td>\n",
" </tr>\n",
" <tr>\n",
" <th>powtórki</th>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>2</td>\n",
" <td>3</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>4</td>\n",
" <td>5</td>\n",
" <td>5</td>\n",
" <td>6</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>7</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>8</td>\n",
" <td>9</td>\n",
" <td>13</td>\n",
" <td>14</td>\n",
" <td>18</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>19</td>\n",
" <td>20</td>\n",
" <td>20</td>\n",
" <td>22</td>\n",
" <td>24</td>\n",
" <td>26</td>\n",
" <td>26</td>\n",
" <td>27</td>\n",
" <td>28</td>\n",
" <td>31</td>\n",
" <td>33</td>\n",
" <td>33</td>\n",
" <td>44</td>\n",
" <td>44</td>\n",
" <td>93</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
]
},
"metadata": {},
"execution_count": 20
}
]
},
{
"cell_type": "code",
"source": [
"len( rejestr_powtorek.keys() )"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "eIwJ4_p6Az7-",
"outputId": "b6709e06-328f-4093-cb0e-ce9a9777cf19"
},
"id": "eIwJ4_p6Az7-",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"52"
]
},
"metadata": {},
"execution_count": 21
}
]
},
{
"cell_type": "markdown",
"source": [
"# robiąc krok 3 naszego pseudokodu dokładnie rzecz biorąc\n",
"\n",
"Tworząc drzewo Huffmana, najpierw sortujemy symbole według ich\n",
"częstości występowania i tworzymy dla każdego z nich liść\n",
"drzewa. Następnie trzeba połączyć dwa liście o najniższych\n",
"częstościach, tworząc węzeł nadrzędny. Powtarzaj ten\n",
"krok, aż utworzysz całe drzewo...\n",
"\n",
"# prościej mówiąc jak się za to zabierzemy\n",
"\n",
"- robimy listę pojedynczych gałęzi"
],
"metadata": {
"id": "9goIrVXYW6HW"
},
"id": "9goIrVXYW6HW"
},
{
"cell_type": "code",
"source": [
"GrafoDrzew.badyle=[]\n",
"\n",
"for x in tabela.index:\n",
"\n",
" nowy_badyl = GrafoDrzew( tabela['powtórki'][x] )\n",
"\n",
" nowy_badyl.przypis( tabela['symbol'][x] )\n",
"\n",
" GrafoDrzew.badyle.append(nowy_badyl)\n",
"\n",
"print(GrafoDrzew.badyle) # dla ciekawskich: odkomentuj tego printa"
],
"metadata": {
"id": "8D5vdJ4FFInM",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "4c28393f-45cd-4165-fa13-268a1fd8998b"
},
"id": "8D5vdJ4FFInM",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[[ GrafoDrzew={1: [None, None], 'NOTA': ['L']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['O']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['D']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['f']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['M']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['ć']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['P']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['Ż']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['ń']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['Ś']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['ź']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['T']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['J']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['K']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['!']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['A']} ]\n",
", [ GrafoDrzew={3: [None, None], 'NOTA': ['W']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': ['ś']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': [';']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': ['ż']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': ['S']} ]\n",
", [ GrafoDrzew={5: [None, None], 'NOTA': ['-']} ]\n",
", [ GrafoDrzew={5: [None, None], 'NOTA': ['.']} ]\n",
", [ GrafoDrzew={6: [None, None], 'NOTA': ['j']} ]\n",
", [ GrafoDrzew={7: [None, None], 'NOTA': ['g']} ]\n",
", [ GrafoDrzew={7: [None, None], 'NOTA': ['l']} ]\n",
", [ GrafoDrzew={7: [None, None], 'NOTA': ['h']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['b']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['p']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['ó']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['ą']} ]\n",
", [ GrafoDrzew={9: [None, None], 'NOTA': ['ę']} ]\n",
", [ GrafoDrzew={13: [None, None], 'NOTA': [',']} ]\n",
", [ GrafoDrzew={14: [None, None], 'NOTA': ['u']} ]\n",
", [ GrafoDrzew={18: [None, None], 'NOTA': ['m']} ]\n",
", [ GrafoDrzew={19: [None, None], 'NOTA': ['w']} ]\n",
", [ GrafoDrzew={19: [None, None], 'NOTA': ['d']} ]\n",
", [ GrafoDrzew={19: [None, None], 'NOTA': ['ł']} ]\n",
", [ GrafoDrzew={20: [None, None], 'NOTA': ['t']} ]\n",
", [ GrafoDrzew={20: [None, None], 'NOTA': ['c']} ]\n",
", [ GrafoDrzew={22: [None, None], 'NOTA': ['n']} ]\n",
", [ GrafoDrzew={24: [None, None], 'NOTA': ['r']} ]\n",
", [ GrafoDrzew={26: [None, None], 'NOTA': ['y']} ]\n",
", [ GrafoDrzew={26: [None, None], 'NOTA': ['s']} ]\n",
", [ GrafoDrzew={27: [None, None], 'NOTA': ['k']} ]\n",
", [ GrafoDrzew={28: [None, None], 'NOTA': ['\\n']} ]\n",
", [ GrafoDrzew={31: [None, None], 'NOTA': ['z']} ]\n",
", [ GrafoDrzew={33: [None, None], 'NOTA': ['o']} ]\n",
", [ GrafoDrzew={33: [None, None], 'NOTA': ['e']} ]\n",
", [ GrafoDrzew={44: [None, None], 'NOTA': ['i']} ]\n",
", [ GrafoDrzew={44: [None, None], 'NOTA': ['a']} ]\n",
", [ GrafoDrzew={93: [None, None], 'NOTA': [' ']} ]\n",
"]\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"- wyjmujemy z listy 2 z najniższymi wartościami... czyli 2 pierwsze od lewej bo sobie sortowaliśmy te dane 😎"
],
"metadata": {
"id": "zdfDSu0ahBv5"
},
"id": "zdfDSu0ahBv5"
},
{
"cell_type": "code",
"source": [
"male_badyle = [\n",
" GrafoDrzew.badyle.pop(0),\n",
" GrafoDrzew.badyle.pop(0)\n",
"]\n",
"\n",
"print(male_badyle)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "ZpuGsELchTeb",
"outputId": "2be830c0-b1f9-491f-a4b1-810c31bd1611"
},
"id": "ZpuGsELchTeb",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[[ GrafoDrzew={1: [None, None], 'NOTA': ['L']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['O']} ]\n",
"]\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"- klejimy go do nowego badyla, jego wartosc to suma tych 2 skladowych"
],
"metadata": {
"id": "zCYlFm54kPUm"
},
"id": "zCYlFm54kPUm"
},
{
"cell_type": "code",
"source": [
"nowy_superbadyl = male_badyle[0] + male_badyle[1]\n",
"\n",
"print(nowy_superbadyl)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "WammzRMXkNKv",
"outputId": "8e3b8c80-cc81-496d-dd60-e0659798fea5"
},
"id": "WammzRMXkNKv",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[ GrafoDrzew={2: [{1: [None, None], 'NOTA': ['L']}, {1: [None, None], 'NOTA': ['O']}]} ]\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"- wstawiamy sklejony badyl z powrotem do listy wszystkich badyli, tylko że niestety musimy go wstawić tak, żeby nie psuć posortowania 😪"
],
"metadata": {
"id": "VXqwOJ_gmuxb"
},
"id": "VXqwOJ_gmuxb"
},
{
"cell_type": "code",
"source": [
"for i in range(len(GrafoDrzew.badyle)):\n",
" if nowy_superbadyl <= GrafoDrzew.badyle[i]:\n",
" GrafoDrzew.badyle.insert(i, nowy_superbadyl)\n",
" break\n",
"\n",
"print(GrafoDrzew.badyle)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "DreCQtfdm6MM",
"outputId": "df670f52-fb54-4632-b4b1-7362b98db8f5"
},
"id": "DreCQtfdm6MM",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[[ GrafoDrzew={1: [None, None], 'NOTA': ['D']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['f']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['M']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['ć']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['P']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['Ż']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['ń']} ]\n",
", [ GrafoDrzew={1: [None, None], 'NOTA': ['Ś']} ]\n",
", [ GrafoDrzew={2: [{1: [None, None], 'NOTA': ['L']}, {1: [None, None], 'NOTA': ['O']}]} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['ź']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['T']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['J']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['K']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['!']} ]\n",
", [ GrafoDrzew={2: [None, None], 'NOTA': ['A']} ]\n",
", [ GrafoDrzew={3: [None, None], 'NOTA': ['W']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': ['ś']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': [';']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': ['ż']} ]\n",
", [ GrafoDrzew={4: [None, None], 'NOTA': ['S']} ]\n",
", [ GrafoDrzew={5: [None, None], 'NOTA': ['-']} ]\n",
", [ GrafoDrzew={5: [None, None], 'NOTA': ['.']} ]\n",
", [ GrafoDrzew={6: [None, None], 'NOTA': ['j']} ]\n",
", [ GrafoDrzew={7: [None, None], 'NOTA': ['g']} ]\n",
", [ GrafoDrzew={7: [None, None], 'NOTA': ['l']} ]\n",
", [ GrafoDrzew={7: [None, None], 'NOTA': ['h']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['b']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['p']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['ó']} ]\n",
", [ GrafoDrzew={8: [None, None], 'NOTA': ['ą']} ]\n",
", [ GrafoDrzew={9: [None, None], 'NOTA': ['ę']} ]\n",
", [ GrafoDrzew={13: [None, None], 'NOTA': [',']} ]\n",
", [ GrafoDrzew={14: [None, None], 'NOTA': ['u']} ]\n",
", [ GrafoDrzew={18: [None, None], 'NOTA': ['m']} ]\n",
", [ GrafoDrzew={19: [None, None], 'NOTA': ['w']} ]\n",
", [ GrafoDrzew={19: [None, None], 'NOTA': ['d']} ]\n",
", [ GrafoDrzew={19: [None, None], 'NOTA': ['ł']} ]\n",
", [ GrafoDrzew={20: [None, None], 'NOTA': ['t']} ]\n",
", [ GrafoDrzew={20: [None, None], 'NOTA': ['c']} ]\n",
", [ GrafoDrzew={22: [None, None], 'NOTA': ['n']} ]\n",
", [ GrafoDrzew={24: [None, None], 'NOTA': ['r']} ]\n",
", [ GrafoDrzew={26: [None, None], 'NOTA': ['y']} ]\n",
", [ GrafoDrzew={26: [None, None], 'NOTA': ['s']} ]\n",
", [ GrafoDrzew={27: [None, None], 'NOTA': ['k']} ]\n",
", [ GrafoDrzew={28: [None, None], 'NOTA': ['\\n']} ]\n",
", [ GrafoDrzew={31: [None, None], 'NOTA': ['z']} ]\n",
", [ GrafoDrzew={33: [None, None], 'NOTA': ['o']} ]\n",
", [ GrafoDrzew={33: [None, None], 'NOTA': ['e']} ]\n",
", [ GrafoDrzew={44: [None, None], 'NOTA': ['i']} ]\n",
", [ GrafoDrzew={44: [None, None], 'NOTA': ['a']} ]\n",
", [ GrafoDrzew={93: [None, None], 'NOTA': [' ']} ]\n",
"]\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"- powtarzać, aż nie zostanie tylko jeden!\n",
"> \"Jeden, by wszystkimi rządzić, jeden, by wszystkie odnaleźć, Jeden, by wszystkie zgromadzić i w ciemności związać.\" ~ *__Władcy Pierścieni__, J.R.R. Tolkien*\n",
"\n",
"# świetnie, to teraz tylko zamknąć to wszystko w funkcje i pętlę `while`"
],
"metadata": {
"id": "VPQf2ZbapqB9"
},
"id": "VPQf2ZbapqB9"
},
{
"cell_type": "code",
"source": [
"def buduj_ygdrasil():\n",
"\n",
" GrafoDrzew.badyle=[]\n",
"\n",
" for x in tabela.index:\n",
" nowy_badyl = GrafoDrzew( tabela['powtórki'][x] )\n",
" nowy_badyl.przypis( tabela['symbol'][x] )\n",
" GrafoDrzew.badyle.append(nowy_badyl)\n",
"\n",
" while len(GrafoDrzew.badyle) > 1:\n",
"\n",
" male_badyle = [\n",
" GrafoDrzew.badyle.pop(0),\n",
" GrafoDrzew.badyle.pop(0)\n",
" ]\n",
"\n",
" nowy_superbadyl = male_badyle[0] + male_badyle[1]\n",
"\n",
" for i in range(len(GrafoDrzew.badyle)):\n",
" if nowy_superbadyl <= GrafoDrzew.badyle[i]:\n",
" GrafoDrzew.badyle.insert(i, nowy_superbadyl)\n",
" nowy_superbadyl = None\n",
" break\n",
" if nowy_superbadyl is not None:\n",
" GrafoDrzew.badyle.append(nowy_superbadyl)\n",
"\n",
" GrafoDrzew.ygdrasil = GrafoDrzew.badyle[0]\n",
" return GrafoDrzew.ygdrasil\n",
"\n"
],
"metadata": {
"id": "uA5kE0PXpyVY"
},
"id": "uA5kE0PXpyVY",
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"buduj_ygdrasil().DEBUGPRINT()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "QrYcZLJ0tgdE",
"outputId": "d6ddd4e7-f0f4-4e72-b686-64c39e98d1d7"
},
"id": "QrYcZLJ0tgdE",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"{692: [{291: [{132: [{65: [{32: [{16: [{8: [None, None], 'NOTA': ['b']},\n",
" {8: [None, None], 'NOTA': ['p']}]},\n",
" {16: [{8: [{4: [{2: [{1: [None, None], 'NOTA': ['L']},\n",
" {1: [None, None], 'NOTA': ['O']}]},\n",
" {2: [None, None], 'NOTA': ['ź']}]},\n",
" {4: [{2: [{1: [None, None], 'NOTA': ['M']},\n",
" {1: [None, None], 'NOTA': ['ć']}]},\n",
" {2: [{1: [None, None], 'NOTA': ['D']},\n",
" {1: [None, None], 'NOTA': ['f']}]}]}]},\n",
" {8: [{4: [{2: [None, None], 'NOTA': ['K']},\n",
" {2: [None, None], 'NOTA': ['!']}]},\n",
" {4: [{2: [None, None], 'NOTA': ['T']},\n",
" {2: [None, None], 'NOTA': ['J']}]}]}]}]},\n",
" {33: [None, None], 'NOTA': ['o']}]},\n",
" {67: [{33: [None, None], 'NOTA': ['e']},\n",
" {34: [{16: [{8: [{4: [None, None], 'NOTA': [';']},\n",
" {4: [None, None], 'NOTA': ['ż']}]},\n",
" {8: [{4: [{2: [{1: [None, None], 'NOTA': ['ń']},\n",
" {1: [None, None], 'NOTA': ['Ś']}]},\n",
" {2: [{1: [None, None], 'NOTA': ['P']},\n",
" {1: [None, None], 'NOTA': ['Ż']}]}]},\n",
" {4: [None, None], 'NOTA': ['ś']}]}]},\n",
" {18: [{9: [{4: [None, None], 'NOTA': ['S']},\n",
" {5: [{2: [None, None], 'NOTA': ['A']},\n",
" {3: [None, None], 'NOTA': ['W']}]}]},\n",
" {9: [None, None], 'NOTA': ['ę']}]}]}]}]},\n",
" {159: [{75: [{37: [{18: [None, None], 'NOTA': ['m']},\n",
" {19: [None, None], 'NOTA': ['w']}]},\n",
" {38: [{19: [None, None], 'NOTA': ['d']},\n",
" {19: [None, None], 'NOTA': ['ł']}]}]},\n",
" {84: [{40: [{20: [None, None], 'NOTA': ['t']},\n",
" {20: [None, None], 'NOTA': ['c']}]},\n",
" {44: [None, None], 'NOTA': ['i']}]}]}]},\n",
" {401: [{182: [{89: [{44: [None, None], 'NOTA': ['a']},\n",
" {45: [{22: [None, None], 'NOTA': ['n']},\n",
" {23: [{10: [{5: [None, None], 'NOTA': ['-']},\n",
" {5: [None, None], 'NOTA': ['.']}]},\n",
" {13: [{6: [None, None], 'NOTA': ['j']},\n",
" {7: [None, None], 'NOTA': ['g']}]}]}]}]},\n",
" {93: [None, None], 'NOTA': [' ']}]},\n",
" {219: [{103: [{50: [{24: [None, None], 'NOTA': ['r']},\n",
" {26: [None, None], 'NOTA': ['y']}]},\n",
" {53: [{26: [None, None], 'NOTA': ['s']},\n",
" {27: [{13: [None, None], 'NOTA': [',']},\n",
" {14: [{7: [None, None], 'NOTA': ['l']},\n",
" {7: [None, None], 'NOTA': ['h']}]}]}]}]},\n",
" {116: [{55: [{27: [None, None], 'NOTA': ['k']},\n",
" {28: [None, None], 'NOTA': ['\\n']}]},\n",
" {61: [{30: [{14: [None, None], 'NOTA': ['u']},\n",
" {16: [{8: [None, None], 'NOTA': ['ó']},\n",
" {8: [None, None], 'NOTA': ['ą']}]}]},\n",
" {31: [None, None], 'NOTA': ['z']}]}]}]}]}]}"
]
},
"metadata": {},
"execution_count": 27
}
]
},
{
"cell_type": "markdown",
"source": [
"# trochę to mętne...\n",
"ale mamy jakieś dane, które powiedzą nam czy to działa? otóż **TAK**, najwyższa gałąź, czyli nasz pień drzewa musi mieć wartość, równą ilości wszystkich znaków w zdaniu, bo to suma powtórzeń każdego ze znaków\n",
"\n",
"# przypomnienie zebranych danych i test zgodności\n"
],
"metadata": {
"id": "US9U8BcHvHxl"
},
"id": "US9U8BcHvHxl"
},
{
"cell_type": "code",
"source": [
"print(\n",
" \"\\t\\tilość znaków w sumie: \",\n",
" len( zdanie )\n",
")\n",
"print(\n",
" \"\\t\\twartość szczytu drzewa: \",\n",
" GrafoDrzew.ygdrasil.wartosc\n",
")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0gdGIYLytw4f",
"outputId": "7a3f3e23-7bcf-4111-efe4-c1aedee7afc2"
},
"id": "0gdGIYLytw4f",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\t\tilość znaków w sumie: 692\n",
"\t\twartość szczytu drzewa: 692\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"1. [x] liczenie częstotliwości występowania symboli\n",
"2. [x] sortuj utworzony zestaw powtórki-symbol\n",
"3. [x] utwórz drzewo z posortowanych danych\n",
"4. [ ] przypisz kody binarne do każdego węzła drzewa\n",
"5. [ ] zapisz pierwotny tekst z użyciem nowych zastępczych kodów binarnych, zamiast kodów ASCII liter\n",
"\n",
"# Binarne znaki i słownik umowny\n",
"\n",
"tak jak jest to zaprezentowane w prezentacji każda litera otrzyma swój kod w zależności od pozycji w drzewie. tu przyda się **rekurencyjność** z użyciem funkcji.\n"
],
"metadata": {
"id": "jFu0kDhvx8gY"
},
"id": "jFu0kDhvx8gY"
},
{
"cell_type": "code",
"source": [
"slownik_bitowy = dict()\n",
"\n",
"def kod_huffmana(badyl, bity=\"\"):\n",
" if len(badyl.przypisy)>0:\n",
" slownik_bitowy[ badyl.przypisy[0] ] = bity\n",
" if badyl.lewy is not None:\n",
" kod_huffmana(badyl.lewy, bity+\"0\")\n",
" if badyl.prawy is not None:\n",
" kod_huffmana(badyl.prawy, bity+\"1\")"
],
"metadata": {
"id": "Cz-Jbv7rvndb"
},
"id": "Cz-Jbv7rvndb",
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"kod_huffmana(GrafoDrzew.ygdrasil)\n",
"\n",
"pandas.DataFrame.from_dict(\n",
" slownik_bitowy,\n",
" orient='index',\n",
" columns=[\"kod binarny\"]\n",
")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"id": "N61Ys6md05nY",
"outputId": "aa3e40a2-3de1-4596-e80f-268208ad3df9"
},
"id": "N61Ys6md05nY",
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
" kod binarny\n",
"b 000000\n",
"p 000001\n",
"L 000010000\n",
"O 000010001\n",
"ź 00001001\n",
"M 000010100\n",
"ć 000010101\n",
"D 000010110\n",
"f 000010111\n",
"K 00001100\n",
"! 00001101\n",
"T 00001110\n",
"J 00001111\n",
"o 0001\n",
"e 0010\n",
"; 0011000\n",
"ż 0011001\n",
"ń 001101000\n",
"Ś 001101001\n",
"P 001101010\n",
"Ż 001101011\n",
"ś 0011011\n",
"S 0011100\n",
"A 00111010\n",
"W 00111011\n",
"ę 001111\n",
"m 01000\n",
"w 01001\n",
"d 01010\n",
"ł 01011\n",
"t 01100\n",
"c 01101\n",
"i 0111\n",
"a 1000\n",
"n 10010\n",
"- 1001100\n",
". 1001101\n",
"j 1001110\n",
"g 1001111\n",
" 101\n",
"r 11000\n",
"y 11001\n",
"s 11010\n",
", 110110\n",
"l 1101110\n",
"h 1101111\n",
"k 11100\n",
"\\n 11101\n",
"u 111100\n",
"ó 1111010\n",
"ą 1111011\n",
"z 11111"
],
"text/html": [
"\n",
" <div id=\"df-ffd4c925-ab7b-44e5-8a52-d656c5f7c120\" class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>kod binarny</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>b</th>\n",
" <td>000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>p</th>\n",
" <td>000001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>L</th>\n",
" <td>000010000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>O</th>\n",
" <td>000010001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ź</th>\n",
" <td>00001001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>M</th>\n",
" <td>000010100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ć</th>\n",
" <td>000010101</td>\n",
" </tr>\n",
" <tr>\n",
" <th>D</th>\n",
" <td>000010110</td>\n",
" </tr>\n",
" <tr>\n",
" <th>f</th>\n",
" <td>000010111</td>\n",
" </tr>\n",
" <tr>\n",
" <th>K</th>\n",
" <td>00001100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>!</th>\n",
" <td>00001101</td>\n",
" </tr>\n",
" <tr>\n",
" <th>T</th>\n",
" <td>00001110</td>\n",
" </tr>\n",
" <tr>\n",
" <th>J</th>\n",
" <td>00001111</td>\n",
" </tr>\n",
" <tr>\n",
" <th>o</th>\n",
" <td>0001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>e</th>\n",
" <td>0010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>;</th>\n",
" <td>0011000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ż</th>\n",
" <td>0011001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ń</th>\n",
" <td>001101000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Ś</th>\n",
" <td>001101001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>P</th>\n",
" <td>001101010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Ż</th>\n",
" <td>001101011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ś</th>\n",
" <td>0011011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>S</th>\n",
" <td>0011100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>A</th>\n",
" <td>00111010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>W</th>\n",
" <td>00111011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ę</th>\n",
" <td>001111</td>\n",
" </tr>\n",
" <tr>\n",
" <th>m</th>\n",
" <td>01000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>w</th>\n",
" <td>01001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>d</th>\n",
" <td>01010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ł</th>\n",
" <td>01011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>t</th>\n",
" <td>01100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>c</th>\n",
" <td>01101</td>\n",
" </tr>\n",
" <tr>\n",
" <th>i</th>\n",
" <td>0111</td>\n",
" </tr>\n",
" <tr>\n",
" <th>a</th>\n",
" <td>1000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>n</th>\n",
" <td>10010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>-</th>\n",
" <td>1001100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>.</th>\n",
" <td>1001101</td>\n",
" </tr>\n",
" <tr>\n",
" <th>j</th>\n",
" <td>1001110</td>\n",
" </tr>\n",
" <tr>\n",
" <th>g</th>\n",
" <td>1001111</td>\n",
" </tr>\n",
" <tr>\n",
" <th></th>\n",
" <td>101</td>\n",
" </tr>\n",
" <tr>\n",
" <th>r</th>\n",
" <td>11000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>y</th>\n",
" <td>11001</td>\n",
" </tr>\n",
" <tr>\n",
" <th>s</th>\n",
" <td>11010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>,</th>\n",
" <td>110110</td>\n",
" </tr>\n",
" <tr>\n",
" <th>l</th>\n",
" <td>1101110</td>\n",
" </tr>\n",
" <tr>\n",
" <th>h</th>\n",
" <td>1101111</td>\n",
" </tr>\n",
" <tr>\n",
" <th>k</th>\n",
" <td>11100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>\\n</th>\n",
" <td>11101</td>\n",
" </tr>\n",
" <tr>\n",
" <th>u</th>\n",
" <td>111100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ó</th>\n",
" <td>1111010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>ą</th>\n",
" <td>1111011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>z</th>\n",
" <td>11111</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <div class=\"colab-df-buttons\">\n",
"\n",
" <div class=\"colab-df-container\">\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-ffd4c925-ab7b-44e5-8a52-d656c5f7c120')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
" <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
" </svg>\n",
" </button>\n",
"\n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" .colab-df-buttons div {\n",
" margin-bottom: 4px;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-ffd4c925-ab7b-44e5-8a52-d656c5f7c120 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-ffd4c925-ab7b-44e5-8a52-d656c5f7c120');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
"\n",
"\n",
"<div id=\"df-bdd18139-a1cc-4976-bd17-ae47af2a8e69\">\n",
" <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-bdd18139-a1cc-4976-bd17-ae47af2a8e69')\"\n",
" title=\"Suggest charts\"\n",
" style=\"display:none;\">\n",
"\n",
"<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <g>\n",
" <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
" </g>\n",
"</svg>\n",
" </button>\n",
"\n",
"<style>\n",
" .colab-df-quickchart {\n",
" --bg-color: #E8F0FE;\n",
" --fill-color: #1967D2;\n",
" --hover-bg-color: #E2EBFA;\n",
" --hover-fill-color: #174EA6;\n",
" --disabled-fill-color: #AAA;\n",
" --disabled-bg-color: #DDD;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-quickchart {\n",
" --bg-color: #3B4455;\n",
" --fill-color: #D2E3FC;\n",
" --hover-bg-color: #434B5C;\n",
" --hover-fill-color: #FFFFFF;\n",
" --disabled-bg-color: #3B4455;\n",
" --disabled-fill-color: #666;\n",
" }\n",
"\n",
" .colab-df-quickchart {\n",
" background-color: var(--bg-color);\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: var(--fill-color);\n",
" height: 32px;\n",
" padding: 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-quickchart:hover {\n",
" background-color: var(--hover-bg-color);\n",
" box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: var(--button-hover-fill-color);\n",
" }\n",
"\n",
" .colab-df-quickchart-complete:disabled,\n",
" .colab-df-quickchart-complete:disabled:hover {\n",
" background-color: var(--disabled-bg-color);\n",
" fill: var(--disabled-fill-color);\n",
" box-shadow: none;\n",
" }\n",
"\n",
" .colab-df-spinner {\n",
" border: 2px solid var(--fill-color);\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" animation:\n",
" spin 1s steps(1) infinite;\n",
" }\n",
"\n",
" @keyframes spin {\n",
" 0% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" border-left-color: var(--fill-color);\n",
" }\n",
" 20% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 30% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 40% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 60% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 80% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" 90% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" }\n",
"</style>\n",
"\n",
" <script>\n",
" async function quickchart(key) {\n",
" const quickchartButtonEl =\n",
" document.querySelector('#' + key + ' button');\n",
" quickchartButtonEl.disabled = true; // To prevent multiple clicks.\n",
" quickchartButtonEl.classList.add('colab-df-spinner');\n",
" try {\n",
" const charts = await google.colab.kernel.invokeFunction(\n",
" 'suggestCharts', [key], {});\n",
" } catch (error) {\n",
" console.error('Error during call to suggestCharts:', error);\n",
" }\n",
" quickchartButtonEl.classList.remove('colab-df-spinner');\n",
" quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
" }\n",
" (() => {\n",
" let quickchartButtonEl =\n",
" document.querySelector('#df-bdd18139-a1cc-4976-bd17-ae47af2a8e69 button');\n",
" quickchartButtonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
" })();\n",
" </script>\n",
"</div>\n",
" </div>\n",
" </div>\n"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "dataframe",
"summary": "{\n \"name\": \")\",\n \"rows\": 52,\n \"fields\": [\n {\n \"column\": \"kod binarny\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 52,\n \"samples\": [\n \"001101010\",\n \"11001\",\n \"11101\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}"
}
},
"metadata": {},
"execution_count": 30
}
]
},
{
"cell_type": "markdown",
"source": [
"# czas na zastosowanie naszego słownika i porównania\n",
"\n",
"żeby ułatwić wizualizacje wyników zamiast operacji na bitach znaki ascii zostały zastąpione 8-znakowymi stringami."
],
"metadata": {
"id": "KVaIpjlB2c5b"
},
"id": "KVaIpjlB2c5b"
},
{
"cell_type": "code",
"source": [
"zdanie_bitowo_w_ascii = ''.join(\n",
" format(ord(znak_zdania), '08b') for znak_zdania in zdanie\n",
")\n",
"zdanie_bitowo_z_kodowaniem_huffmana = ''.join(\n",
" slownik_bitowy[znak_zdania] for znak_zdania in zdanie\n",
")\n",
"\n",
"\n",
"print(\"#\")\n",
"print(\n",
" \"\\trozmiar orginalnego zdania, zapisanego w ASCII: \\t\\t\",\n",
" len( zdanie_bitowo_w_ascii ),\n",
" \"bitów\"\n",
")\n",
"\n",
"print(\n",
" \"\\trozmiar kompresowanego zdania, zapisanego kodowaniem Huffmana: \\t\",\n",
" len( zdanie_bitowo_z_kodowaniem_huffmana ),\n",
" \"bitów\"\n",
")\n",
"print(\"#\")\n",
"print(\n",
" \"\\tstopień kompresji: \\t\\t\",\n",
" len( zdanie_bitowo_w_ascii )/len( zdanie_bitowo_z_kodowaniem_huffmana )\n",
")\n",
"print(\"#\")\n",
"print(\n",
" \"\\twspółczynnik kompresji: \\t\",\n",
" len( zdanie_bitowo_z_kodowaniem_huffmana )/len( zdanie_bitowo_w_ascii )*100,\n",
" \"%\"\n",
")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "sQOKcgtZ1EqG",
"outputId": "c469cb3e-5512-47ca-9c86-5f401df8c525"
},
"id": "sQOKcgtZ1EqG",
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"#\n",
"\trozmiar orginalnego zdania, zapisanego w ASCII: \t\t 5586 bitów\n",
"\trozmiar kompresowanego zdania, zapisanego kodowaniem Huffmana: \t 3395 bitów\n",
"#\n",
"\tstopień kompresji: \t\t 1.645360824742268\n",
"#\n",
"\twspółczynnik kompresji: \t 60.77694235588973 %\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"# Podsumowanie w prezentacji\n",
"\n",
"linki:\n",
"- prezentacja: [...]"
],
"metadata": {
"id": "lCEcTdDa6AhA"
},
"id": "lCEcTdDa6AhA"
},
{
"cell_type": "markdown",
"source": [],
"metadata": {
"id": "AGCapXJy6awv"
},
"id": "AGCapXJy6awv"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment