By primitive expressions, I mean + - * / sqrt, unless there are others that I am missing. I'm wondering how to write a Scheme expression that finds the 6th root using only these functions.
I know that I can find the cube root of the square root, but cube root doesn't appear to be a primitive expression.
Consider expt, passing in a fractional power as its second argument.
But let's say we didn't know about expt. Could we still compute it?
One way to do it is by applying something like Newton's method. For example, let's say we wanted to compute n^(1/4). Of course, we already know we can just take the sqrt twice to do this, but let's see how Newton's method may apply to this problem.
Given n, we'd like to discover roots x of the function:
f(x) = x^4 - n
Concretely, if we wanted to look for 16^(1/4), then we'd look for a root for the function:
f(x) = x^4 - 16
We already know if we plug in x=2 in there, we'll discover that 2 is a root of this function. But say that we didn't know that. How would we discover the x values that make this function zero?
Newton's method says that if we have a guess at x, call it x_0, we can improve that guess by doing the following process:
x_1 = x_0 - f(x_0) / f'(x_0)
where f'(x) is notation for the derivative of f(x). For the case above, the derivative of f(x) is 4x^3.
And we can get better guesses x_2, x_3, ... by repeating the computation:
x_2 = x_1 - f(x_1) / f'(x_1)
x_3 = x_2 - f(x_2) / f'(x_2)
...
until we get tired.
Let's write this all in code now:
(define (f x)
(- (* x x x x) 16))
(define (f-prime x)
(* 4 x x x))
(define (improve guess)
(- guess (/ (f guess)
(f-prime guess))))
(define approx-quad-root-of-16
(improve (improve (improve (improve (improve 1.0))))))
The code above just expresses f(x), f'(x), and the idea of improving an initial guess five times. Let's see what the value of approx-quad-root-of-16 is:
> approx-quad-root-of-16
2.0457437305170534
Hey, cool. It's actually doing something, and it's close to 2. Not bad for starting with such a bad first guess of 1.0.
Of course, it's a little silly to hardcode 16 in there. Let's generalize, and turn it into a function that takes an arbitrary n instead, so we can compute the quad root of anything:
(define (approx-quad-root-of-n n)
(define (f x)
(- (* x x x x) n))
(define (f-prime x)
(* 4 x x x))
(define (improve guess)
(- guess (/ (f guess)
(f-prime guess))))
(improve (improve (improve (improve (improve 1.0))))))
Does this do something effective? Let's see:
> (approx-quad-root-of-n 10)
1.7800226459895
> (expt (approx-quad-root-of-n 10) 4)
10.039269440807693
Cool: it is doing something useful. But note that it's not that precise yet. To get better precision, we should keep calling improve, and not just four or five times. Think loops or recursion: repeat the improvement till the solution is "close enough".
This is a sketch of how to solve these kinds of problems. For a little more detail, look at the section on computing square roots in Structure and Interpretation of Computer Programs.
You may want to try out a numeric way, which may be inefficient for larger numbers, but it works.
Also if you also count pow as a primitive (since you also count sqrt) you could do this:
pow(yournum, 1/6);
Related
This is a question about the notion of derivatives from the Isabelle libraries.
I am trying to understand what (f has_field_derivative D x) (at x within S) means. I know (at x within S) is a filter, but intuitively I was imagining that the following statement is true
lemma DERIV_at_within:
"(∀x ∈ S. (f has_field_derivative D x) (at x))
= (∀x. (f has_field_derivative D x) (at x within S))"
If it is not, how else should I interpret (at x within S) in the context of derivatives?
at x within A is the pointed neighbourhood of x, intersected with A. For instance, at_right is an abbreviation for at x within {x<..}, i.e. the right-neighbourhood of x. This allows you to express one-sided derivatives.
Occasionally, one also sees assumptions like ∀x∈{a..b}. (f has_field_derivative f' x) (at x within {a..b}). This means that f is differentiable with derivative f' between a and b, but the derivatives at the edges (i.e. at a and b) need only be one-sided.
Note also that at x = at x within UNIV. Also, if A is an open set containint x, you simply have at x within A = at x.
Typically, you only really need has_field_derivative with at x within … if you want something like a one-sided limit (or, in higher dimensions, if you somehow want to constrain the direction of approach).
I have an assignment to make a tail-recursive function that takes 3 integers(possibly very large), p q and r, and calculates the modulo of the division (p^q)/r. I figured out how to do make a function that achieves the goal, but it is not tail recursive.
(define (mod-exp p q r)
(if (= 0 p)
0
(if (= 0 q)
1
(if (= 0 (remainder r 2))
(remainder (* (mod-exp p (quotient q 2) r)
(mod-exp p (quotient q 2) r))
r)
(remainder (* (remainder p r)
(remainder (mod-exp p (- q 1) r) r))
r)))))
I'm having a hard time wrapping my head around making this tail-recursive, I don't see how I can "accumulate" the remainder.
I'm pretty much restricted to using the basic math operators and quotient and remainder for this task.
I see that you're implementing binary exponentiation, with the extra feature that it's reduced mod r.
What you may want to do is take a normal (tail recursive) binary exponentiation algorithm and simply change the 2-ary functions + and * to your own user defined 3-ary functions +/mod and */mode which also take r and reduce the result mod r before returning it.
Now how do you do binary exponentiation in a tail recursive way? You need the main function to call into a helper function that takes an extra accumulator parameter - initial value 1. This is kind of similar to tail recursive REVERSE using a helper function REVAPPEND - if you're familiar with that.
Hope that helps and feel free to ask if you need more information.
I'm learning the book SICP, and for the exercise 1.15:
Exercise 1.15. The sine of an angle (specified in radians) can be computed by making use of the approximation sin x x if x is sufficiently small, and the trigonometric identity
to reduce the size of the argument of sin. (For purposes of this exercise an angle is considered ``sufficiently small'' if its magnitude is not greater than 0.1 radians.) These ideas are incorporated in the following procedures:
(define (cube x) (* x x x))
(define (p x) (- (* 3 x) (* 4 (cube x))))
(define (sine angle)
(if (not (> (abs angle) 0.1))
angle
(p (sine (/ angle 3.0)))))
a. How many times is the procedure p applied when (sine 12.15) is evaluated?
b. What is the order of growth in space and number of steps (as a function of a) used by the process generated by the sine procedure when (sine a) is evaluated?
I get the answer of the "order of growth in number of steps" by myself is log3 a. But I found something say, the constants in the expression can be ignored, so it's the same as log a, which looks simpler.
I understand the 2n can be simplified to n, and 2n2 + 1 can be simplified to n2, but not sure if this is applied to log3 a too.
Yes, you can (since we're just interested in the order of the number of steps, not the exact number of steps)
Consider the formula for changing the base of a logarithm:
logb(x) = loga(x) / loga(b)
In other words you can rewrite log3(a) as:
log3(a) = log10(a) / log10(3)
Since log10(3) is just a constant, then for the order of growth we are only interested in the log10(a) term.
(define (myminus x y)
(cond ((zero? y) x)
(else (sub1 (myminus x (sub1 y))))))
(define (myminus_v2 x y)
(cond ((zero? y) x)
(else (myminus_v2 (sub1 x) (sub1 y)))))
Please comment on the differences between these functions in terms of how
much memory is required on the stack for each recursive call. Also, which version might
you expect to be faster, and why?
Thanks!
They should both have a number of steps proportional to y.
The second one is a tail call meaning the interpreter can do a tail elimination meaning it takes up a constant space on the stack whereas in the first the size of the stack is proportional to Y.
myminus creates y continuations to sub1 what the recursion evaluates to. This means you can exhaust rackets memory limit making the program fail. In my trials even as little as 10 million will not succeed with the standard 128MB limit in DrRacket.
myminus_v2 is tail recursive and since racket have same properties as what scheme requires, that tail calls are to be optimized to a goto and not grow the stack, y can be any size, i.e. only your available memory and processing power is the limit to the size.
Your procedures are fine examples of peano arithmetic.
I am trying to understand a solution that I read for an exercise that defines a logarithmic time procedure for finding the nth digit in the Fibonacci sequence. The problem is 1.19 in Structure and Interpretation of Computer Programs (SICP).
SPOILER ALERT: The solution to this problem is discussed below.
Fib(n) can be calculated in linear time as follows: Start with a = 1 and b = 0. Fib(n) always equals the value of b. So initially, with n = 0, Fib(0) = 0. Each time the following transformation is applied, n is incremented by 1 and Fib(n) equals the value of b.
a <-- a + b
b <-- a
To do this in logarithmic time, the problem description defines a transformation T as the transformation
a' <-- bq + aq + ap
b' <-- bp + aq
where p = 0 and q = 1, initially, so that this transformation is the same as the one above.
Then applying the above transformation twice, the exercise guides us to express the new values a'' and b'' in terms of the original values of a and b.
a'' <-- b'q + a'q + a'p = (2pq + q^2)b + (2pq + q^2)a + (p^2 + q^2)a
b' <-- b'p + a'q = (p^2 + q^2)b + (2pq + q^2)a
The exercise then refers to such application of applying a transformation twice as "squaring a transformation". Am I correct in my understanding?
The solution to this exercise applies the technique of using the value of squared transformations above to produce a solution that runs in logarithmic time. How does the problem run in logarithmic time? It seems to me that every time we use the result of applying a squared transformation, we need to do one transformation instead of two. So how do we successively cut the number of steps in half every time?
The solution from schemewiki.org is posted below:
(define (fib n)
(fib-iter 1 0 0 1 n))
(define (fib-iter a b p q count)
(cond ((= count 0) b)
((even? count)
(fib-iter a
b
(+ (square p) (square q))
(+ (* 2 p q) (square q))
(/ count 2)))
(else (fib-iter (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(- count 1)))))
(define (square x) (* x x))
The exercise then refers to such application of applying a transformation twice as "squaring a transformation". Am I correct in my understanding?
Yes, squaring a transformation means applying it twice or (as is the case in the solution to this exercise) finding another transformation that is equivalent to applying it twice.
How does the problem run in logarithmic time? It seems to me that every time we use the result of applying a squared transformation, we need to do one transformation instead of two. So how do we successively cut the number of steps in half every time?
Squaring the given transformation enables us to cut down the number of steps because the values of p and q grow much faster in the squared transformation than they do in the original one. This is analogous to the way you can compute exponents using successive squaring much faster than by repeated multiplication.
So how do we successively cut the number of steps in half every time?
This is in the code given. Whenever count is even, (/ count 2) is passed for count on the next iteration. No matter what value of n is passed in on the initial iteration, it will be even on alternating iterations (worst case).
You can read my blog post on SICP Exercise 1.19: Computing Fibonacci numbers if you want to see a step-by-step derivation of the squared transformation in this exercise.
#Bill-the-Lizard provides a nice proof, but you are allowing yourself to be conflicted by what you think of the word "twice" and the word "square", in relation to transforms.
a) Computing twice the term T--that is, two-times-T--is a case of multiplication. The process of multiplication is simply a process of incrementing T by a constant value at each step, where the constant value is the original term itself.
BUT by contrast:
b) The given fibonacci transform is a process that requires use of the most current state of term T at each step of manipulation (as opposed to the use of a constant value). AND, the formula for manipulation is not a simple increment, but in effect, a quadratic expression (i.e. involves squaring at each successive step).
Like bill says, this successive squaring effect will become very clear if you step through it in your debugger (I prefer to compute a few simple cases by hand whenever I get stuck somewhere).
Think of the process another way:
To reach your destination if you could cover the square of the current distance in the next step, but still somehow manage to take a constant amount of time to complete each step, you're going to get there way faster than if you take constant steps, each in constant time.