On this page:
4.1 Add:   Optelling a.d.h.v. increment en decrement
4.1.1 Add:   Iteratief proces (Staartrecursie)
4.1.2 Add:   Recursief proces (Constructieve Recursie)
4.2 Multiply:   Linear proces (Recursief & Iteratief)
4.3 Multiply:   Logaritmisch proces (Recursief & Iteratief)
4.4 Mutuele recursie:   De Hofstadter-reeksen
4.5 Mutuele recursie:   De Hofstadter-reeksen printen d.m.v. do
4.6 Reeksen:   Het getal van Euler
4.7 Reeksen:   Het getal van Euler d.m.v. do
4.8 Recursiediepte:   "weird"
4.8.1 Recursiediepte:   Implementeer de procedure "weird"
4.8.2 Recursiediepte:   Bereken de recursiediepte van "weird"
4.9 Terugkeerpunten
4.10 Terugkeerpunten:   Binaire vormen
4.11 display-n met do
4.12 display-n:   parasol met do
4.13 Extra Oefeningen
4.13.1 Multiply:   Simulatie
4.13.2 sinus
4.13.3 cosinus
4.13.4 Boog-sinus
4.13.5 Recursiediepte:   Tabel
4.13.6 Examen Informatica 2e  Zit 1994
4.13.6.1 display-n
4.13.6.2 squares
4.13.6.3 explain
4.13.7 Iteratie met een teller
4.13.7.1 power-close-to
4.13.7.2 performantie
4.13.7.3 Efficiënter
7.8

4 Soorten Recursie en Soorten Processen

4.1 Add: Optelling a.d.h.v. increment en decrement

Veronderstel dat je beschikt over een voorgedefinieerde increment-functie 1+ (resp. decrement-functie 1-) die één enkel argument neemt en er 1 bij optelt (resp. van aftrekt). Definieer nu zelf de optelling (add a b) aan de hand van deze increment- en decrement functies.

(define (1- x) (- x 1))
(define (1+ x) (+ 1 x))

 

4.1.1 Add: Iteratief proces (Staartrecursie)

Schrijf een versie van add die een iteratief proces genereert d.m.v. staartrecursie. Noem deze versie (iter-add a b). Ook hier mag je ervan uitgaan dat a en b positievegetallen zijn.

(define (iter-add a b)
  (if (= a 0)
      b
      (iter-add (1- a) (1+ b))))

 

> (iter-add 4 5)

9

4.1.2 Add: Recursief proces (Constructieve Recursie)

Schrijf een versie van add die een recursief process genereert. Noem deze versie (rec-add a b). Je mag ervan uitgaan dat a en b positieve getallen zijn.

(define (rec-add a b)
  (if (= b 0)
      a
      (1+ (rec-add a (1- b)))))

 

> (rec-add 4 5)

9

4.2 Multiply: Linear proces (Recursief & Iteratief)

In paragraaf 1.2.4 van Abelson & Sussman wordt de machtsverheffing gedefinieerd in termen van de vermenigvuldiging. Definieer nu op analoge wijze de vermenigvuldiging aan de hand van de optelling. Schrijf hiervoor een functie die twee positieve getallen vermenigvuldigt, en die als volgt werkt:

\[a \times 0 = 0\\a \times 1 = (a \times 0) + a\\a \times 2 = (a \times 1) + a\]

Doe dit zowel op recursieve als iteratieve wijze. Noem de recursieve versie (rec-multiply a b). Noem de iteratieve versie (iter-multiply a b).

(define (rec-multiply a b)
  (if (zero? b)
      0
      (+ a (rec-multiply a (- b 1)))))
 
(define (iter-multiply a b)
  (define (iter result counter)
    (if (zero? counter)
        result
        (iter (+ result a) (- counter 1))))
  (iter 0 b))

 

> (rec-multiply 5 2)

10

> (iter-multiply 5 2)

10

4.3 Multiply: Logaritmisch proces (Recursief & Iteratief)

