├── jupyter_top.png ├── jupyter_list.png ├── jupyter_open.png ├── errata.txt ├── .gitignore ├── README.md └── source ├── Chapter02.ipynb ├── Chapter08.ipynb ├── Chapter07.ipynb ├── Chapter03.ipynb ├── Chapter10.ipynb ├── Chapter04.ipynb ├── Chapter05.ipynb └── Chapter09.ipynb /jupyter_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsjshg/pyalgdata/HEAD/jupyter_top.png -------------------------------------------------------------------------------- /jupyter_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsjshg/pyalgdata/HEAD/jupyter_list.png -------------------------------------------------------------------------------- /jupyter_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsjshg/pyalgdata/HEAD/jupyter_open.png -------------------------------------------------------------------------------- /errata.txt: -------------------------------------------------------------------------------- 1 | 正誤表(7刷まで) 2 | 3 | 39ページ 2.3.3 15行目 (誤)定数項が無視されるので → (正)定数倍は無視できるので 4 | 5 | 173ページ 10.2.4 7行目 (誤)オイラーの公式 → (正)オイラーの定理 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .ipynb_checkpoints/ 3 | .virtual_documents/ 4 | Untitled.ipynb 5 | source/.ipynb_checkpoints/ 6 | source/py_html 7 | source/__pycache__/ 8 | source/notebookutil.py 9 | source/python-3.7.3-embed-win32.zip 10 | source/Supplement.ipynb 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [「Pythonで学ぶアルゴリズムとデータ構造」](https://www.kspub.co.jp/book/detail/5178034.html)公開資料 2 | 3 | 正誤表は[こちら](errata.txt)です。 4 | 5 | ## Pythonの環境構築 6 | 7 | Pythonは[PSF(Python Software Foundation)](https://www.python.org/)によって管理され、オープンソースで開発が進められている。Windows、macOS、Linux/UNIXなど多くのOSで動作する。Pythonにはバージョンがあり2系と3系があるが、2系は2019年末でPSFのサポートが終了するため、3系を利用するのが良い。本書のコードは、3.6以上のバージョンで動作する。 8 | 9 | Pythonには数多くの外部パッケージがあり、標準のPythonにこれらを追加することで、便利なライブラリを利用することができる。本書は、標準のPythonだけで学習を進められるようにしてあるが、Pythonを使った実践的なプログラミングには外部パッケージは必須となる。 10 | 11 | 環境の構築には、大きく分けて2つの方法がある。 12 | 1. PSFが配布する標準のPythonをインストールしたあとに、外部パッケージを個別に追加する 13 | 1. 外部パッケージを同梱した配布形式(ディストリビューション)を利用する 14 | 15 | 標準のPythonは、[PSFのサイト](https://www.python.org/)からダウンロードできる。使っているOSに応じて、3.6以上のインストーラを取得してセットアップしてほしい。環境変数PATHにインストール先を追加すれば、OSのシェルから`python`コマンドや外部パッケージ追加のための`pip`コマンドが実行できるようになる。macOSなどUNIX系OSでは、`python3`や`pip3`というコマンド名になっている。ここで、OSのシェルとはWindowsではWindows PowerShellまたはコマンドプロンプト、macOSではターミナルを意味する。 16 | 17 | 外部パッケージを同梱したディストリビューションとしては、[米Anaconda社](https://www.anaconda.com/)が配布する[Anaconda](https://www.anaconda.com/distribution/)が広く使われている。Anacondaをセットアップすれば、次に説明する追加の作業は必要ない。またAnaconda環境では、外部パッケージの管理にcondaコマンドを利用する。 18 | 19 | OSごとの環境設定に関しては筆者が作った別の[サイト](https://tsjshg.github.io/pysetup/)も参考にしてほしい。 20 | 21 | ### 必要な外部パッケージ 22 | 23 | 公開資料のコードを実行するには、[Jupyter](https://jupyter.org/)環境が必要となる。2020年ごろまではJupyter Notebookが主流だったが、最近はより進化したJupyterLabが使われる余蘊いなってきた。このため、JupyterLabを利用するのがよいだろう。`pip install jupyterlab`としてインストールする。すべてのコードを実行するには、さらに以下の2つのパッケージも必要となる。それぞれ、`pip`コマンドを使ってインストールしてほしい。 24 | 25 | - networkx 26 | - matplotlib 27 | 28 | ## JupyterLabの使い方 29 | 30 | OSのシェルから`jupyter lab`または、`jupyter-lab`と入力すると、Webブラウザが起動し、次のような画面が表示される。コマンドを起動したディレクトリがカレントディレクトリになる。AnacondaではGUIのアプリケーションからJupyterLabの環境をインストールし起動することもできる。 31 | 32 | ![Jupyter起動画面](jupyter_top.png) 33 | 34 | Jupyterはサーバで実行されているPython環境を、クライアントのWebブラウザを通じて利用する仕組みになっている。1台のPCで利用する場合は、サーバとクライアントが同一のマシンということになる。Notebookにある「Python3」を選択すると、新しいノートブックができる。 35 | 36 | ![新しいノートブック](jupyter_open.png) 37 | 38 | Inから始まるセルにコードを入力する。セルの枠が青色になっているときが編集モードで、コードを入力できる。セルの枠線がなくなっている時は、コマンドモードなのでコードの入力はできない。セルの内部をクリックするか、Enterキーを押すことで編集モードに移行できる。 39 | 40 | セルの中に入力したコードを実行するには、Ctrl+Enterとする。画面への出力がある場合は、Outに番号がついて、Inの下に実行結果が表示される。セルの最終行はコードとして評価されるので、`print`関数を使わなくても内容が画面に表示される。Shift+Enterを使うと、コードの実行と新しい入力セルの追加が同時にできるので便利だ。よく使われる機能はツールバーにまとまっている。慣れてきたら、コマンドモードで使えるショートカットキーを覚えると作業効率が上がる。ノートブックの名前は、「Untitled」となっている部分を右クリックすれば変更できる。ノートブックは拡張子ipynbという名前で1つのファイルにまとまる。 41 | 42 | ![ファイルのリスト](jupyter_list.png) 43 | 44 | 図のようにリストされたipynbファイルをクリックするとノートブックを開くことができる。 45 | 46 | JupyterLabの終了は、「ファイル」メニューから「Shut Down」を使う。OSのシェルから起動した場合は、Ctrl+Cを使って`jupyter lab`コマンドを終わらせてサーバを止めることもできる。 47 | -------------------------------------------------------------------------------- /source/Chapter02.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/plain": [ 11 | "'0b10'" 12 | ] 13 | }, 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "output_type": "execute_result" 17 | } 18 | ], 19 | "source": [ 20 | "bin(2) " 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 2, 26 | "metadata": {}, 27 | "outputs": [ 28 | { 29 | "data": { 30 | "text/plain": [ 31 | "15" 32 | ] 33 | }, 34 | "execution_count": 2, 35 | "metadata": {}, 36 | "output_type": "execute_result" 37 | } 38 | ], 39 | "source": [ 40 | "0b1111" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "metadata": {}, 47 | "outputs": [ 48 | { 49 | "data": { 50 | "text/plain": [ 51 | "4294967295" 52 | ] 53 | }, 54 | "execution_count": 3, 55 | "metadata": {}, 56 | "output_type": "execute_result" 57 | } 58 | ], 59 | "source": [ 60 | "0b11111111111111111111111111111111" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 4, 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "data": { 70 | "text/plain": [ 71 | "'0b101'" 72 | ] 73 | }, 74 | "execution_count": 4, 75 | "metadata": {}, 76 | "output_type": "execute_result" 77 | } 78 | ], 79 | "source": [ 80 | "bin(5)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 5, 86 | "metadata": {}, 87 | "outputs": [ 88 | { 89 | "data": { 90 | "text/plain": [ 91 | "'0b1010'" 92 | ] 93 | }, 94 | "execution_count": 5, 95 | "metadata": {}, 96 | "output_type": "execute_result" 97 | } 98 | ], 99 | "source": [ 100 | "bin(5*2)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 6, 106 | "metadata": {}, 107 | "outputs": [ 108 | { 109 | "data": { 110 | "text/plain": [ 111 | "10" 112 | ] 113 | }, 114 | "execution_count": 6, 115 | "metadata": {}, 116 | "output_type": "execute_result" 117 | } 118 | ], 119 | "source": [ 120 | "5 << 1" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 7, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "text/plain": [ 131 | "'0b10010'" 132 | ] 133 | }, 134 | "execution_count": 7, 135 | "metadata": {}, 136 | "output_type": "execute_result" 137 | } 138 | ], 139 | "source": [ 140 | "bin(18)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 8, 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "data": { 150 | "text/plain": [ 151 | "'0b1001'" 152 | ] 153 | }, 154 | "execution_count": 8, 155 | "metadata": {}, 156 | "output_type": "execute_result" 157 | } 158 | ], 159 | "source": [ 160 | "bin(18//2)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 9, 166 | "metadata": {}, 167 | "outputs": [ 168 | { 169 | "data": { 170 | "text/plain": [ 171 | "9" 172 | ] 173 | }, 174 | "execution_count": 9, 175 | "metadata": {}, 176 | "output_type": "execute_result" 177 | } 178 | ], 179 | "source": [ 180 | "18 >> 1" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 10, 186 | "metadata": {}, 187 | "outputs": [ 188 | { 189 | "data": { 190 | "text/plain": [ 191 | "False" 192 | ] 193 | }, 194 | "execution_count": 10, 195 | "metadata": {}, 196 | "output_type": "execute_result" 197 | } 198 | ], 199 | "source": [ 200 | "0.1 + 0.1 + 0.1 == 0.3" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 11, 206 | "metadata": {}, 207 | "outputs": [ 208 | { 209 | "name": "stdout", 210 | "output_type": "stream", 211 | "text": [ 212 | "0.1000000000000000055511151231257827021181583404541015625\n", 213 | "0.299999999999999988897769753748434595763683319091796875\n" 214 | ] 215 | } 216 | ], 217 | "source": [ 218 | "from decimal import Decimal\n", 219 | "\n", 220 | "print(Decimal.from_float(0.1))\n", 221 | "print(Decimal.from_float(0.3))" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 12, 227 | "metadata": { 228 | "scrolled": true 229 | }, 230 | "outputs": [ 231 | { 232 | "name": "stdout", 233 | "output_type": "stream", 234 | "text": [ 235 | "1.7976931348623157e+308\n", 236 | "2.2250738585072014e-308\n" 237 | ] 238 | } 239 | ], 240 | "source": [ 241 | "import sys\n", 242 | "print(sys.float_info.max)\n", 243 | "print(sys.float_info.min)" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 13, 249 | "metadata": {}, 250 | "outputs": [ 251 | { 252 | "data": { 253 | "text/plain": [ 254 | "30" 255 | ] 256 | }, 257 | "execution_count": 13, 258 | "metadata": {}, 259 | "output_type": "execute_result" 260 | } 261 | ], 262 | "source": [ 263 | "my_array = [10, 20, 30, 40, 50] \n", 264 | "my_array[2] " 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 14, 270 | "metadata": {}, 271 | "outputs": [], 272 | "source": [ 273 | "my_array.insert(2, 10)" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": {}, 280 | "outputs": [], 281 | "source": [] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "metadata": {}, 308 | "outputs": [], 309 | "source": [] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": {}, 321 | "source": [ 322 | "# 練習問題解答" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "## 2.1\n", 330 | "\n", 331 | "CPUやメモリは高度に集積された半導体から作られる電子回路だ。CPUは計算を実行するための装置で、メモリは情報を保持するためにある。それぞれの役割の違いが、拡大写真の外観に現れている。メモリはデータを保持するという目的に特化しているため、電子回路の形が均一になっている。一方でCPUは、さまざまな種類の計算を実行できるよう、回路が複雑に入り組んでいるのがわかる。CPUの中にも、メモリのような規則正しい電子回路が見えるかもしれない。これは、キャッシュメモリと呼ばれもので、CPUが計算のために一時的にデータを保持する場所だ。メモリと目的が同じなので、回路の形が似ている。" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": [ 338 | "## 2.2\n", 339 | "\n", 340 | "先頭に0bをつけることで、Pythonの処理系に、2進数のリテラル表現だと認識させることができる。" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 15, 346 | "metadata": {}, 347 | "outputs": [ 348 | { 349 | "data": { 350 | "text/plain": [ 351 | "10" 352 | ] 353 | }, 354 | "execution_count": 15, 355 | "metadata": {}, 356 | "output_type": "execute_result" 357 | } 358 | ], 359 | "source": [ 360 | "0b1010" 361 | ] 362 | }, 363 | { 364 | "cell_type": "markdown", 365 | "metadata": {}, 366 | "source": [ 367 | "2進数の1010を10進数に変換するには、右側の桁から順に考えて、$0 \\times 2^{0} + 1 \\times 2^{1} + 0 \\times 2^{2} + 1 \\times 2^{3} = 2 + 8 = 10$となる。\n", 368 | "\n", 369 | "組み込み関数binを使うと、10進数の整数を2進数で表現できる。" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 16, 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "data": { 379 | "text/plain": [ 380 | "'0b11001'" 381 | ] 382 | }, 383 | "execution_count": 16, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | } 387 | ], 388 | "source": [ 389 | "bin(25)" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "metadata": {}, 395 | "source": [ 396 | "2進数を10進数にしたときの計算を思い出そう。もとの数を2で割った余りを並べれば、2進数に変換できることがわかる。具体的には、あまりを一番下の位から順に並べていき、計算された商を次々に2で割っていく。商が0になると計算は終了だ。ちょっとしたコードを書いてみよう。" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 17, 402 | "metadata": {}, 403 | "outputs": [ 404 | { 405 | "name": "stdout", 406 | "output_type": "stream", 407 | "text": [ 408 | "11001\n" 409 | ] 410 | } 411 | ], 412 | "source": [ 413 | "res = []\n", 414 | "val = 25\n", 415 | "while val:\n", 416 | " val, r = divmod(val, 2)\n", 417 | " res.append(str(r))\n", 418 | "print(''.join(res[::-1]))" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": {}, 424 | "source": [ 425 | "## 2.3\n", 426 | "\n", 427 | "小数の数値を変数に格納して、メソッドを実行してみる。" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 18, 433 | "metadata": {}, 434 | "outputs": [ 435 | { 436 | "data": { 437 | "text/plain": [ 438 | "(1, 4)" 439 | ] 440 | }, 441 | "execution_count": 18, 442 | "metadata": {}, 443 | "output_type": "execute_result" 444 | } 445 | ], 446 | "source": [ 447 | "a = 0.25\n", 448 | "a.as_integer_ratio()" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 19, 454 | "metadata": {}, 455 | "outputs": [ 456 | { 457 | "data": { 458 | "text/plain": [ 459 | "(3602879701896397, 36028797018963968)" 460 | ] 461 | }, 462 | "execution_count": 19, 463 | "metadata": {}, 464 | "output_type": "execute_result" 465 | } 466 | ], 467 | "source": [ 468 | "b = 0.1\n", 469 | "b.as_integer_ratio()" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": {}, 475 | "source": [ 476 | "as_integer_ratioは、整数を2つ1組で返す。これらの比をとると、正確にもとのfloat型の値を再現できる。0.25が$\\frac{1}{4}$と等しいことは明らかだ。コンピュータの中で正確に表現できていない0.1も、2つの整数の比と等しくなる。" 477 | ] 478 | }, 479 | { 480 | "cell_type": "code", 481 | "execution_count": 20, 482 | "metadata": {}, 483 | "outputs": [ 484 | { 485 | "data": { 486 | "text/plain": [ 487 | "True" 488 | ] 489 | }, 490 | "execution_count": 20, 491 | "metadata": {}, 492 | "output_type": "execute_result" 493 | } 494 | ], 495 | "source": [ 496 | "0.1 == 3602879701896397/36028797018963968" 497 | ] 498 | }, 499 | { 500 | "cell_type": "markdown", 501 | "metadata": {}, 502 | "source": [ 503 | "さらに詳しく知りたい場合は、以下のドキュメントを参考にすると良いだろう。\n", 504 | "\n", 505 | "https://docs.python.org/3/tutorial/floatingpoint.html" 506 | ] 507 | }, 508 | { 509 | "cell_type": "markdown", 510 | "metadata": {}, 511 | "source": [ 512 | "## 2.4\n", 513 | "\n", 514 | "$\\mathcal{O}(n^{2})$\n", 515 | "\n", 516 | "入力データのサイズは、$n^{2}$となる。これらすべてに対する計算を2回繰り返すので、$2 \\times n^{2}$となるが、漸近記法では定数倍は無視する。計算量は、入力データサイズの変化と計算時間の関係に注目しているためこのような計算になる。詳しくは、次章で扱う。" 517 | ] 518 | }, 519 | { 520 | "cell_type": "markdown", 521 | "metadata": {}, 522 | "source": [ 523 | "## 2.5\n", 524 | "\n", 525 | "$\\mathcal{O}(n)$\n", 526 | "\n", 527 | "配列は要素がメモリ空間上に連続して並んでいる。配列の途中に要素を挿入すると、その位置から後ろにある要素を1つずつ移動する必要がある。何個移動しなければならないかは、挿入する位置による。平均的には$n/2$個くらいと考えられるが、最悪の場合は$n$個となる。いずれにしても、定数倍は無視されるので$\\mathcal{O}(n)$となる。" 528 | ] 529 | } 530 | ], 531 | "metadata": { 532 | "kernelspec": { 533 | "display_name": "Python 3", 534 | "language": "python", 535 | "name": "python3" 536 | }, 537 | "language_info": { 538 | "codemirror_mode": { 539 | "name": "ipython", 540 | "version": 3 541 | }, 542 | "file_extension": ".py", 543 | "mimetype": "text/x-python", 544 | "name": "python", 545 | "nbconvert_exporter": "python", 546 | "pygments_lexer": "ipython3", 547 | "version": "3.6.5" 548 | } 549 | }, 550 | "nbformat": 4, 551 | "nbformat_minor": 2 552 | } 553 | -------------------------------------------------------------------------------- /source/Chapter08.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 10, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "def prime_test(n):\n", 10 | " \"\"\"引数が素数かどうかを判定する\"\"\"\n", 11 | " m = int(pow(n, 0.5))\n", 12 | " for d in range(2, m + 1):\n", 13 | " if n % d == 0:\n", 14 | " return False\n", 15 | " return True" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 11, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "# nが大きくなっても正確に計算するためのコード\n", 25 | "\n", 26 | "# 3.7以前ではdecimalを利用する。\n", 27 | "from decimal import Decimal, getcontext\n", 28 | "\n", 29 | "def prime_test_large_n(n):\n", 30 | " # 計算の精度を指定する\n", 31 | " getcontext().prec = len(str(n))\n", 32 | " m = int(Decimal(n).sqrt())\n", 33 | " for d in range(2, m + 1):\n", 34 | " if n % d == 0:\n", 35 | " return False\n", 36 | " return True" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "# 3.8以降では、math.isqrtが利用できる。\n", 46 | "import math\n", 47 | "\n", 48 | "def prime_test(n):\n", 49 | " \"\"\"引数が素数かどうかを判定する\"\"\"\n", 50 | " m = math.isqrt(n)\n", 51 | " for d in range(2, m + 1):\n", 52 | " if n % d == 0:\n", 53 | " return False\n", 54 | " return True" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 12, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "data": { 64 | "text/plain": [ 65 | "True" 66 | ] 67 | }, 68 | "execution_count": 12, 69 | "metadata": {}, 70 | "output_type": "execute_result" 71 | } 72 | ], 73 | "source": [ 74 | "prime_test(71)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 13, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "data": { 84 | "text/plain": [ 85 | "False" 86 | ] 87 | }, 88 | "execution_count": 13, 89 | "metadata": {}, 90 | "output_type": "execute_result" 91 | } 92 | ], 93 | "source": [ 94 | "prime_test(5489)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 14, 100 | "metadata": {}, 101 | "outputs": [ 102 | { 103 | "data": { 104 | "text/plain": [ 105 | "True" 106 | ] 107 | }, 108 | "execution_count": 14, 109 | "metadata": {}, 110 | "output_type": "execute_result" 111 | } 112 | ], 113 | "source": [ 114 | "prime_test(2147483647)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "# これは諦めた方がいい。\n", 124 | "prime_test(2305843009213693951)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 15, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "import random\n", 134 | "random.seed(8)\n", 135 | "\n", 136 | "class kSAT:\n", 137 | " \n", 138 | " @classmethod\n", 139 | " def generate(cls, k, var_num, clause_num):\n", 140 | " \"\"\"変数の数(var_num)と節の数(clause_num)をとりkSAT問題を作る\"\"\"\n", 141 | " ksat = cls()\n", 142 | " var_list = list(range(var_num))\n", 143 | " # 問題の本体を格納するための変数\n", 144 | " res = []\n", 145 | " while len(res) < clause_num:\n", 146 | " clause = []\n", 147 | " # 高々k個の変数が含まれる\n", 148 | " clause_size = random.randint(1, k)\n", 149 | " for i in random.sample(var_list, clause_size):\n", 150 | " # 1ならnotで変数を否定する\n", 151 | " prefix = random.choice((0, 1))\n", 152 | " clause.append((prefix, i))\n", 153 | " # 同一の節を判定できるよう変数の添え字でソート\n", 154 | " clause.sort(key=lambda x: x[1])\n", 155 | " if clause not in res: res.append(clause)\n", 156 | " # kSATのインスタンスに格納\n", 157 | " ksat.body = res\n", 158 | " return ksat\n", 159 | " \n", 160 | " def test(self, var_list):\n", 161 | " \"\"\"受け取ったvar_listのTrue、Falseを使って論理式を評価する\"\"\"\n", 162 | " res = []\n", 163 | " for clause in self.body:\n", 164 | " clause_data = [not var_list[i] if p else var_list[i] for p, i in clause]\n", 165 | " # 各節はどれか1つでもTrueならTrue\n", 166 | " res.append(any(clause_data))\n", 167 | " # 全体は、すべてがTrueならTrue\n", 168 | " return all(res)\n", 169 | " \n", 170 | " def __str__(self):\n", 171 | " res = []\n", 172 | " for clause in self.body:\n", 173 | " clause_str = [f'¬x{i}' if p else f'x{i}' for p, i in clause]\n", 174 | " res.append('(' + ' ∨ '.join(clause_str) + ')')\n", 175 | " return ' ∧ '.join(res)" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 16, 181 | "metadata": {}, 182 | "outputs": [ 183 | { 184 | "name": "stdout", 185 | "output_type": "stream", 186 | "text": [ 187 | "(¬x2) ∧ (x1) ∧ (¬x0 ∨ ¬x1 ∨ ¬x3)\n" 188 | ] 189 | } 190 | ], 191 | "source": [ 192 | "ksat = kSAT.generate(3, 4, 3)\n", 193 | "print(ksat)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 17, 199 | "metadata": { 200 | "scrolled": true 201 | }, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "output_type": "stream", 206 | "text": [ 207 | "[False, True, False, True]\n" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "while True:\n", 213 | " cand = random.choices([True, False], k=4)\n", 214 | " if ksat.test(cand):\n", 215 | " print(cand)\n", 216 | " break" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": null, 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "# 練習問題解答" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "## 8.1\n", 273 | "\n", 274 | "指定された桁数の正の整数をランダムに得る関数は、練習問題3.1で実装した。" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 18, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "import random\n", 284 | "\n", 285 | "def rand_n_digit_int(n):\n", 286 | " return random.randint(10**(n-1), 10**n - 1)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": {}, 292 | "source": [ 293 | "1以上の整数を引数にとり、その桁数の素数を探す関数を作る。" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 19, 299 | "metadata": { 300 | "scrolled": true 301 | }, 302 | "outputs": [], 303 | "source": [ 304 | "def explore_n_digit_prime(n):\n", 305 | " cnt = 1\n", 306 | " while True:\n", 307 | " d = rand_n_digit_int(n)\n", 308 | " if prime_test(d):\n", 309 | " break\n", 310 | " cnt += 1\n", 311 | " return d, cnt" 312 | ] 313 | }, 314 | { 315 | "cell_type": "markdown", 316 | "metadata": {}, 317 | "source": [ 318 | "引数を8にすれば8桁の素数を見つけることができる。" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": 20, 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "name": "stdout", 328 | "output_type": "stream", 329 | "text": [ 330 | "11774039を2回目で見つけた。\n" 331 | ] 332 | } 333 | ], 334 | "source": [ 335 | "d, cnt = explore_n_digit_prime(8)\n", 336 | "\n", 337 | "print(f'{d}を{cnt}回目で見つけた。')" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": {}, 343 | "source": [ 344 | "$n$が大きくなると、prime_testの実行に時間がかかるようになる。また、10桁を超えるような場合は、関数内部で平方根を整数にする部分で計算誤差の影響がでる可能性がある。これはprime_test_large_nを使うなどの方法で回避できる。計算量の問題も含め、このあたりのややこしい問題をすべて解決してくれる方法を9章で説明する。" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "## 8.2\n", 352 | "\n", 353 | "ここでは大まかな時間がわかれば良いので、timeモジュールを使う。コードのパフォーマンスを測定するには、通常timeitモジュールを使って実装した方が正確になる。" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 21, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "import time\n", 363 | "\n", 364 | "def create_n_list(n):\n", 365 | " s = time.time()\n", 366 | " [random.random() for i in range(rand_n_digit_int(n))]\n", 367 | " e = time.time()\n", 368 | " return e - s" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 22, 374 | "metadata": {}, 375 | "outputs": [ 376 | { 377 | "name": "stdout", 378 | "output_type": "stream", 379 | "text": [ 380 | "1\t1.7881393432617188e-05\n", 381 | "2\t2.8848648071289062e-05\n", 382 | "3\t0.0001087188720703125\n", 383 | "4\t0.0011823177337646484\n", 384 | "5\t0.004484891891479492\n", 385 | "6\t0.04946303367614746\n", 386 | "7\t1.2673771381378174\n", 387 | "8\t3.4475929737091064\n" 388 | ] 389 | } 390 | ], 391 | "source": [ 392 | "for i in range(1, 9):\n", 393 | " print('{}\\t{}'.format(i, create_n_list(i)))" 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "metadata": {}, 399 | "source": [ 400 | "8桁目で時間が7桁の時の約10倍になっているのがわかる。9桁目も10倍の時間で済むならば、それほどかからないようにも思えるが、これにはメモリの問題が影響してくる。次の問題でそれを検討する。" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "## 8.3\n", 408 | "\n", 409 | "sys.getsizeofは、引数にとったオブジェクトのサイズをbyte単位で返す。大きな数字になるとわかりにくいので、1024で2回割って、MB単位で表示する。" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": 23, 415 | "metadata": {}, 416 | "outputs": [], 417 | "source": [ 418 | "import sys\n", 419 | "\n", 420 | "def create_n_list(n):\n", 421 | " s = time.time()\n", 422 | " test_list = [random.random() for i in range(rand_n_digit_int(n))]\n", 423 | " e = time.time()\n", 424 | " return e - s, sys.getsizeof(test_list)" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": 24, 430 | "metadata": {}, 431 | "outputs": [ 432 | { 433 | "name": "stdout", 434 | "output_type": "stream", 435 | "text": [ 436 | "1\t5.412101745605469e-05\t0.0001220703125\n", 437 | "2\t3.4809112548828125e-05\t0.000732421875\n", 438 | "3\t8.869171142578125e-05\t0.0027618408203125\n", 439 | "4\t0.0022881031036376953\t0.07424163818359375\n", 440 | "5\t0.02197408676147461\t0.6988677978515625\n", 441 | "6\t0.10741972923278809\t5.1781158447265625\n", 442 | "7\t0.9176919460296631\t54.6070556640625\n", 443 | "8\t13.03581190109253\t728.7974243164062\n" 444 | ] 445 | } 446 | ], 447 | "source": [ 448 | "for i in range(1, 9):\n", 449 | " t, s = create_n_list(i)\n", 450 | " print(f'{i}\\t{t}\\t{s/1024/1024}')" 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": {}, 456 | "source": [ 457 | "8桁(1億から10億未満)にもなる長さでは、サイズが数百MBほどになる。このまま9桁のサイズになると数GBになってしまう。これは一般的なパーソナルコンピュータのメモリサイズと同じくらいだ。かなりの高性能機でなければ、9桁のサイズのリストは扱わない方が良いだろう。\n", 458 | "\n", 459 | "実際には、リストを作ったあとさまざまな計算をすることになる。6桁(数百万)〜7桁(数千万)くらいのサイズを越えるようであれば、データベースサーバの導入など、Pythonプログラムだけで処理しない方法を検討したほうがよいだろう。" 460 | ] 461 | } 462 | ], 463 | "metadata": { 464 | "kernelspec": { 465 | "display_name": "Python 3", 466 | "language": "python", 467 | "name": "python3" 468 | }, 469 | "language_info": { 470 | "codemirror_mode": { 471 | "name": "ipython", 472 | "version": 3 473 | }, 474 | "file_extension": ".py", 475 | "mimetype": "text/x-python", 476 | "name": "python", 477 | "nbconvert_exporter": "python", 478 | "pygments_lexer": "ipython3", 479 | "version": "3.6.5" 480 | } 481 | }, 482 | "nbformat": 4, 483 | "nbformat_minor": 2 484 | } 485 | -------------------------------------------------------------------------------- /source/Chapter07.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from collections import namedtuple\n", 10 | "import random\n", 11 | "# 乱数のシードは章の番号\n", 12 | "random.seed(7)\n", 13 | "\n", 14 | "# 品物(Item)は簡単なクラスなのでnamedtupleで作る。\n", 15 | "Item = namedtuple('Item', ('name', 'weight', 'price'))\n", 16 | "\n", 17 | "# 品物の個数\n", 18 | "num = 20\n", 19 | "\n", 20 | "# 品物を保持するリスト\n", 21 | "item_list = []\n", 22 | "max_weight = 5\n", 23 | "# 品物の個数numより大きな数字にする\n", 24 | "max_price = 50\n", 25 | "\n", 26 | "# 値段の候補リストを作り、シャッフル\n", 27 | "price_list = list(range(1, max_price+1))\n", 28 | "random.shuffle(price_list)\n", 29 | "\n", 30 | "# ランダムに品物を作ってみる。名前は番号\n", 31 | "for i in range(num):\n", 32 | " w = random.randint(1, max_weight)\n", 33 | " item = Item(i, w, price_list.pop())\n", 34 | " item_list.append(item)" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 2, 40 | "metadata": {}, 41 | "outputs": [ 42 | { 43 | "data": { 44 | "text/plain": [ 45 | "[Item(name=0, weight=3, price=21),\n", 46 | " Item(name=1, weight=1, price=10),\n", 47 | " Item(name=2, weight=5, price=26),\n", 48 | " Item(name=3, weight=1, price=42),\n", 49 | " Item(name=4, weight=5, price=4),\n", 50 | " Item(name=5, weight=1, price=5),\n", 51 | " Item(name=6, weight=5, price=35),\n", 52 | " Item(name=7, weight=2, price=7),\n", 53 | " Item(name=8, weight=4, price=24),\n", 54 | " Item(name=9, weight=5, price=38),\n", 55 | " Item(name=10, weight=4, price=46),\n", 56 | " Item(name=11, weight=3, price=33),\n", 57 | " Item(name=12, weight=4, price=14),\n", 58 | " Item(name=13, weight=5, price=3),\n", 59 | " Item(name=14, weight=4, price=6),\n", 60 | " Item(name=15, weight=3, price=28),\n", 61 | " Item(name=16, weight=3, price=27),\n", 62 | " Item(name=17, weight=2, price=45),\n", 63 | " Item(name=18, weight=2, price=16),\n", 64 | " Item(name=19, weight=2, price=37)]" 65 | ] 66 | }, 67 | "execution_count": 2, 68 | "metadata": {}, 69 | "output_type": "execute_result" 70 | } 71 | ], 72 | "source": [ 73 | "item_list" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 3, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "class Knapsack:\n", 83 | " \n", 84 | " def __init__(self, size):\n", 85 | " # このナップサックが保持できる最大の重さ\n", 86 | " self.size = size\n", 87 | " # 現在の重さ\n", 88 | " self.weight = 0\n", 89 | " # 入っているものの価値の総和\n", 90 | " self.value = 0\n", 91 | " # 保持しているItemの配列\n", 92 | " self.items = []\n", 93 | " \n", 94 | " def append(self, item):\n", 95 | " \"\"\"このナップサックにItemを追加する。\"\"\"\n", 96 | " if not self.has_room_for(item):\n", 97 | " raise ValueError('このアイテムは入れられません。重量オーバーです。')\n", 98 | " self.items.append(item)\n", 99 | " self.weight += item.weight\n", 100 | " self.value += item.price\n", 101 | " \n", 102 | " def has_room_for(self, item):\n", 103 | " \"\"\"引数にとったアイテムを入れる余裕があるかどうかを真偽値で返す。 \"\"\"\n", 104 | " return self.size >= self.weight + item.weight\n", 105 | " \n", 106 | " def __str__(self):\n", 107 | " val = '重さ {} kg / 価値 {} 万円'.format(self.weight, self.value)\n", 108 | " return val" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 4, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "def greedy(items, size_limit):\n", 118 | " # 単位重さあたりの値段で品物を並び替える。\n", 119 | " sorted_item_list = sorted(items, key=lambda x: x.price/x.weight, reverse=True)\n", 120 | " my_knapsack = Knapsack(size_limit)\n", 121 | " for v in sorted_item_list:\n", 122 | " # 入る余地があるなら品物を入れる。\n", 123 | " try:\n", 124 | " my_knapsack.append(v)\n", 125 | " except ValueError:\n", 126 | " continue\n", 127 | " return my_knapsack" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 5, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "name": "stdout", 137 | "output_type": "stream", 138 | "text": [ 139 | "重さ 39 kg / 価値 407 万円\n" 140 | ] 141 | } 142 | ], 143 | "source": [ 144 | "knap_g = greedy(item_list, 40)\n", 145 | "print(knap_g)" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 6, 151 | "metadata": {}, 152 | "outputs": [ 153 | { 154 | "data": { 155 | "text/plain": [ 156 | "[Item(name=3, weight=1, price=42),\n", 157 | " Item(name=17, weight=2, price=45),\n", 158 | " Item(name=19, weight=2, price=37),\n", 159 | " Item(name=10, weight=4, price=46),\n", 160 | " Item(name=11, weight=3, price=33),\n", 161 | " Item(name=1, weight=1, price=10),\n", 162 | " Item(name=15, weight=3, price=28),\n", 163 | " Item(name=16, weight=3, price=27),\n", 164 | " Item(name=18, weight=2, price=16),\n", 165 | " Item(name=9, weight=5, price=38),\n", 166 | " Item(name=0, weight=3, price=21),\n", 167 | " Item(name=6, weight=5, price=35),\n", 168 | " Item(name=8, weight=4, price=24),\n", 169 | " Item(name=5, weight=1, price=5)]" 170 | ] 171 | }, 172 | "execution_count": 6, 173 | "metadata": {}, 174 | "output_type": "execute_result" 175 | } 176 | ], 177 | "source": [ 178 | "knap_g.items" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 7, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "import itertools\n", 188 | "\n", 189 | "def brute_force(items, size_limit):\n", 190 | " # 答えの候補\n", 191 | " candidate = None\n", 192 | " # 0と1を20個並べるすべてのパターンをこれで作れる\n", 193 | " for pattern in itertools.product((0, 1), repeat=len(items)):\n", 194 | " my_box = []\n", 195 | " for i, val in enumerate(pattern):\n", 196 | " if val: my_box.append(item_list[i])\n", 197 | " w = sum([item.weight for item in my_box])\n", 198 | " # ナップサックの重量制限を守れないならループの次へ\n", 199 | " if w > size_limit: continue\n", 200 | " # 総額を計算しこれまでの最高を上回るなら候補として残す\n", 201 | " value = sum([item.price for item in my_box])\n", 202 | " if candidate is None or value > candidate.value:\n", 203 | " knapsack = Knapsack(size_limit)\n", 204 | " for v in my_box:\n", 205 | " knapsack.append(v)\n", 206 | " candidate = knapsack\n", 207 | " return candidate" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 8, 213 | "metadata": {}, 214 | "outputs": [ 215 | { 216 | "name": "stdout", 217 | "output_type": "stream", 218 | "text": [ 219 | "重さ 40 kg / 価値 409 万円\n" 220 | ] 221 | } 222 | ], 223 | "source": [ 224 | "knap_bf = brute_force(item_list, 40)\n", 225 | "print(knap_bf)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 9, 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [ 234 | "def dp(items, size_limit):\n", 235 | " n = len(items)\n", 236 | " # 価値を記録する表を作成 (行が品物、列が許容サイズ)\n", 237 | " table = [[0]*(size_limit+1) for i in range(n+1)]\n", 238 | " # 価値を更新したかどうかを記録するための表\n", 239 | " flag = [[False]*(size_limit+1) for i in range(n+1)]\n", 240 | " # 表を下に進むループ(考慮に入れる品物)\n", 241 | " for i in range(1, n+1):\n", 242 | " # いれるかどうか考えている品物\n", 243 | " target = items[i-1]\n", 244 | " w = target.weight\n", 245 | " # 表を右に進むループ(重さの上限)\n", 246 | " for j in range(1, size_limit+1):\n", 247 | " # 1行上の最適解\n", 248 | " yellow = table[i-1][j]\n", 249 | " table[i][j] = yellow\n", 250 | " # 今の許容範囲jを越えるなら論外\n", 251 | " if w > j: continue\n", 252 | " # ちょうどtarget分の重さが少ないときの最適解\n", 253 | " pink = table[i-1][j-w]\n", 254 | " # この品物を入れたときの価値\n", 255 | " include_this = target.price + pink\n", 256 | " table[i][j] = max(yellow, include_this)\n", 257 | " flag[i][j] = include_this > yellow\n", 258 | " # 後処理:表を右下から遡って入れた品物を調べる\n", 259 | " i = n\n", 260 | " j = size_limit\n", 261 | " my_knapsack = Knapsack(size_limit)\n", 262 | " while i > 0 and j > 0:\n", 263 | " if flag[i][j]:\n", 264 | " # この価値の更新で追加した品物は、i-1\n", 265 | " my_knapsack.append(items[i-1])\n", 266 | " # 表を左へ戻る\n", 267 | " j -= items[i-1].weight\n", 268 | " i -= 1\n", 269 | " return my_knapsack" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 10, 275 | "metadata": {}, 276 | "outputs": [ 277 | { 278 | "name": "stdout", 279 | "output_type": "stream", 280 | "text": [ 281 | "重さ 40 kg / 価値 409 万円\n" 282 | ] 283 | } 284 | ], 285 | "source": [ 286 | "knap_dp = dp(item_list, 40)\n", 287 | "print(knap_dp)" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "metadata": {}, 308 | "outputs": [], 309 | "source": [] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": null, 321 | "metadata": {}, 322 | "outputs": [], 323 | "source": [] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": null, 328 | "metadata": {}, 329 | "outputs": [], 330 | "source": [] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "metadata": {}, 335 | "source": [ 336 | "## 練習問題解答" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "## 7.1\n", 344 | "\n", 345 | "ループの中でcontinueが実行されると、最も内側のループの次のステップへ進む。break分が実行されると、そのロープを抜ける。ナップサックに順々に品物を詰めていき、入らない品物が初めて見つかったとき、breakが実行されると探索がそこで終わってしまう。ナップサックの余った容量には、別の品物が入る可能性は残っているので、返される結果は貪欲法が正しく実行された時よりも悪いものとなる。\n", 346 | "\n", 347 | "ちなみに、このコードではappendメソッドがナップサックに入らない品物を引数にとると例外を発生することがわかっているので、try-except構文を使っているが、次のように書くこともできる。" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": 11, 353 | "metadata": {}, 354 | "outputs": [], 355 | "source": [ 356 | "def greedy2(items, size_limit):\n", 357 | " sorted_item_list = sorted(items, key=lambda x: x.price/x.weight, reverse=True)\n", 358 | " my_knapsack = Knapsack(size_limit)\n", 359 | " for v in sorted_item_list:\n", 360 | " if my_knapsack.has_room_for(v):\n", 361 | " my_knapsack.append(v)\n", 362 | " return my_knapsack" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "こちらのコードの方が本文中のコード7.3よりすっきりしていて見やすい。ただ、appendメソッドの中でhas_room_forを呼んでいるので、品物を追加する場合は、同じメソッドを2回呼ぶことになる。has_room_forの処理が重い場合は気をつける必要があるが、いまは気にするほどのことはない。どちらが良い実装なのか、という疑問に対する答えは悩ましいところだ。はっきりとどちらが良いという判断がつかない場合も多い。また、メソッドや関数の動作をドキュメントにして残しておくということも重要だ。いまはクラスの実装と利用を両方1人でしているが、誰かが作ったクラスを利用する場合や、自分が作ったクラスを利用してもらう場合もあるだろう。オープンソースの場合は中身を公開できるが、それでもやはりドキュメントを書くことは重要になる。クラスの各メソッドや関数がどのような動作をするのか、わかりやすく利用者に伝わるようにする必要がある。" 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "metadata": {}, 375 | "source": [ 376 | "## 7.2\n", 377 | "\n", 378 | "元の問題との違いは、荷物を分割できるところにあるので、この問題は分割ナップサック問題と呼ばれることもある。分割はdivisionなので、関数名はdiv_knapsackにしよう。この問題は、元の0-1ナップサック問題と比べて驚くほど簡単に最適解を求めることができる。" 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": 12, 384 | "metadata": {}, 385 | "outputs": [], 386 | "source": [ 387 | "def div_knapsack(items, size_limit):\n", 388 | " # 単位重さあたりの価値で品物を並び替える。\n", 389 | " sorted_item_list = sorted(items, key=lambda x: x.price/x.weight, reverse=True)\n", 390 | " my_knapsack = Knapsack(size_limit)\n", 391 | " for v in sorted_item_list:\n", 392 | " # 入る余地があるなら品物を入れる。\n", 393 | " try:\n", 394 | " my_knapsack.append(v)\n", 395 | " except ValueError:\n", 396 | " break\n", 397 | " # vの一部を入るだけ入れる。\n", 398 | " w = my_knapsack.size - my_knapsack.weight\n", 399 | " p = v.price * (w / v.weight)\n", 400 | " virtual_item = Item(-1, w, p)\n", 401 | " my_knapsack.append(virtual_item)\n", 402 | " return my_knapsack" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 13, 408 | "metadata": {}, 409 | "outputs": [ 410 | { 411 | "name": "stdout", 412 | "output_type": "stream", 413 | "text": [ 414 | "重さ 40 kg / 価値 412.4 万円\n" 415 | ] 416 | } 417 | ], 418 | "source": [ 419 | "print(div_knapsack(item_list, 40))" 420 | ] 421 | }, 422 | { 423 | "cell_type": "markdown", 424 | "metadata": {}, 425 | "source": [ 426 | "貪欲法の実装を改造してプログラムを作った。単位重さあたりの価値が高い品物から順に追加していき、品物が入らなくなったところで、ループを抜ける。あらたに、ナップサックに残っている容量にピッタリ収まる仮想的な品物を作り、これを追加した。これが分割ナップサック問題の最適解になる。元の問題の最適解を求めるのは難しいが、問題をすこし変形すると簡単に最適解が求まることはよくある。同じ品物リストであれば、分割ナップサック問題の解は、元の0-1ナップサック問題の解よりも価値が高い。この分割ナップサック問題を利用して、本章で扱った総当たりで計算するアルゴリズムの計算量を削減する方法もある。これは一般的には、分岐限定法と呼ばれる方法の1つになる。" 427 | ] 428 | }, 429 | { 430 | "cell_type": "markdown", 431 | "metadata": {}, 432 | "source": [ 433 | "## 7.3\n", 434 | "\n", 435 | "やさしいナップサック問題は、その名の通り簡単に解くことができる。0-1ナップサック問題の近似解法として紹介した貪欲法で最適解を求めることができる。問題文の式(7.1)が成り立つので、ある品物$i$がナップサックに入るにも関わらず入れないということは、残りの品物すべてを入れても品物$i$より軽くなる(価値が低くなる)ことを意味する。つまり、貪欲法で最適解を求めることができる。\n", 436 | "\n", 437 | "まずはサンプルデータを用意しよう。" 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": 14, 443 | "metadata": {}, 444 | "outputs": [ 445 | { 446 | "data": { 447 | "text/plain": [ 448 | "[Item(name=0, weight=1.9488239423104798, price=1.9488239423104798),\n", 449 | " Item(name=1, weight=2.6551378490580415, price=2.6551378490580415),\n", 450 | " Item(name=2, weight=4.838261805722269, price=4.838261805722269),\n", 451 | " Item(name=3, weight=10.071377534468683, price=10.071377534468683),\n", 452 | " Item(name=4, weight=20.534943054392652, price=20.534943054392652),\n", 453 | " Item(name=5, weight=43.84009665712613, price=43.84009665712613),\n", 454 | " Item(name=6, weight=93.38942230837242, price=93.38942230837242),\n", 455 | " Item(name=7, weight=195.43001615072168, price=195.43001615072168),\n", 456 | " Item(name=8, weight=411.01576631127926, price=411.01576631127926),\n", 457 | " Item(name=9, weight=862.345132902462, price=862.345132902462),\n", 458 | " Item(name=10, weight=1811.0706460021188, price=1811.0706460021188)]" 459 | ] 460 | }, 461 | "execution_count": 14, 462 | "metadata": {}, 463 | "output_type": "execute_result" 464 | } 465 | ], 466 | "source": [ 467 | "random.seed(7)\n", 468 | "\n", 469 | "#品物の数\n", 470 | "num = 10\n", 471 | "\n", 472 | "# 品物を生成するための数を保持するリスト\n", 473 | "val_list = []\n", 474 | "\n", 475 | "# 最初の品物の重さ。重さの10%の標準偏差で正規分布に従う乱数を生成\n", 476 | "w = random.gauss(2, 2*0.1)\n", 477 | "val_list.append(w)\n", 478 | "\n", 479 | "for i in range(num):\n", 480 | " total_w = sum(val_list)\n", 481 | " w = total_w + abs(random.gauss(total_w * 0.1, 1))\n", 482 | " val_list.append(w)\n", 483 | "\n", 484 | "tender_list = []\n", 485 | "for i, v in enumerate(val_list):\n", 486 | " tender_list.append(Item(i, v, v))\n", 487 | "\n", 488 | "tender_list" 489 | ] 490 | }, 491 | { 492 | "cell_type": "markdown", 493 | "metadata": {}, 494 | "source": [ 495 | "重さ(値段)に関する条件は、かなり急速に数値が多きくなることがわかる。いくつかの品物が入るように、ナップサックのサイズを3,000としよう。貪欲法のアルゴリズムを少し改造する。" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": 15, 501 | "metadata": {}, 502 | "outputs": [], 503 | "source": [ 504 | "def greedy_for_tender(items, size_limit):\n", 505 | " # 重さ(または値段)で品物を並び替える。\n", 506 | " sorted_item_list = sorted(items, key=lambda x: x.weight, reverse=True)\n", 507 | " my_knapsack = Knapsack(size_limit)\n", 508 | " for v in sorted_item_list:\n", 509 | " # 入る余地があるなら品物を入れる。\n", 510 | " try:\n", 511 | " my_knapsack.append(v)\n", 512 | " except ValueError:\n", 513 | " continue\n", 514 | " return my_knapsack" 515 | ] 516 | }, 517 | { 518 | "cell_type": "code", 519 | "execution_count": 16, 520 | "metadata": {}, 521 | "outputs": [], 522 | "source": [ 523 | "answer_knapsack = greedy_for_tender(tender_list, 3000)" 524 | ] 525 | }, 526 | { 527 | "cell_type": "code", 528 | "execution_count": 17, 529 | "metadata": { 530 | "scrolled": true 531 | }, 532 | "outputs": [ 533 | { 534 | "name": "stdout", 535 | "output_type": "stream", 536 | "text": [ 537 | "重さ 2999.628623700569 kg / 価値 2999.628623700569 万円\n" 538 | ] 539 | } 540 | ], 541 | "source": [ 542 | "print(answer_knapsack)" 543 | ] 544 | }, 545 | { 546 | "cell_type": "code", 547 | "execution_count": 18, 548 | "metadata": {}, 549 | "outputs": [ 550 | { 551 | "data": { 552 | "text/plain": [ 553 | "[Item(name=10, weight=1811.0706460021188, price=1811.0706460021188),\n", 554 | " Item(name=9, weight=862.345132902462, price=862.345132902462),\n", 555 | " Item(name=7, weight=195.43001615072168, price=195.43001615072168),\n", 556 | " Item(name=6, weight=93.38942230837242, price=93.38942230837242),\n", 557 | " Item(name=4, weight=20.534943054392652, price=20.534943054392652),\n", 558 | " Item(name=3, weight=10.071377534468683, price=10.071377534468683),\n", 559 | " Item(name=2, weight=4.838261805722269, price=4.838261805722269),\n", 560 | " Item(name=0, weight=1.9488239423104798, price=1.9488239423104798)]" 561 | ] 562 | }, 563 | "execution_count": 18, 564 | "metadata": {}, 565 | "output_type": "execute_result" 566 | } 567 | ], 568 | "source": [ 569 | "answer_knapsack.items" 570 | ] 571 | } 572 | ], 573 | "metadata": { 574 | "kernelspec": { 575 | "display_name": "Python 3", 576 | "language": "python", 577 | "name": "python3" 578 | }, 579 | "language_info": { 580 | "codemirror_mode": { 581 | "name": "ipython", 582 | "version": 3 583 | }, 584 | "file_extension": ".py", 585 | "mimetype": "text/x-python", 586 | "name": "python", 587 | "nbconvert_exporter": "python", 588 | "pygments_lexer": "ipython3", 589 | "version": "3.6.5" 590 | } 591 | }, 592 | "nbformat": 4, 593 | "nbformat_minor": 2 594 | } 595 | -------------------------------------------------------------------------------- /source/Chapter03.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/plain": [ 11 | "97" 12 | ] 13 | }, 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "output_type": "execute_result" 17 | } 18 | ], 19 | "source": [ 20 | "import math\n", 21 | "math.gcd(18915, 14938)" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 2, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "def simple_gcd(a, b):\n", 31 | " if a < b:\n", 32 | " a, b = b, a\n", 33 | " x = b\n", 34 | " while True:\n", 35 | " if a % x == 0 and b % x == 0:\n", 36 | " return x\n", 37 | " x -= 1" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 3, 43 | "metadata": {}, 44 | "outputs": [ 45 | { 46 | "data": { 47 | "text/plain": [ 48 | "97" 49 | ] 50 | }, 51 | "execution_count": 3, 52 | "metadata": {}, 53 | "output_type": "execute_result" 54 | } 55 | ], 56 | "source": [ 57 | "simple_gcd(18915, 14938)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 4, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "def euclidean_algorithm(a, b):\n", 67 | " while True:\n", 68 | " r = a % b\n", 69 | " if r == 0:\n", 70 | " return b\n", 71 | " a, b = b, r" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 5, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "data": { 81 | "text/plain": [ 82 | "97" 83 | ] 84 | }, 85 | "execution_count": 5, 86 | "metadata": {}, 87 | "output_type": "execute_result" 88 | } 89 | ], 90 | "source": [ 91 | "euclidean_algorithm(18915, 14938)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 6, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "def euclidean_recursion(a, b):\n", 101 | " r = a % b\n", 102 | " if r == 0:\n", 103 | " return b\n", 104 | " return euclidean_recursion(b, r)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 7, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "data": { 114 | "text/plain": [ 115 | "97" 116 | ] 117 | }, 118 | "execution_count": 7, 119 | "metadata": {}, 120 | "output_type": "execute_result" 121 | } 122 | ], 123 | "source": [ 124 | "euclidean_recursion(14938, 18915)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 8, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "data": { 134 | "text/plain": [ 135 | "[30, 75, 69, 16, 47, 77, 60, 80, 74, 8, 77, 1, 60, 33, 70, 29, 24, 91, 60, 69]" 136 | ] 137 | }, 138 | "execution_count": 8, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "import random\n", 145 | "random.seed(3)\n", 146 | "my_array = [random.randint(0, 99) for i in range(20)]\n", 147 | "my_array" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 9, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "data": { 157 | "text/plain": [ 158 | "[1, 8, 16, 24, 29, 30, 33, 47, 60, 60, 60, 69, 69, 70, 74, 75, 77, 77, 80, 91]" 159 | ] 160 | }, 161 | "execution_count": 9, 162 | "metadata": {}, 163 | "output_type": "execute_result" 164 | } 165 | ], 166 | "source": [ 167 | "sorted(my_array)" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 10, 173 | "metadata": {}, 174 | "outputs": [ 175 | { 176 | "data": { 177 | "text/plain": [ 178 | "[1, 8, 16, 24, 29, 30, 33, 47, 60, 60, 60, 69, 69, 70, 74, 75, 77, 77, 80, 91]" 179 | ] 180 | }, 181 | "execution_count": 10, 182 | "metadata": {}, 183 | "output_type": "execute_result" 184 | } 185 | ], 186 | "source": [ 187 | "my_array.sort()\n", 188 | "my_array" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 11, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "def selection_sort(array):\n", 198 | " x = array.copy()\n", 199 | " n = len(x)\n", 200 | " for i in range(n):\n", 201 | " min_idx = i\n", 202 | " for j in range(i, n):\n", 203 | " if x[j] < x[min_idx]:\n", 204 | " min_idx = j\n", 205 | " x[i], x[min_idx] = x[min_idx], x[i]\n", 206 | " return x" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 12, 212 | "metadata": {}, 213 | "outputs": [ 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "[30, 60, 77, 24, 69, 80, 91, 74, 70, 8, 1, 33, 60, 16, 60, 47, 29, 69, 75, 77]" 218 | ] 219 | }, 220 | "execution_count": 12, 221 | "metadata": {}, 222 | "output_type": "execute_result" 223 | } 224 | ], 225 | "source": [ 226 | "random.shuffle(my_array)\n", 227 | "my_array" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 13, 233 | "metadata": { 234 | "scrolled": true 235 | }, 236 | "outputs": [ 237 | { 238 | "data": { 239 | "text/plain": [ 240 | "[1, 8, 16, 24, 29, 30, 33, 47, 60, 60, 60, 69, 69, 70, 74, 75, 77, 77, 80, 91]" 241 | ] 242 | }, 243 | "execution_count": 13, 244 | "metadata": {}, 245 | "output_type": "execute_result" 246 | } 247 | ], 248 | "source": [ 249 | "selection_sort(my_array)" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 14, 255 | "metadata": {}, 256 | "outputs": [ 257 | { 258 | "data": { 259 | "text/plain": [ 260 | "4.348750618999475" 261 | ] 262 | }, 263 | "execution_count": 14, 264 | "metadata": {}, 265 | "output_type": "execute_result" 266 | } 267 | ], 268 | "source": [ 269 | "import timeit\n", 270 | "my_array = [random.randint(0, 99) for i in range(10000)]\n", 271 | "timeit.timeit('selection_sort(my_array)', globals=globals(), number=1)" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 15, 277 | "metadata": {}, 278 | "outputs": [ 279 | { 280 | "data": { 281 | "text/plain": [ 282 | "0.0029410540009848773" 283 | ] 284 | }, 285 | "execution_count": 15, 286 | "metadata": {}, 287 | "output_type": "execute_result" 288 | } 289 | ], 290 | "source": [ 291 | "my_array = [random.randint(0, 99) for i in range(10000)]\n", 292 | "timeit.timeit('sorted(my_array)', globals=globals(), number=1)" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": null, 298 | "metadata": {}, 299 | "outputs": [], 300 | "source": [] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": null, 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": null, 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": null, 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "# 練習問題解答" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "## 3.1\n", 349 | "\n", 350 | "指定された桁数の自然数を、ランダムに生成する関数は次のようにかける。" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 16, 356 | "metadata": {}, 357 | "outputs": [], 358 | "source": [ 359 | "import random\n", 360 | "\n", 361 | "def rand_n_digit_int(n):\n", 362 | " return random.randint(10**(n-1), 10**n - 1)" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "100桁の数も簡単に作ることができる。" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 17, 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "data": { 379 | "text/plain": [ 380 | "8276097815023773090731313729939727465057369192982260387834087532409282742027662928913639507654491077" 381 | ] 382 | }, 383 | "execution_count": 17, 384 | "metadata": {}, 385 | "output_type": "execute_result" 386 | } 387 | ], 388 | "source": [ 389 | "rand_n_digit_int(100)" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "metadata": {}, 395 | "source": [ 396 | "ユークリッドの互除法は100桁の数を2つ引数にとっても現実的な時間で答えが返ってくる。" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 18, 402 | "metadata": {}, 403 | "outputs": [], 404 | "source": [ 405 | "def euclidean_algorithm(a, b):\n", 406 | " while True:\n", 407 | " r = a % b\n", 408 | " if r == 0:\n", 409 | " return b\n", 410 | " a, b = b, r" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": 19, 416 | "metadata": {}, 417 | "outputs": [ 418 | { 419 | "data": { 420 | "text/plain": [ 421 | "1" 422 | ] 423 | }, 424 | "execution_count": 19, 425 | "metadata": {}, 426 | "output_type": "execute_result" 427 | } 428 | ], 429 | "source": [ 430 | "a = rand_n_digit_int(100)\n", 431 | "b = rand_n_digit_int(100)\n", 432 | "euclidean_algorithm(a, b)" 433 | ] 434 | }, 435 | { 436 | "cell_type": "markdown", 437 | "metadata": {}, 438 | "source": [ 439 | "約数を調べ上げる方法は、10桁以上になるとかなり時間がかかる。" 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": 20, 445 | "metadata": {}, 446 | "outputs": [], 447 | "source": [ 448 | "def simple_gcd(a, b):\n", 449 | " if a < b:\n", 450 | " a, b = b, a\n", 451 | " x = b\n", 452 | " while True:\n", 453 | " if a % x == 0 and b % x == 0:\n", 454 | " return x\n", 455 | " x -= 1" 456 | ] 457 | }, 458 | { 459 | "cell_type": "markdown", 460 | "metadata": {}, 461 | "source": [ 462 | "以下のコードの実行には、数分かかるだろう。" 463 | ] 464 | }, 465 | { 466 | "cell_type": "code", 467 | "execution_count": 21, 468 | "metadata": {}, 469 | "outputs": [ 470 | { 471 | "data": { 472 | "text/plain": [ 473 | "1" 474 | ] 475 | }, 476 | "execution_count": 21, 477 | "metadata": {}, 478 | "output_type": "execute_result" 479 | } 480 | ], 481 | "source": [ 482 | "a = rand_n_digit_int(9)\n", 483 | "b = rand_n_digit_int(9)\n", 484 | "simple_gcd(a, b)" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "ユークリッドの互除法では1000桁ほどの数でもすぐに答えが返ってくる。この桁数と計算量の関係は、8章で再び検討する。" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "## 3.2\n", 499 | "\n", 500 | "math.factorialで階乗を計算できる。" 501 | ] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "execution_count": 22, 506 | "metadata": {}, 507 | "outputs": [ 508 | { 509 | "data": { 510 | "text/plain": [ 511 | "120" 512 | ] 513 | }, 514 | "execution_count": 22, 515 | "metadata": {}, 516 | "output_type": "execute_result" 517 | } 518 | ], 519 | "source": [ 520 | "import math\n", 521 | "\n", 522 | "math.factorial(5)" 523 | ] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": 23, 528 | "metadata": {}, 529 | "outputs": [], 530 | "source": [ 531 | "def factorial(n):\n", 532 | " res = 1\n", 533 | " while n:\n", 534 | " res *= n\n", 535 | " n -= 1\n", 536 | " return res" 537 | ] 538 | }, 539 | { 540 | "cell_type": "code", 541 | "execution_count": 24, 542 | "metadata": {}, 543 | "outputs": [ 544 | { 545 | "data": { 546 | "text/plain": [ 547 | "120" 548 | ] 549 | }, 550 | "execution_count": 24, 551 | "metadata": {}, 552 | "output_type": "execute_result" 553 | } 554 | ], 555 | "source": [ 556 | "factorial(5)" 557 | ] 558 | }, 559 | { 560 | "cell_type": "code", 561 | "execution_count": 25, 562 | "metadata": {}, 563 | "outputs": [], 564 | "source": [ 565 | "def factorial_rec(n):\n", 566 | " if n == 1:\n", 567 | " return 1\n", 568 | " return n * factorial_rec(n-1)" 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": 26, 574 | "metadata": {}, 575 | "outputs": [ 576 | { 577 | "data": { 578 | "text/plain": [ 579 | "120" 580 | ] 581 | }, 582 | "execution_count": 26, 583 | "metadata": {}, 584 | "output_type": "execute_result" 585 | } 586 | ], 587 | "source": [ 588 | "factorial_rec(5)" 589 | ] 590 | }, 591 | { 592 | "cell_type": "markdown", 593 | "metadata": {}, 594 | "source": [ 595 | "ちなみに、$n$の階乗$n!$は、$n$が大きくなるとあっという間に大きな数になる。" 596 | ] 597 | }, 598 | { 599 | "cell_type": "code", 600 | "execution_count": 27, 601 | "metadata": {}, 602 | "outputs": [ 603 | { 604 | "data": { 605 | "text/plain": [ 606 | "3628800" 607 | ] 608 | }, 609 | "execution_count": 27, 610 | "metadata": {}, 611 | "output_type": "execute_result" 612 | } 613 | ], 614 | "source": [ 615 | "factorial(10)" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 28, 621 | "metadata": {}, 622 | "outputs": [ 623 | { 624 | "data": { 625 | "text/plain": [ 626 | "93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000" 627 | ] 628 | }, 629 | "execution_count": 28, 630 | "metadata": {}, 631 | "output_type": "execute_result" 632 | } 633 | ], 634 | "source": [ 635 | "factorial(100)" 636 | ] 637 | }, 638 | { 639 | "cell_type": "markdown", 640 | "metadata": {}, 641 | "source": [ 642 | "## 3.3 \n", 643 | "\n", 644 | "組み込み関数sortedには、ソートの順番を逆にできる引数reverseがある。" 645 | ] 646 | }, 647 | { 648 | "cell_type": "code", 649 | "execution_count": 29, 650 | "metadata": {}, 651 | "outputs": [ 652 | { 653 | "data": { 654 | "text/plain": [ 655 | "[96,\n", 656 | " 96,\n", 657 | " 94,\n", 658 | " 84,\n", 659 | " 80,\n", 660 | " 76,\n", 661 | " 65,\n", 662 | " 61,\n", 663 | " 59,\n", 664 | " 57,\n", 665 | " 57,\n", 666 | " 54,\n", 667 | " 51,\n", 668 | " 48,\n", 669 | " 44,\n", 670 | " 35,\n", 671 | " 26,\n", 672 | " 23,\n", 673 | " 19,\n", 674 | " 12]" 675 | ] 676 | }, 677 | "execution_count": 29, 678 | "metadata": {}, 679 | "output_type": "execute_result" 680 | } 681 | ], 682 | "source": [ 683 | "my_array = [rand_n_digit_int(2) for i in range(20)]\n", 684 | "sorted(my_array, reverse=True)" 685 | ] 686 | }, 687 | { 688 | "cell_type": "markdown", 689 | "metadata": {}, 690 | "source": [ 691 | "自分自身の中身を書き換えるsortメソッドにも同じ引数がある。" 692 | ] 693 | }, 694 | { 695 | "cell_type": "code", 696 | "execution_count": 30, 697 | "metadata": {}, 698 | "outputs": [ 699 | { 700 | "data": { 701 | "text/plain": [ 702 | "[96,\n", 703 | " 96,\n", 704 | " 94,\n", 705 | " 84,\n", 706 | " 80,\n", 707 | " 76,\n", 708 | " 65,\n", 709 | " 61,\n", 710 | " 59,\n", 711 | " 57,\n", 712 | " 57,\n", 713 | " 54,\n", 714 | " 51,\n", 715 | " 48,\n", 716 | " 44,\n", 717 | " 35,\n", 718 | " 26,\n", 719 | " 23,\n", 720 | " 19,\n", 721 | " 12]" 722 | ] 723 | }, 724 | "execution_count": 30, 725 | "metadata": {}, 726 | "output_type": "execute_result" 727 | } 728 | ], 729 | "source": [ 730 | "my_array.sort(reverse=True)\n", 731 | "my_array" 732 | ] 733 | }, 734 | { 735 | "cell_type": "markdown", 736 | "metadata": {}, 737 | "source": [ 738 | "ソートした配列の順番をひっくり返す方法でも良い。" 739 | ] 740 | }, 741 | { 742 | "cell_type": "code", 743 | "execution_count": 31, 744 | "metadata": {}, 745 | "outputs": [ 746 | { 747 | "data": { 748 | "text/plain": [ 749 | "[96,\n", 750 | " 96,\n", 751 | " 94,\n", 752 | " 84,\n", 753 | " 80,\n", 754 | " 76,\n", 755 | " 65,\n", 756 | " 61,\n", 757 | " 59,\n", 758 | " 57,\n", 759 | " 57,\n", 760 | " 54,\n", 761 | " 51,\n", 762 | " 48,\n", 763 | " 44,\n", 764 | " 35,\n", 765 | " 26,\n", 766 | " 23,\n", 767 | " 19,\n", 768 | " 12]" 769 | ] 770 | }, 771 | "execution_count": 31, 772 | "metadata": {}, 773 | "output_type": "execute_result" 774 | } 775 | ], 776 | "source": [ 777 | "# 一度シャッフルしてから実行してみる。\n", 778 | "random.shuffle(my_array)\n", 779 | "my_array.sort()\n", 780 | "list(reversed(my_array))\n", 781 | "# 次のコードも同じ\n", 782 | "# my_array[::-1]" 783 | ] 784 | }, 785 | { 786 | "cell_type": "markdown", 787 | "metadata": {}, 788 | "source": [ 789 | "## 3.4 \n", 790 | "\n", 791 | "datetimeモジュールを使って今日の日付を取得し、1970/1/1から何日経過しているか計算する。以下の結果は実行した日によってもちろん結果が違う。" 792 | ] 793 | }, 794 | { 795 | "cell_type": "code", 796 | "execution_count": 32, 797 | "metadata": {}, 798 | "outputs": [ 799 | { 800 | "data": { 801 | "text/plain": [ 802 | "datetime.timedelta(18221)" 803 | ] 804 | }, 805 | "execution_count": 32, 806 | "metadata": {}, 807 | "output_type": "execute_result" 808 | } 809 | ], 810 | "source": [ 811 | "import datetime\n", 812 | "\n", 813 | "today = datetime.date.today()\n", 814 | "epoch_date = datetime.date(1970, 1, 1)\n", 815 | "\n", 816 | "elapsed_days = today - epoch_date\n", 817 | "elapsed_days" 818 | ] 819 | }, 820 | { 821 | "cell_type": "markdown", 822 | "metadata": {}, 823 | "source": [ 824 | "1日は24時間で1時間は3600秒なので、以下の計算でこの値が、time.time()の戻り値とかなり近いことを確認できる。datetime.timedelta型のdays属性に経過日数が整数型で格納されている。" 825 | ] 826 | }, 827 | { 828 | "cell_type": "code", 829 | "execution_count": 33, 830 | "metadata": {}, 831 | "outputs": [ 832 | { 833 | "data": { 834 | "text/plain": [ 835 | "1574294400" 836 | ] 837 | }, 838 | "execution_count": 33, 839 | "metadata": {}, 840 | "output_type": "execute_result" 841 | } 842 | ], 843 | "source": [ 844 | "elapsed_days.days*24*3600" 845 | ] 846 | }, 847 | { 848 | "cell_type": "code", 849 | "execution_count": 34, 850 | "metadata": {}, 851 | "outputs": [ 852 | { 853 | "data": { 854 | "text/plain": [ 855 | "1574300882.250085" 856 | ] 857 | }, 858 | "execution_count": 34, 859 | "metadata": {}, 860 | "output_type": "execute_result" 861 | } 862 | ], 863 | "source": [ 864 | "import time\n", 865 | "\n", 866 | "time.time()" 867 | ] 868 | }, 869 | { 870 | "cell_type": "markdown", 871 | "metadata": {}, 872 | "source": [ 873 | "datetimeモジュールの詳しい使い方は、以下の公式ドキュメントが参考になる。\n", 874 | "\n", 875 | "https://docs.python.org/3/library/datetime.html" 876 | ] 877 | } 878 | ], 879 | "metadata": { 880 | "kernelspec": { 881 | "display_name": "Python 3", 882 | "language": "python", 883 | "name": "python3" 884 | }, 885 | "language_info": { 886 | "codemirror_mode": { 887 | "name": "ipython", 888 | "version": 3 889 | }, 890 | "file_extension": ".py", 891 | "mimetype": "text/x-python", 892 | "name": "python", 893 | "nbconvert_exporter": "python", 894 | "pygments_lexer": "ipython3", 895 | "version": "3.6.5" 896 | } 897 | }, 898 | "nbformat": 4, 899 | "nbformat_minor": 2 900 | } 901 | -------------------------------------------------------------------------------- /source/Chapter10.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/plain": [ 11 | "'70df01e7b0c1b7042aabb5a3c1e2fbd5'" 12 | ] 13 | }, 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "output_type": "execute_result" 17 | } 18 | ], 19 | "source": [ 20 | "import hashlib\n", 21 | "\n", 22 | "# ファイルの内容をfileに格納(事前にファイルのダウンロードが必要)\n", 23 | "with open('python-3.7.3-embed-win32.zip', 'br') as f:\n", 24 | " file = f.read()\n", 25 | " \n", 26 | "# hashlibからmd5のインスタンスを作り、16進数表示のチェックサムを得る\n", 27 | "hashlib.md5(file).hexdigest()" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": {}, 34 | "outputs": [ 35 | { 36 | "data": { 37 | "text/plain": [ 38 | "'8c0c3027e3cfc3d644caab3847a505b0'" 39 | ] 40 | }, 41 | "execution_count": 2, 42 | "metadata": {}, 43 | "output_type": "execute_result" 44 | } 45 | ], 46 | "source": [ 47 | "hashlib.md5('あ'.encode('UTF-8')).hexdigest()" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 3, 53 | "metadata": {}, 54 | "outputs": [ 55 | { 56 | "name": "stdout", 57 | "output_type": "stream", 58 | "text": [ 59 | "1\n", 60 | "2\n" 61 | ] 62 | } 63 | ], 64 | "source": [ 65 | "print(hash(1))\n", 66 | "print(hash(2))" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 4, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "name": "stdout", 76 | "output_type": "stream", 77 | "text": [ 78 | "366da3d0fc5e8ed36f9eac8083a46ae8\n", 79 | "76f34d73a1a6753d1243c9ba0afe3457\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "import pickle\n", 85 | "print(hashlib.md5(pickle.dumps(1)).hexdigest())\n", 86 | "print(hashlib.md5(pickle.dumps(2)).hexdigest())" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": 5, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "def to_byte_hash(val):\n", 96 | " byte_data = pickle.dumps(val)\n", 97 | " return hashlib.md5(byte_data).hexdigest()" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 6, 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "data": { 107 | "text/plain": [ 108 | "'390c7c5300a128c476f944872a93ccf4'" 109 | ] 110 | }, 111 | "execution_count": 6, 112 | "metadata": {}, 113 | "output_type": "execute_result" 114 | } 115 | ], 116 | "source": [ 117 | "array_10k = list(range(10000))\n", 118 | "to_byte_hash(array_10k)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 7, 124 | "metadata": {}, 125 | "outputs": [ 126 | { 127 | "data": { 128 | "text/plain": [ 129 | "'ab2d4961f8ac559b4e50588ff7992826'" 130 | ] 131 | }, 132 | "execution_count": 7, 133 | "metadata": {}, 134 | "output_type": "execute_result" 135 | } 136 | ], 137 | "source": [ 138 | "array_10k[0] = -1\n", 139 | "to_byte_hash(array_10k)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 8, 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "data": { 149 | "text/plain": [ 150 | "'390c7c5300a128c476f944872a93ccf4'" 151 | ] 152 | }, 153 | "execution_count": 8, 154 | "metadata": {}, 155 | "output_type": "execute_result" 156 | } 157 | ], 158 | "source": [ 159 | "array_10k[0] = 0\n", 160 | "to_byte_hash(array_10k)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 9, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "def block_to_hash(block_data, nonce):\n", 170 | " # 関数の引数にnonceを連結して、sha256を使ってハッシュ値を計算\n", 171 | " input_str = str(block_data) + str(nonce)\n", 172 | " h = hashlib.sha256(input_str.encode('UTF-8')).hexdigest()\n", 173 | " # 先頭から0がいくつ並んでいるかを数える。\n", 174 | " cnt = 0\n", 175 | " for v in h:\n", 176 | " if v == '0':\n", 177 | " cnt += 1\n", 178 | " else:\n", 179 | " break\n", 180 | " return h, cnt" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 10, 186 | "metadata": {}, 187 | "outputs": [ 188 | { 189 | "name": "stdout", 190 | "output_type": "stream", 191 | "text": [ 192 | "799585回目の計算で成功しました\n", 193 | "00000472cdbfc8538016d49f00d7c5ad01cb9e6fc7c733ddbe70ed1e90deb773\n" 194 | ] 195 | } 196 | ], 197 | "source": [ 198 | "# 先頭に0が5つ並んだハッシュ値を探す\n", 199 | "my_block = 'prev_block_tx0_tx1_tx2'\n", 200 | "c = 1\n", 201 | "while True:\n", 202 | " hash_val, cnt = block_to_hash(my_block, c)\n", 203 | " if cnt == 5:\n", 204 | " print('{}回目の計算で成功しました'.format(c))\n", 205 | " print(hash_val)\n", 206 | " break\n", 207 | " c += 1" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 11, 213 | "metadata": {}, 214 | "outputs": [ 215 | { 216 | "name": "stdout", 217 | "output_type": "stream", 218 | "text": [ 219 | "618970019642690137449562111\n", 220 | "162259276829213363391578010288127\n" 221 | ] 222 | } 223 | ], 224 | "source": [ 225 | "p = pow(2, 89) - 1\n", 226 | "q = pow(2, 107) - 1\n", 227 | "print(p)\n", 228 | "print(q)" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 12, 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "data": { 238 | "text/plain": [ 239 | "100433627766186892221372630609062766858404681029709092356097" 240 | ] 241 | }, 242 | "execution_count": 12, 243 | "metadata": {}, 244 | "output_type": "execute_result" 245 | } 246 | ], 247 | "source": [ 248 | "n = p*q\n", 249 | "n" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 13, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "r = 65537\n", 259 | "\n", 260 | "def find_s(phi_pq, r):\n", 261 | " k = 1\n", 262 | " while True:\n", 263 | " y = 1 + k * phi_pq\n", 264 | " s, d = divmod(y, r)\n", 265 | " if d == 0:\n", 266 | " return k, s\n", 267 | " k += 1" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 14, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "data": { 277 | "text/plain": [ 278 | "15499423397885381203395986760745292550657831765628692176393" 279 | ] 280 | }, 281 | "execution_count": 14, 282 | "metadata": {}, 283 | "output_type": "execute_result" 284 | } 285 | ], 286 | "source": [ 287 | "k, s = find_s((p-1)*(q-1), r)\n", 288 | "s" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 15, 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [ 297 | "rsa_encode = pow\n", 298 | "rsa_decode = pow" 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": 16, 304 | "metadata": {}, 305 | "outputs": [ 306 | { 307 | "data": { 308 | "text/plain": [ 309 | "29483" 310 | ] 311 | }, 312 | "execution_count": 16, 313 | "metadata": {}, 314 | "output_type": "execute_result" 315 | } 316 | ], 317 | "source": [ 318 | "# 「猫」をUnicodeコードポイントの整数に変換\n", 319 | "msg = ord('猫')\n", 320 | "msg" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 17, 326 | "metadata": {}, 327 | "outputs": [ 328 | { 329 | "data": { 330 | "text/plain": [ 331 | "82321475112792022243592670346886009619295334480301269316154" 332 | ] 333 | }, 334 | "execution_count": 17, 335 | "metadata": {}, 336 | "output_type": "execute_result" 337 | } 338 | ], 339 | "source": [ 340 | "secret_msg = rsa_encode(msg, r, n)\n", 341 | "secret_msg" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": 18, 347 | "metadata": {}, 348 | "outputs": [ 349 | { 350 | "data": { 351 | "text/plain": [ 352 | "'猫'" 353 | ] 354 | }, 355 | "execution_count": 18, 356 | "metadata": {}, 357 | "output_type": "execute_result" 358 | } 359 | ], 360 | "source": [ 361 | "decoded_msg = rsa_decode(secret_msg, s, n)\n", 362 | "chr(decoded_msg)" 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": 19, 368 | "metadata": {}, 369 | "outputs": [], 370 | "source": [ 371 | "def block_sort_encode(val):\n", 372 | " res = []\n", 373 | " res.append(val)\n", 374 | " # もとのデータを追加してあるので、forの回数は1つ少ない\n", 375 | " for i in range(len(val) - 1):\n", 376 | " # 1つ前(現在の最新)をもってくる\n", 377 | " temp = res[-1]\n", 378 | " # 先頭の文字を末尾に移動\n", 379 | " res.append(temp[1:] + temp[0])\n", 380 | " res.sort()\n", 381 | " # 元のデータがどこへ行ったか探す\n", 382 | " idx = res.index(val)\n", 383 | " # 末尾の文字をすべて集めたものが結果\n", 384 | " encoded_str = [v[-1] for v in res]\n", 385 | " return ''.join(encoded_str), idx" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": 20, 391 | "metadata": {}, 392 | "outputs": [], 393 | "source": [ 394 | "input_str = '明日の会合は午後が良いですが,良い天気だとお昼ご飯のあとで眠くなってしまうので,午後は午後でも遅い時間が良いです.'" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 21, 400 | "metadata": {}, 401 | "outputs": [ 402 | { 403 | "data": { 404 | "text/plain": [ 405 | "('の良良良遅まと後間す眠昼てでで気なっいい後とのだあく飯う日合後しでのはは,会い午午午明.おい天でがが,も時ごでがす', 43)" 406 | ] 407 | }, 408 | "execution_count": 21, 409 | "metadata": {}, 410 | "output_type": "execute_result" 411 | } 412 | ], 413 | "source": [ 414 | "bsorted_str = block_sort_encode(input_str)\n", 415 | "bsorted_str" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": 22, 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [ 424 | "def block_sort_decode(val, idx):\n", 425 | " # ソートしたときにそれぞれ何処へいくかを格納する\n", 426 | " char_last_idx = []\n", 427 | " for i, v in enumerate(val):\n", 428 | " char_last_idx.append((v, i))\n", 429 | " # 文字でソートする。同じ文字の場合は、元の順序を保持する。\n", 430 | " char_last_idx.sort()\n", 431 | " # すべて0で初期化\n", 432 | " last_to_front_idx = [0] * len(char_last_idx)\n", 433 | " for i, v in enumerate(char_last_idx):\n", 434 | " # 末尾にあった文字が先頭へ行ったとき何処へ行ったか\n", 435 | " last_to_front_idx[v[1]] = i\n", 436 | " res = val[idx]\n", 437 | " # 最初は、元のデータのあった場所から\n", 438 | " i = last_to_front_idx[idx]\n", 439 | " while i != idx:\n", 440 | " res += val[i]\n", 441 | " # 次に行く場所へ添え字を更新\n", 442 | " i = last_to_front_idx[i]\n", 443 | " # 逆順にして返す\n", 444 | " return res[::-1]" 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": 23, 450 | "metadata": {}, 451 | "outputs": [ 452 | { 453 | "data": { 454 | "text/plain": [ 455 | "'明日の会合は午後が良いですが,良い天気だとお昼ご飯のあとで眠くなってしまうので,午後は午後でも遅い時間が良いです.'" 456 | ] 457 | }, 458 | "execution_count": 23, 459 | "metadata": {}, 460 | "output_type": "execute_result" 461 | } 462 | ], 463 | "source": [ 464 | "block_sort_decode(*bsorted_str)" 465 | ] 466 | }, 467 | { 468 | "cell_type": "code", 469 | "execution_count": null, 470 | "metadata": {}, 471 | "outputs": [], 472 | "source": [] 473 | }, 474 | { 475 | "cell_type": "code", 476 | "execution_count": null, 477 | "metadata": {}, 478 | "outputs": [], 479 | "source": [] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": null, 484 | "metadata": {}, 485 | "outputs": [], 486 | "source": [] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": null, 491 | "metadata": {}, 492 | "outputs": [], 493 | "source": [] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "execution_count": null, 498 | "metadata": {}, 499 | "outputs": [], 500 | "source": [] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": null, 505 | "metadata": {}, 506 | "outputs": [], 507 | "source": [] 508 | }, 509 | { 510 | "cell_type": "markdown", 511 | "metadata": {}, 512 | "source": [ 513 | "# 練習問題解答" 514 | ] 515 | }, 516 | { 517 | "cell_type": "markdown", 518 | "metadata": {}, 519 | "source": [ 520 | "## 10.1\n", 521 | "\n", 522 | "文字列を暗号学的ハッシュ関数で変換するときに、ランダムに生成した小さなデータを付与することがある。これはソルトと呼ばれ、同じ入力に対して、違う出力が出るようにし、わかりやすい文字列を変換した時に情報が漏洩するのを防ぐ役割がある。\n", 523 | "\n", 524 | "関数の名前は、hash_with_saltとしよう。また、saltを引数でとるようにし、渡されなかったときは、内部でランダムに生成しよう。" 525 | ] 526 | }, 527 | { 528 | "cell_type": "code", 529 | "execution_count": 24, 530 | "metadata": {}, 531 | "outputs": [], 532 | "source": [ 533 | "import random\n", 534 | "random.seed(10)\n", 535 | "\n", 536 | "def hash_with_salt(val, salt=None):\n", 537 | " if not salt:\n", 538 | " salt = str(random.randint(1, 100))\n", 539 | " m = hashlib.sha3_512()\n", 540 | " m.update(val.encode('UTF-8') + salt.encode('UTF-8'))\n", 541 | " return m.hexdigest(), salt" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": 25, 547 | "metadata": {}, 548 | "outputs": [ 549 | { 550 | "data": { 551 | "text/plain": [ 552 | "('f129003122900957bc0b3e324bbb9cf2970c35555de9ab7accc2be7374ce4ea7f22f877c0a9bb994552e4daff00214a9aa6026807439003a40118f9d694f2949',\n", 553 | " '74')" 554 | ] 555 | }, 556 | "execution_count": 25, 557 | "metadata": {}, 558 | "output_type": "execute_result" 559 | } 560 | ], 561 | "source": [ 562 | "hash_with_salt('password')" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": {}, 568 | "source": [ 569 | "文字列をバイト列に変換するために、encodeメソッドを使った。ハッシュ関数への入力に小さなデータを加えれば、元の文字列がpasswordであることは推測されにくくなる。なぜなら、単純にsha3_512アルゴリズムを使った場合とは全く違う文字列になるからだ。" 570 | ] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": 26, 575 | "metadata": {}, 576 | "outputs": [ 577 | { 578 | "data": { 579 | "text/plain": [ 580 | "'e9a75486736a550af4fea861e2378305c4a555a05094dee1dca2f68afea49cc3a50e8de6ea131ea521311f4d6fb054a146e8282f8e35ff2e6368c1a62e909716'" 581 | ] 582 | }, 583 | "execution_count": 26, 584 | "metadata": {}, 585 | "output_type": "execute_result" 586 | } 587 | ], 588 | "source": [ 589 | "hashlib.sha3_512(b'password').hexdigest()" 590 | ] 591 | }, 592 | { 593 | "cell_type": "markdown", 594 | "metadata": {}, 595 | "source": [ 596 | "saltがわかっていれば、受け取った文字列と暗号化した文字列を照合できる。" 597 | ] 598 | }, 599 | { 600 | "cell_type": "code", 601 | "execution_count": 27, 602 | "metadata": {}, 603 | "outputs": [ 604 | { 605 | "data": { 606 | "text/plain": [ 607 | "('f129003122900957bc0b3e324bbb9cf2970c35555de9ab7accc2be7374ce4ea7f22f877c0a9bb994552e4daff00214a9aa6026807439003a40118f9d694f2949',\n", 608 | " '74')" 609 | ] 610 | }, 611 | "execution_count": 27, 612 | "metadata": {}, 613 | "output_type": "execute_result" 614 | } 615 | ], 616 | "source": [ 617 | "hash_with_salt('password', '74')" 618 | ] 619 | }, 620 | { 621 | "cell_type": "markdown", 622 | "metadata": {}, 623 | "source": [ 624 | "## 10.2\n", 625 | "\n", 626 | "9章の練習問題で見付けた素数から2つを持ってこよう。これらをpとqとして、掛け合わせた数をnとする。これは公開鍵の一部となるが、大きな数の素因数分解には時間がかかるので、nからpとqを推測することは現代の計算機では難しい。" 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "execution_count": 28, 632 | "metadata": {}, 633 | "outputs": [], 634 | "source": [ 635 | "p = 13029484142325529160278057675859771418199793915415887667143262604822998091867456519520695610374545430551058468053152467257405794784202459324043791117505269283282033102238957121905893454537518737843627\n", 636 | "# こちらを使っても良い。p = 2**521 - 1\n", 637 | "q = 88301801407902026726739202138197965620005436413133983858109294983959431890532621456825116199688536186064925748393033767927900577431907264510871051225548778689126932900125905788253448444634254212249591\n", 638 | "# q = 2**607 -1\n", 639 | "n = p * q" 640 | ] 641 | }, 642 | { 643 | "cell_type": "markdown", 644 | "metadata": {}, 645 | "source": [ 646 | "r = 65537として、秘密鍵sを計算しよう。" 647 | ] 648 | }, 649 | { 650 | "cell_type": "code", 651 | "execution_count": 29, 652 | "metadata": {}, 653 | "outputs": [], 654 | "source": [ 655 | "k, s = find_s((p-1)*(q-1), r)" 656 | ] 657 | }, 658 | { 659 | "cell_type": "markdown", 660 | "metadata": {}, 661 | "source": [ 662 | "わかりやすいように名前を付けておこう。" 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": 30, 668 | "metadata": {}, 669 | "outputs": [], 670 | "source": [ 671 | "public_key = r\n", 672 | "private_key = s" 673 | ] 674 | }, 675 | { 676 | "cell_type": "markdown", 677 | "metadata": {}, 678 | "source": [ 679 | "あとはこの章で作った組み込み関数powの別名で暗号化と復号化ができるが、powをそのまま使っても構わない。また、折角なので文字列を暗号化してみよう。" 680 | ] 681 | }, 682 | { 683 | "cell_type": "code", 684 | "execution_count": 31, 685 | "metadata": {}, 686 | "outputs": [], 687 | "source": [ 688 | "top_secret = 'とんでもない秘密'" 689 | ] 690 | }, 691 | { 692 | "cell_type": "code", 693 | "execution_count": 32, 694 | "metadata": {}, 695 | "outputs": [ 696 | { 697 | "data": { 698 | "text/plain": [ 699 | "5578442738491896392425476467725517650170222265267332165510" 700 | ] 701 | }, 702 | "execution_count": 32, 703 | "metadata": {}, 704 | "output_type": "execute_result" 705 | } 706 | ], 707 | "source": [ 708 | "int(top_secret.encode('UTF-8').hex(), 16)" 709 | ] 710 | }, 711 | { 712 | "cell_type": "code", 713 | "execution_count": 33, 714 | "metadata": {}, 715 | "outputs": [ 716 | { 717 | "data": { 718 | "text/plain": [ 719 | "5578442738491896392425476467725517650170222265267332165510" 720 | ] 721 | }, 722 | "execution_count": 33, 723 | "metadata": {}, 724 | "output_type": "execute_result" 725 | } 726 | ], 727 | "source": [ 728 | "# 機密情報を整数に変換する\n", 729 | "top_secret_int = int(top_secret.encode('UTF-8').hex(), 16)\n", 730 | "top_secret_int" 731 | ] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": 34, 736 | "metadata": {}, 737 | "outputs": [ 738 | { 739 | "data": { 740 | "text/plain": [ 741 | "1052983848199668843728459043526597292807303529046302034682346063883032627421508627032086221876808727163856446581770021494470979583782194461247570709679818184711552945620729052807414218209978482442376731211199402646372412318947712356396703420765398057776596808275134648818061897098517310132292631308276045391792748749449415336216662502846487852602584423235134206457513329587038135791916661154965541380" 742 | ] 743 | }, 744 | "execution_count": 34, 745 | "metadata": {}, 746 | "output_type": "execute_result" 747 | } 748 | ], 749 | "source": [ 750 | "# 暗号化する。\n", 751 | "crypto_data = pow(top_secret_int, public_key, n)\n", 752 | "crypto_data" 753 | ] 754 | }, 755 | { 756 | "cell_type": "code", 757 | "execution_count": 35, 758 | "metadata": {}, 759 | "outputs": [], 760 | "source": [ 761 | "# 送られて来た暗号を復号化する\n", 762 | "received_data = pow(crypto_data, private_key, n)" 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": 36, 768 | "metadata": {}, 769 | "outputs": [ 770 | { 771 | "data": { 772 | "text/plain": [ 773 | "5578442738491896392425476467725517650170222265267332165510" 774 | ] 775 | }, 776 | "execution_count": 36, 777 | "metadata": {}, 778 | "output_type": "execute_result" 779 | } 780 | ], 781 | "source": [ 782 | "received_data" 783 | ] 784 | }, 785 | { 786 | "cell_type": "code", 787 | "execution_count": 37, 788 | "metadata": {}, 789 | "outputs": [ 790 | { 791 | "data": { 792 | "text/plain": [ 793 | "'とんでもない秘密'" 794 | ] 795 | }, 796 | "execution_count": 37, 797 | "metadata": {}, 798 | "output_type": "execute_result" 799 | } 800 | ], 801 | "source": [ 802 | "# 整数値をもとの文字列に戻す\n", 803 | "bytes.fromhex(format(received_data, 'x')).decode('UTF-8')" 804 | ] 805 | }, 806 | { 807 | "cell_type": "markdown", 808 | "metadata": {}, 809 | "source": [ 810 | "## 10.3\n", 811 | "\n", 812 | "httpsからはじまるURLをもったサイトであれば、なんでもよい。たとえば、https://www.python.org などブラウザで開き、アドレスバーの横(通常左側)にある南京錠のマークをクリックすると、HTTPS通信のために利用されている公開鍵暗号基盤の証明書の情報などが参照できる。ハッシュのアルゴリズムにSHA256が使われていることなどが確認できるだろう。公開鍵にはRSA暗号が使われていることが多いが、一部サイトでは楕円曲線暗号と呼ばれるアルゴリズムが使われていることもある。このように、現代社会はアルゴリズムに支えられている。また、この分野は日進月歩であるため、基礎的な知識を学習したあとも、常に新しい情報を獲得し理解し続ける姿勢が重要となる。" 813 | ] 814 | } 815 | ], 816 | "metadata": { 817 | "kernelspec": { 818 | "display_name": "Python 3", 819 | "language": "python", 820 | "name": "python3" 821 | }, 822 | "language_info": { 823 | "codemirror_mode": { 824 | "name": "ipython", 825 | "version": 3 826 | }, 827 | "file_extension": ".py", 828 | "mimetype": "text/x-python", 829 | "name": "python", 830 | "nbconvert_exporter": "python", 831 | "pygments_lexer": "ipython3", 832 | "version": "3.6.5" 833 | } 834 | }, 835 | "nbformat": 4, 836 | "nbformat_minor": 2 837 | } 838 | -------------------------------------------------------------------------------- /source/Chapter04.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "def merge_arrays(left, right=[]):\n", 10 | " res = []\n", 11 | " i, j = 0, 0\n", 12 | " n, m = len(left), len(right)\n", 13 | " # どちらかの配列を調べ尽くしたらそこで終了\n", 14 | " while i < n and j < m:\n", 15 | " if left[i] < right[j]:\n", 16 | " res.append(left[i])\n", 17 | " i += 1\n", 18 | " else:\n", 19 | " res.append(right[j])\n", 20 | " j += 1\n", 21 | " # 残りはそのまま後ろに連結する。\n", 22 | " return res + left[i:] + right[j:]" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "def step(array):\n", 32 | " res = []\n", 33 | " for i in range(0, len(array), 2):\n", 34 | " # 長さ2もしくは1の配列がスライスの結果として返る。\n", 35 | " res.append(merge_arrays(*array[i:i+2]))\n", 36 | " return res" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 3, 42 | "metadata": {}, 43 | "outputs": [ 44 | { 45 | "data": { 46 | "text/plain": [ 47 | "[[30, 38], [13, 92], [50, 61], [11, 19], [2, 8], [51, 70], [37, 97], [7]]" 48 | ] 49 | }, 50 | "execution_count": 3, 51 | "metadata": {}, 52 | "output_type": "execute_result" 53 | } 54 | ], 55 | "source": [ 56 | "import random\n", 57 | "random.seed(4)\n", 58 | "my_array = [random.randint(0, 100) for i in range(15)]\n", 59 | "my_array = [[v] for v in my_array]\n", 60 | "step1 = step(my_array)\n", 61 | "step1" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 4, 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "data": { 71 | "text/plain": [ 72 | "[[13, 30, 38, 92], [11, 19, 50, 61], [2, 8, 51, 70], [7, 37, 97]]" 73 | ] 74 | }, 75 | "execution_count": 4, 76 | "metadata": {}, 77 | "output_type": "execute_result" 78 | } 79 | ], 80 | "source": [ 81 | "step2 = step(step1)\n", 82 | "step2" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 5, 88 | "metadata": {}, 89 | "outputs": [ 90 | { 91 | "data": { 92 | "text/plain": [ 93 | "[[2, 7, 8, 11, 13, 19, 30, 37, 38, 50, 51, 61, 70, 92, 97]]" 94 | ] 95 | }, 96 | "execution_count": 5, 97 | "metadata": {}, 98 | "output_type": "execute_result" 99 | } 100 | ], 101 | "source": [ 102 | "step3 = step(step2)\n", 103 | "step4 = step(step3)\n", 104 | "step4" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 6, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "def merge_sort(array):\n", 114 | " # すべての数をリストに変換する\n", 115 | " res = [[v] for v in array]\n", 116 | " while len(res[0]) != len(array):\n", 117 | " res = step(res)\n", 118 | " # リストの中にリストが入ってしまうのでこれを取り出す\n", 119 | " return res[0]" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 7, 125 | "metadata": {}, 126 | "outputs": [ 127 | { 128 | "data": { 129 | "text/plain": [ 130 | "[28, 66, 68, 46, 35, 99, 22, 13, 33, 27, 3, 82, 33, 34, 24]" 131 | ] 132 | }, 133 | "execution_count": 7, 134 | "metadata": {}, 135 | "output_type": "execute_result" 136 | } 137 | ], 138 | "source": [ 139 | "my_array = [random.randint(0,100) for i in range(15)]\n", 140 | "my_array" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 8, 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "data": { 150 | "text/plain": [ 151 | "[3, 13, 22, 24, 27, 28, 33, 33, 34, 35, 46, 66, 68, 82, 99]" 152 | ] 153 | }, 154 | "execution_count": 8, 155 | "metadata": {}, 156 | "output_type": "execute_result" 157 | } 158 | ], 159 | "source": [ 160 | "merge_sort(my_array)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 9, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "def merge_sort(array):\n", 170 | " if len(array) <= 1:\n", 171 | " return array\n", 172 | " mid_idx = len(array) // 2\n", 173 | " left = array[:mid_idx]\n", 174 | " right = array[mid_idx:]\n", 175 | " return merge_arrays(merge_sort(left), merge_sort(right))" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 10, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "def quick_sort(array):\n", 185 | " # 空の配列はそのまま返す\n", 186 | " if not array:\n", 187 | " return array\n", 188 | " # 最後の要素をpivotにする。\n", 189 | " p = array[-1]\n", 190 | " left = []\n", 191 | " right = []\n", 192 | " pivots = []\n", 193 | " # pivotとの関係で要素を分割する\n", 194 | " for v in array:\n", 195 | " if v < p:\n", 196 | " left.append(v)\n", 197 | " elif v == p:\n", 198 | " pivots.append(v)\n", 199 | " else:\n", 200 | " right.append(v)\n", 201 | " # 左と右は再び関数を適用して返す。\n", 202 | " return quick_sort(left) + pivots + quick_sort(right)" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 11, 208 | "metadata": {}, 209 | "outputs": [ 210 | { 211 | "data": { 212 | "text/plain": [ 213 | "[21, 39, 37, 80, 93, 47, 11, 77, 43, 85, 49, 64, 31, 22, 31]" 214 | ] 215 | }, 216 | "execution_count": 11, 217 | "metadata": {}, 218 | "output_type": "execute_result" 219 | } 220 | ], 221 | "source": [ 222 | "my_array = [random.randint(0, 100) for i in range(15)]\n", 223 | "my_array" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 12, 229 | "metadata": {}, 230 | "outputs": [ 231 | { 232 | "data": { 233 | "text/plain": [ 234 | "[11, 21, 22, 31, 31, 37, 39, 43, 47, 49, 64, 77, 80, 85, 93]" 235 | ] 236 | }, 237 | "execution_count": 12, 238 | "metadata": {}, 239 | "output_type": "execute_result" 240 | } 241 | ], 242 | "source": [ 243 | "quick_sort(my_array)" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 13, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [ 252 | "sample_data = []\n", 253 | "for i in range(100):\n", 254 | " sample_data.append([random.randint(0, 5000) for i in range(2000)])" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": 14, 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "import time\n", 264 | "\n", 265 | "def performance_check(method, data, num=3):\n", 266 | " s = time.time()\n", 267 | " for i in range(num):\n", 268 | " for v in data: method(v)\n", 269 | " e = time.time()\n", 270 | " return e - s" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 15, 276 | "metadata": {}, 277 | "outputs": [ 278 | { 279 | "data": { 280 | "text/plain": [ 281 | "2.5252842903137207" 282 | ] 283 | }, 284 | "execution_count": 15, 285 | "metadata": {}, 286 | "output_type": "execute_result" 287 | } 288 | ], 289 | "source": [ 290 | "performance_check(merge_sort, sample_data)" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 16, 296 | "metadata": { 297 | "scrolled": true 298 | }, 299 | "outputs": [ 300 | { 301 | "data": { 302 | "text/plain": [ 303 | "1.3420770168304443" 304 | ] 305 | }, 306 | "execution_count": 16, 307 | "metadata": {}, 308 | "output_type": "execute_result" 309 | } 310 | ], 311 | "source": [ 312 | "performance_check(quick_sort, sample_data)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 17, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "sorted_data = []\n", 322 | "for i in range(100):\n", 323 | " sorted_data.append(sorted([random.randint(0, 5000) for i in range(2000)]))" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": 18, 329 | "metadata": {}, 330 | "outputs": [ 331 | { 332 | "data": { 333 | "text/plain": [ 334 | "1.789849042892456" 335 | ] 336 | }, 337 | "execution_count": 18, 338 | "metadata": {}, 339 | "output_type": "execute_result" 340 | } 341 | ], 342 | "source": [ 343 | "performance_check(merge_sort, sorted_data)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 19, 349 | "metadata": {}, 350 | "outputs": [ 351 | { 352 | "data": { 353 | "text/plain": [ 354 | "63.00237321853638" 355 | ] 356 | }, 357 | "execution_count": 19, 358 | "metadata": {}, 359 | "output_type": "execute_result" 360 | } 361 | ], 362 | "source": [ 363 | "performance_check(quick_sort, sorted_data)" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 20, 369 | "metadata": {}, 370 | "outputs": [], 371 | "source": [ 372 | "def quick_sort(array):\n", 373 | " if array == []:\n", 374 | " return array\n", 375 | " p = random.choice(array) # 変更点はここだけ。\n", 376 | " left = []\n", 377 | " right = []\n", 378 | " pivots = []\n", 379 | " for v in array:\n", 380 | " if v < p:\n", 381 | " left.append(v)\n", 382 | " elif v == p:\n", 383 | " pivots.append(v)\n", 384 | " else:\n", 385 | " right.append(v)\n", 386 | " # 左と右は再び関数を適用して返す。\n", 387 | " return quick_sort(left) + pivots + quick_sort(right)" 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": 21, 393 | "metadata": {}, 394 | "outputs": [ 395 | { 396 | "data": { 397 | "text/plain": [ 398 | "1.9804668426513672" 399 | ] 400 | }, 401 | "execution_count": 21, 402 | "metadata": {}, 403 | "output_type": "execute_result" 404 | } 405 | ], 406 | "source": [ 407 | "performance_check(quick_sort, sorted_data)" 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": null, 413 | "metadata": {}, 414 | "outputs": [], 415 | "source": [] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": null, 420 | "metadata": {}, 421 | "outputs": [], 422 | "source": [] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": null, 427 | "metadata": {}, 428 | "outputs": [], 429 | "source": [] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": null, 434 | "metadata": {}, 435 | "outputs": [], 436 | "source": [] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": null, 441 | "metadata": {}, 442 | "outputs": [], 443 | "source": [] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": null, 448 | "metadata": {}, 449 | "outputs": [], 450 | "source": [] 451 | }, 452 | { 453 | "cell_type": "markdown", 454 | "metadata": {}, 455 | "source": [ 456 | "# 練習問題解答" 457 | ] 458 | }, 459 | { 460 | "cell_type": "markdown", 461 | "metadata": {}, 462 | "source": [ 463 | "## 4.1 \n", 464 | "\n", 465 | "二分木では、高さが1つ増えると枝を2つ分けて伸ばすことができる。つまり、高さが$k$となる木には、$2^{k}$枚の葉を作ることができる。うまくイメージできない場合は、高さが2や3の時を図示してみるとよいだろう。" 466 | ] 467 | }, 468 | { 469 | "cell_type": "markdown", 470 | "metadata": {}, 471 | "source": [ 472 | "## 4.2\n", 473 | "\n", 474 | "この章で作ったコードのうち、ソートされた配列同士をマージする部分だけを変えればよい。" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": 23, 480 | "metadata": {}, 481 | "outputs": [], 482 | "source": [ 483 | "from heapq import merge\n", 484 | "\n", 485 | "def merge_sort(array):\n", 486 | " if len(array) <= 1:\n", 487 | " return array\n", 488 | " mid_idx = len(array) // 2\n", 489 | " left = array[:mid_idx]\n", 490 | " right = array[mid_idx:]\n", 491 | " return merge(merge_sort(left), merge_sort(right))" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "簡単なリストで実行するとわかるが、これまでのようにリストが戻り値にならない。" 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": 24, 504 | "metadata": {}, 505 | "outputs": [ 506 | { 507 | "data": { 508 | "text/plain": [ 509 | "" 510 | ] 511 | }, 512 | "execution_count": 24, 513 | "metadata": {}, 514 | "output_type": "execute_result" 515 | } 516 | ], 517 | "source": [ 518 | "merge_sort([2, 3, 1])" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "metadata": {}, 524 | "source": [ 525 | "組み込み関数listを使えばリストに変換できる。また、for文などで利用する場合は、このままinの後に書くことができる。" 526 | ] 527 | }, 528 | { 529 | "cell_type": "code", 530 | "execution_count": 25, 531 | "metadata": {}, 532 | "outputs": [ 533 | { 534 | "data": { 535 | "text/plain": [ 536 | "[1, 2, 3]" 537 | ] 538 | }, 539 | "execution_count": 25, 540 | "metadata": {}, 541 | "output_type": "execute_result" 542 | } 543 | ], 544 | "source": [ 545 | "list(merge_sort([2, 3, 1]))" 546 | ] 547 | }, 548 | { 549 | "cell_type": "code", 550 | "execution_count": 26, 551 | "metadata": {}, 552 | "outputs": [ 553 | { 554 | "name": "stdout", 555 | "output_type": "stream", 556 | "text": [ 557 | "1\n", 558 | "2\n", 559 | "3\n" 560 | ] 561 | } 562 | ], 563 | "source": [ 564 | "for v in merge_sort([2, 3, 1]):\n", 565 | " print(v)" 566 | ] 567 | }, 568 | { 569 | "cell_type": "markdown", 570 | "metadata": {}, 571 | "source": [ 572 | "ここで紹介したmerge関数と同じように、rangeやmap、reversedなどが戻り値としてリストそのものを返さない利点はメモリ効率にある。これらの関数の戻り値が巨大な場合は、当初からメモリを確保してしまうより、必要となったときに次々に値を返す方が余計なメモリを使わなく済むからだ。" 573 | ] 574 | }, 575 | { 576 | "cell_type": "markdown", 577 | "metadata": {}, 578 | "source": [ 579 | "## 4.3\n", 580 | "\n", 581 | "いろいろな方法を考えることができる。実直に確認するには、次のようなコードを考えることができるだろう。" 582 | ] 583 | }, 584 | { 585 | "cell_type": "code", 586 | "execution_count": 27, 587 | "metadata": {}, 588 | "outputs": [], 589 | "source": [ 590 | "def is_sorted(array):\n", 591 | " n = len(array)\n", 592 | " if n == 0:\n", 593 | " return True\n", 594 | " for i in range(1, n):\n", 595 | " if array[i-1] > array[i]:\n", 596 | " return False\n", 597 | " return True" 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": 28, 603 | "metadata": {}, 604 | "outputs": [ 605 | { 606 | "data": { 607 | "text/plain": [ 608 | "True" 609 | ] 610 | }, 611 | "execution_count": 28, 612 | "metadata": {}, 613 | "output_type": "execute_result" 614 | } 615 | ], 616 | "source": [ 617 | "is_sorted([1, 2, 3, 3, 4])" 618 | ] 619 | }, 620 | { 621 | "cell_type": "code", 622 | "execution_count": 29, 623 | "metadata": {}, 624 | "outputs": [ 625 | { 626 | "data": { 627 | "text/plain": [ 628 | "False" 629 | ] 630 | }, 631 | "execution_count": 29, 632 | "metadata": {}, 633 | "output_type": "execute_result" 634 | } 635 | ], 636 | "source": [ 637 | "is_sorted([1, 2, 5, 3, 4])" 638 | ] 639 | }, 640 | { 641 | "cell_type": "markdown", 642 | "metadata": {}, 643 | "source": [ 644 | "リスト内包表記を使ってコードを短くすることもできる。" 645 | ] 646 | }, 647 | { 648 | "cell_type": "code", 649 | "execution_count": 30, 650 | "metadata": {}, 651 | "outputs": [], 652 | "source": [ 653 | "def is_sorted(array):\n", 654 | " temp = [array[i] <= array[i+1] for i in range(len(array)-1)]\n", 655 | " return all(temp)" 656 | ] 657 | }, 658 | { 659 | "cell_type": "code", 660 | "execution_count": 31, 661 | "metadata": {}, 662 | "outputs": [ 663 | { 664 | "data": { 665 | "text/plain": [ 666 | "True" 667 | ] 668 | }, 669 | "execution_count": 31, 670 | "metadata": {}, 671 | "output_type": "execute_result" 672 | } 673 | ], 674 | "source": [ 675 | "is_sorted([1, 2, 3, 3, 4])" 676 | ] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "execution_count": 32, 681 | "metadata": {}, 682 | "outputs": [ 683 | { 684 | "data": { 685 | "text/plain": [ 686 | "False" 687 | ] 688 | }, 689 | "execution_count": 32, 690 | "metadata": {}, 691 | "output_type": "execute_result" 692 | } 693 | ], 694 | "source": [ 695 | "is_sorted([1, 2, 5, 3, 4])" 696 | ] 697 | }, 698 | { 699 | "cell_type": "markdown", 700 | "metadata": {}, 701 | "source": [ 702 | "組み込み関数sortedを使った大胆な方法もあり得る。" 703 | ] 704 | }, 705 | { 706 | "cell_type": "code", 707 | "execution_count": 33, 708 | "metadata": {}, 709 | "outputs": [], 710 | "source": [ 711 | "def is_sorted(array):\n", 712 | " return sorted(array) == array" 713 | ] 714 | }, 715 | { 716 | "cell_type": "code", 717 | "execution_count": 34, 718 | "metadata": {}, 719 | "outputs": [ 720 | { 721 | "data": { 722 | "text/plain": [ 723 | "True" 724 | ] 725 | }, 726 | "execution_count": 34, 727 | "metadata": {}, 728 | "output_type": "execute_result" 729 | } 730 | ], 731 | "source": [ 732 | "is_sorted([1, 2, 3, 3, 4])" 733 | ] 734 | }, 735 | { 736 | "cell_type": "code", 737 | "execution_count": 35, 738 | "metadata": {}, 739 | "outputs": [ 740 | { 741 | "data": { 742 | "text/plain": [ 743 | "False" 744 | ] 745 | }, 746 | "execution_count": 35, 747 | "metadata": {}, 748 | "output_type": "execute_result" 749 | } 750 | ], 751 | "source": [ 752 | "is_sorted([1, 2, 5, 3, 4])" 753 | ] 754 | }, 755 | { 756 | "cell_type": "markdown", 757 | "metadata": {}, 758 | "source": [ 759 | "## 4.4\n", 760 | "\n", 761 | "まず、rand_strを実装する。" 762 | ] 763 | }, 764 | { 765 | "cell_type": "code", 766 | "execution_count": 36, 767 | "metadata": {}, 768 | "outputs": [], 769 | "source": [ 770 | "import random\n", 771 | "\n", 772 | "def rand_str(n):\n", 773 | " alphabet = 'abcdefghijklmnopqrstuvwxyz'\n", 774 | " res = random.sample(alphabet, n)\n", 775 | " return ''.join(res)" 776 | ] 777 | }, 778 | { 779 | "cell_type": "markdown", 780 | "metadata": {}, 781 | "source": [ 782 | "1〜10までの長さで、このような文字列を20個作り、リストにする。" 783 | ] 784 | }, 785 | { 786 | "cell_type": "code", 787 | "execution_count": 37, 788 | "metadata": {}, 789 | "outputs": [], 790 | "source": [ 791 | "rand_str_list = [rand_str(random.randint(1, 10)) for i in range(20)]" 792 | ] 793 | }, 794 | { 795 | "cell_type": "code", 796 | "execution_count": 38, 797 | "metadata": {}, 798 | "outputs": [ 799 | { 800 | "data": { 801 | "text/plain": [ 802 | "['c',\n", 803 | " 'xbw',\n", 804 | " 'iucdw',\n", 805 | " 'vtr',\n", 806 | " 'wxhipt',\n", 807 | " 'mxby',\n", 808 | " 'gaqxc',\n", 809 | " 'eihdvtwrg',\n", 810 | " 'ygdw',\n", 811 | " 'lgb',\n", 812 | " 'vr',\n", 813 | " 'yk',\n", 814 | " 'xedmki',\n", 815 | " 'jzldanocyq',\n", 816 | " 'bk',\n", 817 | " 'u',\n", 818 | " 's',\n", 819 | " 'odfxpc',\n", 820 | " 'zhxstqblu',\n", 821 | " 'bzcjhkm']" 822 | ] 823 | }, 824 | "execution_count": 38, 825 | "metadata": {}, 826 | "output_type": "execute_result" 827 | } 828 | ], 829 | "source": [ 830 | "rand_str_list" 831 | ] 832 | }, 833 | { 834 | "cell_type": "markdown", 835 | "metadata": {}, 836 | "source": [ 837 | "普通にソートすると、辞書順にならぶ。" 838 | ] 839 | }, 840 | { 841 | "cell_type": "code", 842 | "execution_count": 39, 843 | "metadata": {}, 844 | "outputs": [ 845 | { 846 | "data": { 847 | "text/plain": [ 848 | "['bk',\n", 849 | " 'bzcjhkm',\n", 850 | " 'c',\n", 851 | " 'eihdvtwrg',\n", 852 | " 'gaqxc',\n", 853 | " 'iucdw',\n", 854 | " 'jzldanocyq',\n", 855 | " 'lgb',\n", 856 | " 'mxby',\n", 857 | " 'odfxpc',\n", 858 | " 's',\n", 859 | " 'u',\n", 860 | " 'vr',\n", 861 | " 'vtr',\n", 862 | " 'wxhipt',\n", 863 | " 'xbw',\n", 864 | " 'xedmki',\n", 865 | " 'ygdw',\n", 866 | " 'yk',\n", 867 | " 'zhxstqblu']" 868 | ] 869 | }, 870 | "execution_count": 39, 871 | "metadata": {}, 872 | "output_type": "execute_result" 873 | } 874 | ], 875 | "source": [ 876 | "sorted(rand_str_list)" 877 | ] 878 | }, 879 | { 880 | "cell_type": "markdown", 881 | "metadata": {}, 882 | "source": [ 883 | "次のようにすると文字列の長さでソートすることができる。" 884 | ] 885 | }, 886 | { 887 | "cell_type": "code", 888 | "execution_count": 40, 889 | "metadata": {}, 890 | "outputs": [ 891 | { 892 | "data": { 893 | "text/plain": [ 894 | "['c',\n", 895 | " 'u',\n", 896 | " 's',\n", 897 | " 'vr',\n", 898 | " 'yk',\n", 899 | " 'bk',\n", 900 | " 'xbw',\n", 901 | " 'vtr',\n", 902 | " 'lgb',\n", 903 | " 'mxby',\n", 904 | " 'ygdw',\n", 905 | " 'iucdw',\n", 906 | " 'gaqxc',\n", 907 | " 'wxhipt',\n", 908 | " 'xedmki',\n", 909 | " 'odfxpc',\n", 910 | " 'bzcjhkm',\n", 911 | " 'eihdvtwrg',\n", 912 | " 'zhxstqblu',\n", 913 | " 'jzldanocyq']" 914 | ] 915 | }, 916 | "execution_count": 40, 917 | "metadata": {}, 918 | "output_type": "execute_result" 919 | } 920 | ], 921 | "source": [ 922 | "sorted(rand_str_list, key=lambda x: len(x))" 923 | ] 924 | }, 925 | { 926 | "cell_type": "markdown", 927 | "metadata": {}, 928 | "source": [ 929 | "keyの引数に渡した関数は、ソートする前に各要素に対して実行される。ここでは、各要素の長さがlen関数によって計算されるので、短いものから長いものへと文字列が並び替えられる。" 930 | ] 931 | } 932 | ], 933 | "metadata": { 934 | "kernelspec": { 935 | "display_name": "Python 3", 936 | "language": "python", 937 | "name": "python3" 938 | }, 939 | "language_info": { 940 | "codemirror_mode": { 941 | "name": "ipython", 942 | "version": 3 943 | }, 944 | "file_extension": ".py", 945 | "mimetype": "text/x-python", 946 | "name": "python", 947 | "nbconvert_exporter": "python", 948 | "pygments_lexer": "ipython3", 949 | "version": "3.8.8" 950 | } 951 | }, 952 | "nbformat": 4, 953 | "nbformat_minor": 4 954 | } 955 | -------------------------------------------------------------------------------- /source/Chapter05.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/plain": [ 11 | "[79, 32, 94, 45, 88, 94, 83, 67, 3, 59, 99, 31, 83, 6, 20]" 12 | ] 13 | }, 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "output_type": "execute_result" 17 | } 18 | ], 19 | "source": [ 20 | "import random\n", 21 | "random.seed(5)\n", 22 | "my_array = [random.randint(0, 100) for i in range(15)]\n", 23 | "my_array" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "data": { 33 | "text/plain": [ 34 | "True" 35 | ] 36 | }, 37 | "execution_count": 2, 38 | "metadata": {}, 39 | "output_type": "execute_result" 40 | } 41 | ], 42 | "source": [ 43 | "32 in my_array" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 3, 49 | "metadata": {}, 50 | "outputs": [ 51 | { 52 | "data": { 53 | "text/plain": [ 54 | "False" 55 | ] 56 | }, 57 | "execution_count": 3, 58 | "metadata": {}, 59 | "output_type": "execute_result" 60 | } 61 | ], 62 | "source": [ 63 | "50 in my_array" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 4, 69 | "metadata": {}, 70 | "outputs": [ 71 | { 72 | "data": { 73 | "text/plain": [ 74 | "1" 75 | ] 76 | }, 77 | "execution_count": 4, 78 | "metadata": {}, 79 | "output_type": "execute_result" 80 | } 81 | ], 82 | "source": [ 83 | "my_array.index(32)" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 5, 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "ename": "ValueError", 93 | "evalue": "50 is not in list", 94 | "output_type": "error", 95 | "traceback": [ 96 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 97 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 98 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmy_array\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m50\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 99 | "\u001b[0;31mValueError\u001b[0m: 50 is not in list" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "my_array.index(50)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 6, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "def linear_search(array, target):\n", 114 | " for v in array:\n", 115 | " if target == v:\n", 116 | " return True\n", 117 | " return False" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 7, 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "data": { 127 | "text/plain": [ 128 | "True" 129 | ] 130 | }, 131 | "execution_count": 7, 132 | "metadata": {}, 133 | "output_type": "execute_result" 134 | } 135 | ], 136 | "source": [ 137 | "linear_search(my_array, 32)" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 8, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "False" 149 | ] 150 | }, 151 | "execution_count": 8, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "linear_search(my_array, 50)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 9, 163 | "metadata": {}, 164 | "outputs": [ 165 | { 166 | "data": { 167 | "text/plain": [ 168 | "5" 169 | ] 170 | }, 171 | "execution_count": 9, 172 | "metadata": {}, 173 | "output_type": "execute_result" 174 | } 175 | ], 176 | "source": [ 177 | "import bisect\n", 178 | "my_array.sort()\n", 179 | "bisect.bisect(my_array, 40)" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 10, 185 | "metadata": {}, 186 | "outputs": [ 187 | { 188 | "data": { 189 | "text/plain": [ 190 | "5" 191 | ] 192 | }, 193 | "execution_count": 10, 194 | "metadata": {}, 195 | "output_type": "execute_result" 196 | } 197 | ], 198 | "source": [ 199 | "bisect.bisect(my_array, 32)\n" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 11, 205 | "metadata": {}, 206 | "outputs": [ 207 | { 208 | "data": { 209 | "text/plain": [ 210 | "5" 211 | ] 212 | }, 213 | "execution_count": 11, 214 | "metadata": {}, 215 | "output_type": "execute_result" 216 | } 217 | ], 218 | "source": [ 219 | "bisect.bisect_right(my_array, 32)" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 12, 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "data": { 229 | "text/plain": [ 230 | "4" 231 | ] 232 | }, 233 | "execution_count": 12, 234 | "metadata": {}, 235 | "output_type": "execute_result" 236 | } 237 | ], 238 | "source": [ 239 | "bisect.bisect_left(my_array, 32)" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 13, 245 | "metadata": {}, 246 | "outputs": [ 247 | { 248 | "data": { 249 | "text/plain": [ 250 | "1" 251 | ] 252 | }, 253 | "execution_count": 13, 254 | "metadata": {}, 255 | "output_type": "execute_result" 256 | } 257 | ], 258 | "source": [ 259 | "test_array = [1, 2, 2, 2, 3, 4]\n", 260 | "bisect.bisect_left(test_array, 2)" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 14, 266 | "metadata": {}, 267 | "outputs": [ 268 | { 269 | "data": { 270 | "text/plain": [ 271 | "4" 272 | ] 273 | }, 274 | "execution_count": 14, 275 | "metadata": {}, 276 | "output_type": "execute_result" 277 | } 278 | ], 279 | "source": [ 280 | "bisect.bisect_right(test_array, 2)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "https://github.com/python/cpython/blob/master/Lib/bisect.py" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 15, 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [ 296 | "class Node:\n", 297 | "\n", 298 | " def __init__(self, value):\n", 299 | " self.value = value\n", 300 | " self.left = None\n", 301 | " self.right = None\n", 302 | "\n", 303 | " def __str__(self):\n", 304 | " # Nodeクラスのインスタンスを文字列表現にする\n", 305 | " left = f'[{self.left.value}]' if self.left else '[]'\n", 306 | " right = f'[{self.right.value}]' if self.right else '[]'\n", 307 | " return f'{left} <- {self.value} -> {right}'\n", 308 | "\n", 309 | "\n", 310 | "class BinarySearchTree:\n", 311 | "\n", 312 | " def __init__(self):\n", 313 | " self.nodes = []\n", 314 | "\n", 315 | " def add_node(self, value):\n", 316 | " node = Node(value)\n", 317 | " if self.nodes:\n", 318 | " # 自分の親ノードを探す\n", 319 | " parent, direction = self.find_parent(value)\n", 320 | " if direction == 'left':\n", 321 | " parent.left = node\n", 322 | " else:\n", 323 | " parent.right = node\n", 324 | " # この木のノードとして格納\n", 325 | " self.nodes.append(node)\n", 326 | " \n", 327 | " def find_parent(self, value):\n", 328 | " node = self.nodes[0]\n", 329 | " # nodeがNoneになるまでループを回す\n", 330 | " while node:\n", 331 | " p = node # 戻り値の候補(親かもしれない)としてとっておく。\n", 332 | " if p.value == value:\n", 333 | " raise ValueError('すでにある値と同じ値を格納することはできません。')\n", 334 | " if p.value > value:\n", 335 | " direction = 'left'\n", 336 | " node = p.left\n", 337 | " else:\n", 338 | " direction = 'right'\n", 339 | " node = p.right\n", 340 | " return p, direction" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 16, 346 | "metadata": {}, 347 | "outputs": [ 348 | { 349 | "name": "stdout", 350 | "output_type": "stream", 351 | "text": [ 352 | "[4] <- 10 -> [20]\n", 353 | "[12] <- 20 -> [30]\n", 354 | "[] <- 12 -> []\n", 355 | "[3] <- 4 -> [9]\n", 356 | "[] <- 3 -> []\n", 357 | "[] <- 9 -> []\n", 358 | "[] <- 30 -> []\n" 359 | ] 360 | } 361 | ], 362 | "source": [ 363 | "btree = BinarySearchTree()\n", 364 | "for v in [10, 20, 12, 4, 3, 9, 30]:\n", 365 | " btree.add_node(v)\n", 366 | "\n", 367 | "# 1つ1つのノードを文字列にする\n", 368 | "for node in btree.nodes:\n", 369 | " print(node)" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 17, 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "name": "stdout", 379 | "output_type": "stream", 380 | "text": [ 381 | "[] <- 3 -> [4]\n", 382 | "[] <- 4 -> [9]\n", 383 | "[] <- 9 -> [10]\n", 384 | "[] <- 10 -> [12]\n", 385 | "[] <- 12 -> [20]\n", 386 | "[] <- 20 -> [30]\n", 387 | "[] <- 30 -> []\n" 388 | ] 389 | } 390 | ], 391 | "source": [ 392 | "btree = BinarySearchTree()\n", 393 | "for v in sorted([10, 20, 12, 4, 3, 9, 30]):\n", 394 | " btree.add_node(v)\n", 395 | "\n", 396 | "for node in btree.nodes:\n", 397 | " print(node)" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 18, 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "import heapq\n", 407 | "\n", 408 | "def heap_sort(array):\n", 409 | " heap = []\n", 410 | " for v in array:\n", 411 | " heapq.heappush(heap, v)\n", 412 | " return [heapq.heappop(heap) for i in range(len(heap))]" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 19, 418 | "metadata": {}, 419 | "outputs": [ 420 | { 421 | "data": { 422 | "text/plain": [ 423 | "[14, 47, 60, 31, 48, 69, 13, 73, 31, 1, 93, 27, 52, 35, 23]" 424 | ] 425 | }, 426 | "execution_count": 19, 427 | "metadata": {}, 428 | "output_type": "execute_result" 429 | } 430 | ], 431 | "source": [ 432 | "my_array = [random.randint(0,100) for i in range(15)]\n", 433 | "my_array" 434 | ] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "execution_count": 20, 439 | "metadata": {}, 440 | "outputs": [ 441 | { 442 | "data": { 443 | "text/plain": [ 444 | "[1, 13, 14, 23, 27, 31, 31, 35, 47, 48, 52, 60, 69, 73, 93]" 445 | ] 446 | }, 447 | "execution_count": 20, 448 | "metadata": {}, 449 | "output_type": "execute_result" 450 | } 451 | ], 452 | "source": [ 453 | "heap_sort(my_array)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": 21, 459 | "metadata": {}, 460 | "outputs": [], 461 | "source": [ 462 | "# 空の辞書型をつくる\n", 463 | "my_dic = {}\n", 464 | "\n", 465 | "# キー:文字列「taro」、値:整数の10\n", 466 | "my_dic['taro'] = 10\n", 467 | "\n", 468 | "# キー:整数の2、値:リスト\n", 469 | "my_dic[2] = [1, 2, 3]" 470 | ] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": 22, 475 | "metadata": {}, 476 | "outputs": [ 477 | { 478 | "data": { 479 | "text/plain": [ 480 | "{'taro': 30, 2: [1, 2, 3, 25]}" 481 | ] 482 | }, 483 | "execution_count": 22, 484 | "metadata": {}, 485 | "output_type": "execute_result" 486 | } 487 | ], 488 | "source": [ 489 | "my_dic['taro'] = 30\n", 490 | "my_dic[2].append(25)\n", 491 | "my_dic" 492 | ] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": 23, 497 | "metadata": {}, 498 | "outputs": [ 499 | { 500 | "ename": "TypeError", 501 | "evalue": "unhashable type: 'list'", 502 | "output_type": "error", 503 | "traceback": [ 504 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 505 | "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", 506 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmy_dic\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'my list'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 507 | "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" 508 | ] 509 | } 510 | ], 511 | "source": [ 512 | "my_dic[[1, 2, 3]] = 'my list'" 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": 24, 518 | "metadata": {}, 519 | "outputs": [ 520 | { 521 | "data": { 522 | "text/plain": [ 523 | "4052057174978533987" 524 | ] 525 | }, 526 | "execution_count": 24, 527 | "metadata": {}, 528 | "output_type": "execute_result" 529 | } 530 | ], 531 | "source": [ 532 | "hash('taro')" 533 | ] 534 | }, 535 | { 536 | "cell_type": "code", 537 | "execution_count": 25, 538 | "metadata": { 539 | "scrolled": true 540 | }, 541 | "outputs": [ 542 | { 543 | "ename": "TypeError", 544 | "evalue": "unhashable type: 'list'", 545 | "output_type": "error", 546 | "traceback": [ 547 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 548 | "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", 549 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# エラーになる\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mhash\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 550 | "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" 551 | ] 552 | } 553 | ], 554 | "source": [ 555 | "# エラーになる\n", 556 | "hash([1, 2, 3])" 557 | ] 558 | }, 559 | { 560 | "cell_type": "code", 561 | "execution_count": 26, 562 | "metadata": {}, 563 | "outputs": [ 564 | { 565 | "data": { 566 | "text/plain": [ 567 | "True" 568 | ] 569 | }, 570 | "execution_count": 26, 571 | "metadata": {}, 572 | "output_type": "execute_result" 573 | } 574 | ], 575 | "source": [ 576 | "a = 'abc'\n", 577 | "hash(a) == hash('abc')" 578 | ] 579 | }, 580 | { 581 | "cell_type": "code", 582 | "execution_count": 27, 583 | "metadata": {}, 584 | "outputs": [ 585 | { 586 | "data": { 587 | "text/plain": [ 588 | "-230584300921356288" 589 | ] 590 | }, 591 | "execution_count": 27, 592 | "metadata": {}, 593 | "output_type": "execute_result" 594 | } 595 | ], 596 | "source": [ 597 | "hash(123) - hash(123.1)" 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": 28, 603 | "metadata": {}, 604 | "outputs": [ 605 | { 606 | "data": { 607 | "text/plain": [ 608 | "(3, 1)" 609 | ] 610 | }, 611 | "execution_count": 28, 612 | "metadata": {}, 613 | "output_type": "execute_result" 614 | } 615 | ], 616 | "source": [ 617 | "divmod(301, 100)" 618 | ] 619 | }, 620 | { 621 | "cell_type": "code", 622 | "execution_count": 29, 623 | "metadata": {}, 624 | "outputs": [ 625 | { 626 | "data": { 627 | "text/plain": [ 628 | "(-10, 1)" 629 | ] 630 | }, 631 | "execution_count": 29, 632 | "metadata": {}, 633 | "output_type": "execute_result" 634 | } 635 | ], 636 | "source": [ 637 | "divmod(-999, 100)" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": 30, 643 | "metadata": {}, 644 | "outputs": [], 645 | "source": [ 646 | "class HashTable:\n", 647 | "\n", 648 | " def __init__(self, table_size=100):\n", 649 | " # テーブルのサイズを引数で変更できるようにしてある\n", 650 | " self.data = [[] for i in range(table_size)]\n", 651 | " self.n = table_size\n", 652 | "\n", 653 | " def get_hash(self, v):\n", 654 | " # オブジェクトのハッシュ値を計算する\n", 655 | " return hash(v) % self.n\n", 656 | " \n", 657 | " def search(self, key):\n", 658 | " # keyを使って値を探す\n", 659 | " i = self.get_hash(key)\n", 660 | " for j, v in enumerate(self.data[i]):\n", 661 | " if v[0] == key:\n", 662 | " return (i, j)\n", 663 | " return (i, -1)\n", 664 | "\n", 665 | " def set(self, key, value):\n", 666 | " # データを格納するべき場所を探す\n", 667 | " i, j = self.search(key)\n", 668 | " if j != -1 :\n", 669 | " # すでにある値を書き換える\n", 670 | " self.data[i][j][1] = value\n", 671 | " else:\n", 672 | " # 新たなデータとして付け加える\n", 673 | " self.data[i].append([key, value])\n", 674 | " \n", 675 | " def get(self, key):\n", 676 | " i, j = self.search(key)\n", 677 | " if j != -1:\n", 678 | " return self.data[i][j][1]\n", 679 | " # キーが見付からない場合はエラーを返す\n", 680 | " raise KeyError(f'{key} was not found in this HashTable!')" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": 31, 686 | "metadata": {}, 687 | "outputs": [ 688 | { 689 | "data": { 690 | "text/plain": [ 691 | "10" 692 | ] 693 | }, 694 | "execution_count": 31, 695 | "metadata": {}, 696 | "output_type": "execute_result" 697 | } 698 | ], 699 | "source": [ 700 | "my_hash_table = HashTable()\n", 701 | "my_hash_table.set('taro', 10)\n", 702 | "my_hash_table.get('taro')" 703 | ] 704 | }, 705 | { 706 | "cell_type": "code", 707 | "execution_count": null, 708 | "metadata": {}, 709 | "outputs": [], 710 | "source": [] 711 | }, 712 | { 713 | "cell_type": "code", 714 | "execution_count": null, 715 | "metadata": {}, 716 | "outputs": [], 717 | "source": [] 718 | }, 719 | { 720 | "cell_type": "code", 721 | "execution_count": null, 722 | "metadata": {}, 723 | "outputs": [], 724 | "source": [] 725 | }, 726 | { 727 | "cell_type": "code", 728 | "execution_count": null, 729 | "metadata": {}, 730 | "outputs": [], 731 | "source": [] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": null, 736 | "metadata": {}, 737 | "outputs": [], 738 | "source": [] 739 | }, 740 | { 741 | "cell_type": "code", 742 | "execution_count": null, 743 | "metadata": {}, 744 | "outputs": [], 745 | "source": [] 746 | }, 747 | { 748 | "cell_type": "markdown", 749 | "metadata": {}, 750 | "source": [ 751 | "# 練習問題解答" 752 | ] 753 | }, 754 | { 755 | "cell_type": "markdown", 756 | "metadata": {}, 757 | "source": [ 758 | "## 5.1\n", 759 | "\n", 760 | "randomモジュールのsampleは、重複がないサンプリングをしてくれるので、ランダムに10個の整数を生成するには、10種類以上の整数を用意してこの関数を使えば良い。" 761 | ] 762 | }, 763 | { 764 | "cell_type": "code", 765 | "execution_count": 32, 766 | "metadata": {}, 767 | "outputs": [ 768 | { 769 | "name": "stdout", 770 | "output_type": "stream", 771 | "text": [ 772 | "[5] <- 12 -> [14]\n", 773 | "[2] <- 5 -> []\n", 774 | "[0] <- 2 -> [4]\n", 775 | "[3] <- 4 -> []\n", 776 | "[] <- 14 -> [17]\n", 777 | "[15] <- 17 -> [19]\n", 778 | "[] <- 15 -> []\n", 779 | "[] <- 0 -> []\n", 780 | "[] <- 19 -> []\n", 781 | "[] <- 3 -> []\n" 782 | ] 783 | } 784 | ], 785 | "source": [ 786 | "vals = random.sample(range(20), 10)\n", 787 | "\n", 788 | "btree = BinarySearchTree()\n", 789 | "for v in vals:\n", 790 | " btree.add_node(v)\n", 791 | " \n", 792 | "for node in btree.nodes:\n", 793 | " print(node)" 794 | ] 795 | }, 796 | { 797 | "cell_type": "markdown", 798 | "metadata": {}, 799 | "source": [ 800 | "この出力を頼りに、手書きで木構造を再現すると木への理解が深まるだろう。\n", 801 | "\n", 802 | "random.sampleを用いずに10個の異なる整数が格納されたリストを作るには、次のようなコードを書くことができる。" 803 | ] 804 | }, 805 | { 806 | "cell_type": "code", 807 | "execution_count": 33, 808 | "metadata": { 809 | "scrolled": false 810 | }, 811 | "outputs": [ 812 | { 813 | "data": { 814 | "text/plain": [ 815 | "[0, 4, 5, 6, 9, 10, 11, 12, 13, 17]" 816 | ] 817 | }, 818 | "execution_count": 33, 819 | "metadata": {}, 820 | "output_type": "execute_result" 821 | } 822 | ], 823 | "source": [ 824 | "res = set()\n", 825 | "\n", 826 | "while len(res) < 10:\n", 827 | " res.add(random.randint(0, 19))\n", 828 | "\n", 829 | "list(res)" 830 | ] 831 | }, 832 | { 833 | "cell_type": "markdown", 834 | "metadata": {}, 835 | "source": [ 836 | "## 5.2\n", 837 | "\n", 838 | "`5.1`のvalsをそのまま利用しよう。" 839 | ] 840 | }, 841 | { 842 | "cell_type": "code", 843 | "execution_count": 34, 844 | "metadata": {}, 845 | "outputs": [ 846 | { 847 | "data": { 848 | "text/plain": [ 849 | "[0, 3, 2, 4, 12, 17, 15, 5, 19, 14]" 850 | ] 851 | }, 852 | "execution_count": 34, 853 | "metadata": {}, 854 | "output_type": "execute_result" 855 | } 856 | ], 857 | "source": [ 858 | "import heapq\n", 859 | "\n", 860 | "heapq.heapify(vals)\n", 861 | "vals" 862 | ] 863 | }, 864 | { 865 | "cell_type": "markdown", 866 | "metadata": {}, 867 | "source": [ 868 | "ヒープは完全二分木になっていて、要素を順に並べていけば木構造を再現できる。手書きで再現してみると良いだろう。計算でも確認できる。$i=2$として計算してみよう。" 869 | ] 870 | }, 871 | { 872 | "cell_type": "code", 873 | "execution_count": 35, 874 | "metadata": {}, 875 | "outputs": [ 876 | { 877 | "data": { 878 | "text/plain": [ 879 | "True" 880 | ] 881 | }, 882 | "execution_count": 35, 883 | "metadata": {}, 884 | "output_type": "execute_result" 885 | } 886 | ], 887 | "source": [ 888 | "i = 2\n", 889 | "vals[i] <= vals[2*i + 1] and vals[i] <= vals[2*i + 2]" 890 | ] 891 | }, 892 | { 893 | "cell_type": "markdown", 894 | "metadata": {}, 895 | "source": [ 896 | "heapqモジュールに関するさらに詳しい内容は、公式ドキュメントが参考になる。\n", 897 | "\n", 898 | "https://docs.python.org/3/library/heapq.html" 899 | ] 900 | }, 901 | { 902 | "cell_type": "markdown", 903 | "metadata": {}, 904 | "source": [ 905 | "## 5.3\n", 906 | "\n", 907 | "公式ドキュメントやWebの情報が参考になるだろう。また、1と1.0を使った実行例からも、これらの違いを推測できるかもしれない。" 908 | ] 909 | }, 910 | { 911 | "cell_type": "code", 912 | "execution_count": 36, 913 | "metadata": {}, 914 | "outputs": [ 915 | { 916 | "data": { 917 | "text/plain": [ 918 | "4304853376" 919 | ] 920 | }, 921 | "execution_count": 36, 922 | "metadata": {}, 923 | "output_type": "execute_result" 924 | } 925 | ], 926 | "source": [ 927 | "id(1)" 928 | ] 929 | }, 930 | { 931 | "cell_type": "code", 932 | "execution_count": 37, 933 | "metadata": {}, 934 | "outputs": [ 935 | { 936 | "data": { 937 | "text/plain": [ 938 | "4548879632" 939 | ] 940 | }, 941 | "execution_count": 37, 942 | "metadata": {}, 943 | "output_type": "execute_result" 944 | } 945 | ], 946 | "source": [ 947 | "id(1.0)" 948 | ] 949 | }, 950 | { 951 | "cell_type": "code", 952 | "execution_count": 38, 953 | "metadata": {}, 954 | "outputs": [ 955 | { 956 | "data": { 957 | "text/plain": [ 958 | "1" 959 | ] 960 | }, 961 | "execution_count": 38, 962 | "metadata": {}, 963 | "output_type": "execute_result" 964 | } 965 | ], 966 | "source": [ 967 | "hash(1.0)" 968 | ] 969 | }, 970 | { 971 | "cell_type": "code", 972 | "execution_count": 39, 973 | "metadata": {}, 974 | "outputs": [ 975 | { 976 | "data": { 977 | "text/plain": [ 978 | "1" 979 | ] 980 | }, 981 | "execution_count": 39, 982 | "metadata": {}, 983 | "output_type": "execute_result" 984 | } 985 | ], 986 | "source": [ 987 | "hash(1)" 988 | ] 989 | }, 990 | { 991 | "cell_type": "markdown", 992 | "metadata": {}, 993 | "source": [ 994 | "hash関数は、数値として同じ値は同じハッシュ値を返す。これはhashがその計算の根拠を、オブジェクトとして等しいかどうかに置いているためだ。一方、idはメモリ空間のどこにそのオブジェクトがあるかを、計算の根拠にしている。1と1.0は整数と小数で本来別のオブジェクトなので、メモリ空間では別の場所に保持されている。このため、idの戻り値は全く異なったものとなる。" 995 | ] 996 | }, 997 | { 998 | "cell_type": "markdown", 999 | "metadata": {}, 1000 | "source": [ 1001 | "## 5.4\n", 1002 | "\n", 1003 | "サイズが100のハッシュテーブルを作り、ランダムに整数のキーを生成して、値として同じ文字列を格納する操作を300回繰り返す。キーは、0〜2000の整数から選ぶことにする。" 1004 | ] 1005 | }, 1006 | { 1007 | "cell_type": "code", 1008 | "execution_count": 40, 1009 | "metadata": {}, 1010 | "outputs": [], 1011 | "source": [ 1012 | "my_hash_table = HashTable(table_size=100)\n", 1013 | "\n", 1014 | "for i in range(300):\n", 1015 | " my_hash_table.set(random.randint(0, 2000), 'test')" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "markdown", 1020 | "metadata": {}, 1021 | "source": [ 1022 | "data属性を表示すれば、ハッシュテーブルの中身がわかる。" 1023 | ] 1024 | }, 1025 | { 1026 | "cell_type": "code", 1027 | "execution_count": 41, 1028 | "metadata": {}, 1029 | "outputs": [ 1030 | { 1031 | "data": { 1032 | "text/plain": [ 1033 | "[[[1200, 'test'], [300, 'test'], [400, 'test']],\n", 1034 | " [[1601, 'test'], [601, 'test'], [1201, 'test']],\n", 1035 | " [[402, 'test']],\n", 1036 | " [[503, 'test'],\n", 1037 | " [1903, 'test'],\n", 1038 | " [303, 'test'],\n", 1039 | " [603, 'test'],\n", 1040 | " [1203, 'test'],\n", 1041 | " [903, 'test']],\n", 1042 | " [[1404, 'test'], [304, 'test'], [704, 'test'], [1304, 'test']],\n", 1043 | " [[705, 'test'], [605, 'test'], [305, 'test']],\n", 1044 | " [[6, 'test'], [406, 'test']],\n", 1045 | " [[1207, 'test'], [1307, 'test'], [1007, 'test'], [707, 'test']],\n", 1046 | " [],\n", 1047 | " [[1609, 'test'], [1409, 'test']],\n", 1048 | " [[1010, 'test'], [1410, 'test'], [1710, 'test']],\n", 1049 | " [[1411, 'test'], [1511, 'test']],\n", 1050 | " [[512, 'test'], [712, 'test']],\n", 1051 | " [[1613, 'test'], [513, 'test'], [1713, 'test']],\n", 1052 | " [[114, 'test'], [1414, 'test']],\n", 1053 | " [],\n", 1054 | " [[116, 'test'],\n", 1055 | " [1116, 'test'],\n", 1056 | " [1516, 'test'],\n", 1057 | " [1216, 'test'],\n", 1058 | " [716, 'test'],\n", 1059 | " [1416, 'test']],\n", 1060 | " [[617, 'test'], [1717, 'test'], [1917, 'test']],\n", 1061 | " [[18, 'test'], [218, 'test']],\n", 1062 | " [[1919, 'test'], [1319, 'test'], [619, 'test']],\n", 1063 | " [[1220, 'test'], [120, 'test'], [1120, 'test'], [1920, 'test']],\n", 1064 | " [[221, 'test'], [921, 'test']],\n", 1065 | " [[1722, 'test'], [1022, 'test'], [1122, 'test'], [1422, 'test']],\n", 1066 | " [[1923, 'test'], [23, 'test']],\n", 1067 | " [[524, 'test'], [1124, 'test'], [424, 'test'], [1024, 'test']],\n", 1068 | " [[1325, 'test']],\n", 1069 | " [[626, 'test'],\n", 1070 | " [1426, 'test'],\n", 1071 | " [726, 'test'],\n", 1072 | " [426, 'test'],\n", 1073 | " [826, 'test'],\n", 1074 | " [1726, 'test']],\n", 1075 | " [[727, 'test'], [827, 'test'], [927, 'test'], [627, 'test'], [327, 'test']],\n", 1076 | " [[1828, 'test'], [928, 'test'], [1628, 'test'], [728, 'test']],\n", 1077 | " [[1929, 'test'], [1029, 'test'], [129, 'test']],\n", 1078 | " [[1730, 'test'],\n", 1079 | " [330, 'test'],\n", 1080 | " [1230, 'test'],\n", 1081 | " [230, 'test'],\n", 1082 | " [1330, 'test'],\n", 1083 | " [730, 'test']],\n", 1084 | " [[831, 'test'], [1331, 'test']],\n", 1085 | " [[732, 'test'], [1532, 'test'], [1632, 'test'], [1132, 'test']],\n", 1086 | " [[133, 'test'], [633, 'test'], [933, 'test'], [1333, 'test'], [1633, 'test']],\n", 1087 | " [[634, 'test']],\n", 1088 | " [[1235, 'test'], [135, 'test'], [1535, 'test'], [1635, 'test']],\n", 1089 | " [[1936, 'test'], [1836, 'test'], [836, 'test']],\n", 1090 | " [[37, 'test']],\n", 1091 | " [[1438, 'test']],\n", 1092 | " [[639, 'test'], [739, 'test']],\n", 1093 | " [[540, 'test'], [340, 'test']],\n", 1094 | " [[1641, 'test'], [741, 'test']],\n", 1095 | " [[742, 'test'], [1442, 'test'], [542, 'test']],\n", 1096 | " [[1443, 'test'], [243, 'test']],\n", 1097 | " [[1944, 'test'], [744, 'test'], [1744, 'test']],\n", 1098 | " [[1945, 'test'], [345, 'test'], [1745, 'test'], [1145, 'test']],\n", 1099 | " [[646, 'test'], [46, 'test'], [946, 'test'], [346, 'test'], [746, 'test']],\n", 1100 | " [[1547, 'test'], [947, 'test'], [847, 'test'], [1847, 'test']],\n", 1101 | " [[648, 'test'], [948, 'test'], [1748, 'test']],\n", 1102 | " [[1449, 'test'], [749, 'test'], [1049, 'test']],\n", 1103 | " [[1050, 'test'], [1250, 'test'], [650, 'test']],\n", 1104 | " [[351, 'test']],\n", 1105 | " [[752, 'test']],\n", 1106 | " [[353, 'test'], [853, 'test']],\n", 1107 | " [[1954, 'test'], [554, 'test']],\n", 1108 | " [[1155, 'test'], [1055, 'test'], [1555, 'test']],\n", 1109 | " [[1956, 'test'], [56, 'test']],\n", 1110 | " [[857, 'test'], [557, 'test'], [1057, 'test']],\n", 1111 | " [[1958, 'test']],\n", 1112 | " [[1359, 'test'], [1959, 'test']],\n", 1113 | " [[360, 'test'], [1760, 'test'], [160, 'test'], [560, 'test']],\n", 1114 | " [[861, 'test'], [561, 'test'], [1061, 'test'], [161, 'test']],\n", 1115 | " [[962, 'test']],\n", 1116 | " [[363, 'test'], [863, 'test'], [763, 'test'], [1263, 'test'], [1363, 'test']],\n", 1117 | " [[564, 'test']],\n", 1118 | " [[165, 'test'], [65, 'test'], [1965, 'test'], [365, 'test']],\n", 1119 | " [[1966, 'test'], [1466, 'test']],\n", 1120 | " [[967, 'test'], [67, 'test'], [267, 'test'], [367, 'test'], [667, 'test']],\n", 1121 | " [[368, 'test'], [1368, 'test']],\n", 1122 | " [[1069, 'test']],\n", 1123 | " [[770, 'test'], [370, 'test'], [1270, 'test'], [70, 'test']],\n", 1124 | " [[271, 'test'], [971, 'test']],\n", 1125 | " [[772, 'test'], [1672, 'test']],\n", 1126 | " [[1473, 'test'], [1373, 'test']],\n", 1127 | " [[1674, 'test'], [1074, 'test'], [1474, 'test']],\n", 1128 | " [[75, 'test'], [575, 'test'], [1375, 'test'], [1775, 'test'], [1475, 'test']],\n", 1129 | " [[1676, 'test'],\n", 1130 | " [1476, 'test'],\n", 1131 | " [1876, 'test'],\n", 1132 | " [1776, 'test'],\n", 1133 | " [1576, 'test']],\n", 1134 | " [[1277, 'test'], [1777, 'test'], [977, 'test']],\n", 1135 | " [[378, 'test'], [1678, 'test'], [1278, 'test']],\n", 1136 | " [[679, 'test'], [1379, 'test'], [579, 'test']],\n", 1137 | " [[1880, 'test']],\n", 1138 | " [[981, 'test'], [1781, 'test']],\n", 1139 | " [[182, 'test']],\n", 1140 | " [[1283, 'test'], [1683, 'test']],\n", 1141 | " [[984, 'test'],\n", 1142 | " [1184, 'test'],\n", 1143 | " [684, 'test'],\n", 1144 | " [1284, 'test'],\n", 1145 | " [1584, 'test'],\n", 1146 | " [1084, 'test']],\n", 1147 | " [[985, 'test']],\n", 1148 | " [[886, 'test'], [186, 'test'], [486, 'test'], [1586, 'test']],\n", 1149 | " [],\n", 1150 | " [[1388, 'test'], [1588, 'test'], [188, 'test'], [1888, 'test']],\n", 1151 | " [[889, 'test'], [289, 'test'], [1089, 'test'], [1189, 'test']],\n", 1152 | " [[990, 'test'], [290, 'test']],\n", 1153 | " [[1891, 'test'], [1291, 'test'], [591, 'test']],\n", 1154 | " [[692, 'test'], [1892, 'test'], [1092, 'test'], [1692, 'test']],\n", 1155 | " [[693, 'test']],\n", 1156 | " [[1494, 'test'], [994, 'test']],\n", 1157 | " [[95, 'test'], [1495, 'test']],\n", 1158 | " [[696, 'test'], [596, 'test'], [1696, 'test']],\n", 1159 | " [[1997, 'test'], [697, 'test']],\n", 1160 | " [[298, 'test'], [1498, 'test'], [1998, 'test'], [1598, 'test']],\n", 1161 | " [[1599, 'test'], [99, 'test'], [999, 'test'], [1999, 'test']]]" 1162 | ] 1163 | }, 1164 | "execution_count": 41, 1165 | "metadata": {}, 1166 | "output_type": "execute_result" 1167 | } 1168 | ], 1169 | "source": [ 1170 | "my_hash_table.data" 1171 | ] 1172 | }, 1173 | { 1174 | "cell_type": "markdown", 1175 | "metadata": {}, 1176 | "source": [ 1177 | "キーが違っても同じハッシュ値が生成される衝突が頻繁に起きている。それぞれの要素の長さを調べてみよう。このような時は、collectionsのCounterが便利だ。" 1178 | ] 1179 | }, 1180 | { 1181 | "cell_type": "code", 1182 | "execution_count": 42, 1183 | "metadata": {}, 1184 | "outputs": [ 1185 | { 1186 | "data": { 1187 | "text/plain": [ 1188 | "Counter({3: 22, 1: 15, 6: 5, 4: 20, 2: 28, 0: 3, 5: 7})" 1189 | ] 1190 | }, 1191 | "execution_count": 42, 1192 | "metadata": {}, 1193 | "output_type": "execute_result" 1194 | } 1195 | ], 1196 | "source": [ 1197 | "import collections\n", 1198 | "\n", 1199 | "collections.Counter([len(v) for v in my_hash_table.data])" 1200 | ] 1201 | }, 1202 | { 1203 | "cell_type": "markdown", 1204 | "metadata": {}, 1205 | "source": [ 1206 | "ハッシュテーブルではハッシュ値の衝突が起きると、その後に線形探索をしなければならない。できるだけハッシュ値が衝突しない方が良いが、これはテーブルのサイズと格納するデータの種類に依存する。" 1207 | ] 1208 | } 1209 | ], 1210 | "metadata": { 1211 | "kernelspec": { 1212 | "display_name": "Python 3", 1213 | "language": "python", 1214 | "name": "python3" 1215 | }, 1216 | "language_info": { 1217 | "codemirror_mode": { 1218 | "name": "ipython", 1219 | "version": 3 1220 | }, 1221 | "file_extension": ".py", 1222 | "mimetype": "text/x-python", 1223 | "name": "python", 1224 | "nbconvert_exporter": "python", 1225 | "pygments_lexer": "ipython3", 1226 | "version": "3.6.5" 1227 | } 1228 | }, 1229 | "nbformat": 4, 1230 | "nbformat_minor": 2 1231 | } 1232 | -------------------------------------------------------------------------------- /source/Chapter09.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "a\ta%3\n", 13 | "0\t0\n", 14 | "1\t1\n", 15 | "2\t2\n", 16 | "3\t0\n", 17 | "4\t1\n", 18 | "5\t2\n", 19 | "6\t0\n", 20 | "7\t1\n", 21 | "8\t2\n", 22 | "9\t0\n" 23 | ] 24 | } 25 | ], 26 | "source": [ 27 | "print('a\\ta%3')\n", 28 | "for a in range(10):\n", 29 | " print('{}\\t{}'.format(a, a % 3))" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 2, 35 | "metadata": {}, 36 | "outputs": [ 37 | { 38 | "name": "stdout", 39 | "output_type": "stream", 40 | "text": [ 41 | " a 1 2 3 4\n", 42 | " 0 0 0 0 0\n", 43 | " 1 1 1 1 1\n", 44 | " 2 2 1 2 1\n" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "m = 3\n", 50 | "# 3文字分のスペースを用意して右に詰めるテンプレートを用意\n", 51 | "tmpl = '{:>3}' * 5\n", 52 | "print(tmpl.format('a',1, 2, 3, 4))\n", 53 | "for a in range(m):\n", 54 | " print(tmpl.format(a, a % m, a**2 % m, a**3 % m, a**4 % m))" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 3, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | " a 1 2 3 4 5 6\n", 67 | " 0 0 0 0 0 0 0\n", 68 | " 1 1 1 1 1 1 1\n", 69 | " 2 2 4 3 1 2 4\n", 70 | " 3 3 4 2 1 3 4\n", 71 | " 4 4 1 4 1 4 1\n" 72 | ] 73 | } 74 | ], 75 | "source": [ 76 | "def make_table(m):\n", 77 | " # ノートブックの出力した時に揃えたい場合は、タブで区切るとよい。\n", 78 | " tmpl = '{:>5}' * (m+2)\n", 79 | " header = ['a']\n", 80 | " for i in range(1, m+2):\n", 81 | " header.append(i)\n", 82 | " print(tmpl.format(*header))\n", 83 | " for a in range(m):\n", 84 | " vals = [a]\n", 85 | " for i in range(1, m+2):\n", 86 | " vals.append(a**i % m)\n", 87 | " print(tmpl.format(*vals))\n", 88 | "\n", 89 | "make_table(5)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 4, 95 | "metadata": {}, 96 | "outputs": [ 97 | { 98 | "name": "stdout", 99 | "output_type": "stream", 100 | "text": [ 101 | " a 1 2 3 4 5 6 7 8\n", 102 | " 0 0 0 0 0 0 0 0 0\n", 103 | " 1 1 1 1 1 1 1 1 1\n", 104 | " 2 2 4 1 2 4 1 2 4\n", 105 | " 3 3 2 6 4 5 1 3 2\n", 106 | " 4 4 2 1 4 2 1 4 2\n", 107 | " 5 5 4 6 2 3 1 5 4\n", 108 | " 6 6 1 6 1 6 1 6 1\n" 109 | ] 110 | } 111 | ], 112 | "source": [ 113 | "make_table(7)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "name": "stdout", 123 | "output_type": "stream", 124 | "text": [ 125 | " a 1 2 3 4 5 6 7 8 9 10 11 12\n", 126 | " 0 0 0 0 0 0 0 0 0 0 0 0 0\n", 127 | " 1 1 1 1 1 1 1 1 1 1 1 1 1\n", 128 | " 2 2 4 8 5 10 9 7 3 6 1 2 4\n", 129 | " 3 3 9 5 4 1 3 9 5 4 1 3 9\n", 130 | " 4 4 5 9 3 1 4 5 9 3 1 4 5\n", 131 | " 5 5 3 4 9 1 5 3 4 9 1 5 3\n", 132 | " 6 6 3 7 9 10 5 8 4 2 1 6 3\n", 133 | " 7 7 5 2 3 10 4 6 9 8 1 7 5\n", 134 | " 8 8 9 6 4 10 3 2 5 7 1 8 9\n", 135 | " 9 9 4 3 5 1 9 4 3 5 1 9 4\n", 136 | " 10 10 1 10 1 10 1 10 1 10 1 10 1\n" 137 | ] 138 | } 139 | ], 140 | "source": [ 141 | "make_table(11)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 6, 147 | "metadata": {}, 148 | "outputs": [ 149 | { 150 | "data": { 151 | "text/plain": [ 152 | "941835055627266580503849904967699901590716766919018357565070304823361950367133399542900732284740322521777601905844547593169420188251086886593749196515935593390760187536652106011738575268871993180323759280672417628126657919746903728302044759198496254666440746221659888235849603836511703211069339843375687631356965152411001271698316229606922973356474572299363406732709208587020403633067083930974650069592877377059498048072785729976382608971319618733031236005822959589832916224338394073166035579246147249595264792543752636950473018848366902225627697454311879689812932526825509334542866382263556272489279429356882466403996520939487971338000344947977940225249438532912399615364903618081969750348075979653061340342530492238946980498318447336574248761427952274156947727336055614197092783781607144631121397751009634105863743169220955671203559926685306709828760131344538095242249457431847268039325167407424486632206091946848270839470226382467084978728910799861601117508687889374415646983323949653375444093256741699800036628107453844287185775336103449457085514679807049052115565563109534658262806875921471717780892675216900237693632501844379750725771178531796264471634281068107501293738468624648371711114511954596238782667602042645497822490876708528867530093230214291105358334603390938072544261335670342441093862818271051936448031750937060803069317961355195230345032244387664841067443166356088284819136509159841938961987903300799474677318621397979085676164779967337622441462091434668401611731314402454564938689396462656506389953672887608838510221612430002406358864838414555627383367893015913119404563425096163107984391371010754477067653870421898699550009175321767782787132870708270696069165665441369569285328374990233380187351864180143587858807812968677376" 153 | ] 154 | }, 155 | "execution_count": 6, 156 | "metadata": {}, 157 | "output_type": "execute_result" 158 | } 159 | ], 160 | "source": [ 161 | "2**5800" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 7, 167 | "metadata": {}, 168 | "outputs": [ 169 | { 170 | "data": { 171 | "text/plain": [ 172 | "1" 173 | ] 174 | }, 175 | "execution_count": 7, 176 | "metadata": {}, 177 | "output_type": "execute_result" 178 | } 179 | ], 180 | "source": [ 181 | "2**5800 % 5801" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "# これは現実的な時間で計算が終わらない。\n", 191 | "2**(2**100) % 5801" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 8, 197 | "metadata": {}, 198 | "outputs": [ 199 | { 200 | "data": { 201 | "text/plain": [ 202 | "(218560448315211965775293656, 576)" 203 | ] 204 | }, 205 | "execution_count": 8, 206 | "metadata": {}, 207 | "output_type": "execute_result" 208 | } 209 | ], 210 | "source": [ 211 | "divmod(2**100, 5800)" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 9, 217 | "metadata": {}, 218 | "outputs": [ 219 | { 220 | "data": { 221 | "text/plain": [ 222 | "2162" 223 | ] 224 | }, 225 | "execution_count": 9, 226 | "metadata": {}, 227 | "output_type": "execute_result" 228 | } 229 | ], 230 | "source": [ 231 | "pow(2, 2**100, 5801)" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 10, 237 | "metadata": {}, 238 | "outputs": [ 239 | { 240 | "data": { 241 | "text/plain": [ 242 | "'0b101011'" 243 | ] 244 | }, 245 | "execution_count": 10, 246 | "metadata": {}, 247 | "output_type": "execute_result" 248 | } 249 | ], 250 | "source": [ 251 | "bin(43)" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 11, 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [ 260 | "def a_k_mod_m(a, k, m):\n", 261 | " b = 1\n", 262 | " for i in reversed(bin(k)[2:]):\n", 263 | " if i == '1':\n", 264 | " b = a * b % m\n", 265 | " a = a**2 % m\n", 266 | " return b" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": 12, 272 | "metadata": {}, 273 | "outputs": [ 274 | { 275 | "data": { 276 | "text/plain": [ 277 | "97" 278 | ] 279 | }, 280 | "execution_count": 12, 281 | "metadata": {}, 282 | "output_type": "execute_result" 283 | } 284 | ], 285 | "source": [ 286 | "a_k_mod_m(7,43,123)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 13, 292 | "metadata": {}, 293 | "outputs": [ 294 | { 295 | "data": { 296 | "text/plain": [ 297 | "2162" 298 | ] 299 | }, 300 | "execution_count": 13, 301 | "metadata": {}, 302 | "output_type": "execute_result" 303 | } 304 | ], 305 | "source": [ 306 | "a_k_mod_m(2, 2**100, 5801)" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": 14, 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [ 315 | "import random\n", 316 | "\n", 317 | "def random_div(n, repeat=10):\n", 318 | " \"\"\"与えられた数が素数かどうかを判断する。\n", 319 | " ランダムに割り算をして約数を見つける。\n", 320 | " \"\"\"\n", 321 | " if n % 2 == 0:\n", 322 | " return 'composite'\n", 323 | " d_max = int(pow(n, 0.5))\n", 324 | " # 奇数列を作る\n", 325 | " odd_seq = range(3, d_max+1, 2)\n", 326 | " for cnt in range(repeat):\n", 327 | " d = random.choice(odd_seq)\n", 328 | " if n % d == 0:\n", 329 | " return 'composite'\n", 330 | " return 'probably prime'" 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "execution_count": 15, 336 | "metadata": {}, 337 | "outputs": [], 338 | "source": [ 339 | "# 8章のprime_test_large_nと同じ変更を加えたもの\n", 340 | "from decimal import Decimal, getcontext\n", 341 | "\n", 342 | "def random_div_large_n(n, repeat=10):\n", 343 | " \"\"\"与えられた数が素数かどうかを判断する。\n", 344 | " ランダムに割り算をして約数を見つける。\n", 345 | " \"\"\"\n", 346 | " if n % 2 == 0:\n", 347 | " return 'composite'\n", 348 | " getcontext().prec = len(str(n))\n", 349 | " d_max = int(Decimal(n).sqrt())\n", 350 | " # 奇数列を作る\n", 351 | " odd_seq = range(3, d_max+1, 2)\n", 352 | " for cnt in range(repeat):\n", 353 | " d = random.choice(odd_seq)\n", 354 | " if n % d == 0:\n", 355 | " return 'composite'\n", 356 | " return 'probably prime'" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 7, 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "# 3.8以降では、math.isqrtを利用できる。\n", 366 | "import math\n", 367 | "\n", 368 | "def random_div(n, repeat=10):\n", 369 | " \"\"\"与えられた数が素数かどうかを判断する。\n", 370 | " ランダムに割り算をして約数を見つける。\n", 371 | " \"\"\"\n", 372 | " if n % 2 == 0:\n", 373 | " return 'composite'\n", 374 | " d_max = math.isqrt(n)\n", 375 | " # 奇数列を作る\n", 376 | " odd_seq = range(3, d_max+1, 2)\n", 377 | " for cnt in range(repeat):\n", 378 | " d = random.choice(odd_seq)\n", 379 | " if n % d == 0:\n", 380 | " return 'composite'\n", 381 | " return 'probably prime'" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": 16, 387 | "metadata": {}, 388 | "outputs": [ 389 | { 390 | "data": { 391 | "text/plain": [ 392 | "'probably prime'" 393 | ] 394 | }, 395 | "execution_count": 16, 396 | "metadata": {}, 397 | "output_type": "execute_result" 398 | } 399 | ], 400 | "source": [ 401 | "random_div(71)" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 17, 407 | "metadata": {}, 408 | "outputs": [ 409 | { 410 | "name": "stdout", 411 | "output_type": "stream", 412 | "text": [ 413 | "composite\n", 414 | "composite\n", 415 | "composite\n", 416 | "composite\n", 417 | "composite\n", 418 | "composite\n", 419 | "probably prime\n", 420 | "composite\n", 421 | "composite\n", 422 | "composite\n", 423 | "composite\n", 424 | "probably prime\n", 425 | "composite\n", 426 | "composite\n", 427 | "probably prime\n", 428 | "composite\n", 429 | "composite\n", 430 | "probably prime\n", 431 | "composite\n", 432 | "composite\n" 433 | ] 434 | } 435 | ], 436 | "source": [ 437 | "for i in range(20):\n", 438 | " print(random_div(1105))" 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": 18, 444 | "metadata": {}, 445 | "outputs": [], 446 | "source": [ 447 | "def fermat_check(n):\n", 448 | " cnt = 0\n", 449 | " for a in range(2, n):\n", 450 | " if pow(a, n-1, n) != 1:\n", 451 | " cnt += 1\n", 452 | " return cnt" 453 | ] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": 19, 458 | "metadata": {}, 459 | "outputs": [ 460 | { 461 | "data": { 462 | "text/plain": [ 463 | "0" 464 | ] 465 | }, 466 | "execution_count": 19, 467 | "metadata": {}, 468 | "output_type": "execute_result" 469 | } 470 | ], 471 | "source": [ 472 | "fermat_check(71)" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": 20, 478 | "metadata": {}, 479 | "outputs": [ 480 | { 481 | "data": { 482 | "text/plain": [ 483 | "94" 484 | ] 485 | }, 486 | "execution_count": 20, 487 | "metadata": {}, 488 | "output_type": "execute_result" 489 | } 490 | ], 491 | "source": [ 492 | "fermat_check(99)" 493 | ] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "execution_count": 21, 498 | "metadata": {}, 499 | "outputs": [ 500 | { 501 | "data": { 502 | "text/plain": [ 503 | "336" 504 | ] 505 | }, 506 | "execution_count": 21, 507 | "metadata": {}, 508 | "output_type": "execute_result" 509 | } 510 | ], 511 | "source": [ 512 | "fermat_check(1105)" 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": 22, 518 | "metadata": {}, 519 | "outputs": [], 520 | "source": [ 521 | "def squeeze_q(n):\n", 522 | " \"\"\"奇数の引数nから1を引いた数を計算し、\n", 523 | " 2の成分を搾り取った奇数と2の個数を返す。\n", 524 | " \"\"\"\n", 525 | " k = 0\n", 526 | " x = n-1\n", 527 | " while x % 2 != 1:\n", 528 | " k += 1\n", 529 | " x //= 2\n", 530 | " return (n-1) // pow(2, k), k\n", 531 | "\n", 532 | "def rabin_miller(n, repeat=10):\n", 533 | " if n < 2:\n", 534 | " return 'give me more than 1.'\n", 535 | " if n == 2:\n", 536 | " return 'prime'\n", 537 | " if n % 2 == 0:\n", 538 | " return 'composite'\n", 539 | " q, k = squeeze_q(n)\n", 540 | " cnt = 0\n", 541 | " while cnt < repeat:\n", 542 | " a = random.randint(2, n-1)\n", 543 | " # 1つ目の条件\n", 544 | " cond_1 = pow(a, q, n) != 1\n", 545 | " temp = []\n", 546 | " for i in range(k):\n", 547 | " y = pow(2, i) * q\n", 548 | " c = pow(a, y, n) != n-1\n", 549 | " temp.append(c)\n", 550 | " # 2つ目の条件\n", 551 | " cond_2 = all(temp)\n", 552 | " if cond_1 and cond_2:\n", 553 | " return 'composite'\n", 554 | " cnt += 1\n", 555 | " return 'probably prime'" 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": 23, 561 | "metadata": {}, 562 | "outputs": [ 563 | { 564 | "data": { 565 | "text/plain": [ 566 | "'probably prime'" 567 | ] 568 | }, 569 | "execution_count": 23, 570 | "metadata": {}, 571 | "output_type": "execute_result" 572 | } 573 | ], 574 | "source": [ 575 | "rabin_miller(71)" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 24, 581 | "metadata": {}, 582 | "outputs": [ 583 | { 584 | "data": { 585 | "text/plain": [ 586 | "'composite'" 587 | ] 588 | }, 589 | "execution_count": 24, 590 | "metadata": {}, 591 | "output_type": "execute_result" 592 | } 593 | ], 594 | "source": [ 595 | "rabin_miller(1105)" 596 | ] 597 | }, 598 | { 599 | "cell_type": "code", 600 | "execution_count": 25, 601 | "metadata": {}, 602 | "outputs": [ 603 | { 604 | "name": "stdout", 605 | "output_type": "stream", 606 | "text": [ 607 | "[2] / 3\n", 608 | "[3] / 7\n", 609 | "[5] / 31\n", 610 | "[7] / 127\n", 611 | "[13] / 8191\n", 612 | "[17] / 131071\n", 613 | "[19] / 524287\n", 614 | "[31] / 2147483647\n", 615 | "[61] / 2305843009213693951\n", 616 | "[89] / 618970019642690137449562111\n", 617 | "[107] / 162259276829213363391578010288127\n", 618 | "[127] / 170141183460469231731687303715884105727\n", 619 | "[521] / 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151\n", 620 | "[607] / 531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219031728127\n" 621 | ] 622 | } 623 | ], 624 | "source": [ 625 | "def M(n):\n", 626 | " # Mersenne数を返す\n", 627 | " return pow(2, n)-1\n", 628 | "\n", 629 | "for n in range(2, 1000):\n", 630 | " if rabin_miller(M(n)) == 'probably prime':\n", 631 | " print('[{}] / {}'.format(n, M(n)))" 632 | ] 633 | }, 634 | { 635 | "cell_type": "code", 636 | "execution_count": null, 637 | "metadata": {}, 638 | "outputs": [], 639 | "source": [] 640 | }, 641 | { 642 | "cell_type": "code", 643 | "execution_count": null, 644 | "metadata": {}, 645 | "outputs": [], 646 | "source": [] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": null, 651 | "metadata": {}, 652 | "outputs": [], 653 | "source": [] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": null, 658 | "metadata": {}, 659 | "outputs": [], 660 | "source": [] 661 | }, 662 | { 663 | "cell_type": "code", 664 | "execution_count": null, 665 | "metadata": {}, 666 | "outputs": [], 667 | "source": [] 668 | }, 669 | { 670 | "cell_type": "code", 671 | "execution_count": null, 672 | "metadata": {}, 673 | "outputs": [], 674 | "source": [] 675 | }, 676 | { 677 | "cell_type": "markdown", 678 | "metadata": {}, 679 | "source": [ 680 | "# 練習問題解答" 681 | ] 682 | }, 683 | { 684 | "cell_type": "markdown", 685 | "metadata": {}, 686 | "source": [ 687 | "## 9.1\n", 688 | "\n", 689 | "互いに素は英語で、mutually primeの他、coprimeとも言うので、関数の名前を、count_coprimeとしよう。実直にやるなら簡単に実装できる。" 690 | ] 691 | }, 692 | { 693 | "cell_type": "code", 694 | "execution_count": 26, 695 | "metadata": {}, 696 | "outputs": [], 697 | "source": [ 698 | "import math\n", 699 | "\n", 700 | "def count_coprime(m):\n", 701 | " # 1とは互いに素なので、結果をひとまず1で初期化\n", 702 | " res = 1\n", 703 | " # mとは互いに素ではない。\n", 704 | " for i in range(2, m):\n", 705 | " if math.gcd(m, i) == 1: res += 1\n", 706 | " return res " 707 | ] 708 | }, 709 | { 710 | "cell_type": "markdown", 711 | "metadata": {}, 712 | "source": [ 713 | "確かめてみよう。$m=6$とすると、6以下で6と互いに素な数は、5と1の2つしかない。" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "execution_count": 27, 719 | "metadata": {}, 720 | "outputs": [ 721 | { 722 | "data": { 723 | "text/plain": [ 724 | "2" 725 | ] 726 | }, 727 | "execution_count": 27, 728 | "metadata": {}, 729 | "output_type": "execute_result" 730 | } 731 | ], 732 | "source": [ 733 | "count_coprime(6)" 734 | ] 735 | }, 736 | { 737 | "cell_type": "markdown", 738 | "metadata": {}, 739 | "source": [ 740 | "$m$が素数のときは、1とその数自身以外の約数がない。したがって、互いに素な数は$m-1$個となる。" 741 | ] 742 | }, 743 | { 744 | "cell_type": "code", 745 | "execution_count": 28, 746 | "metadata": {}, 747 | "outputs": [ 748 | { 749 | "data": { 750 | "text/plain": [ 751 | "22" 752 | ] 753 | }, 754 | "execution_count": 28, 755 | "metadata": {}, 756 | "output_type": "execute_result" 757 | } 758 | ], 759 | "source": [ 760 | "count_coprime(23)" 761 | ] 762 | }, 763 | { 764 | "cell_type": "markdown", 765 | "metadata": {}, 766 | "source": [ 767 | "count_coprimeで計算される値は、オイラーのファイ関数と呼ばれ$\\varphi(m)$と表現されることが多い。この応用例については10章で少し触れる。" 768 | ] 769 | }, 770 | { 771 | "cell_type": "markdown", 772 | "metadata": {}, 773 | "source": [ 774 | "## 9.2\n", 775 | "\n", 776 | "関数の名前は、is_carmichaelとしよう。1から順に引数の1つ手前まで、互いに素である場合に、フェルマーの小定理が成り立つかどうかを確認していく。" 777 | ] 778 | }, 779 | { 780 | "cell_type": "code", 781 | "execution_count": 29, 782 | "metadata": {}, 783 | "outputs": [], 784 | "source": [ 785 | "import math\n", 786 | "\n", 787 | "def is_carmichael(n):\n", 788 | " for a in range(1, n):\n", 789 | " if math.gcd(n, a) == 1 and pow(a, n-1, n) != 1:\n", 790 | " return False\n", 791 | " return True" 792 | ] 793 | }, 794 | { 795 | "cell_type": "markdown", 796 | "metadata": {}, 797 | "source": [ 798 | "確認してみよう。" 799 | ] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "execution_count": 30, 804 | "metadata": {}, 805 | "outputs": [ 806 | { 807 | "data": { 808 | "text/plain": [ 809 | "True" 810 | ] 811 | }, 812 | "execution_count": 30, 813 | "metadata": {}, 814 | "output_type": "execute_result" 815 | } 816 | ], 817 | "source": [ 818 | "is_carmichael(1105)" 819 | ] 820 | }, 821 | { 822 | "cell_type": "markdown", 823 | "metadata": {}, 824 | "source": [ 825 | "この他にも、2465や8911がカーマイケル数であることが知られている。" 826 | ] 827 | }, 828 | { 829 | "cell_type": "code", 830 | "execution_count": 31, 831 | "metadata": {}, 832 | "outputs": [ 833 | { 834 | "data": { 835 | "text/plain": [ 836 | "True" 837 | ] 838 | }, 839 | "execution_count": 31, 840 | "metadata": {}, 841 | "output_type": "execute_result" 842 | } 843 | ], 844 | "source": [ 845 | "is_carmichael(2465) and is_carmichael(8911)" 846 | ] 847 | }, 848 | { 849 | "cell_type": "markdown", 850 | "metadata": {}, 851 | "source": [ 852 | "この章で見たように、互いに素な数に限定しなければ、フェルマーの小定理が成り立たないときもある。本章ではこれを、合成数の証人の根拠ととしていた。しかし、大きなカーマイケル数ではfermar_checkで確認できる証人もかなり少なくなる。たとえば、5049001というカーマイケル数の証人の数を計算してみよう。" 853 | ] 854 | }, 855 | { 856 | "cell_type": "code", 857 | "execution_count": 32, 858 | "metadata": {}, 859 | "outputs": [ 860 | { 861 | "data": { 862 | "text/plain": [ 863 | "189000" 864 | ] 865 | }, 866 | "execution_count": 32, 867 | "metadata": {}, 868 | "output_type": "execute_result" 869 | } 870 | ], 871 | "source": [ 872 | "# すこし計算に時間がかかる\n", 873 | "fermat_check(5049001)" 874 | ] 875 | }, 876 | { 877 | "cell_type": "markdown", 878 | "metadata": {}, 879 | "source": [ 880 | "これはかなり少ない。全体の4%弱だ。" 881 | ] 882 | }, 883 | { 884 | "cell_type": "code", 885 | "execution_count": 33, 886 | "metadata": {}, 887 | "outputs": [ 888 | { 889 | "data": { 890 | "text/plain": [ 891 | "0.037433162494189445" 892 | ] 893 | }, 894 | "execution_count": 33, 895 | "metadata": {}, 896 | "output_type": "execute_result" 897 | } 898 | ], 899 | "source": [ 900 | "p = 189000/(5049001-2)\n", 901 | "p" 902 | ] 903 | }, 904 | { 905 | "cell_type": "code", 906 | "execution_count": 34, 907 | "metadata": {}, 908 | "outputs": [ 909 | { 910 | "data": { 911 | "text/plain": [ 912 | "0.6828242749240893" 913 | ] 914 | }, 915 | "execution_count": 34, 916 | "metadata": {}, 917 | "output_type": "execute_result" 918 | } 919 | ], 920 | "source": [ 921 | "pow((1-p), 10)" 922 | ] 923 | }, 924 | { 925 | "cell_type": "markdown", 926 | "metadata": {}, 927 | "source": [ 928 | "また10回の試行では、連続でニセの証人をつかまされる確率が約7割もある。ラビン・ミラー法のすごさとありがたさがわかる。\n", 929 | "\n", 930 | "前問のcount_coprimeと本問のis_carmichaelの実装はかなり単純なので、引数が大きくなると計算量が増す。これらについても数論の成果を利用した効率的なアルゴリズムが知られている。興味のある読者は調べてみるとよいだろう。" 931 | ] 932 | }, 933 | { 934 | "cell_type": "markdown", 935 | "metadata": {}, 936 | "source": [ 937 | "## 9.3\n", 938 | "\n", 939 | "Python3では、/演算子は小数を返し、//演算子は整数を返す。squeeze_qで行われている除算は割り切れることが分かっており、小数を返す必要はない。小数型と整数型はコンピュータ内部での表現方法が違うため、小数になった時点で、整数だけを使った計算とは結果が異なる可能性がある。また、小数は扱える数の範囲が仕様で決まっているため、この範囲に収まらない大きさの整数で行われている計算はそれ以上続行できない。\n", 940 | "\n", 941 | "例をみてみよう。大きなメルセンヌ素数に2を掛けて、2で割る計算を考えてみる。" 942 | ] 943 | }, 944 | { 945 | "cell_type": "code", 946 | "execution_count": 35, 947 | "metadata": {}, 948 | "outputs": [ 949 | { 950 | "data": { 951 | "text/plain": [ 952 | "531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219031728127" 953 | ] 954 | }, 955 | "execution_count": 35, 956 | "metadata": {}, 957 | "output_type": "execute_result" 958 | } 959 | ], 960 | "source": [ 961 | "n = 607\n", 962 | "M(n)" 963 | ] 964 | }, 965 | { 966 | "cell_type": "code", 967 | "execution_count": 36, 968 | "metadata": {}, 969 | "outputs": [ 970 | { 971 | "data": { 972 | "text/plain": [ 973 | "531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219031728127" 974 | ] 975 | }, 976 | "execution_count": 36, 977 | "metadata": {}, 978 | "output_type": "execute_result" 979 | } 980 | ], 981 | "source": [ 982 | "M(n) * 2 // 2" 983 | ] 984 | }, 985 | { 986 | "cell_type": "code", 987 | "execution_count": 37, 988 | "metadata": {}, 989 | "outputs": [ 990 | { 991 | "data": { 992 | "text/plain": [ 993 | "531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219031728128" 994 | ] 995 | }, 996 | "execution_count": 37, 997 | "metadata": {}, 998 | "output_type": "execute_result" 999 | } 1000 | ], 1001 | "source": [ 1002 | "int(M(n) * 2 / 2)" 1003 | ] 1004 | }, 1005 | { 1006 | "cell_type": "markdown", 1007 | "metadata": {}, 1008 | "source": [ 1009 | "`/`を使ったため小数になり、結果が違っているのがわかるだろう。" 1010 | ] 1011 | }, 1012 | { 1013 | "cell_type": "code", 1014 | "execution_count": 38, 1015 | "metadata": {}, 1016 | "outputs": [ 1017 | { 1018 | "data": { 1019 | "text/plain": [ 1020 | "190797007524439073807468042969529173669356994749940177394741882673528979787005053706368049835514900244303495954950709725762186311224148828811920216904542206960744666169364221195289538436845390250168663932838805192055137154390912666527533007309292687539092257043362517857366624699975402375462954490293259233303137330643531556539739921926201438606439020075174723029056838272505051571967594608350063404495977660656269020823960825567012344189908927956646011998057988548630107637380993519826582389781888135705408653045219655801758081251164080554609057468028203308718724654081055323215860189611391296030471108443146745671967766308925858547271507311563765171008318248647110097614890313562856541784154881743146033909602737947385055355960331855614540900081456378659068370317267696980001187750995491090350108417050917991562167972281070161305972518044872048331306383715094854938415738549894606070722584737978176686422134354526989443028353644037187375385397838259511833166416134323695660367676897722287918773420968982326089026150031515424165462111337527431154890666327374921446276833564519776797633875503548665093914556482031482248883127023777039667707976559857333357013727342079099064400455741830654320379350833236245819348824064783585692924881021978332974949906122664421376034687815350484991" 1021 | ] 1022 | }, 1023 | "execution_count": 38, 1024 | "metadata": {}, 1025 | "output_type": "execute_result" 1026 | } 1027 | ], 1028 | "source": [ 1029 | "n = 4253\n", 1030 | "M(n)" 1031 | ] 1032 | }, 1033 | { 1034 | "cell_type": "code", 1035 | "execution_count": 39, 1036 | "metadata": { 1037 | "scrolled": true 1038 | }, 1039 | "outputs": [ 1040 | { 1041 | "ename": "OverflowError", 1042 | "evalue": "integer division result too large for a float", 1043 | "output_type": "error", 1044 | "traceback": [ 1045 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 1046 | "\u001b[0;31mOverflowError\u001b[0m Traceback (most recent call last)", 1047 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mM\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 1048 | "\u001b[0;31mOverflowError\u001b[0m: integer division result too large for a float" 1049 | ] 1050 | } 1051 | ], 1052 | "source": [ 1053 | "M(n) * 2 / 2" 1054 | ] 1055 | }, 1056 | { 1057 | "cell_type": "markdown", 1058 | "metadata": {}, 1059 | "source": [ 1060 | "浮動小数点数で扱えない範囲になってしまいエラーとなる。" 1061 | ] 1062 | }, 1063 | { 1064 | "cell_type": "markdown", 1065 | "metadata": {}, 1066 | "source": [ 1067 | "また、2で割るという計算は、ビットを1つ右にシフトするという意味と同じになる。>>という演算子でビットをシフトできる。" 1068 | ] 1069 | }, 1070 | { 1071 | "cell_type": "code", 1072 | "execution_count": 40, 1073 | "metadata": {}, 1074 | "outputs": [ 1075 | { 1076 | "data": { 1077 | "text/plain": [ 1078 | "2" 1079 | ] 1080 | }, 1081 | "execution_count": 40, 1082 | "metadata": {}, 1083 | "output_type": "execute_result" 1084 | } 1085 | ], 1086 | "source": [ 1087 | "4 >> 1 # 4 // 2と同じ" 1088 | ] 1089 | }, 1090 | { 1091 | "cell_type": "markdown", 1092 | "metadata": {}, 1093 | "source": [ 1094 | "さらに、2のk乗で割るという計算は、kビット右にシフトすることを意味するので、次のように書くことができる。" 1095 | ] 1096 | }, 1097 | { 1098 | "cell_type": "code", 1099 | "execution_count": 41, 1100 | "metadata": {}, 1101 | "outputs": [ 1102 | { 1103 | "data": { 1104 | "text/plain": [ 1105 | "4" 1106 | ] 1107 | }, 1108 | "execution_count": 41, 1109 | "metadata": {}, 1110 | "output_type": "execute_result" 1111 | } 1112 | ], 1113 | "source": [ 1114 | "16 >> 2 # 16 // 4と同じ" 1115 | ] 1116 | }, 1117 | { 1118 | "cell_type": "markdown", 1119 | "metadata": {}, 1120 | "source": [ 1121 | "関数squeeze_qの中の//は、>>演算子と置き換えることができる。" 1122 | ] 1123 | }, 1124 | { 1125 | "cell_type": "markdown", 1126 | "metadata": {}, 1127 | "source": [ 1128 | "## 9.4\n", 1129 | "\n", 1130 | "素数が10個見付かるまでwhileループを回せばよい。回数をカウントしておき、最後に出力するようにしておこう。" 1131 | ] 1132 | }, 1133 | { 1134 | "cell_type": "code", 1135 | "execution_count": 42, 1136 | "metadata": {}, 1137 | "outputs": [], 1138 | "source": [ 1139 | "cnt = 0\n", 1140 | "# 同じ素数を掴まないようにするため\n", 1141 | "prime_set = set()\n", 1142 | "\n", 1143 | "while len(prime_set) < 10:\n", 1144 | " n = random.randint(10**199, 10**200 - 1)\n", 1145 | " if rabin_miller(n) == 'probably prime':\n", 1146 | " prime_set.add(n)\n", 1147 | " cnt += 1" 1148 | ] 1149 | }, 1150 | { 1151 | "cell_type": "code", 1152 | "execution_count": 43, 1153 | "metadata": {}, 1154 | "outputs": [ 1155 | { 1156 | "name": "stdout", 1157 | "output_type": "stream", 1158 | "text": [ 1159 | "2938回の計算で、以下の10個を見つけた。\n", 1160 | "10721266183262137436686627264280929985573962254622582339385699456000413523287983945569478953984751433704464083992812931131945307485358646963998768622724200943831494608438080494064409149824346596460941\n", 1161 | "14439678293455394474289971635296971170122064651664666205075702218120152322187255753401134513223026711374816338671156413998160375962360250463376611558994078901628172076473913815893167041023762176863587\n", 1162 | "17263042046130646722572950493701124052959821679803447185372340605590659533757696498853637533966761540083217186425253856772535706431744517639195040204984111044282585478725500969180252489566605458459933\n", 1163 | "20705731340145562463279471490708995615328782290158074493959341815430522197652251352641827569159669507145689773797669453516197358807477828807931009655600107768897946854892579812215015962074955495139891\n", 1164 | "21808259757138609699697083413261931820607546027091650988361450918504547617482959476055190683429662989207753924730869146931280026057912864176035806752285024024289668318257432803879820965705525021883007\n", 1165 | "37542539390462331617409395371212329649173363123616058071651280878416666495652656847062810587697400769526707471606915947099505015421427644845880530290008587356958649523492149722558406211825384289664063\n", 1166 | "38645933471571769617495023487160471245945621902657815212593088807709346293704266998551741782368157731410779296418252462120053810104304657077006606881196955531288700912236376504983937723669745478154531\n", 1167 | "49988068905580559350433545364530380367368442158112096333151789557587570132320970545748045223316867927085160106853833610082882522418794537540180976528222378168652889540405464302237894656686027129716981\n", 1168 | "63177468201713058241512834671485500791839485360146624533171263076455188611564051378967729912259661279874429574240692010508411347194647073215889930506138021693078890368405726792038504508440596801586351\n", 1169 | "87351833631245790397286499392404539187725921405975890682181391340079052264536170067403102285850049032347558565294138669698207164320921632977466893139347408630968079597785740280478261954136702466979639\n" 1170 | ] 1171 | } 1172 | ], 1173 | "source": [ 1174 | "print(f'{cnt}回の計算で、以下の10個を見つけた。')\n", 1175 | "for v in sorted(prime_set):\n", 1176 | " print(v) " 1177 | ] 1178 | } 1179 | ], 1180 | "metadata": { 1181 | "kernelspec": { 1182 | "display_name": "Python 3", 1183 | "language": "python", 1184 | "name": "python3" 1185 | }, 1186 | "language_info": { 1187 | "codemirror_mode": { 1188 | "name": "ipython", 1189 | "version": 3 1190 | }, 1191 | "file_extension": ".py", 1192 | "mimetype": "text/x-python", 1193 | "name": "python", 1194 | "nbconvert_exporter": "python", 1195 | "pygments_lexer": "ipython3", 1196 | "version": "3.6.5" 1197 | } 1198 | }, 1199 | "nbformat": 4, 1200 | "nbformat_minor": 2 1201 | } 1202 | --------------------------------------------------------------------------------