Notes: The exercise 2.37 on Hexlet's site has an error, noted in the comments. Also, their page for exercise 2.38 should probably have 0 tests. And finally, I did not calculate the exact number in the final exercise 2.43, but I included a relevant discussion.
62 lines
2.6 KiB
Text
62 lines
2.6 KiB
Text
The normal function presented here:
|
|
(define (queens board-size)
|
|
(define (queen-cols k)
|
|
(if (= k 0)
|
|
(list empty-board)
|
|
(filter
|
|
(lambda (positions) (safe? k positions))
|
|
(flatmap
|
|
(lambda (rest-of-queens)
|
|
(map (lambda (new-row)
|
|
(adjoin-position
|
|
new-row k rest-of-queens))
|
|
(enumerate-interval 1 board-size)))
|
|
(queen-cols (- k 1))))))
|
|
(queen-cols board-size))
|
|
|
|
gets executed in roughly the following way: Before any of the calls to filter
|
|
or flatmap are performed (queen-cols (- k 1)) will be run. This will happen
|
|
recursively, so it will recursively call until (queen-cols 0), which will
|
|
return a list with a single empty-board.
|
|
Then, in the (queens-col 1) call, eight new boards, each with a single queen,
|
|
will be returned, then in the (queens-col 2), 64 will be created, then
|
|
filtered, and so forth, each time we exit the current call in the stack, we
|
|
have already filtered the results of the previous call.
|
|
|
|
On the other hand Louis Reasoner's function looks like this:
|
|
(define (queens board-size)
|
|
(define (queen-cols k)
|
|
(if (= k 0)
|
|
(list empty-board)
|
|
(filter
|
|
(lambda (positions) (safe? k positions))
|
|
(flatmap
|
|
(lambda (new-row)
|
|
(map (lambda (rest-of-queens)
|
|
(adjoin-position new-row k rest-of-queens))
|
|
(queen-cols (- k 1))))
|
|
(enumerate-interval 1 board-size)))))
|
|
(queen-cols board-size))
|
|
|
|
And it first calls enumerate-interval, and generates a list of 8 numbers.
|
|
After that, each of these numbers get the outer lambda applied to them, which
|
|
then calls (queen-cols 7) eight times. It will then adjoin, and afterwards
|
|
filter the results. It should be noted, these sub-calls WILL generate filtered
|
|
lists of queens, it's not as if all possible cases will be generated, and only
|
|
then filtered, however, each of these recursive subcalls will generate a list
|
|
of (queen-cols (- k 1)) eight times, instead of once, and so on recursively.
|
|
|
|
In other words, for the older version of the function, the execution time T(n)
|
|
(assuming board-size is 8), is roughly equal to:
|
|
T(n) = T(n-1) + O(8*Q(n-1))
|
|
^adjoining and filtering ^
|
|
(Q(n-1) is length of result of (queen-cols n))
|
|
unraveling this we get: O(Q(n-1)+Q(n-2)+Q(n-3)+...+Q(0))
|
|
|
|
Whereas the new function's time looks like this:
|
|
T(n) = 8*T(n-1) + O(8*Q(n-1))
|
|
unraveling, we get, very roughly:
|
|
O(8*Q(n-1) + 8*8*Q(n-2) + ... + 8^8*Q(0))
|
|
|
|
I haven't tried to run the exact numbers, because it would involve learning
|
|
exactly what Q(0), ... ,Q(8) are, which I didn't care to do.
|