Shows the reasons you might want to memoize a function like the fibonacci sequence.
A Pen by Connor MacDonald on CodePen.
| <div class="container"> | |
| <h1>Fibonacci Sequence</h1> | |
| <form> | |
| <div class="form-group"> | |
| <label for="number"> | |
| Fibonacci sequence number: | |
| </label> | |
| <input type="number" class="form-control" name="number" id="number" min="1" value="1" /> | |
| <small class="form-text text-muted">Please enter a number in the fibonacci sequence where n > 0.</small> | |
| </div> | |
| </form> | |
| <hr /> | |
| <h2>Calculate using the following methods</h2> | |
| <div class="btn-group" role="group" aria-label="Basic example"> | |
| <button class="btn btn-primary" id="calculate-for" >For loop</button> | |
| <button class="btn btn-danger" id="calculate-recursive">Recursive</button> | |
| <button class="btn btn-primary" id="calculate-recursive-memoized">Recursive, memoized</button> | |
| </div> | |
| <button class="btn btn-default" id="clear-results">Clear Results</button> | |
| <hr /> | |
| <h3>Results:</h3> | |
| <div id="results"></div> | |
| </div> |
Shows the reasons you might want to memoize a function like the fibonacci sequence.
A Pen by Connor MacDonald on CodePen.
| class fibonacciApp { | |
| constructor() { | |
| this.tolerableTimeThreshold = '100'; | |
| this.elements = { | |
| results: document.getElementById('results'), | |
| number: document.getElementById('number'), | |
| buttons: { | |
| for: document.getElementById('calculate-for'), | |
| recursive: document.getElementById('calculate-recursive'), | |
| recursiveMemoized: document.getElementById('calculate-recursive-memoized'), | |
| clear: document.getElementById('clear-results') | |
| } | |
| }; | |
| /** | |
| * TODO: LOL figure out a better way to memoize this | |
| */ | |
| this.fibonacciRecursiveMemoized = (function() { | |
| var memo = {}; | |
| function fib(n) { | |
| var value; | |
| if (n === 0 || n === 1) { | |
| value = n; | |
| } else { | |
| if (n in memo) { | |
| value = memo[n]; | |
| } else { | |
| value = fib(n-1) + fib(n-2); | |
| memo[n] = value; | |
| } | |
| } | |
| return value; | |
| } | |
| return fib; | |
| })(); | |
| this.registerEventListeners(); | |
| } | |
| registerEventListeners() { | |
| this.elements.buttons.clear.addEventListener('click', () => { | |
| this.elements.results.innerHTML = ' '; | |
| }) | |
| this.elements.buttons.recursiveMemoized.addEventListener('click', () => this.executeAlgorithm(this.fibonacciRecursiveMemoized)); | |
| // TODO: Could we not bind this? Makes printing out the funnction a little more difficult. | |
| this.elements.buttons.recursive.addEventListener('click', () => this.executeAlgorithm(this.fibonacciRecursive.bind(this))); | |
| this.elements.buttons.for.addEventListener('click', () => this.executeAlgorithm(this.fibonacciFor)); | |
| } | |
| executeAlgorithm(method) { | |
| let value = this.elements.number.value; | |
| try { | |
| let valueInteger = parseInt(value); | |
| if (valueInteger >= 0) { | |
| let startTime = new Date().getTime(); | |
| let result = method(valueInteger); | |
| let endTime = new Date().getTime(); | |
| this.printResults( | |
| value, | |
| result, | |
| endTime - startTime, | |
| method | |
| ) | |
| } else { | |
| throw { message: `Invalid integer ${value}` }; | |
| } | |
| } catch (e) { | |
| this.printException(e.message, value, method); | |
| } | |
| } | |
| printResults(n, result, executionTime, method) { | |
| this.elements.results.innerHTML += | |
| `<div class="alert ${(executionTime>this.tolerableTimeThreshold) ? 'alert-warning' : 'alert-secondary'}"> | |
| <ul class="list-group"> | |
| <li class="list-group-item active"><strong>Result:</strong> ${result}</li> | |
| <li class="list-group-item"><strong>Method:</strong> ${method.name}</li> | |
| <li class="list-group-item"><strong>Input for parameter n:</strong> ${n}<br></li> | |
| <li class="list-group-item"><strong>Execution Time:</strong> ${executionTime} ms <br></li> | |
| </ul> | |
| <hr> | |
| <h3>What just ran:</h3> | |
| <pre>${method.toString()}</pre> | |
| </div>`; | |
| } | |
| /** | |
| * Add an exception to the list of things printed to the screen | |
| */ | |
| printException(errorText, n, method) { | |
| this.elements.results.innerHTML += | |
| `<div class="alert alert-danger"> | |
| <strong>Method:</strong> ${method.name} <strong>n:</strong> ${n} | |
| <pre>EXCEPTION<br />${errorText}</pre> | |
| </div>`; | |
| } | |
| /* | |
| * Run the fibonacci sequence in a recursive pattern to achieve the result. | |
| * This has a big performance hit after about 32. | |
| * the complexity of this algorithm is about 2^n | |
| */ | |
| fibonacciRecursive(n) { | |
| if (n === 0 || n === 1) { | |
| return n; | |
| } | |
| else { | |
| return this.fibonacciRecursive(n - 1) + this.fibonacciRecursive(n - 2); | |
| } | |
| } | |
| /** | |
| * Fibonnaci sequence completed going in the normal algorithm. | |
| * Although less "elegant", this is significantly less complex. | |
| */ | |
| fibonacciFor(n) { | |
| let accumulator = 1, | |
| previousNMinus1 = 0, | |
| previousNMinus2 = 1; | |
| for (var counter = 1; counter <= n; counter++ ) { | |
| accumulator = previousNMinus1 + previousNMinus2; | |
| previousNMinus2 = previousNMinus1; | |
| previousNMinus1 = accumulator; | |
| } | |
| return accumulator; | |
| } | |
| } | |
| let fib = new fibonacciApp(); |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" /> |