diff --git a/.idea/ClassicComputerScienceProblemsInPython.iml b/.idea/ClassicComputerScienceProblemsInPython.iml new file mode 100644 index 0000000..6711606 --- /dev/null +++ b/.idea/ClassicComputerScienceProblemsInPython.iml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0325094 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + ApexVCS + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3901a63 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Chapter1/Chapter1.ipynb b/Chapter1/Chapter1.ipynb new file mode 100644 index 0000000..c29d2c1 --- /dev/null +++ b/Chapter1/Chapter1.ipynb @@ -0,0 +1,570 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fibonacci" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RecursionError('maximum recursion depth exceeded',)\n" + ] + } + ], + "source": [ + "def fib1(n:int) -> int:\n", + " return fib1(n-1) + fib1(n-2)\n", + "try:\n", + " fib1(3)\n", + "except RecursionError as e:\n", + " print(repr(e))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "21" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def fib2(n:int) -> int:\n", + " if n < 2:\n", + " return n\n", + " else:\n", + " return fib2(n-2) + fib2(n-1)\n", + " \n", + "fib2(8)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "21 12586269025\n" + ] + } + ], + "source": [ + "from typing import Dict\n", + "memo:Dict[int, int] = {0:0, 1:1} #base cases\n", + "def fib3(n:int) -> int:\n", + " if n not in memo:\n", + " memo[n] = fib3(n-1) + fib3(n-2)\n", + " return memo[n]\n", + "print(fib3(8), fib3(50))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "21 12586269025\n" + ] + } + ], + "source": [ + "from functools import lru_cache\n", + "@lru_cache(maxsize=None)\n", + "def fib4(n: int) -> int:\n", + " if n < 2:\n", + " return n\n", + " else:\n", + " return fib4(n-2) + fib4(n-1)\n", + " \n", + "print(fib4(8), fib4(50))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "21 12586269025\n" + ] + } + ], + "source": [ + "def fib5(n:int) -> int:\n", + " if n == 0:\n", + " return n\n", + " else:\n", + " last:int = 0\n", + " _next:int = 1\n", + " for _ in range(1,n):\n", + " last, _next = _next, last + _next\n", + " return _next\n", + " \n", + "print(fib5(8), fib5(50))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0,\n", + " 1,\n", + " 1,\n", + " 2,\n", + " 3,\n", + " 5,\n", + " 8,\n", + " 13,\n", + " 21,\n", + " 34,\n", + " 55,\n", + " 89,\n", + " 144,\n", + " 233,\n", + " 377,\n", + " 610,\n", + " 987,\n", + " 1597,\n", + " 2584,\n", + " 4181,\n", + " 6765,\n", + " 10946,\n", + " 17711,\n", + " 28657,\n", + " 46368,\n", + " 75025,\n", + " 121393,\n", + " 196418,\n", + " 317811,\n", + " 514229,\n", + " 832040,\n", + " 1346269,\n", + " 2178309,\n", + " 3524578,\n", + " 5702887,\n", + " 9227465,\n", + " 14930352,\n", + " 24157817,\n", + " 39088169,\n", + " 63245986,\n", + " 102334155,\n", + " 165580141,\n", + " 267914296,\n", + " 433494437,\n", + " 701408733,\n", + " 1134903170,\n", + " 1836311903,\n", + " 2971215073,\n", + " 4807526976,\n", + " 7778742049,\n", + " 12586269025]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from typing import Generator\n", + "def fib6(n:int) -> int:\n", + " yield 0\n", + " if n> 0: \n", + " yield 1\n", + " last:int = 0\n", + " _next:int = 1\n", + " for _ in range(1,n):\n", + " last, _next = _next, last + _next\n", + " yield _next\n", + " \n", + "list([i for i in fib6(50)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Trivial Compression" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "class CompressedGene:\n", + " def __init__(self, gene: str) -> None:\n", + " self._compress(gene)\n", + "\n", + " def _compress(self, gene: str) -> None:\n", + " self.bit_string: int = 1 # start with sentinel\n", + " for nucleotide in gene.upper():\n", + " self.bit_string <<= 2 # shift left two bits\n", + " if nucleotide == \"A\": # change last two bits to 00\n", + " self.bit_string |= 0b00\n", + " elif nucleotide == \"C\": # change last two bits to 01\n", + " self.bit_string |= 0b01\n", + " elif nucleotide == \"G\": # change last two bits to 10\n", + " self.bit_string |= 0b10\n", + " elif nucleotide == \"T\": # change last two bits to 11\n", + " self.bit_string |= 0b11\n", + " else:\n", + " raise ValueError(\"Invalid Nucleotide:{}\".format(nucleotide))\n", + "\n", + " def decompress(self) -> str:\n", + " gene: str = \"\"\n", + " for i in range(0, self.bit_string.bit_length() - 1, 2): # - 1 to exclude sentinel\n", + " bits: int = self.bit_string >> i & 0b11 # get just 2 relevant bits\n", + " if bits == 0b00: # A\n", + " gene += \"A\"\n", + " elif bits == 0b01: # C\n", + " gene += \"C\"\n", + " elif bits == 0b10: # G\n", + " gene += \"G\"\n", + " elif bits == 0b11: # T\n", + " gene += \"T\"\n", + " else:\n", + " raise ValueError(\"Invalid bits:{}\".format(bits))\n", + " return gene[::-1] # [::-1] reverses string by slicing backwards\n", + "\n", + " def __str__(self) -> str: # string representation for pretty printing\n", + " return self.decompress()\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "original is 4749 bytes\n", + "compressed is 1280 bytes\n", + "TAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAGTAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAG\n", + "original and decompressed are the same: True\n" + ] + } + ], + "source": [ + "from sys import getsizeof\n", + "original:str = \"TAGGGATTAACCATTAACCGTTATATATATATAGCCATGGACAGTAG\"*100\n", + "print(\"original is {} bytes\".format(getsizeof(original)))\n", + "compressed:CompressedGene = CompressedGene(original)\n", + "print(\"compressed is {} bytes\".format(getsizeof(compressed.bit_string)))\n", + "print(compressed)\n", + "print(\"original and decompressed are the same: {}\".format(original == compressed.decompress()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unbreakable Encryption" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "from secrets import token_bytes\n", + "from typing import Tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [], + "source": [ + "def random_key(length:int) -> int:\n", + " # generate length random bytes\n", + " tb:bytes = token_bytes(length)\n", + " # convert to a bit string in the form of an int\n", + " return int.from_bytes(tb, \"big\")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(2977035838, 3607335770)\n" + ] + } + ], + "source": [ + "def encrypt(original:str) -> Tuple[int, int]:\n", + " original_bytes: bytes = original.encode()\n", + " dummy:int = random_key(len(original_bytes))\n", + " original_key:int = int.from_bytes(original_bytes, \"big\")\n", + " encrypted: int = original_key^dummy # XOR\n", + " return dummy, encrypted\n", + "\n", + "print(encrypt(\"fred\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [], + "source": [ + "def decrypt(key1:int, key2:int) -> str:\n", + " decrypted:int = key1^key2 #XOR\n", + " temp:bytes = decrypted.to_bytes((decrypted.bit_length()+7)//8, \"big\")\n", + " return temp.decode()" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "One Time Pad!\n" + ] + } + ], + "source": [ + "key1, key2 = encrypt(\"One Time Pad!\")\n", + "result:str = decrypt(key1, key2)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Calcluating Pi" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.1415916535897743\n" + ] + } + ], + "source": [ + "def calculate_pi(n_terms:int) -> float:\n", + " numerator:float = 4.0\n", + " denominator:float = 1.0\n", + " operation:float=1.0\n", + " pi:float = 0.0\n", + " for _ in range(n_terms):\n", + " pi += operation*(numerator/denominator)\n", + " denominator += 2.0\n", + " operation *= -1.0\n", + " return pi\n", + "\n", + "print(calculate_pi(1000000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Towers of Hanoi" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import TypeVar, Generic, List\n", + "T = TypeVar(\"T\")\n", + "\n", + "class Stack(Generic[T]):\n", + " \n", + " def __init__(self) -> None:\n", + " self._container: List[T] = []\n", + " \n", + " def push(self, item:T) -> None:\n", + " self._container.append(item)\n", + " \n", + " def pop(self) -> T:\n", + " return self._container.pop()\n", + " \n", + " def __repr__(self) -> str:\n", + " return repr(self._container)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [], + "source": [ + "num_discs:int = 3\n", + "tower_a: Stack[int] = Stack()\n", + "tower_b: Stack[int] = Stack()\n", + "tower_c: Stack[int] = Stack()\n", + "for i in range(1, num_discs+1):\n", + " tower_a.push(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi(begin:Stack[int], end:Stack[int], temp:Stack[int], n:int) -> None:\n", + " if n == 1:\n", + " end.push(begin.pop())\n", + " else:\n", + " hanoi(begin, temp, end, n-1)\n", + " hanoi(begin, end, temp, 1)\n", + " hanoi(temp, end, begin, n-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [] [1, 2, 3]\n" + ] + } + ], + "source": [ + "hanoi(tower_a, tower_c, tower_b, num_discs)\n", + "print(tower_a, tower_b, tower_c)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [], + "source": [ + "def do_hanoi(num_discs:int) -> list:\n", + " tower_a: Stack[int] = Stack()\n", + " tower_b: Stack[int] = Stack()\n", + " tower_c: Stack[int] = Stack()\n", + " for i in range(1, num_discs+1):\n", + " tower_a.push(i)\n", + " hanoi(tower_a, tower_c, tower_b, num_discs)\n", + " print(tower_a, tower_b, tower_c)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [] [1, 2, 3, 4]\n" + ] + } + ], + "source": [ + "do_hanoi(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n" + ] + } + ], + "source": [ + "do_hanoi(19)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Chapter2/Chapter 2.ipynb b/Chapter2/Chapter 2.ipynb new file mode 100644 index 0000000..70cbfc7 --- /dev/null +++ b/Chapter2/Chapter 2.ipynb @@ -0,0 +1,994 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DNA Search" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from enum import IntEnum\n", + "from typing import Tuple, List" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "Nucleotide:IntEnum = IntEnum(\"Nucleotide\", (\"A\", \"C\", \"G\", \"T\"))\n", + "Codon = Tuple[Nucleotide, Nucleotide, Nucleotide]\n", + "Gene = List[Codon]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "gene_str: str = \"ACGTGGCTCTCTAACGTACGTACGTACGGGGTTTATATATACCCTAGGACTCCCTTT\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def string_to_gene(s:str) -> Gene:\n", + " gene:Gene = []\n", + " for i in range(0, len(s), 3):\n", + " if (i+2) >= len(s): #Checking for the end\n", + " return gene\n", + " codon:Codon = (Nucleotide[s[i]], Nucleotide[s[i+1]], Nucleotide[s[i+2]])\n", + " gene.append(codon)\n", + " return gene" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , ), (, , )]\n" + ] + } + ], + "source": [ + "my_gene: Gene = string_to_gene(gene_str)\n", + "print(my_gene)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Linear Search" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "def linear_contains(gene:Gene, key_codon:Codon) -> bool:\n", + " for codon in gene:\n", + " if codon == key_codon:\n", + " return True\n", + " return False\n", + "\n", + "acg:Codon = (Nucleotide.A, Nucleotide.C, Nucleotide.G)\n", + "gat:Codon = (Nucleotide.G, Nucleotide.A, Nucleotide.T)\n", + "print(linear_contains(my_gene, acg)) # Should be True\n", + "print(linear_contains(my_gene, gat)) # Expect False" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "# Alternative using __contains__() method. we would never actually write the above.\n", + "print(acg in my_gene)\n", + "print(gat in my_gene)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Binary Search" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def binary_contains(gene:Gene, key_codon:Codon) -> bool:\n", + " low:int = 0\n", + " high:int = len(gene) - 1\n", + " while low <= high: #i.e. while there still is a search space\n", + " mid:int = (low+high)//2\n", + " if gene[mid] < key_codon:\n", + " low = mid+1\n", + " elif gene[mid] > key_codon:\n", + " high = mid-1\n", + " else:\n", + " return True\n", + " return False" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "my_sorted_gene = sorted(my_gene)\n", + "print(binary_contains(my_sorted_gene, acg)) # Should be True\n", + "print(binary_contains(my_sorted_gene, gat)) # Should be False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generic Example" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "from typing import TypeVar, Iterable, Sequence, Generic, List, Callable, Set, Deque, Dict, Any, Optional\n", + "from typing_extensions import Protocol\n", + "from heapq import heappush, heappop" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "T = TypeVar(\"T\")\n", + "\n", + "def linear_contains(iterable:Iterable[T], key:T) ->bool:\n", + " for item in iterable:\n", + " if item == key:\n", + " return True\n", + " return False\n", + "\n", + "C = TypeVar(\"C\", bound=\"Comparable\")\n", + "\n", + "class Comparable(Protocol):\n", + " \n", + " def __eq__(self, other:Any) -> bool:\n", + " ...\n", + " \n", + " def __lt__(self:C, other:C) -> bool:\n", + " ...\n", + " \n", + " def __gt__(self:C, other:C) -> bool:\n", + " return (not self < other) and (self != other)\n", + " \n", + " def __le__(self:C, other:C) -> bool:\n", + " return (self < other) or (self == other)\n", + " \n", + " def __ge__(self:C, other:C) -> bool:\n", + " return (other < self) or (other == self)\n", + " \n", + "def binary_contains(sequence:Sequence[C], key:C) -> bool:\n", + " low:int = 0\n", + " high:int = len(sequence) - 1\n", + " while low <= high:\n", + " mid:int = (low + high) //2\n", + " if sequence[mid] < key:\n", + " low = mid + 1\n", + " elif key < sequence[mid]:\n", + " high = mid - 1\n", + " else:\n", + " return True\n", + " return False" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "True\n", + "False\n" + ] + } + ], + "source": [ + "print(linear_contains([1, 5, 15, 15, 15, 15, 20], 5)) # Expect True\n", + "print(binary_contains([\"a\", \"d\", \"e\", \"f\", \"z\"], \"f\")) # Expect True\n", + "print(binary_contains([\"john\", \"mark\", \"ronald\", \"sarah\"], \"sheila\")) # Expect False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Maze Solving" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "from enum import Enum\n", + "from typing import List, NamedTuple, Callable, Optional\n", + "import random\n", + "from math import sqrt\n", + "from generic_search import dfs, bfs, node_to_path, astar, Node" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "class Cell(str, Enum):\n", + " EMPTY = \" \"\n", + " BLOCKED = \"X\"\n", + " START = \"S\"\n", + " GOAL = \"G\"\n", + " PATH = \"*\"\n", + " \n", + "class MazeLocation(NamedTuple):\n", + " row:int\n", + " column:int\n", + " \n", + "class Maze:\n", + " def __init__(self, rows:int=10, columns:int=10, sparseness:float=0.2,\n", + " start:MazeLocation=MazeLocation(0,0),\n", + " goal:MazeLocation=MazeLocation(9,9)) -> None:\n", + " self._rows:int = rows\n", + " self._columns:int = columns\n", + " self.start:MazeLocation = start\n", + " self.goal:MazeLocation = goal\n", + " self._grid:List[List[Cell]] = [[Cell.EMPTY for c in range(columns)] for r in range(rows)]\n", + " self._randomly_fill(rows, columns, sparseness)\n", + " self._grid[start.row][start.column] = Cell.START\n", + " self._grid[goal.row][goal.column] = Cell.GOAL\n", + " \n", + " def _randomly_fill(self, rows:int, columns:int, sparseness:float) -> None:\n", + " for row in range(rows):\n", + " for column in range(columns):\n", + " if random.uniform(0, 1.0) < sparseness:\n", + " self._grid[row][column] = Cell.BLOCKED\n", + " \n", + " def __str__(self) -> str:\n", + " output:str = \"\"\n", + " for row in self._grid:\n", + " output += \"\".join([c.value for c in row]) + \"\\n\"\n", + " return output\n", + " \n", + " def goal_test(self, ml:MazeLocation) -> bool:\n", + " return ml == self.goal\n", + " \n", + " def successors(self, ml: MazeLocation) -> List[MazeLocation]:\n", + " locations: List[MazeLocation] = []\n", + " if ml.row + 1 < self._rows and self._grid[ml.row + 1][ml.column] != Cell.BLOCKED:\n", + " locations.append(MazeLocation(ml.row + 1, ml.column))\n", + " if ml.row - 1 >= 0 and self._grid[ml.row - 1][ml.column] != Cell.BLOCKED:\n", + " locations.append(MazeLocation(ml.row - 1, ml.column))\n", + " if ml.column + 1 < self._columns and self._grid[ml.row][ml.column + 1] != Cell.BLOCKED:\n", + " locations.append(MazeLocation(ml.row, ml.column + 1))\n", + " if ml.column - 1 >= 0 and self._grid[ml.row][ml.column - 1] != Cell.BLOCKED:\n", + " locations.append(MazeLocation(ml.row, ml.column - 1))\n", + " return locations\n", + "\n", + " \n", + " def mark(self, path: List[MazeLocation]) -> None:\n", + " \n", + " for maze_location in path:\n", + " self._grid[maze_location.row][maze_location.column] = Cell.PATH\n", + " self._grid[self.start.row][self.start.column] = Cell.START\n", + " self._grid[self.goal.row][self.goal.column] = Cell.GOAL\n", + " \n", + " def clear(self, path:List[MazeLocation]) -> None:\n", + " for maze_location in path:\n", + " self._grid[maze_location.row][maze_location.column] = Cell.EMPTY\n", + " self._grid[self.start.row][self.start.column] = Cell.START\n", + " self._grid[self.goal.row][self.goal.column] = Cell.GOAL\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S XX X \n", + "X X \n", + " X X \n", + "X X \n", + "X X \n", + " X \n", + " X \n", + " X\n", + " X X\n", + "XXXX G\n", + "\n" + ] + } + ], + "source": [ + "maze:Maze = Maze()\n", + "print(maze)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The DFS Algorithm" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "class Stack(Generic[T]):\n", + " \n", + " def __init__(self) -> None:\n", + " self._container: List[T] = []\n", + " \n", + " @property\n", + " def empty(self) -> bool:\n", + " return not self._container\n", + " \n", + " def push(self, item:T) -> None:\n", + " self._container.append(item)\n", + " \n", + " def pop(self) -> T:\n", + " return self._container.pop()\n", + " \n", + " def __repr__(self) -> str:\n", + " return repr(self._container)\n", + " \n", + "class Node(Generic[T]):\n", + " \n", + " def __init__(self, state:T, parent:Optional[Node], cost:float=0.0, heuristic:float=0.0) -> None:\n", + " self.state:T = state\n", + " self.parent:Optional[Node] = parent\n", + " self.cost:float = cost\n", + " self.heuristic:float = heuristic\n", + " \n", + " def __lt__(self, other:Node) -> bool:\n", + " return (self.cost + self.heuristic) < (other.cost + other.heuristic)\n", + " \n", + "def dfs(initial:T, goal_test:Callable[[T], bool], successors:Callable[[T], List[T]]) -> Optional[Node[T]]:\n", + " # Frontier is where we haven't gone yet.\n", + " frontier:Stack[Node[T]] = Stack()\n", + " frontier.push(Node(initial, None))\n", + " # Explored is where we've been\n", + " explored: Set[T] = {initial}\n", + " \n", + " while not frontier.empty:\n", + " current_node: Node[T] = frontier.pop()\n", + " current_state: T = current_node.state\n", + " # if we've found the goal, we're done\n", + " if goal_test(current_state):\n", + " return current_node\n", + " # check where we can go next but haven't yet explored\n", + " for child in successors(current_state):\n", + " if child in explored: # we can skip this\n", + " continue\n", + " explored.add(child)\n", + " frontier.push(Node(child, current_node))\n", + " \n", + " return None # We tried but never found goal\n", + "\n", + "def node_to_path(node:Node[T]) -> List[T]:\n", + " path: List[T] = [node.state]\n", + " # Work backwards from end to front\n", + " while node.parent is not None:\n", + " node = node.parent\n", + " path.append(node.state)\n", + " path.reverse()\n", + " return path" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Applying DFS to solve the maze" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S X X\n", + " X X \n", + " XX \n", + " X X \n", + " XX X \n", + " X X \n", + " \n", + "X \n", + " \n", + " X X XG\n", + "\n", + "S*X X\n", + " *X X \n", + "** XX \n", + "* X X \n", + "***XX X \n", + " X*X \n", + " ** \n", + "X* \n", + " *********\n", + " X X XG\n", + "\n" + ] + } + ], + "source": [ + "m:Maze = Maze()\n", + "print(m)\n", + "solution1:Optional[Node[MazeLocation]] = dfs(m.start, m.goal_test, m.successors)\n", + "if solution1 is None:\n", + " print(\"No solution found using DFS\")\n", + "else:\n", + " path1: List[MazeLocation] = node_to_path(solution1)\n", + " m.mark(path1)\n", + " print(m)\n", + " m.clear(path1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Breadth-first Search" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [], + "source": [ + "class Queue(Generic[T]):\n", + " \n", + " def __init__(self) -> None:\n", + " self._container: Deque[T] = Deque()\n", + " \n", + " @property\n", + " def empty(self) -> bool:\n", + " return not self._container\n", + " \n", + " def push(self, item:T) -> None:\n", + " self._container.append(item)\n", + " \n", + " def pop(self) -> T:\n", + " return self._container.popleft() #FIFO\n", + " \n", + " def __repr__(self) -> str:\n", + " return repr(self._container)\n", + " \n", + "def bfs(initial:T, goal_test:Callable[[T], bool], successors:Callable[[T], List[T]]) -> Optional[Node[T]]:\n", + " # Frontier is where we haven't gone yet.\n", + " frontier:Queue[Node[T]] = Queue()\n", + " frontier.push(Node(initial, None))\n", + " # Explored is where we've been\n", + " explored: Set[T] = {initial}\n", + " \n", + " while not frontier.empty:\n", + " current_node: Node[T] = frontier.pop()\n", + " current_state: T = current_node.state\n", + " # if we've found the goal, we're done\n", + " if goal_test(current_state):\n", + " return current_node\n", + " # check where we can go next but haven't yet explored\n", + " for child in successors(current_state):\n", + " if child in explored: # we can skip this\n", + " continue\n", + " explored.add(child)\n", + " frontier.push(Node(child, current_node))\n", + " \n", + " return None # We tried but never found goal\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S X X\n", + "* X X \n", + "* XX \n", + "* X X \n", + "* XX X \n", + "*X X \n", + "** \n", + "X* \n", + " *********\n", + " X X XG\n", + "\n" + ] + } + ], + "source": [ + "solution2:Optional[Node[MazeLocation]] = bfs(m.start, m.goal_test, m.successors)\n", + "if solution2 is None:\n", + " print(\"No solution found using BFS\")\n", + "else:\n", + " path1: List[MazeLocation] = node_to_path(solution2)\n", + " m.mark(path1)\n", + " print(m)\n", + " m.clear(path1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### A* Search" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "class PriorityQueue(Generic[T]):\n", + " \n", + " def __init__(self) -> None:\n", + " self._container:List [T] = []\n", + " \n", + " @property\n", + " def empty(self) -> bool:\n", + " return not self._container\n", + " \n", + " def push(self, item:T) -> None:\n", + " heappush(self._container, item) # In by priority\n", + " \n", + " def pop(self) -> T:\n", + " return heappop(self._container) # out by priority\n", + " \n", + " def __repr__(self) -> str:\n", + " return repr(self._container)\n", + " \n", + "\n", + "def euclidean_distance(goal:MazeLocation) -> Callable[[MazeLocation], float]:\n", + " def distance(ml:MazeLocation) -> float:\n", + " xdist:int = ml.column - goal.column\n", + " ydist:int = ml.row - goal.row\n", + " return sqrt((xdist*xdist) + (ydist*ydist))\n", + " \n", + " return distance\n", + "\n", + "def manhattan_distance(goal:MazeLocation) -> Callable[[MazeLocation], float]:\n", + " def distance(ml:MazeLocation) -> float:\n", + " xdist:int = ml.column - goal.column\n", + " ydist:int = ml.row - goal.row\n", + " return abs(xdist) + abs(ydist)\n", + " \n", + " return distance\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MazeLocation(row=9, column=9)\n", + "5.656854249492381 8\n" + ] + } + ], + "source": [ + "test_ml = MazeLocation(row=5, column=5)\n", + "print(m.goal)\n", + "ed = euclidean_distance(m.goal)\n", + "md = manhattan_distance(m.goal)\n", + "print(ed(test_ml), md(test_ml))" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [], + "source": [ + "def astar(initial:T, goal_test:Callable[[T], bool], \n", + " successors:Callable[[T], List[T]],\n", + " heuristic: Callable[[T], float]) -> Optional[Node[T]]:\n", + " frontier: PriorityQueue[Node[T]] = PriorityQueue()\n", + " frontier.push(Node(initial, None, 0.0, heuristic(initial)))\n", + " explored: Dict[T, float] = {initial:0.0}\n", + " while not frontier.empty:\n", + " current_node:Node[T] = frontier.pop()\n", + " current_state: T = current_node.state\n", + " # If we found goal, we're done\n", + " if goal_test(current_state):\n", + " return current_node\n", + " # Check where we can go next and where we haven't explored\n", + " for child in successors(current_state):\n", + " new_cost:float = current_node.cost + 1 # Assumes a grid with equal step costs\n", + " if child not in explored or explored[child] > new_cost:\n", + " explored[child] = new_cost\n", + " frontier.push(Node(child, current_node, new_cost, heuristic(child)))\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S X X\n", + "* X X \n", + "* XX \n", + "* X X \n", + "***XX X \n", + " X*X \n", + " ********\n", + "X *\n", + " *\n", + " X X XG\n", + "\n" + ] + } + ], + "source": [ + "distance:Callable[[MazeLocation], float] = manhattan_distance(m.goal)\n", + "solution3:Optional[Node[MazeLocation]] = astar(m.start, m.goal_test, m.successors, distance)\n", + "if solution3 is None:\n", + " print(\"No solution found using A*\")\n", + "else:\n", + " path1: List[MazeLocation] = node_to_path(solution3)\n", + " m.mark(path1)\n", + " print(m)\n", + " m.clear(path1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Missionaries and cannibals" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [], + "source": [ + "MAX_NUM:int = 3\n", + " \n", + "class MCState:\n", + " \n", + " def __init__(self, missionaries:int, cannibals:int, boat:bool) -> None:\n", + " self.wm:int = missionaries\n", + " self.wc:int = cannibals\n", + " self.em:int = MAX_NUM - self.wm\n", + " self.ec:int = MAX_NUM - self.wc\n", + " self.boat:bool = boat\n", + " \n", + " def __str__(self) -> str:\n", + " return \"On the west bank there are {} missionaries and {} cannibals.\\n\"\\\n", + " \"On the east bank there are {} missionaries and {} cannibals.\\n\"\\\n", + " \"The boat is on the {} bank.\".format(self.wm, self.wc, self.em, self.ec,\n", + " (\"west\" if self.boat else \"east\"))\n", + " \n", + " def goal_test(self) -> bool:\n", + " return self.is_legal and self.em == MAX_NUM and self.ec==MAX_NUM\n", + " \n", + " @property\n", + " def is_legal(self) -> bool:\n", + " if self.wm < self.wc and self.wm > 0:\n", + " return False\n", + " if self.em < self.ec and self.em > 0:\n", + " return False\n", + " return True\n", + " \n", + " def successors(self) -> List[MCState]:\n", + " sucs: List[MCState] = []\n", + " if self.boat: # boat on west bank\n", + " if self.wm > 1:\n", + " sucs.append(MCState(self.wm - 2, self.wc, not self.boat))\n", + " if self.wm > 0:\n", + " sucs.append(MCState(self.wm - 1, self.wc, not self.boat))\n", + " if self.wc > 1:\n", + " sucs.append(MCState(self.wm, self.wc - 2, not self.boat))\n", + " if self.wc > 0:\n", + " sucs.append(MCState(self.wm, self.wc - 1, not self.boat))\n", + " if (self.wc > 0) and (self.wm > 0):\n", + " sucs.append(MCState(self.wm - 1, self.wc - 1, not self.boat))\n", + " else: # boat on east bank\n", + " if self.em > 1:\n", + " sucs.append(MCState(self.wm + 2, self.wc, not self.boat))\n", + " if self.em > 0:\n", + " sucs.append(MCState(self.wm + 1, self.wc, not self.boat))\n", + " if self.ec > 1:\n", + " sucs.append(MCState(self.wm, self.wc + 2, not self.boat))\n", + " if self.ec > 0:\n", + " sucs.append(MCState(self.wm, self.wc + 1, not self.boat))\n", + " if (self.ec > 0) and (self.em > 0):\n", + " sucs.append(MCState(self.wm + 1, self.wc + 1, not self.boat))\n", + " return [x for x in sucs if x.is_legal]\n", + "\n", + " \n", + " return [x for x in sucs if x.is_legal]\n", + "\n", + "def display_solution(path: List[MCState]):\n", + " if len(path) == 0: # sanity check\n", + " return\n", + " old_state: MCState = path[0]\n", + " print(old_state)\n", + " for current_state in path[1:]:\n", + " if current_state.boat:\n", + " print(\"{} missionaries and {} cannibals moved from the east bank to the west bank.\\n\"\n", + " .format(old_state.em - current_state.em, old_state.ec - current_state.ec))\n", + " else:\n", + " print(\"{} missionaries and {} cannibals moved from the west bank to the east bank.\\n\"\n", + " .format(old_state.wm - current_state.wm, old_state.wc - current_state.wc))\n", + " print(current_state)\n", + " old_state = current_state\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "On the west bank there are 3 missionaries and 3 cannibals.\n", + "On the east bank there are 0 missionaries and 0 cannibals.\n", + "The boat is on the west bank. ['On the west bank there are 3 missionaries and 1 cannibals.\\nOn the east bank there are 0 missionaries and 2 cannibals.\\nThe boat is on the east bank.', 'On the west bank there are 3 missionaries and 2 cannibals.\\nOn the east bank there are 0 missionaries and 1 cannibals.\\nThe boat is on the east bank.', 'On the west bank there are 2 missionaries and 2 cannibals.\\nOn the east bank there are 1 missionaries and 1 cannibals.\\nThe boat is on the east bank.']\n" + ] + } + ], + "source": [ + "mc = MCState(3, 3, True)\n", + "print(mc, [suc.__str__() for suc in mc.successors()])" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "On the west bank there are 3 missionaries and 3 cannibals.\n", + "On the east bank there are 0 missionaries and 0 cannibals.\n", + "The boat is on the west bank.\n", + "0 missionaries and 2 cannibals moved from the west bank to the east bank.\n", + "\n", + "On the west bank there are 3 missionaries and 1 cannibals.\n", + "On the east bank there are 0 missionaries and 2 cannibals.\n", + "The boat is on the east bank.\n", + "0 missionaries and 1 cannibals moved from the east bank to the west bank.\n", + "\n", + "On the west bank there are 3 missionaries and 2 cannibals.\n", + "On the east bank there are 0 missionaries and 1 cannibals.\n", + "The boat is on the west bank.\n", + "0 missionaries and 2 cannibals moved from the west bank to the east bank.\n", + "\n", + "On the west bank there are 3 missionaries and 0 cannibals.\n", + "On the east bank there are 0 missionaries and 3 cannibals.\n", + "The boat is on the east bank.\n", + "0 missionaries and 1 cannibals moved from the east bank to the west bank.\n", + "\n", + "On the west bank there are 3 missionaries and 1 cannibals.\n", + "On the east bank there are 0 missionaries and 2 cannibals.\n", + "The boat is on the west bank.\n", + "2 missionaries and 0 cannibals moved from the west bank to the east bank.\n", + "\n", + "On the west bank there are 1 missionaries and 1 cannibals.\n", + "On the east bank there are 2 missionaries and 2 cannibals.\n", + "The boat is on the east bank.\n", + "1 missionaries and 1 cannibals moved from the east bank to the west bank.\n", + "\n", + "On the west bank there are 2 missionaries and 2 cannibals.\n", + "On the east bank there are 1 missionaries and 1 cannibals.\n", + "The boat is on the west bank.\n", + "2 missionaries and 0 cannibals moved from the west bank to the east bank.\n", + "\n", + "On the west bank there are 0 missionaries and 2 cannibals.\n", + "On the east bank there are 3 missionaries and 1 cannibals.\n", + "The boat is on the east bank.\n", + "0 missionaries and 1 cannibals moved from the east bank to the west bank.\n", + "\n", + "On the west bank there are 0 missionaries and 3 cannibals.\n", + "On the east bank there are 3 missionaries and 0 cannibals.\n", + "The boat is on the west bank.\n", + "0 missionaries and 2 cannibals moved from the west bank to the east bank.\n", + "\n", + "On the west bank there are 0 missionaries and 1 cannibals.\n", + "On the east bank there are 3 missionaries and 2 cannibals.\n", + "The boat is on the east bank.\n", + "1 missionaries and 0 cannibals moved from the east bank to the west bank.\n", + "\n", + "On the west bank there are 1 missionaries and 1 cannibals.\n", + "On the east bank there are 2 missionaries and 2 cannibals.\n", + "The boat is on the west bank.\n", + "1 missionaries and 1 cannibals moved from the west bank to the east bank.\n", + "\n", + "On the west bank there are 0 missionaries and 0 cannibals.\n", + "On the east bank there are 3 missionaries and 3 cannibals.\n", + "The boat is on the east bank.\n" + ] + } + ], + "source": [ + "start:MCState = MCState(MAX_NUM, MAX_NUM, True)\n", + "solution:Optional[Node[MCState]] = bfs(start, MCState.goal_test, MCState.successors)\n", + "if solution is None:\n", + " print(\"No solution found\")\n", + "else:\n", + " path:List[MCState] = node_to_path(solution)\n", + " display_solution(path)" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "On the west bank there are 3 missionaries and 2 cannibals.\n", + "On the east bank there are 0 missionaries and 1 cannibals.\n", + "The boat is on the west bank. ['On the west bank there are 2 missionaries and 2 cannibals.\\nOn the east bank there are 1 missionaries and 1 cannibals.\\nThe boat is on the east bank.', 'On the west bank there are 3 missionaries and 0 cannibals.\\nOn the east bank there are 0 missionaries and 3 cannibals.\\nThe boat is on the east bank.', 'On the west bank there are 3 missionaries and 1 cannibals.\\nOn the east bank there are 0 missionaries and 2 cannibals.\\nThe boat is on the east bank.']\n" + ] + } + ], + "source": [ + "mc = MCState(3, 2, True)\n", + "print(mc, [suc.__str__() for suc in mc.successors()])" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "No solution found\n" + ] + } + ], + "source": [ + "start:MCState = MCState(3, 0, True)\n", + "solution:Optional[Node[MCState]] = bfs(start, MCState.goal_test, MCState.successors)\n", + "if solution is None:\n", + " print(\"No solution found\")\n", + "else:\n", + " path:List[MCState] = node_to_path(solution)\n", + " display_solution(path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}