1

I am working on coming up with a solution for coming with a list of prime numbers using the Sieve of Eratosthenes. So the program is supposed to find prime numbers up to a specific number "n". I believe I have come up with an incomplete solution but not sure how to proceed from here.

;;;This is a helper function
(define sievehelper 
   (lambda (list)
      ;;; This is the base condition where we are comparing 
      ;;; that the divisor is less than the square root of n""
      (if (> (car list) (sqrt (car (reverse list))))
          '()
          ;;; If the base condition has not be reached, then send it through 
          ;;; this filter function where not-divisible by will go through
          ;;; the specified list and only output the list which contains
          ;;; the numbers that are not divisible by (car list)
          (filterfunc (not-divisible-by (car list)) 
                      list)))

I have tested the other helper function fliterfunc on its own and it works fine.

;;;; This is the main function that calls the above helper function
(define sieve 
   (lambda (n)
      ;;; `makelist` is a helper function to generate the list from 2 to n
      (sievehelper (makelist n))))

I have tested the makelist helper function separately and it works fine.

My question is with the helper function "sievehelper" in terms of how to iterate through the different elements in the list as the divisor.

Any help is appreciated. Thank you.

Will Ness
  • 62,652
  • 8
  • 86
  • 167
  • Note that Sieve of Eratosthenes does not involve testing for divisibility. If you do that, you have a different sieve. – molbdnilo Feb 23 '21 at 08:26
  • I agree that it does not involve divisibility. That is why I tried to call it a modified sieve because this is what we have been asked to do, specifically the filter function – user15155886 Feb 23 '21 at 08:28
  • Well, I read "I am working on [...] a solution [...] using the Sieve of Eratosthenes".as meaning what it says. (You need one more step in addition to a filtering pass; I suspect that you become stuck through attempting one function to do all the work.) – molbdnilo Feb 23 '21 at 09:00
  • *"My question is with the helper function `sievehelper` in terms of how to iterate through the different elements in the list as the divisor."* this actually isn't clear at all. what exactly is the difficulty? first, you need to show us the functions which you say you've implemented and tested, mainly, `filterfunc`. `makelist` is obvious but `filterfunc` could go several different ways and we need to see yours, to be able to help. then what's left is `not-divisible-by`. Do you need help implementing it, is that the question? have you tried coding it? please clarify. – Will Ness Feb 23 '21 at 15:56

2 Answers2

2

One piece of code that leads to getting stuck is (if ( > (car list) (sqrt (car(reverse list)))), which looks a lot like the loop condition you might use in other languages (and the word "iterate" hints at peeking at other languages, as well).
I would recommend that you start over, with a different strategy.

When you work with lists, you usually want to recurse on their structure alone.

Assume that you have the list of all integers, starting with 2.
As a first step, you want to keep the two, and remove all its multiples from the remainder of the list.
Now, the result of that removal will give you a list that starts with the next prime number - i.e. 3 - so you can repeat the procedure with that partial result which will remove all multiples of 3, and repeat again with that partial result, and so on until there is no more list.

(Note that this is far from as efficient as it could be, but is more a "get started with thinking recursively" level of suggestion. Read Will's answer for more.)

Applying some wishful thinking and assuming that there is a procedure remove-multiples-of which does what it sounds like, this could look like this:

(define (my-sieve-helper ls)
  (if (null? ls)
      '()
      (cons (car ls)
            (my-sieve-helper (remove-multiples-of (car ls) (cdr ls))))))

So, remove-multiples-of...
This is the same as keeping all the numbers that are not divisible by a number, so let's dream up another function:

(define (remove-multiples-of x ls) (filter (not-divisible-by x) ls))

where (not-divisible-by x) is a procedure that takes a number and returns whether that number is not divisible by x.

(define (not-divisible-by n) (lambda (x) (not (zero? (remainder x n)))))

And now we can add a suitable wrapper.
I did this:

(define (from-to m n)
  (if (> m n)
      '()
      (cons m (from-to (+ m 1) n))))

(define (my-sieve n) (my-sieve-helper (from-to 2 n)))

Test:

> (my-sieve 100)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)
molbdnilo
  • 55,783
  • 3
  • 31
  • 71
  • Thank you so very much for the suggestion. I will try this out – user15155886 Feb 23 '21 at 16:17
  • "until there is no more list" is also a loop stop condition, but one that happens to markedly worsen the time complexity of the OP code. :) – Will Ness Feb 23 '21 at 19:39
  • Thanks @molbdnilo. My problems were 2 fold, the base condition checking was incorrect and also there was call back to the recursive structure. Also in this part can you please explain what the cons (car ls) is doing '''(cons (car ls) (my-sieve-helper (remove-multiples-of (car ls) (cdr ls)))) "" Is it creating a list by adding the 1st element of the remaining list back to the head of the list – user15155886 Feb 23 '21 at 22:33
  • @user15155886 no, `(cons (car ls) (my-sieve-helper (remove-multiples-of (car ls) (cdr ls))))` == `(let* ( (x (car ls)) (xs (cdr ls)) (ys (remove-multiples-of x xs)) (zs (my-sieve-helper ys)) ) (cons x zs))` so it is adding the first element of the list to the result of recursively processing the result of removing the multiples of the first element in the rest of the list. – Will Ness Feb 28 '21 at 09:03
1

Well, your question presents an interesting case of under-specification that can be advantageous, delaying the actual specification -- not just implementation as usual -- of subroutines used by your piece of code.

Here we have sievehelper which uses the non-implemented non-specified filterfunc and not-divisible-by. Despite their suggestive names these function can do anything, as long as they work, when used together, and make the function using them, sievehelper, also do its work as intended. Delayed specification for the win!

So let's first see what can be intended with the sievehelper as given, what does it do? Assuming the obvious meaning of the two subroutines, it seems to be intended to perform a one-step filtering of its working sequence, culling it from any multiples of its "head" element, the one in its car position.

It would signal the stopping condition by returning (). That stopping condition is a*a > z, for the input list of [a,b,c,...,z].

Otherwise, it does not perform the looping, but just the one step of it. Your sieve doesn't account for that at all, so will need to be changed to continually call this helper, performing step after step as is usual in sieving algorithms, until the square of the first element is bigger than the last element in the working sequence, when it is indeed safe to stop the culling as all the multiples in the sequence will have already been removed from it as multiples of the smaller primes ...... provided that those smaller primes were present in the initial sequence.

So this discovered requirement falls on the third non-implemented subrouting in use, makelist. You do mention that it must create the list of sequential natural numbers from 2 to n, and now we understand why we needed it to do so.

So then, in order to iterate through the different versions of the input list as each divisor is filtered out from it in turn, using your sievehelper definition as given, your sieve function must be changed as e.g.

(define sieve 
   (lambda (n)
      (let ((init (makelist n)))
        (let loop ((this              init ) 
                   (next (sievehelper init)))
            (if (null? next)
              this
              (cons (car this) 
                    (loop              next 
                          (sievehelper next))))))))

This code comes from the perspective of working with a pair -- the current sequence, and its next iteration. On each step the next version of the list is found after all the multiples of its head element are removed from it by sievehelper (including the head element itself, the next found prime) -- or the empty list is instead returned to signal the end of processing when all the numbers in the list are known to already be prime, by construction.

Trying it out in Racket:

> (sieve 50)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)

(The above code incorporates the fix by ad-absurdum from the followup Q&A entry, to the error that this code originally contained).

To run it, further definitions had to be made:

(define filterfunc filter)

(define (not-divisible-by n)
  (let ((m n))
    (lambda (x)
      (let ((ret (not (= x m))))
         (if (>= x m) (set! m (+ m n)) #f)
         ret))))

(define (makelist n)
  (range 2 (+ 1 n)))   ;; in Racket

Defining the not-divisible-by as we did here makes it to be the sieve of Eratosthenes indeed, only working via additions and comparisons. And stopping early as in your original code keeps its time complexity from getting worse than it should.

Will Ness
  • 62,652
  • 8
  • 86
  • 167
  • Thanks Will. This also definitely helps me understand this issue more and provides another solution that I can work on – user15155886 Feb 23 '21 at 22:46