Veronderstel dat je beschikt over procedures (double a) en (halve a) die hun argument verdubbelen respectievelijk halveren. Definieer een functie die hetzelfde is als de multiply uit de vorige vraag, maar die in logaritmische tijd haar resultaat berekent. Doe dit zowel op recursieve als op iteratieve wijze. Noem de recursieve versie (rec-fast-multiply a b). Noem de iteratieve versie (iter-fast-multiply a b).

Hint: Gebruik de analogie met de definitie van fast-expt uit Abelson&Sussman paragraaf 1.2.4

(define (double x) (+ x x))
(define (halve x) (/ x 2))

 

(define (square x) (* x x))
 
(define (rec-fast-multiply a b)
  (cond ((zero? b) 0)
        ((even? b) (rec-fast-multiply (double a) (halve b)))
        (else (+ a (rec-fast-multiply a (- b 1))))))
 
(define (iter-fast-multiply a b)
  (define (iter a b acc)
    (cond ((zero? b) acc)
          ((even? b) (iter (double a) (halve b) acc))
          (else (iter a (- b 1) (+ acc a)))))
  (iter a b 0))

 

> (rec-fast-multiply 3 4)

12

> (rec-fast-multiply 100 200)

20000

> (iter-fast-multiply 3 4)

12

> (iter-fast-multiply 100 200)

20000

4.4 Mutuele recursie: De Hofstadter-reeksen

In zijn beroemde boek "Gödel, Escher, Bach" beschrijft Douglas Hofstadter tal van reeksen, waaronder de Vrouwelijk (\(V\)) en Mannelijk (\(M\)) reeksen. Deze reeksen worden als volgt gedefinieerd:

\[V (n) = \left\lbrace\begin{array}{ll} 1 & \mbox{als } n=0 \\ n - M(V(n-1)) & \mbox{als } n>0 \\ \end{array}\right.\]

\[M (n) = \left\lbrace\begin{array}{ll} 0 & \mbox{als } n=0 \\ n - V(M(n-1)) & \mbox{als } n>0 \\ \end{array}\right.\]

Implementeer de mutueel-recursieve procedures (hofstadter-V n) en (hofstadter-M n) om het n-de getal van respectievelijk de Vrouwelijk en Mannelijk reeksen te berekenen.

(define (hofstadter-V n)
  (if (= n 0)
      1
      (- n (hofstadter-M (hofstadter-V (- n 1))))))
 
(define (hofstadter-M n)
  (if (= n 0)
      0
      (- n (hofstadter-V (hofstadter-M (- n 1))))))

 

> (hofstadter-V 7)

5

> (hofstadter-M 7)

4

4.5 Mutuele recursie: De Hofstadter-reeksen printen d.m.v. do

Schrijf een procedure (hofstadter-reeksen n) die een tabel print met de eerste n elementen van de vrouwelijke reeks in de linker kolom, en de eerste n elementen van de mannelijke reeks in de rechter kolom. Implementeer hofstadter-reeksen door gebruik te maken van een do-lus.

(define (hofstadter-V n)
  (if (= n 0)
      1
      (- n (hofstadter-M (hofstadter-V (- n 1))))))
 
(define (hofstadter-M n)
  (if (= n 0)
      0
      (- n (hofstadter-V (hofstadter-M (- n 1))))))
 
(define (hofstadter-reeksen n)
  (do ((i 0 (+ i 1)))
    ((= i n))
  (display (hofstadter-V i))
  (display " ")
  (display (hofstadter-M i))
  (newline)))

 

> (hofstadter-reeksen 10)

1 0

1 0

2 1

2 2

3 2

3 3

4 4

5 4

5 5

6 6

Merk op dat we tussen twee getallen precies één spatie laten. Verder komt er ook een (newline) na elk rij.

4.6 Reeksen: Het getal van Euler

Onderstaande reeksontwikkeling heeft als som het getal van Euler, \(e = 2,718281828...\).

\[e = \frac{1}{0!} +\frac{1}{1!} +\frac{1}{2!} +\frac{1}{3!} + ...\]

We kunnen in Scheme een procedure (calc-e n) definiëren die het getal \(e\) benadert door de som van de eerste \(n + 1\) termen van de reeks te berekenen:

(define (factorial n)
  (if (zero? n)
      1
      (* n (factorial (- n 1)))))
 
(define (calc-e n)
  (if (= n 0)
      1
      (+ (/ 1 (factorial n))
         (calc-e (- n 1)))))

 

Hierbij geeft (factorial n) het resultaat \(n!\), waarbij precies n vermenigvuldigingen worden gedaan (zie Abelson&Sussman blz. 32). Hoeveel vermenigvuldigingen neemt (calc-e n) dan in beslag? Verander de definitie van calc-e zodat je zo min mogelijk vermenigvuldigingen doet.

Hint: Verweef de definities van calc-e en factorial met elkaar en maak gebruik van een iteratief proces.

(define (calc-e n)
  (define (iter ctr res fac-prev)
    (define new-fac (* ctr fac-prev))
    (if (> ctr n)
        res
        (iter (+ ctr 1) (+ res (/ 1 new-fac)) new-fac)))
  (iter 1 1 1))

 

> (exact->inexact (calc-e 10))

2.7182818011463845

4.7 Reeksen: Het getal van Euler d.m.v. do

Onderstaande reeksontwikkeling heeft als som het getal van Euler, \(e = 2,718281828...\).

\[e = \frac{1}{0!} +\frac{1}{1!} +\frac{1}{2!} +\frac{1}{3!} + ...\]

We kunnen in Scheme een procedure (calc-e n) definiëren die het getal \(e\) benadert door de som van de eerste \(n + 1\) termen van de reeks te berekenen:

(define (factorial n)
  (if (zero? n)
      1
      (* n (factorial (- n 1)))))
 
(define (calc-e n)
  (if (= n 0)
      1
      (+ (/ 1 (factorial n))
         (calc-e (- n 1)))))

 

Verander de definitie van calc-e door de definities van calc-e en factorial met elkaar te verweven. Maak deze keer gebruik van een do-lus, zodat jouw calc-e-procedure een iteratief proces genereert.

(define (calc-e n)
  (do ((ctr 1 (+ ctr 1))
       (res 1 (+ res (/ 1 (* ctr fac-prev))))
       (fac-prev 1 (* ctr fac-prev)))
    ((> ctr n) res)))

 

> (exact->inexact (calc-e 10))

2.7182818011463845

4.8 Recursiediepte: "weird"

De (wiskundige) functie weird is als volgt gedefinieerd:

\[weird(x) = \left\lbrace\begin{array}{ll} 1 & \mbox{als } x=1 \\ weird (x/2) & \mbox{als x even is}\\ weird (3*x+1) & \mbox{anders}\end{array}\right.\]

4.8.1 Recursiediepte: Implementeer de procedure "weird"

Schrijf de procedure (weird x) die de wiskundige functie van eerder implementeert. Genereert je oplossing een recursief of iteratief proces?

(define (weird x)
  (cond
    ((= x 1) 1)
    ((even? x) (weird (/ x 2)))
    (else (weird (+ (* 3 x) 1)))))

 

> (weird 15)

1

4.8.2 Recursiediepte: Bereken de recursiediepte van "weird"

Schrijf de procedure (depth-weird x) die het aantal recursieve oproepen (= de recursie-diepte) van weird voor een bepaalde x teruggeeft.

(define (depth-weird x)
  (cond
    ((= x 1) 0)
    ((even? x) (+ 1 (depth-weird (/ x 2))))
    (else (+ (depth-weird (+ (* 3 x) 1)) 1))))

 

> (depth-weird 15)

17

4.9 Terugkeerpunten

Geef de output van de uitdrukking (count1 4) en (count2 4) indien count1 en count2 als volgt gedefinieerd zijn:

(define (count1 x)
  (cond ((= 0 x) (display x))
        (else (display x)
              (count1 (- x 1)))))
 
