Racket/Erastothenes: Skillnad mellan sidversioner

Från Täpp-Anders
Hoppa till navigeringHoppa till sök
Ingen redigeringssammanfattning
Ingen redigeringssammanfattning
Rad 1: Rad 1:
[[category:Algoritmer]]
[[category:Programmering]]
[[category:Racket]]
<pre>
#lang racket
#lang racket


;;;;;
;;;;;
;; Erastothenes såll implementerad i Racket
;; Eratosthenes såll implementerad i Racket
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se
;; Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se
;;;;;
;;;;;


(require racket/format)


;; Skapar en vektor där indexet motsvarar talet.
;; Skapar en vektor där indexet motsvarar talet.
;; Om (vector-ref v n) är #t, så är n ett primtal.
;; Om (vector-ref v n) är #t, så är n ett primtal.
(define (make-sieve limit)
(define (make-sieve limit)
  ;; 1. SKAPA VEKTORN
  ;; Vi skapar en vektor med storleken (limit + 1). Eftersom index börjar på 0,
  ;; måste vektorn vara ett element större än 'limit' för att 'limit' ska få plats som sista index.
  ;; Vi antar från början att ALLA tal är primtal (#t), och sållar sedan bort de som inte är det.
   (define sieve (make-vector (add1 limit) #t))
   (define sieve (make-vector (add1 limit) #t))
    
    
   ;; 0 och 1 är aldrig primtal
   ;; 2. BASFALL (0 OCH 1)
   ;; Primtal är tal som är större än 1 och endast jämt delbara med
   ;; 0 och 1 är aldrig primtal enligt definitionen (ett primtal måste vara större än 1).
   ;; sig själv eller 1. Så börja med att markera 0 och 1 som icke-prim.
   ;; Om användaren har angett en limit som inkluderar dessa, markerar vi dem direkt som #f (falskt).
   (when (>= limit 0) (vector-set! sieve 0 #f))
   (when (>= limit 0) (vector-set! sieve 0 #f))
   (when (>= limit 1) (vector-set! sieve 1 #f))
   (when (>= limit 1) (vector-set! sieve 1 #f))
    
    
   ;; Vi behöver bara gå upp till kvadratroten av limit
  ;; 3. HUVUDLOOPEN (YTTRE)
   ;; p går från start till stopp och markerar alla multiplar som  
   ;; Vi behöver bara gå upp till kvadratroten av limit (avrundat nedåt till heltal).
   ;; icke prima. Dvs 2, 4, 6, 8, 10, 12 osv (alla multiplar av 2)
   ;; Varför? Om ett tal har en faktor som är större än kvadratroten, måste dess
   ;; sedan 3, 6, 9, 12, 15 osv (alla multiplar av 3) etc
   ;; motsvarande partnerfaktor vara mindre än kvadratroten. Har vi redan rensat
  ;; det som inte är markera när vi når sqrt(limit)+1 är alltså primtal
   ;; alla mindre faktorer så har vi automatiskt tagit hand om de större!
   (for ((p (in-range 2 (add1 (integer-sqrt limit)))))
   (for ((p (in-range 2 (add1 (integer-sqrt limit)))))
   
    ;; Om index 'p' fortfarande är #t, så har vi hittat ett primtal.
     (when (vector-ref sieve p)
     (when (vector-ref sieve p)
       ;; Markera alla multiplar av p som falska (starta vid p*p)
     
      ;; 4. SÅLLNINGSLOOPEN (INRE)
       ;; Markera alla multiplar av p som falska (inte primtal).
      ;; Optimering: Vi startar direkt vid (* p p) istället för (* p 2).
      ;; Varför? Alla multiplar mindre än p*p (som 2p, 3p, 4p... upp till (p-1)p)
      ;; har redan markerats som #f i tidigare varv av den yttre loopen.
      ;; Det tredje argumentet 'p' gör att loopen hoppar med p-steg i taget (p*p, p*p+p, p*p+2p, osv).
       (for ((multiple (in-range (* p p) (add1 limit) p)))
       (for ((multiple (in-range (* p p) (add1 limit) p)))
         (vector-set! sieve multiple #f)))) ; inte primtal
         (vector-set! sieve multiple #f)))) ; inte primtal
   ;; returnera sållet till anroparen
       
   ;; Returnera det färdiga sållet (vektorn) till anroparen
   sieve)
   sieve)


Rad 40: Rad 48:
;; Returnerar en lista med alla primtal mellan start och stop
;; Returnerar en lista med alla primtal mellan start och stop
(define (primes-between start stop)
(define (primes-between start stop)
  ;; Kontrollera om intervallet är logiskt. Om start är större än stop finns det inga tal.
   (if (> start stop)
   (if (> start stop)
       '() ;Returnerar tomt om start > stop
       '() ; Returnerar tomt om start > stop
       (let ([is-prime? (make-sieve stop)])
     
         (for/list ([n (in-range (max start 2) (add1 stop))]
      ;; Skapa sållet för hela intervallet upp till 'stop'.
                   #:when (vector-ref is-prime? n))
       (let ((is-prime? (make-sieve stop)))
       
        ;; for/list samlar automatiskt ihop alla 'n' som matchar villkoret till en lista.
        ;; (max start 2) förhindrar att vi letar efter primtal under 2, då de inte finns.
         (for/list ((n (in-range (max start 2) (add1 stop)))
                   #:when (vector-ref is-prime? n)) ; Villkor: Endast om index n är #t i sållet
           n))))
           n))))


;; Hjälpmakro för att tajma hur lång tid operationen tog
;; Hjälpmakro för att tajma hur lång tid operationen tog
(require racket/format)
(define-syntax-rule (timeit expr)
(define-syntax-rule (timeit expr)
  ;; Spara starttiden i millisekunder innan uttrycket körs
   (let ((start (current-inexact-milliseconds)))
   (let ((start (current-inexact-milliseconds)))
    ;; Kör uttrycket 'expr' (t.ex. vår funktion) och spara resultatet i 'result'
     (let ((result expr))
     (let ((result expr))
      ;; Skriv ut tidsskillnaden med snygg lokal formatering
       (printf "Tid: ~a ms\n" (~r (- (current-inexact-milliseconds) start)
       (printf "Tid: ~a ms\n" (~r (- (current-inexact-milliseconds) start)
              #:group-sep " "    ;tusentalsavskiljare
                                #:group-sep   " "    ; tusentalsavskiljare (blanksteg)
              #:decimal-sep ","   ;decimalsavskiljare
                                #:decimal-sep ","     ; decimalsavskiljare (komma)
              #:precision 2))     ;antal decimaler
                                #:precision     2))   ; avrunda till 2 decimaler
       result)))  ; returnera svaret
       result)))  ; Returnera svaret från expr så att programmet kan använda det
 


;; Testkörning
;; Testkörning
(timeit (primes-between 10000000 10000100))
(timeit (primes-between 10000000 10000500))
 
 
</pre>

Versionen från 29 juni 2026 kl. 15.51

  1. lang racket
Eratosthenes såll implementerad i Racket
Täpp-Anders Sikvall 2026-04-03 anders@sikvall.se

(require racket/format)

Skapar en vektor där indexet motsvarar talet.
Om (vector-ref v n) är #t, så är n ett primtal.

(define (make-sieve limit)

 ;; 1. SKAPA VEKTORN
 ;; Vi skapar en vektor med storleken (limit + 1). Eftersom index börjar på 0,
 ;; måste vektorn vara ett element större än 'limit' för att 'limit' ska få plats som sista index.
 ;; Vi antar från början att ALLA tal är primtal (#t), och sållar sedan bort de som inte är det.
 (define sieve (make-vector (add1 limit) #t))
 
 ;; 2. BASFALL (0 OCH 1)
 ;; 0 och 1 är aldrig primtal enligt definitionen (ett primtal måste vara större än 1).
 ;; Om användaren har angett en limit som inkluderar dessa, markerar vi dem direkt som #f (falskt).
 (when (>= limit 0) (vector-set! sieve 0 #f))
 (when (>= limit 1) (vector-set! sieve 1 #f))
 
 ;; 3. HUVUDLOOPEN (YTTRE)
 ;; Vi behöver bara gå upp till kvadratroten av limit (avrundat nedåt till heltal).
 ;; Varför? Om ett tal har en faktor som är större än kvadratroten, måste dess 
 ;; motsvarande partnerfaktor vara mindre än kvadratroten. Har vi redan rensat
 ;; alla mindre faktorer så har vi automatiskt tagit hand om de större!
 (for ((p (in-range 2 (add1 (integer-sqrt limit)))))
   
   ;; Om index 'p' fortfarande är #t, så har vi hittat ett primtal.
   (when (vector-ref sieve p)
     
     ;; 4. SÅLLNINGSLOOPEN (INRE)
     ;; Markera alla multiplar av p som falska (inte primtal).
     ;; Optimering: Vi startar direkt vid (* p p) istället för (* p 2).
     ;; Varför? Alla multiplar mindre än p*p (som 2p, 3p, 4p... upp till (p-1)p) 
     ;; har redan markerats som #f i tidigare varv av den yttre loopen.
     ;; Det tredje argumentet 'p' gör att loopen hoppar med p-steg i taget (p*p, p*p+p, p*p+2p, osv).
     (for ((multiple (in-range (* p p) (add1 limit) p)))
       (vector-set! sieve multiple #f)))) ; inte primtal
       
 ;; Returnera det färdiga sållet (vektorn) till anroparen
 sieve)


Returnerar en lista med alla primtal mellan start och stop

(define (primes-between start stop)

 ;; Kontrollera om intervallet är logiskt. Om start är större än stop finns det inga tal.
 (if (> start stop)
     '() ; Returnerar tomt om start > stop
     
     ;; Skapa sållet för hela intervallet upp till 'stop'.
     (let ((is-prime? (make-sieve stop)))
       
       ;; for/list samlar automatiskt ihop alla 'n' som matchar villkoret till en lista.
       ;; (max start 2) förhindrar att vi letar efter primtal under 2, då de inte finns.
       (for/list ((n (in-range (max start 2) (add1 stop)))
                  #:when (vector-ref is-prime? n)) ; Villkor: Endast om index n är #t i sållet
         n))))


Hjälpmakro för att tajma hur lång tid operationen tog

(define-syntax-rule (timeit expr)

 ;; Spara starttiden i millisekunder innan uttrycket körs
 (let ((start (current-inexact-milliseconds)))
   ;; Kör uttrycket 'expr' (t.ex. vår funktion) och spara resultatet i 'result'
   (let ((result expr))
     ;; Skriv ut tidsskillnaden med snygg lokal formatering
     (printf "Tid: ~a ms\n" (~r (- (current-inexact-milliseconds) start)
                               #:group-sep   " "     ; tusentalsavskiljare (blanksteg)
                               #:decimal-sep ","     ; decimalsavskiljare (komma)
                               #:precision     2))   ; avrunda till 2 decimaler
     result)))  ; Returnera svaret från expr så att programmet kan använda det


Testkörning

(timeit (primes-between 10000000 10000500))