Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

14
Donald Knuth [F]orget about small efficiencies, say about 97% of the time

Transcript of Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Page 1: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Donald Knuth

[F]orget about small efficiencies, say about

97% of the time

Page 2: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

CSC 313 – Advanced Programming Topics

Page 3: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Recursion is useful, powerful technique Often yields compact, easy-to-read code Highlights all possible cases making

testing simple Some algorithms naturally recursive

Divide-and-conquer algorithms Nearly anything that uses a tree LISP & other functional language

programs Recursion can reduce development

time

Why Recurse?

Page 4: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Why Not Use Recursion?

Page 5: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Method Call Overhead

Every method call has some overhead Load address of method to be called Allocate stack memory for method Push parameters onto a stack Push return value from stack Reload return address from the stack Deallocate stack memory

Page 6: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Pain of Recursion

Compiler optimizes much of method call Most become only 1 instruction Leaves allocating stack space as main

cost Program starts by allocating huge

stack Then optimizes assuming stack is

immobile But lots of concurrent method calls will

fill this Rarely code many concurrent calls…

except when using recursion

Page 7: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

QuickSort Results

Page 8: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Convert algorithm to use tail-recursion Make recursive call as last step of

recursion Should directly return result of recursive

call

public int factorial(int i) { if (i <= 1) { return 1; } else { int result = factorial(i – 1); return result * i; }}

public int factorial(int i) { if (i <= 1) { return 1; } else { return i * factorial(i – 1); }}

What Can We Do?

public int factorial(int i, int fact) { if (i <= 1) { return fact; } else { return factorial(i – 1, fact * i); }}

Page 9: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Tail Recursion

Conversion of tail-recursion to iteration easy Create local variable to track most

recent result Recreate recursion using for or while

loop Get benefits of simplicity without the

costs

public int factorial(int i, int fact) { if (i <= 1) { return fact; } else { return factorial(i – 1, fact * i); }}

public int factorial(int i) { int fact; if (i <= 1) { // Stop recursion } else { fact *= i; // Continue recursion }}

public int factorial(int i) { int fact = 1; while (i > 1) { fact *= i; i--; } return fact;}

Page 10: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Tail Recursion Elimination Great technique for REMOVING

RECURSION End method with recursive call Directly return recursive result

NOT ALL ALGORITHMS CAN BE CONVERTED Recursion sometimes required

Page 11: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Remove Recursion By Cheating Slow performance due to stack

overhead While cannot always remove recursion Can remove overhead’s source

Instantiate Stack at beginning of method Program Stack replaced with local Stack

Method iterates while Stack is not empty Start loop by popping value to be

processed

Page 12: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

public void printInOrder(BNode<T> root) {Stack stack = // Instantiate stackstack.push(root);while (!stack.isEmpty()) { Object node = stack.pop(); if (node instanceof BNode) { if (node.hasRight()) { push(node.rightChild()); } push(node.element()); if (node.hasLeft()) { push(node.leftChild()); } } else { System.out.println(node); }}

}

public void printInOrder(BNode<T> root) {if (root.hasLeft()) { printInOrder(root.leftChild());}System.out.println(root.element());if (root.hasRight()) { printInOrder(root.rightChild());}

}

public void printInOrder(BNode<T> root) {Stack stack = // Instantiate stackstack.push(root);while (!stack.isEmpty()) { Object node = stack.pop(); if (node.hasRight()) { push(node.rightChild()); } push(node.element()); if (node.hasLeft()) { push(node.leftChild()); }}

}

Remove Recursion By Cheating

Page 13: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

Cheater Removing Recursion Use local Stack to replace program Stack Stack handles multiple data types Pop value off Stack with each iteration Loop’s actions depend on value’s type

Compiler can now optimize this method Stack methods highly optimized Growing & shrinking this Stack much

easier Single call means better chance of

optimizing

Page 14: Donald Knuth [F]orget about small efficiencies, say about 97% of the time.

For Next Lecture

Lab #3 due before lab time tomorrow Asks you to implement OBSERVER

PATTERN Lab #4 available on web/Angel

tomorrow Will be a different kind of lab

Read pages 109 – 122 of the book How DO we instantiate objects? How SHOULD we instantiate objects? Pizza… haven’t we ALREADY USED it in a

pattern?