(define (count2 x)
  (cond ((= 0 x) (display x))
        (else (count2 (- x 1))
              (display x))))

 

> (count1 4)

43210

> (count2 4)

01234

4.10 Terugkeerpunten: Binaire vormen

Schrijf een procedure (display-as-binary n), die een positief geheel getal n neemt, en dit in binaire vorm afdrukt.

Je mag een recursief proces genereren. Gebruik (display "0") en (display "1") om de cijfers 0 en 1 af te drukken. Let op dat je het getal niet omgekeerd afdrukt. Enkele correcte voorbeelden zijn:

(define (display-as-binary n)
  (if (> n 1)
      (display-as-binary (quotient n 2)))
  (display (modulo n 2)))

 

> (display-as-binary 8)

1000

> (display-as-binary 5)

101

> (display-as-binary 0)

0

4.11 display-n met do

Schrijf een procedure (display-n x n) die twee parameters aanvaardt, een karakter en een positief geheel getal. Ze zal — gebruik makende van de standaard procedure display — het gegeven karakter zoveel maal afdrukken als de tweede parameter aangeeft. Maak gebruik van een do-lus om display-n te implementeren.

(define (display-n x n)
  (do ((i n (- i 1)))
    ((= i 0))
    (display x)))

 

> (display-n "*" 4)

****

4.12 display-n: parasol met do

Schrijf een procedure (parasol n) (n is een geheel getal) die een parasolletje met gegeven hoogte op het scherm ’tekent’.

In het volgend voorbeeld heeft het driehoekje als hoogte 5 (de waarde van de parameter n) en als maximale breedte 9 (\( =(2 \times n) - 1\)). De hoogte van het stokje is altijd 3. Enkele sterretjes en spaties kan je op het scherm printen door gebruik te maken van respectievelijk (display "*") en (display " "). Gebruik uiteraard de display-n procedure als je meerdere sterretjes of spaties op het scherm moet printen. Om een nieuwe lijn te beginnen gebruik je (newline).

Implementeer de parasol-procedure op een iteratieve manier, door gebruik te maken van do-lussen.

(define (display-n x n)
  (do ((i n (- i 1)))
    ((= i 0))
    (display x)))
 
(define (parasol n)
  (do ((ctr 0 (+ ctr 1)))
    ((= ctr n))
    (display-n " " (- n ctr 1))
    (display-n "*" (+ (* ctr 2) 1))
    (newline))
  (do ((ctr 0 (+ ctr 1)))
    ((= ctr 3))
    (display-n " " (- n 1))
    (display "*")
    (newline)))

 

> (parasol 5)

    *

   ***

  *****

 *******

*********

    *

    *

    *

4.13 Extra Oefeningen

4.13.1 Multiply: Simulatie

Schrijf twee functies die door middel van simulatie berekenen hoeveel recursieve oproepen gebeuren in de procedures uit Multiply: Linear proces (Recursief & Iteratief) en Multiply: Logaritmisch proces (Recursief & Iteratief). Noem de functies (sim-multiply a b) en (sim-fast-multiply a b).

(define (sim-multiply a b)
  (if (zero? b)
      1
      (+ 1 (sim-multiply a (- b 1)))))
 
(define (sim-fast-multiply a b)
  (cond ((zero? b) 1)
        ((even? b) (+ 1 (sim-fast-multiply (double a) (halve b))))
        (else (+ 1 (sim-fast-multiply a (- b 1))))))

 

> (sim-multiply 14 2365)

2366

> (sim-fast-multiply 14 2365)

19

4.13.2 sinus

Onderstaande reeksontwikkeling berekent \(sin(x)\). \[sin(x)=\frac{x}{1!} - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + ...\] Definieer een procedure (calc-sin x n), die de som van de eerste n termen berekent op een zo efficiënt mogelijke manier. Geef de formule die je het aantal vermenigvuldigingen oplevert voor n termen.

(define (calc-sin x n)
  (define (iter ctr acc fac xpow sign)
    (if (> ctr n)
        acc
        (let* ((i (- (* 2 ctr) 1))
               (newfac (* fac (- i 1) i))
               (newxpow (* xpow x x))
               (newsign (- sign)))
          (iter (+ ctr 1)
                (+ acc (/ (* newsign newxpow) newfac))
                newfac
                newxpow
                newsign))))
  (iter 2 x 1 x 1))

 

> (calc-sin 0 10)

0

> (calc-sin (/ 3.1415 4) 10)

0.7070904020014415

> (calc-sin (/ 3.1415 2) 10)

0.9999999989269138

4.13.3 cosinus

Zelfde vraag voor de functie \(cos(x)\), met de procedure (calc-cos x n), die de volgende reeks-ontwikkeling heeft: \[cos(x)=\frac{x^0}{0!} - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + ...\]

(define (calc-cos x n)
  (define (iter ctr acc fac xpow sign)
    (if (>= ctr n)
        acc
        (let* ((i (* 2 ctr))
               (newfac (* fac (- i 1) i))
               (newxpow (* xpow x x))
               (newsign (- sign)))
          (iter (+ ctr 1)
                (+ acc (/ (* newsign newxpow) newfac))
                newfac
                newxpow
                newsign))))
  (iter 1 1 1 1 1))

 

> (calc-cos 0 10)

1

> (calc-cos (/ 3.1415 2) 10)

4.6326794876592664e-05

> (calc-cos 3.1415 10)

-0.9999999992346591

4.13.4 Boog-sinus

Schrijf een functie die de reeksontwikkeling voor \(Bgsin(x)\) voor n termen berekent. \[bgsin(x) = x + \frac{1}{2.3}x^3 + \frac{1.3}{2.4.5}x^5 + \frac{1.3.5}{2.4.6.7}x^7+...\] (Opmerking: deze reeksontwikkeling is enkel geldig voor \(-\frac{\pi}{2} \leq bgsin(x) \leq \frac{\pi}{2}\))

Merk op dat de getallen die wegvallen in de noemer, de getallen zijn die in de teller staan. Zo is bijvoorbeeld de term \(\frac{1.3.5}{2.4.6.7}x^7\) gelijk aan \(\frac{1.1.3.3.5.5}{1.2.3.4.5.6.7}x^7\). Het is dit "trukje" dat we gebruiken in onze oplossing.

(define (bgsin x n)
  (define (iter i teller noemer macht som)
    (if (= i n)
        som
        (let* ((macht (* x x macht))
               (2i+1 (+ (* 2 i) 1))
               (2i+2 (+ (* 2 i) 2))
               (2i+3 (+ (* 2 i) 3))
               (teller (* teller 2i+1 2i+1))
               (noemer (* noemer 2i+2 2i+3)))
          (iter (+ i 1)
                teller
                noemer
                macht
                (+ (* (/ teller noemer) macht)
                   som)))))
  (iter 0 1 1 x x))

 

> (bgsin (sin 1) 50)

0.9999999999532735

4.13.5 Recursiediepte: Tabel

Schrijf een procedure (weird-table min max) die op het scherm een tabel afdrukt van de recursie-dieptes van weird voor alle getallen gelegen tussen min en max. Maak hiervoor gebruik van depth-weird uit een vorige oefening.

(define (depth-weird x)
  (cond
    ((= x 1) 0)
    ((even? x) (+ 1 (depth-weird (/ x 2))))
    (else (+ (depth-weird (+ (* 3 x) 1)) 1))))

 

Onze eerste versie ziet er zo uit:
(define (weird-table min max)
  (define (hulp x)
    (cond ((< x max) (display x)
                     (display " ")
                     (display (depth-weird x))
                     (newline)
                     (hulp (+ x 1)))))
  (hulp min))

 

Merk op dat we hier eigenlijk geen hulp procedure nodig hebben en deze dus kunnen herschrijven als:
(define (weird-table min max)
  (cond ((< min max) (display min)
                     (display " ")
                     (display (depth-weird min))
                     (newline)
                     (weird-table (+ min 1) max))))

 

