{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Karatsuba vs Gradeschool multiplication\n", "\n", "These are two naive implementation of binary Karatsuba vs Gradeschool multiplication. Neither implementation takes advantage of the underlying Hardware support for operations on 32 or 64 bit numbers. Thus neither should be used if you actually want to multiply numbers efficiently in practice. (In fact, you shouldn't use Python at all for this task.)" ] }, { "cell_type": "code", "execution_count": 193, "metadata": {}, "outputs": [], "source": [ "def gradeschool_mult(x,y):\n", " '''Multiply two integers via gradeschool algorithm.'''\n", " x = str(x); \n", " z = 0\n", " for i in range(len(x)):\n", " z += 10**i*int(x[len(x)-1-i])*y\n", " \n", " return z" ] }, { "cell_type": "code", "execution_count": 183, "metadata": {}, "outputs": [], "source": [ "def gradeschool_mult(x,y):\n", " if x<10 or y<10: return x*y\n", " x = str(x); y = str(y); \n", " # convert to string of 0/1's, MSB first\n", " n = max(len(x),len(y))\n", " x = \"0\"*(n-len(x))+x; y = \"0\"*(n-len(y))+y # add leading zeroes if needed\n", " m = n//2\n", " xtop = int(x[:-m]); xbot = int(x[-m:])\n", " ytop = int(y[:-m]); ybot = int(y[-m:])\n", " return 10**(2*m)*gradeschool_mult(xtop,ytop)+10**m*(gradeschool_mult(xtop,ybot)+gradeschool_mult(xbot,ytop))+gradeschool_mult(xbot,ybot)" ] }, { "cell_type": "code", "execution_count": 184, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42189" ] }, "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gradeschool_mult(123,343)" ] }, { "cell_type": "code", "execution_count": 194, "metadata": {}, "outputs": [], "source": [ "def karatsuba_mult(x,y):\n", " '''Multiply two simlar length integers via karatsuba algorithm.'''\n", " if x<100 or y<100: return x*y\n", " x = str(x); y = str(y); \n", " # convert to string of 0/1's, MSB first\n", " n = max(len(x),len(y))\n", " x = \"0\"*(n-len(x))+x; y = \"0\"*(n-len(y))+y # add leading zeroes if needed\n", " m = n//2\n", " xtop = int(x[:-m]); xbot = int(x[-m:])\n", " ytop = int(y[:-m]); ybot = int(y[-m:])\n", " return (10**(2*m)-10**m)*karatsuba_mult(xtop,ytop)+(10**m)*karatsuba_mult(xtop+xbot,ytop+ybot) +(1-10**m)*karatsuba_mult(xbot,ybot)\n", " " ] }, { "cell_type": "code", "execution_count": 186, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42760785998990549" ] }, "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ "karatsuba_mult(12342323,3464565463)" ] }, { "cell_type": "code", "execution_count": 187, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42760785998990549" ] }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ "12342323* 3464565463" ] }, { "cell_type": "code", "execution_count": 199, "metadata": {}, "outputs": [], "source": [ "input_lengths = [2**i for i in range(5,16,2)]\n", "gradeschool_times = {}\n", "karatsuba_times = {}\n" ] }, { "cell_type": "code", "execution_count": 201, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gradeschool n = 32\n", "1 loop, best of 1: 80.2 µs per loop\n", "Karatsuba n = 32\n", "1 loop, best of 1: 676 µs per loop\n", "Gradeschool n = 128\n", "1 loop, best of 1: 491 µs per loop\n", "Karatsuba n = 128\n", "1 loop, best of 1: 5.73 ms per loop\n", "Gradeschool n = 512\n", "1 loop, best of 1: 3.01 ms per loop\n", "Karatsuba n = 512\n", "1 loop, best of 1: 74.9 ms per loop\n", "Gradeschool n = 2048\n", "1 loop, best of 1: 119 ms per loop\n", "Karatsuba n = 2048\n", "1 loop, best of 1: 457 ms per loop\n", "Gradeschool n = 8192\n", "1 loop, best of 1: 4.03 s per loop\n", "Karatsuba n = 8192\n", "1 loop, best of 1: 3.86 s per loop\n", "Gradeschool n = 32768\n", "1 loop, best of 1: 2min 22s per loop\n", "Karatsuba n = 32768\n", "1 loop, best of 1: 36.1 s per loop\n" ] } ], "source": [ "import random\n", "for n in input_lengths:\n", " x = random.randrange(10**n)\n", " y = random.randrange(10**n)\n", " r = x*y\n", " #print(r)\n", " print(f\"Gradeschool n = {n}\")\n", " foo = %timeit -o -r1 -n1 gradeschool_mult(x,y)\n", " print(f\"Karatsuba n = {n}\")\n", " bar = %timeit -o -r1 -n1 karatsuba_mult(x,y)\n", " gradeschool_times[n] = foo\n", " karatsuba_times[n] = bar" ] }, { "cell_type": "code", "execution_count": 212, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "gs_lengths, gs_times = zip(*sorted(gradeschool_times.items())) # unpack a list of pairs into two tuples\n", "\n", "ka_lengths, ka_times = zip(*sorted(karatsuba_times.items()))\n", "\n", "gs_times = [t.best for t in gs_times]\n", "ka_times = [t.best for t in ka_times]\n", "\n", "line1, line2 = plt.plot(gs_lengths, gs_times, 'r--', ka_lengths, ka_times, 'b--')\n", "plt.xlabel('input length')\n", "plt.ylabel('time (secs, log scale)')\n", "plt.yscale('log')\n", "plt.legend([line1,line2],['Gradeschool','Karatsuba'])\n", "plt.show()\n" ] }, { "cell_type": "code", "execution_count": 213, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.savefig('karastubavsgschool.png')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }