Схема сита Эратосфена

Я искал в Интернете реализацию «Решета Эратосфена» в схеме, и хотя я нашел много контента, ни один из них, похоже, не сделал его так, как мне нужно.

Проблема в том, что большинство алгоритмов либо используют статический конец, либо используют итерацию. Это в сочетании с моим незнанием языка побудило меня попросить всех вас о помощи.

Мне нужна реализация Sieve, которая принимает один аргумент (число от Sieve до), использует только рекурсию и имеет список «минусов» числа с #t (истина) или #f (ложь).

По сути, алгоритм будет таким:

  1. Составьте список из 2-х введенных чисел, каждое из которых начинается с истины
  2. Рекурсивно пройдите и отметьте каждое число, которое делится на 2 ложных
  3. Затем переходите к следующему «истинному» числу в списке, пока только простые числа не останутся помеченными как истинные.
  4. Вывести список

Пример вывода:

> (эрат-сито 20)

((2 . #t) (3 . #t) (4 . #f) (5 . #t) (6 . #f) (7 . #t) (8 . #f) (9 . #f) (10 . #f) (11 . #t) (12 . #f) (13 . #t) (14 . #f) (15 . #f) (16 . #f) (17 . #t) (18 . #f) (19 . #t) (20 . #f))

Если бы у вас также были комментарии, подробно объясняющие код, это было бы очень полезно.

Спасибо!

ПЕРЕСМОТРЕНО ::: Итак, я выучил небольшую схему, чтобы подробнее объяснить свой вопрос ...

Это входит в список.

(define (makeList n)
 (if (> n 2)
  (append (makeList (- n 1)) (list (cons n (and))))
  (list (cons 2 (and)))))

Это возвращает список, в котором каждое кратное делителя помечено как false.

(define (mark-off-multiples numbers divisor)
 (if (null? numbers)
  '()
  (append 
     (list (cons (car (car numbers)) 
                 (not (zero? (modulo (car (car numbers)) divisor))))) 
     (mark-off-multiples (cdr numbers) divisor))))

Теперь это функция, с которой у меня проблемы, похоже, она должна работать, я прошел ее вручную три раза, но я не могу понять, почему она не возвращает то, что мне нужно.

(define (call-mark-off-multiples-for-each-true-number numbers)
 (if (null? numbers)
  '()
  (if (cdr (car numbers))
    (append (list (car numbers))
            (call-mark-off-multiples-for-each-true-number 
               (mark-off-multiples (cdr numbers) (car (car numbers)))))
    (append (list (car numbers))
            (call-mark-off-multiples-for-each-true-number 
               (cdr numbers))))))

Что я пытаюсь сделать, так это, как следует из названия функции, вызвать кратные уценки для каждого номера, который все еще помечен как истинный в списке. Таким образом, вы передаете ((3.#t)(4.#t)(5.#t)), а затем он вызывает mark-off-multiples для 2 и возвращает (3.#t)(4.#f)(5.#t), а вы добавляете к нему (2.#t). Затем он снова вызывает себя, передавая (3.#t)(4.#f)(5.#t), и вызывает кратные уценки с cdr списка, возвращая (4.#f)(5.#t), и продолжает движение вниз по списку ...

На выходе я получаю список со всеми истинами.

Надеюсь, это поможет вам лучше понять мое затруднительное положение.


person WiseOlMan    schedule 29.03.2012    source источник


Ответы (4)


Вот решение, которое работает.

(define (divides? m n)
  (if (eq? (modulo n m) 0)
      #t
      #f))

(define (mark-true n)
  (cons n #t))

(define (mark-divisors n ns)
  (cond ((null? ns) '())
        ((and (unmarked? (car ns)) 
              (divides? n (car ns))) 
           (cons (cons (car ns) #f) (mark-divisors n (cdr ns))))
        (else (cons (car ns) (mark-divisors n (cdr ns))))))

(define (unmarked? n)
  (not (pair? n)))

(define (eratosthenes x)
  (cond ((null? x) '())
        ((unmarked? (car x)) 
           (cons (mark-true (car x)) 
                 (eratosthenes (mark-divisors (car x) (cdr x)))))
        (else (cons (car x) (eratosthenes (cdr x))))))

(eratosthenes (list 2 3 4 5 6))

Я использовал несколько вспомогательных функций, но вы можете добавить их к функции eratosthenes, если хотите. Я думаю, это сделает весь этот бизнес более читабельным.

mark-true сохраняет значение в #t. mark-divisors берет число n и список чисел и объединяет все числа, которые n делят на #f. Практически все остальное говорит само за себя. Эратосфен работает так, как должен, если первая цифра «немаркирована», он отмечает ее как «истинную» или «простую», а затем «вычеркивает» все ее кратные из оставшейся части списка и затем повторяет для каждой последующей «немаркированной» цифра в списке. Моя функция эратосфена делает по сути то же, что вы пытались сделать со своей. Я не уверен, в чем проблема у вас, но, как правило, полезно сделать помощников, чтобы ваши материалы были более читабельными.

Я сделал это в DrRacket с пакетом SICP Нила Ван Дайка. Я не знаю, какую схему вы используете. Сообщите мне, если у вас возникнут проблемы с тем, чтобы это работало.

person hraesvelgr    schedule 30.03.2012
comment
Хотя этот подход работает, это не Решето Эратосфена. Суть алгоритма сита состоит в том, чтобы избежать проведения дорогостоящего теста на простоту путем последовательного отсеивания кратных известных простых чисел. Все, что вам нужно, - это повторное добавление. - person ; 30.03.2012
comment
Вы правы, функция названа неверно. Однако то, как ОП задает вопрос, полностью сводит на нет цель сита. Он хочет вернуть список со всеми исходными элементами, помеченными t или f. Чтобы сделать это, он должен был бы вести два списка, я думаю, а затем использовать тот, который был сгенерирован ситом, для обозначения первого. - person hraesvelgr; 30.03.2012
comment
Кроме того, тест на простоту не должен быть дорогим. Хотя я не думаю, что это так дешево, как сито, Miller-Rabin довольно быстро. Конечно, лучше, чем проверять все делители до sqrt (n). Хотя тест Миллера-Рабина не является детерминированным, как сказал Сассман, более вероятно, что космическое излучение вызовет сбой в работе вашего компьютера и даст неправильный ответ, чем то, что некоторые псевдопреступления обманывают Миллера-Рабина для получения достаточного количества баз. - person hraesvelgr; 30.03.2012
comment
Я изменил свой ответ, чтобы сделать то, о чем просил ОП. - person hraesvelgr; 30.03.2012
comment
Привет, Джош, извини за задержку. Но я посмотрел на ваш ответ, и он не работает на моем компьютере. Тем не менее, это намного сложнее, чем на мой вкус. Я изменил вопрос, чтобы показать вам больше того, что я хотел. Извините за то, что не стал более явным. - person WiseOlMan; 02.04.2012
comment
Это немного сложно. Я не смог протестировать код, так как у меня сейчас не установлена ​​схема. Я установлю один, а потом еще немного поработаю. - person hraesvelgr; 02.04.2012
comment
Да, правильная реализация сита Эрастотена со связанным списком даже не сработает очень хорошо. Следуя фактическому методу, он лучше всего работает с быстрой структурой произвольного доступа, такой как вектор. - person WorBlux; 19.05.2013

(define (prime-sieve-to n)
  (let* ((sz (quotient n 2)) (sv (make-vector sz 1)) (lm (integer-sqrt n)))
    (for ((i (in-range 1 lm))) 
      (cond ((vector-ref sv i)
        (let ((v (+ 1 (* 2 i))))
          (for ((i (in-range (+ i (* v (/ (- v 1) 2))) sz v)))
            (vector-set! sv i 0))))))
    (cons 2
          (for/list ((i (in-range 1 sz)) 
                     #:when (and (> (vector-ref sv i) 0) (> i 0)))
                    (+ 1 (* 2 i))))))

Это еще одна схема на рэкетном диалекте, которая работает, но до 100000000. Кроме того, за его эффективность я не ручаюсь.

person cobie    schedule 18.05.2013
comment
во внешнем цикле for-each (if (vector-ref sv i) всегда истинно. На схеме 0 означает истинное значение, а не ложь, так как в C. - (map ... (2i+1) ... (filter ... комбинация лучше. :) - person Will Ness; 19.05.2013
comment
внутренняя начальная точка for-each соответствует 3v, но можно начинать с v*v. - что такое range? Какую схему вы используете? В диапазон необходимо включать (lm-1)/2, а не lm (range включает второй аргумент?). При этом код будет быстрее (наверное, примерно в два раза из-за lm). :) - person Will Ness; 19.05.2013
comment
Я использую ракетку, и функция диапазона, такая как (диапазон 1 5), дает 1 2 3 4 - person cobie; 20.05.2013
comment
0 по-прежнему является логическим True в Racket, как в Scheme, в cond, как в if. (/ (- v 1) 2) это просто i; (v*v - 1)/2 = (4i^2+4i+1 - 1)/2 = i(2i+2) = i(v+1). В for i in range iv+i to sz, может быть, использование j менее запутанно? значение lm по-прежнему соответствует индексу (lm-1)/2. для i в диапазоне от 1 до sz всегда означает i>0. :) Если сработает до 100 млн за приемлемое время, для Схемы это впечатляет. Интересно, сколько у него времени, чтобы добраться до 15485864? 32452844? - person Will Ness; 20.05.2013

Итак, цель SoE - не проверять делимость, а просто считать по p числам за раз:

(define (make-list n)              ; list of unmarked numbers 2 ... n
  (let loop ((i n) 
             (a '()))
    (if (= i 1)
      a            ; (cons '(2 . #t) (cons (3 . #t) ... (list '(n . #t))...))
      (loop (- i 1) (cons (cons i #t) a)))))

(define (skip2t xs)                ; skip to first unmarked number
  (if (cdar xs) xs (skip2t (cdr xs))))

(define (mark-each! k n i xs)      ; destructive update of list xs - 
  (set-cdr! (car xs) #f)           ;  mark each k-th elem,
  (if (<= (+ i k) n)               ;  head is i, last is n 
    (mark-each! k n (+ i k)
                    (list-tail xs k))))

(define (erat-sieve n)
  (let ((r  (sqrt n))              ; unmarked multiples start at prime's square
        (xs (make-list n)))
    (let loop ((a xs))
      (let ((p (caar a)))          ; next prime
        (cond ((<= p r)
               (mark-each! p n (* p p) (list-tail a (- (* p p) p)))
               (loop (skip2t (cdr a)))))))
    xs))

Так что (erat-sieve 20) ==> ((2 . #t) (3 . #t) (4) (5 . #t) (6) (7 . #t) (8) (9) (10) (11 . #t) (12) (13 . #t) (14) (15) (16) (17 . #t) (18) (19 . #t) (20))


неограниченное сито по формуле

P = {3,5,7,9, ...} \ U {{p 2, p 2 + 2p, p 2 + 4p, p 2 + 6p, ...} | p в P}

могут быть определены с использованием потоков в стиле SICP (как можно увидеть здесь) :

 ;;;; Stream Implementation
 (define (head s) (car s))
 (define (tail s) ((cdr s))) 
 (define-syntax s-cons
   (syntax-rules () ((s-cons h t) (cons h (lambda () t))))) 

 ;;;; Stream Utility Functions
 (define (from-By x s)
   (s-cons x (from-By (+ x s) s)))
 (define (take n s) 
   (cond ((= n 0) '())
         ((= n 1) (list (car s)))
         (else (cons (head s) (take (- n 1) (tail s))))))
 (define (drop n s)
   (cond ((> n 0) (drop (- n 1) (tail s)))
         (else s)))
 (define (s-map f s)
   (s-cons (f (head s)) (s-map f (tail s))))
 (define (s-diff s1 s2)
   (let ((h1 (head s1)) (h2 (head s2)))
    (cond
     ((< h1 h2) (s-cons h1 (s-diff  (tail s1)       s2 )))
     ((< h2 h1)            (s-diff        s1  (tail s2)))
     (else                 (s-diff  (tail s1) (tail s2))))))
 (define (s-union s1 s2)
   (let ((h1 (head s1)) (h2 (head s2)))
    (cond
     ((< h1 h2) (s-cons h1 (s-union (tail s1)       s2 )))
     ((< h2 h1) (s-cons h2 (s-union       s1  (tail s2))))
     (else      (s-cons h1 (s-union (tail s1) (tail s2)))))))

 ;;;; odd multiples of an odd prime
 (define (mults p) (from-By (* p p) (* 2 p)))

 ;;;; The Sieve itself, bounded, ~ O(n^1.4) in n primes produced
 ;;;;   (unbounded version runs at ~ O(n^2.2), and growing worse)
 ;;;;   **only valid up to m**, includes composites above it        !!NB!!
 (define (primes-To m)
   (define (sieve s) 
    (let ((p (head s))) 
     (cond ((> (* p p) m) s) 
      (else (s-cons p 
              (sieve (s-diff (tail s) (mults p))))))))
   (s-cons 2 (sieve (from-By 3 2))))

 ;;;; all the primes' multiples, tree-merged, removed; 
 ;;;;    ~O(n^1.17..1.15) time in producing 100K .. 1M primes
 ;;;;    ~O(1) space (O(pi(sqrt(m))) probably)
 (define (primes-TM)
   (define (no-mults-From from)
       (s-diff (from-By from 2) (s-tree-join (s-map mults odd-primes))))
   (define odd-primes 
       (s-cons 3 (no-mults-From 5)))
   (s-cons 2 (no-mults-From 3)))

 ;;;; join an ordered stream of streams (here, of primes' multiples)
 ;;;; into one ordered stream, via an infinite right-deepening tree
 (define (s-tree-join sts)                               ;; sts -> s
   (define (join-With of-Tail sts)                       ;; sts -> s
     (s-cons (head (head sts))
              (s-union (tail (head sts)) (of-Tail (tail sts)))))
   (define (pairs sts)                                   ;; sts -> sts
     (s-cons (join-With head sts) (pairs (tail (tail sts)))))
   (join-With (lambda (t) (s-tree-join (pairs t))) sts))

 ;;;; Print 10 last primes from the first thousand primes
 (begin 
   (newline)
   (display (take 10 (drop 990 (primes-To 7919)))) (newline)
   (display (take 10 (drop 990 (primes-TM)))) (newline))

Протестировано на схеме MIT.

person Will Ness    schedule 24.08.2012

код и объяснения можно найти в SICP 3.5.2 Infinite Streams http://mitpress.mit.edu/sicp/full-text/book/book-ZH-24.html#%_sec_3.5.2

person FooBee    schedule 29.03.2012
comment
Обратите внимание: вопреки тому, что говорится в книге, это не решето Эратосфена. Алгоритм - сито Тернера, аккуратный, но ужасно неэффективный. - person Daniel Fischer; 29.03.2012
comment
Я нигде не могу найти оригинальное руководство по SASL 1976 года; обычная формулировка сита Тернера в виде списков взята из переработанного издания 1983 года с выражениями ZF. Дэви 1992 дает это как primes = sieve [2..] ; sieve (p:nos) = p : sieve (remove (multsof p) nos). Любопытно (см. обновление), он допускает две пары remove / multsof реализации. Его также можно настроить на отсрочку, хотя такой псевдокод еще не превратился в код ни на одном из известных мне языков. :) - person Will Ness; 26.01.2020