We kunnen ook gebruik maken van hogere orde om de tabel te printen:
(define (weird-table min max)
  (cond
   ((< min max) (for-each display (list min " " (depth-weird min) "\n"))
                (weird-table (+ min 1) max))))

 

> (weird-table 1 10)

1 0

2 1

3 7

4 2

5 5

6 8

7 16

8 3

9 19

Merk op dat we tussen twee getallen precies één spatie laten. Verder komt er ook een (newline) na elk rij.

4.13.6 Examen Informatica 2eZit 1994
4.13.6.1 display-n

Schrijf een recursieve procedure (display-n x n) die twee parameters aanvaardt, een karakter en een positief geheel getal. Ze zal — gebruik makende van de standaard procedure display — het gegeven karakter zoveel maal afdrukken als de tweede parameter aangeeft.
(define (display-n x n)
  (if (> n 0)
      (begin
        (display x)
        (display-n x (- n 1)))))

 

> (display-n '* 4)

****

4.13.6.2 squares

Schrijf een recursieve procedure squares die een positief geheel getal aanvaardt, en vier vierkanten (met als zijdelengte het gegeven getal) afdrukt, zodanig dat het geheel opnieuw een vierkant vormt. (Noot: je mag ervan uitgaan dat alle karakters even groot op het scherm worden afgedrukt.)

(define (squares n)
  (define (display-full-line)
    (display-n "*" (* n 2))
    (newline))
  (define (display-semi-semi-line)
    (display "*")
    (display-n " " (- n 2))
    (display "*"))
  (define (displaysemi-line)
    (display-semi-semi-line)
    (display-semi-semi-line)
    (newline))
  (define (semi-line-iter i)
    (if (< i (- n 2))
        (begin
          (displaysemi-line)
          (semi-line-iter (+ i 1)))))
  (display-full-line)
  (semi-line-iter 0)
  (display-full-line)
  (semi-line-iter 0)
  (display-full-line))

 

> (squares 3)

******

* ** *

******

* ** *

******

> (squares 5)

**********

*   **   *

*   **   *

*   **   *

**********

*   **   *

*   **   *

*   **   *

**********

4.13.6.3 explain

Zeg voor elk van je oplossingen in (a) en (b) of ze een iteratief dan wel een recursief proces genereren. Argumenteer waarom.

4.13.7 Iteratie met een teller
4.13.7.1 power-close-to

Schrijf een procedure (power-close-to b n) die twee positieve integers b en n als argumenten heeft en de kleinste integer e teruggeeft waarvoor geldt dat \(b^e > n\). Doe dit door gebruik te maken van de Scheme-procedure expt.

Voorbeeld:
> (expt 2 3)

8

want
> (* 2 2 2)

8

(define (power-close-to b n)
  (define (iter e)
    (if (> (expt b e) n)
        e
        (iter (+ e 1))))
  (iter 0))

 

> (power-close-to 2 15)

4

> (power-close-to 2 16)

5

4.13.7.2 performantie

Wat is de performantie van deze procedure?

Het process dat beschreven wordt door de procedure iter wordt \(\frac{\log{n}}{\log{b}}\) keer uitgevoerd. Als we er van uitgaan dat (expt n e) in e stappend wordt uitgevoerd, dan is de performantie van onze expt-power-close-to \(O(\frac{\log{n}}{\log{b}}^2)\)

4.13.7.3 Efficiënter

Schrijf de procedure efficiënter door haar iteratief te schrijven en aan elke nieuwe stap de macht uit de vorige stap door te geven. Noem deze versie (power-close-to++ b n).

(define (power-close-to++ b n)
  (define (iter e macht)
    (if (> macht n)
        e
        (iter (+ e 1) (* macht b))))
  (iter 0 1))

 

De performantie van eff-power-close-to is \(O(\frac{\log{n}}{\log{b}})\), omdat nu de oproep naar expt niet meer nodig is.

> (power-close-to 2 1000)

10

> (power-close-to++ 2 1000)

10