From 1664d0d5b1379af7f63c886b36c86f09099dfb0a Mon Sep 17 00:00:00 2001 From: Hiroaki Ogasawara <13391129+xhiroga@users.noreply.github.com> Date: Thu, 16 May 2024 18:55:01 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=9E=E3=83=BC=E3=82=B8=E3=82=BD?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=80=81=E3=83=92=E3=83=BC=E3=83=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ALDS1_5_B.ipynb | 217 ++++++++++++ .../ALDS1_9_C.ipynb | 330 ++++++++++++++++++ .../algorithm-and-datastructure/README.md | 45 +++ ...3\343\202\267\343\203\247\343\203\263.svg" | 176 ++++++++++ 4 files changed, 768 insertions(+) create mode 100644 computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_5_B.ipynb create mode 100644 computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_9_C.ipynb create mode 100644 "computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/images/\343\203\221\343\203\274\343\203\206\343\202\243\343\202\267\343\203\247\343\203\263.svg" diff --git a/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_5_B.ipynb b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_5_B.ipynb new file mode 100644 index 000000000..5daa57c0d --- /dev/null +++ b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_5_B.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# マージソート\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "クラス内で男女別の成績順のリストを、男女問わずの成績順のリストに合体するようなイメージ。\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "from logging import getLogger, StreamHandler, DEBUG\n", + "\n", + "logger = getLogger(__name__)\n", + "handler = StreamHandler()\n", + "handler.setLevel(DEBUG)\n", + "logger.setLevel(DEBUG)\n", + "logger.addHandler(handler)\n", + "logger.propagate = False\n" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "def merge(left_list: list[int], right_list: list[int]):\n", + " logger.debug(f\"{left_list=}, {right_list=}\")\n", + " merged = []\n", + " left = left_list.pop(0)\n", + " right = right_list.pop(0)\n", + "\n", + " while True:\n", + " if left < right:\n", + " merged.append(left)\n", + " logger.debug(f\"{merged=}, {left=}, {left_list=}, {right=}, {right_list=}\")\n", + " try:\n", + " left = left_list.pop(0)\n", + " except IndexError as _:\n", + " return merged + [right] + right_list\n", + " else:\n", + " merged.append(right)\n", + " logger.debug(f\"{merged=}, {left=}, {left_list=}, {right=}, {right_list=}\")\n", + " try:\n", + " right = right_list.pop(0)\n", + " except IndentationError as _:\n", + " return merged + [left] + left_list\n" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "left_list=[1, 2, 5], right_list=[3, 4, 6]\n", + "left_list=[1, 2, 5], right_list=[3, 4, 6]\n", + "merged=[1], left=1, left_list=[2, 5], right=3, right_list=[4, 6]\n", + "merged=[1], left=1, left_list=[2, 5], right=3, right_list=[4, 6]\n", + "merged=[1, 2], left=2, left_list=[5], right=3, right_list=[4, 6]\n", + "merged=[1, 2], left=2, left_list=[5], right=3, right_list=[4, 6]\n", + "merged=[1, 2, 3], left=5, left_list=[], right=3, right_list=[4, 6]\n", + "merged=[1, 2, 3], left=5, left_list=[], right=3, right_list=[4, 6]\n", + "merged=[1, 2, 3, 4], left=5, left_list=[], right=4, right_list=[6]\n", + "merged=[1, 2, 3, 4], left=5, left_list=[], right=4, right_list=[6]\n", + "merged=[1, 2, 3, 4, 5], left=5, left_list=[], right=6, right_list=[]\n", + "merged=[1, 2, 3, 4, 5], left=5, left_list=[], right=6, right_list=[]\n" + ] + } + ], + "source": [ + "expected = [1, 2, 3, 4, 5, 6]\n", + "actual = merge([1, 2, 5], [3, 4, 6])\n", + "assert expected == actual\n" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "def merge_sort(nums: list[int]):\n", + " logger.debug(f\"{nums=}\")\n", + " if len(nums) == 1:\n", + " return nums\n", + "\n", + " mid = len(nums) // 2\n", + " left_list = merge_sort(nums[:mid])\n", + " right_list = merge_sort(nums[mid:])\n", + " logger.debug(f\"{left_list=}, {right_list=}\")\n", + " return merge(left_list, right_list)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "nums=[1, 2, 5, 3, 4, 6]\n", + "nums=[1, 2, 5, 3, 4, 6]\n", + "nums=[1, 2, 5]\n", + "nums=[1, 2, 5]\n", + "nums=[1]\n", + "nums=[1]\n", + "nums=[2, 5]\n", + "nums=[2, 5]\n", + "nums=[2]\n", + "nums=[2]\n", + "nums=[5]\n", + "nums=[5]\n", + "left_list=[2], right_list=[5]\n", + "left_list=[2], right_list=[5]\n", + "left_list=[2], right_list=[5]\n", + "left_list=[2], right_list=[5]\n", + "merged=[2], left=2, left_list=[], right=5, right_list=[]\n", + "merged=[2], left=2, left_list=[], right=5, right_list=[]\n", + "left_list=[1], right_list=[2, 5]\n", + "left_list=[1], right_list=[2, 5]\n", + "left_list=[1], right_list=[2, 5]\n", + "left_list=[1], right_list=[2, 5]\n", + "merged=[1], left=1, left_list=[], right=2, right_list=[5]\n", + "merged=[1], left=1, left_list=[], right=2, right_list=[5]\n", + "nums=[3, 4, 6]\n", + "nums=[3, 4, 6]\n", + "nums=[3]\n", + "nums=[3]\n", + "nums=[4, 6]\n", + "nums=[4, 6]\n", + "nums=[4]\n", + "nums=[4]\n", + "nums=[6]\n", + "nums=[6]\n", + "left_list=[4], right_list=[6]\n", + "left_list=[4], right_list=[6]\n", + "left_list=[4], right_list=[6]\n", + "left_list=[4], right_list=[6]\n", + "merged=[4], left=4, left_list=[], right=6, right_list=[]\n", + "merged=[4], left=4, left_list=[], right=6, right_list=[]\n", + "left_list=[3], right_list=[4, 6]\n", + "left_list=[3], right_list=[4, 6]\n", + "left_list=[3], right_list=[4, 6]\n", + "left_list=[3], right_list=[4, 6]\n", + "merged=[3], left=3, left_list=[], right=4, right_list=[6]\n", + "merged=[3], left=3, left_list=[], right=4, right_list=[6]\n", + "left_list=[1, 2, 5], right_list=[3, 4, 6]\n", + "left_list=[1, 2, 5], right_list=[3, 4, 6]\n", + "left_list=[1, 2, 5], right_list=[3, 4, 6]\n", + "left_list=[1, 2, 5], right_list=[3, 4, 6]\n", + "merged=[1], left=1, left_list=[2, 5], right=3, right_list=[4, 6]\n", + "merged=[1], left=1, left_list=[2, 5], right=3, right_list=[4, 6]\n", + "merged=[1, 2], left=2, left_list=[5], right=3, right_list=[4, 6]\n", + "merged=[1, 2], left=2, left_list=[5], right=3, right_list=[4, 6]\n", + "merged=[1, 2, 3], left=5, left_list=[], right=3, right_list=[4, 6]\n", + "merged=[1, 2, 3], left=5, left_list=[], right=3, right_list=[4, 6]\n", + "merged=[1, 2, 3, 4], left=5, left_list=[], right=4, right_list=[6]\n", + "merged=[1, 2, 3, 4], left=5, left_list=[], right=4, right_list=[6]\n", + "merged=[1, 2, 3, 4, 5], left=5, left_list=[], right=6, right_list=[]\n", + "merged=[1, 2, 3, 4, 5], left=5, left_list=[], right=6, right_list=[]\n" + ] + } + ], + "source": [ + "expected = [1, 2, 3, 4, 5, 6]\n", + "actual = merge_sort([1, 2, 5, 3, 4, 6])\n", + "assert expected == actual\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "til_machine_learning_py312", + "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.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_9_C.ipynb b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_9_C.ipynb new file mode 100644 index 000000000..b506ef558 --- /dev/null +++ b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/ALDS1_9_C.ipynb @@ -0,0 +1,330 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 優先度付きキュー\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from logging import getLogger, StreamHandler, DEBUG\n", + "\n", + "logger = getLogger(__name__)\n", + "handler = StreamHandler()\n", + "handler.setLevel(DEBUG)\n", + "logger.setLevel(DEBUG)\n", + "logger.addHandler(handler)\n", + "logger.propagate = False\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "\n", + "class PrioritizedQueue:\n", + " def __init__(self, list: list[int] = []):\n", + " self._array = [sys.maxsize] + list\n", + "\n", + " @property\n", + " def array(self) -> list:\n", + " return self._array[1:]\n", + "\n", + " def max_heapify(self, root: int):\n", + " logger.debug(f\"{self._array=}\")\n", + " left = root * 2\n", + " right = left + 1\n", + " max_i = root\n", + "\n", + " if len(self._array) - 1 < left:\n", + " return\n", + "\n", + " if self._array[root] < self._array[left]:\n", + " max_i = left\n", + "\n", + " if right < len(self._array) and self._array[max_i] < self._array[right]:\n", + " max_i = right\n", + "\n", + " if max_i != root:\n", + " self._array[root], self._array[max_i] = (\n", + " self._array[max_i],\n", + " self._array[root],\n", + " )\n", + " self.max_heapify(max_i)\n", + "\n", + " def insert(self, num: int):\n", + " logger.debug(f\"{self._array=}\")\n", + " # 螺旋本では`increaseKey`メソッドに分けて実装している箇所\n", + " self._array.append(num)\n", + " logger.debug(f\"{self._array=}\")\n", + " current = len(self._array) - 1\n", + " parent = current // 2\n", + " while self._array[parent] < self._array[current]:\n", + " self._array[parent], self._array[current] = (\n", + " self._array[current],\n", + " self._array[parent],\n", + " )\n", + " logger.debug(f\"{self._array=}\")\n", + " current = parent\n", + " parent = current // 2\n", + "\n", + " def extract(self) -> int:\n", + " last = self._array.pop()\n", + " if len(self._array) == 1:\n", + " return last\n", + " extracted, self._array[1] = self._array[1], last\n", + " self.max_heapify(1)\n", + " return extracted\n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "self._array=[9223372036854775807, 2, 3, 1]\n", + "self._array=[9223372036854775807, 3, 2, 1]\n" + ] + } + ], + "source": [ + "expected = [3, 2, 1]\n", + "pq = PrioritizedQueue([2, 3, 1])\n", + "pq.max_heapify(1)\n", + "actual = pq.array\n", + "assert expected == actual\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "self._array=[9223372036854775807, 5, 4]\n", + "self._array=[9223372036854775807, 5, 4, 6]\n", + "self._array=[9223372036854775807, 6, 4, 5]\n" + ] + } + ], + "source": [ + "expected = [6, 4, 5]\n", + "pq = PrioritizedQueue([5, 4])\n", + "pq.insert(6)\n", + "actual = pq.array\n", + "assert expected == actual\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "self._array=[9223372036854775807, 4, 6, 8]\n", + "self._array=[9223372036854775807, 8, 6, 4]\n" + ] + } + ], + "source": [ + "expected_extracted = 10\n", + "expected_array = [8, 6, 4]\n", + "pq = PrioritizedQueue([10, 6, 8, 4])\n", + "actual_extracted = pq.extract()\n", + "actual_array = pq.array\n", + "assert expected_extracted == actual_extracted\n", + "assert expected_array == actual_array\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "def main(commands: list[set[str, int | None]]):\n", + " logger.debug(f\"{commands=}\")\n", + " pq = PrioritizedQueue()\n", + " extracted = []\n", + " for cmd, num in commands:\n", + " if cmd == \"insert\":\n", + " pq.insert(num)\n", + " elif cmd == \"extract\":\n", + " extracted.append(pq.extract())\n", + " else:\n", + " return extracted\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "def line_to_set(line: str) -> set[str, int | None]:\n", + " splitted = line.split(\" \")\n", + " if len(splitted) == 2:\n", + " return (splitted[0], int(splitted[1]))\n", + " else:\n", + " return (splitted[0], None)\n", + "\n", + "\n", + "def parse(input: str) -> list[set[str, int | None]]:\n", + " lines = input.splitlines()\n", + " return [line_to_set(line) for line in lines]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "input = \"\"\"insert 10\n", + "end\"\"\"\n", + "expected = [(\"insert\", 10), (\"end\", None)]\n", + "actual = parse(input)\n", + "assert expected == actual\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "commands=[('insert', 8), ('insert', 2), ('extract', None), ('insert', 10), ('extract', None), ('insert', 11), ('extract', None), ('extract', None), ('end', None)]\n", + "self._array=[9223372036854775807]\n", + "self._array=[9223372036854775807, 8]\n", + "self._array=[9223372036854775807, 8]\n", + "self._array=[9223372036854775807, 8, 2]\n", + "self._array=[9223372036854775807, 2]\n", + "self._array=[9223372036854775807, 2]\n", + "self._array=[9223372036854775807, 2, 10]\n", + "self._array=[9223372036854775807, 10, 2]\n", + "self._array=[9223372036854775807, 2]\n", + "self._array=[9223372036854775807, 2]\n", + "self._array=[9223372036854775807, 2, 11]\n", + "self._array=[9223372036854775807, 11, 2]\n", + "self._array=[9223372036854775807, 2]\n" + ] + } + ], + "source": [ + "input = \"\"\"insert 8\n", + "insert 2\n", + "extract\n", + "insert 10\n", + "extract\n", + "insert 11\n", + "extract\n", + "extract\n", + "end\"\"\"\n", + "expected = [8, 10, 11, 2]\n", + "actual = main(parse(input))\n", + "assert expected == actual\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 標準ライブラリによる優先度付きキュー\n" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "import heapq\n", + "\n", + "pq = [5, 9, 6, 7, 4, 1, 0]\n", + "# WARN: heapq.heapify() はミュータブル!\n", + "heapq.heapify(pq)\n", + "popped = heapq.heappop(pq)\n", + "assert 0 == popped\n" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[39], line 8\u001b[0m\n\u001b[0;32m 6\u001b[0m heapq\u001b[38;5;241m.\u001b[39mheappush(pq, \u001b[38;5;241m99\u001b[39m)\n\u001b[0;32m 7\u001b[0m popped \u001b[38;5;241m=\u001b[39m heapq\u001b[38;5;241m.\u001b[39mheappop(pq)\n\u001b[1;32m----> 8\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;241m99\u001b[39m \u001b[38;5;241m==\u001b[39m popped\n", + "\u001b[1;31mAssertionError\u001b[0m: " + ] + } + ], + "source": [ + "pq = [10, 20, 30, 40, 50]\n", + "# heapq._heapify_max(pq)\n", + "# WARN: `heapq.heappush(pq, int)`すると崩れるし、そもそもPublic API ではないため、`_heapify_max`は使わないのが無難。\n", + "# https://discuss.python.org/t/make-max-heap-functions-public-in-heapq/16944/12\n", + "\n", + "heapq.heappush(pq, 99)\n", + "popped = heapq.heappop(pq)\n", + "assert 99 == popped\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "til_machine_learning_py312", + "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.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/README.md b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/README.md index a604592d8..56e20197a 100644 --- a/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/README.md +++ b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/README.md @@ -5,3 +5,48 @@ ## 2章 アルゴリズムと計算量 時間計算量と領域計算量について。領域計算量は空間計算量とも言う。 + +## 5章 探索 + +### 番兵 + +- `for`ループまたは`while`ループで、探索のための比較以外に終了条件の判定を行うことが面倒であるため、リストの最後に目的のデータをダミーで入れておく。 +- Pythonでループを実装する場合や、TypeScriptで`find`などを用いる場合、勝手に最後まで探索してくれるので考慮不要。 + +### ハッシュの衝突 + +アルゴリズム図鑑で紹介されているチェーン法の他に、オープンアドレス法による対応がある。オープンアドレス法には、線形走査法とダブルハッシュ法がある。 + +- 線形走査法 + - 衝突が発生した場合、その1つ後ろの位置を格納箇所とする + - 線形走査法・オープンアドレス法の両方に言えるが、いずれはハッシュ表が満杯になる。 +- ダブルハッシュ法 + - ハッシュが衝突したら、引数に衝突回数を加えて再度ハッシュを求める。 + +[ハッシュ探索②(オープンアドレス法) | Programming Place Plus アルゴリズムとデータ構造編【探索アルゴリズム】 第7章](https://programming-place.net/ppp/contents/algorithm/search/007.html#linear_probing) + +## 7章 高等的整列 + +### パーティション + +![パーティション](./images/パーティション.svg) + +### クイックソート + +再帰的にパーティションを行う。 + +### 計数ソート + +[計数ソート | アルゴリズムビジュアル大事典 +](https://yutaka-watanobe.github.io/star-aida/1.0/algorithms/counting_sort/print.html)を参照。 + +## 10章 ヒープ + +- 最小ヒープは、親が子よりも小さく、根が最小になるヒープ。単にヒープといえば最小ヒープ。 +- 最大ヒープはその逆となる。 + + +## 参考 + +- [プログラミングコンテスト攻略のためのアルゴリズムとデータ構造](https://amzn.to/3QAHDSk) +- [アルゴリズム | ともめも](https://www.tomotaku.com/category/algorithm/) diff --git "a/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/images/\343\203\221\343\203\274\343\203\206\343\202\243\343\202\267\343\203\247\343\203\263.svg" "b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/images/\343\203\221\343\203\274\343\203\206\343\202\243\343\202\267\343\203\247\343\203\263.svg" new file mode 100644 index 000000000..1aedbea95 --- /dev/null +++ "b/computer-science/data-structures-algorithms/_src/algorithm-and-datastructure/images/\343\203\221\343\203\274\343\203\206\343\202\243\343\202\267\343\203\247\343\203\263.svg" @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +