Help understanding Sieve of Eratosthenes implementation - algorithm

This is boring, I know, but I need a little help understanding an implementation of the Sieve of Eratosthenes. It's the solution to this Programming Praxis problem.
(define (primes n)
(let* ((max-index (quotient (- n 3) 2))
(v (make-vector (+ 1 max-index) #t)))
(let loop ((i 0) (ps '(2)))
(let ((p (+ i i 3)) (startj (+ (* 2 i i) (* 6 i) 3)))
(cond ((>= (* p p) n)
(let loop ((j i) (ps ps))
(cond ((> j max-index) (reverse ps))
((vector-ref v j)
(loop (+ j 1) (cons (+ j j 3) ps)))
(else (loop (+ j 1) ps)))))
((vector-ref v i)
(let loop ((j startj))
(if (<= j max-index)
(begin (vector-set! v j #f)
(loop (+ j p)))))
(loop (+ 1 i) (cons p ps)))
(else (loop (+ 1 i) ps)))))))
The part I'm having trouble with is startj. Now, I can see that p is going to be cycling through odd numbers starting at 3, defined as (+ i i 3). But I don't understand the relationship between p and startj, which is (+ (* 2 i i) (* 6 i) 3).
Edit: I understand that the idea is to skip previously sifted numbers. The puzzle definition states that when sifting a number x, sifting should start at the square of x. So, when sifting 3, start by eliminating 9, etc.
However, what I don't understand is how the author came up with that expression for startj (algebraically speaking).
From the puzzle comments:
In general, when sifting by n, sifting starts at n-squared because all the previous multiples of n have already been sieved.
The rest of the expression has to do with the cross-reference between numbers and sieve indexes. There’s a 2 in the expression because we eliminated all the even numbers before we ever started. There’s a 3 in the expression because Scheme vectors are zero-based, and the numbers 0, 1 and 2 aren’t part of the sieve. I think the 6 is actually a combination of the 2 and the 3, but it’s been a while since I looked at the code, so I’ll leave it to you to figure out.
If anyone could help me with this, that'd be great. Thanks!

I think I've figured it out, with the assistance of programmingpraxis' comments on their website.
To restate the problem, startj is defined in the listing as (+ (* 2 i i) (* 6 i) 3), i.e. 2i^2 + 6i + 3.
I didn't initially understand how this expression related to p - since 'sifting' for a number p should start at p^2, I figured that startj should be something relating to 4i^2 + 12i + 9.
However, startj is an index into the vector v, which contains odd numbers starting from 3. Therefore, the index for p^2 is actually (p^2 - 3) / 2.
Expanding the equation: (p^2 - 3) / 2 = ([4i^2 + 12i + 9] - 3) / 2 = 2i^2 + 6i + 3 - which is the value of startj.
I feel it might've been clearer to define startj as (quotient (- (* p p) 3) 2), but anyway - I think that solves it :)

David Seiler: perhaps not the clearest, but in addition to implementing the basic Sieve it also has to implement the three optimizations described in the exercise.
Harto: that was my second exercise. I was still experimenting with my writing style.
Ephemient: correct.
See a more complete explanation in my comment at Programming Praxis.
EDIT: I've added an additional comment at Programming Praxis. And when I actually looked at the code, I was wrong about the derivation of the number 6 in the formula; sorry I mislead you.

Related

Computing a series in racket

Ok, I'll start out by saying that this is a hw question. That being said, I'm not looking for the answer, just some direction to the answer.
I've got to compute a series up to n. My initial thoughts were to use recursion, and do something like the following
(define (series-b n)
(if (= n -1) 0 ; Not sure how to handle this
(+ (/ (expt -1 n) (factorial n)) (series-b (sub1 n)))
)
)
That seems to be the way to do this. However, I'm not really sure how to handle the -1 case, and that is throwing my expected answers off. Thanks in advance.
Edit
I do have some test cases, and they are as follows
n = 0: 1
n = 1: 1/2
n = 2: 2/3
n = 3: 5/8
n = 4: 19/30
n = 5: 91/144
I'm not entirely sure which series that is either.
Edit 2
I've selected soegaard's answer, however, I did make a small change to the final solution, which is:
(define (series-b n)
(for/sum ([i (+ n 1)])
(/ (expt -1 i)
(factorial (+ i 1))))
)
The accepted answer uses (factorial i) rather than (factorial (+ i 1)). I was not yet familiar with for/sum, but that is a really nice way to handle this problem, so thanks!
Your definitions seems right to me. The value of the empty sum is 0, so you are returning the correct value.
Your solution is the canonical one using recursion. An alternative using for/sum looks like this:
(define (series-c n)
(for/sum ([i (+ n 1)])
(/ (expt -1 i)
(factorial (+ i 1))))
You're doing well, and I think you're very close.
Start by writing a few test cases. Make sure to include test cases for the base case.
It's hard to answer the math part of the question, because I can't tell what series it is that you're computing!
You can implement this series as a SRFI 41 stream!
(require srfi/41)
(define negatives (stream-cons -1 (stream-map sub1 negatives)))
(define terms (stream-cons 1 (stream-map / terms (stream-cdr negatives))))
(define series (stream-cons 1 (stream-map + series (stream-cdr terms))))
Example usage:
> (stream->list 10 series)
(1 1/2 2/3 5/8 19/30 91/144 177/280 3641/5760 28673/45360 28319/44800)
Don't like streams? I like soegaard's answer, except that it has to recompute the factorial each time! I wish for/sum has the ability to hold "state" values the way for/fold does. Here's an implementation using for/fold:
(define (factorial-series n)
(define-values (sum _)
(for/fold ((sum 0) (value 1))
((i (in-range -2 (- -3 n) -1)))
(values (+ sum value)
(/ value i))))
sum)
Example usage:
> (map factorial-series (range 10))
(1 1/2 2/3 5/8 19/30 91/144 177/280 3641/5760 28673/45360 28319/44800)

Does call/cc simulate goto this way?

In the book Lisp in Small Pieces, there is the following example code, which is intended to demo that call/cc could simulate goto.
(define (fact n)
(let ((r 1) (k 'void))
(call/cc (lambda (c) (set! k c) 'void))
(set! r (* r n))
(set! n (- n 1))
(if (= n 1) r (k 'recurse))))
However, I'm not sure if I'm misunderstanding something, but I cannot see that this is the way call/cc would simulate goto. When k is applied in the last line, the restored continuation has the r and n of the original continuation, whose values are not changed by the two set! applications. So the entire loop will never terminate.
Is the book wrong in this example? Or did I miss anything?
the restored continuation has the r and n of the original
continuation, whose values are not changed by the two set!
applications.
Nope; that's the important part; the changes to the values are visible. They're not reset. I'm not sure whether the question should be considered a duplicate or not, but this came up in call-with-current-continuation - state saving concept as well, where the asker noted that (look at the question for the whole context):
Calling next 3 times produces 0, 1 and 'done. That means when state used the function k given by generator it didn't
restore the state of the program.
You could test this very simply by printing the values of r and n after saving the continuation. You'll see that the updated values are there. For instance:
(define (fact n)
(let ((r 1) (k 'void))
(call-with-current-continuation (lambda (c) (set! k c) 'void))
(display "r: ") (display r) (newline)
(display "n: ") (display n) (newline)
(set! r (* r n))
(set! n (- n 1))
(if (= n 1) r (k 'recurse))))
> (fact 6)
r: 1
n: 6
r: 6
n: 5
r: 30
n: 4
r: 120
n: 3
r: 360
n: 2
720
Related Questions:
Also see:
Explaining different behavior of variables referenced in continuations? (not so useful in explaining the behavior, but it's related)

Sort prime factors in ascending order Scheme

I am new to Scheme and I want to sort the prime factors of a number into ascending order. I found this code, but it does not sort.
(define (primefact n)
(let loop ([n n] [m 2] [factors (list)])
(cond [(= n 1) factors]
[(= 0 (modulo n m)) (loop (/ n m) 2 (cons m factors))]
[else (loop n (add1 m) factors)])))
Can you please help.
Thank you
I would say it sorts, but descending. If you want to sort the other way, just reverse the result:
(cond [(= n 1) (reverse factors)]
Usually when you need something sorted in the order you get them you can
cons them like this:
(define (primefact-asc n)
(let recur ((n n) (m 2))
(cond ((= n 1) '())
((= 0 (modulo n m)) (cons m (recur (/ n m) m))) ; replaced 2 with m
(else (recur n (+ 1 m))))))
Note that this is not tail recursive since it needs to cons the result, but since the amount of factors in an answer is few (thousands perhaps) it won't matter much.
Also, since it does find the factors in order you don't need to start at 2 every round but the number you found.
Which dialect of Scheme is used?
Three hints:
You need only to test for divisors less equal as the square-root of your Number.
a * b = N ; a < b ---> a <= sqrt( N ).
If you need all Prime-Numbers less some Number, you should use the sieve of eratothenes. See Wikipedia.
Before you start to write a program, look in Wikipedia.
If

Find the Hardy–Ramanujan number using R5RS scheme. Please suggest improvements in idiom and calculations.

I remember once going to see
[Srinivasa Ramanujan] when he was ill
at Putney. I had ridden in taxi cab
number 1729 and remarked that the
number seemed to me rather a dull one,
and that I hoped it was not an
unfavorable omen. "No," he replied,
"it is a very interesting number; it
is the smallest number expressible as
the sum of two cubes in two different
ways." [G. H. Hardy as told in "1729
(number)"]
In "Math Wrath" Joseph Tartakovsky says about this feat, "So what?
Give me two minutes and my calculator watch, and I'll do the same
without exerting any little gray cells." I don't know how
Mr. Tartakovsky would accomplish that proof on a calculator watch, but
the following is my scheme function that enumerates numbers starting
at 1 and stops when it finds a number that is expressable in two
seperate ways by summing the cubes of two positive numbers. And it
indeeds returns 1729.
There are two areas where I would appreciate suggestions for
improvement. One area is, being new to scheme, style and idiom. The other area is around the calculations. Sisc
does not return exact numbers for roots, even when they could be. For
example (expt 27 1/3) yields 2.9999999999999996. But I do get exact
retults when cubing an exact number, (expt 3 3) yields 27. My
solution was to get the exact floor of a cube root and then test
against the cube of the floor and the cube of the floor plus one,
counting as a match if either match. This solution seems messy and hard to reason about. Is there a more straightforward way?
; Find the Hardy-Ramanujan number, which is the smallest positive
; integer that is the sum of the cubes of two positivie integers in
; two seperate ways.
(define (hardy-ramanujan-number)
(let ((how-many-sum-of-2-positive-cubes
; while i^3 + 1 < n/1
; tmp := exact_floor(cube-root(n - i^3))
; if n = i^3 + tmp^3 or n = i^3 + (tmp + 1) ^3 then count := count + 1
; return count
(lambda (n)
(let ((cube (lambda (n) (expt n 3)))
(cube-root (lambda (n) (inexact->exact (expt n 1/3)))))
(let iter ((i 1) (count 0))
(if (> (+ (expt i 3) 1) (/ n 2))
count
(let* ((cube-i (cube i))
(tmp (floor (cube-root (- n cube-i)))))
(iter (+ i 1)
(+ count
(if (or (= n (+ cube-i (cube tmp)))
(= n (+ cube-i (cube (+ tmp 1)))))
1
0))))))))))
(let iter ((n 1))
(if (= (how-many-sum-of-2-positive-cubes n) 2)
n
(iter (+ 1 n))))))
Your code looks mostly fine, I see a few very minor things to comment on:
There's no need to define cube and cube-root at the innermost scope,
Using define for internal functions makes it look a little clearer,
This is related to the second part of your question: you're using inexact->exact on a floating point number which can lead to large rationals (in the sense that you allocate a pair of two big integers) -- it would be better to avoid this,
Doing that still doesn't solve the extra test that you do -- but that's only because you're not certain if you have the right number of if you missed by 1. Given that it should be close to an integer, you can just use round and then do one check, saving you one test.
Fixing the above, and doing it in one function that returns the number when it's found, and using some more "obvious" identifier names, I get this:
(define (hardy-ramanujan-number n)
(define (cube n) (expt n 3))
(define (cube-root n) (inexact->exact (round (expt n 1/3))))
(let iter ([i 1] [count 0])
(if (> (+ (cube i) 1) (/ n 2))
(hardy-ramanujan-number (+ n 1))
(let* ([i^3 (cube i)]
[j^3 (cube (cube-root (- n i^3)))]
[count (if (= n (+ i^3 j^3)) (+ count 1) count)])
(if (= count 2) n (iter (+ i 1) count))))))
I'm running this on Racket, and it looks like it's about 10 times faster (50ms vs 5ms).
Different Schemes behave differently when it comes to exact exponentiation: some return an exact result when possible, some an inexact result in all cases. You can look at ExactExpt, one of my set of implementation contrasts pages, to see which Schemes do what.

SICP exercise 1.16, where is my bug, because it looks right to me

I've just started working through this book for fun; I wish it were homework, but I could never afford to attend MIT, and there are tons of people smarter than me anyway. :p
fast-exp is supposed to find b^n, i.e. 4^2 = 16, 3^3 = 27
(define (fast-exp b n)
(define (fast-exp-iter n-prime a)
(cond ((= n-prime 1) a)
((= (remainder n-prime 2) 1) (fast-exp-iter (- n-prime 1) (* a b)))
(else (fast-exp-iter (/ n-prime 2) (* a b b)))))
(fast-exp-iter n 1))
fast-exp 4 2; Expected 16, Actual 2
You forgot to call fast-exp. Instead, you evaluated three separate atoms. To actually evaluate the fast-exp of 4 to the 2, you'd have to write
(fast-exp 4 2)
The solution you have written here is also incorrect. e.g. Check out (fast-exp 2 6). Expected: 64, actual: 32.
Your solution is calculating wrong answers. (See http://ideone.com/quT6A) In fact, how you in principle can write a tail-recursive fast exponentiation passing only two numbers as arguments? I don't think it's even possible, because in the middle of computation you don't know what multiplier to use if you encounter odd exponent.
But I can give an example of working solution that is exactly what is expected by SICP authors (iterative process using "invariant quantity" (a * b^n), where a is initially 1)
(define (pow x y)
(define (powi acc x y)
(cond
((= y 0) acc)
((odd? y) (powi (* acc x) x (- y 1)))
(else (powi acc (* x x) (/ y 2)))))
(powi 1 x y))

Resources