LINUX.ORG.RU

Любителям LISP, primes.clj: быстрое вычисление простых чисел


0

1

Есть такая программа: http://code.google.com/p/jngmisc/source/browse/clojure/primes/

Я правильно понял, что первый миллион простых чисел: http://code.google.com/p/jngmisc/source/browse/clojure/primes/primes_10%5E6.txt проверяется за 29 секунд?

$ time clojure primes.clj 
"Elapsed time: 28817.209751 msecs"
15485867
"Elapsed time: 94.50152 msecs"
15485867

real    0m29.706s
user    0m34.330s
sys     0m0.380s

Текст программы:

;;;; example macro usage with primes



(defn divides? [candidate-divisor dividend]
  (zero? (rem dividend candidate-divisor)))

(defn prime? [num]
  (when (> num 1)
    (every? (fn [x] (not (divides? x num)))
        (range 2 (inc (int (Math/sqrt num)))))))

(defn primes-from [number]
  (filter prime? (iterate inc number)))

(defn primes-in-range [start end]
  (for [x (primes-from start) :while (<= x end)] x))

(defmacro do-primes [var start end & body]
  `(doseq [~var (primes-in-range ~start ~end)] ~@body))

;; example use
;(do-primes i 100 200
;           (print (format "%d " i)))
;(println)


(defn lazy-primes []
  (letfn [(enqueue [sieve n step]
            (let [m (+ n step)]
              (if (sieve m)
                (recur sieve m step)
                (assoc sieve m step))))
          (next-sieve [sieve candidate]
            (if-let [step (sieve candidate)]
              (-> sieve
                (dissoc candidate)
                (enqueue candidate step))
              (enqueue sieve candidate (+ candidate candidate))))
          (next-primes [sieve candidate]
            (if (sieve candidate)
              (recur (next-sieve sieve candidate) (+ candidate 2))
              (cons candidate 
                (lazy-seq (next-primes (next-sieve sieve candidate) 
                            (+ candidate 2))))))]
    (cons 2 (lazy-seq (next-primes {} 3)))))

(def lazy-primes (memoize lazy-primes))

(println (time (nth (lazy-primes) 1E6)))
(println (time (nth (lazy-primes) 1E6)))

★★★★★

да, для версии 1.2, проверяется за 28 сек. но код совершенно не оптимизован. числа заворачиваются в объекты и т.д.

использование версии 1.3 (alpha6) сразу дает 2-х кратный прирост скорости. Добавление type hints и т.п. также улучшает производительность... Но я бы лично переписал этот код немного, чтобы избежать генерации ненужных последовательностей и т.п.

ott ★★★★★
()
Ответ на: комментарий от ott

Круто. А я писал через решето Эратосфена :) Первый миллион простых чисел получился printf'ами через десяток часов.

Может код неправильный?

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv) {
  if (argc < 3) return -1;
  unsigned long long N1,N2;
  unsigned long long i,j; /* 2^32-1 */
  unsigned char f;
  N1 = atoll(argv[1]);
  N2 = atoll(argv[2]);
  for (i = N1; i <= N2; i++) {
    f = 0;
    for (j = 2; j <= i/2; j++) {
      if (i % j == 0) { f = 1; break; }
    }
    if (f == 1) printf ("D %d %d %d\n", i, j, i/j);
    else printf ("P %d\n", i);
  }
  return 0;
}

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

>Может код неправильный?

Суть в том, что проверить, является ли число простым, легче, чем факторизовать его.

Zenom ★★★
()
Ответ на: комментарий от Zenom

> Суть в том, что проверить, является ли число простым, легче, чем факторизовать его.

Но там ведь тоже деление используется (остаток от деления)?

Или проверка на простоту выполняется через умножения?

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

в кложурном варианте сильное ускорение идет за счет запоминания промежуточных результатов - это нельзя сбрасывать со счетов

ott ★★★★★
()
Ответ на: комментарий от pacify

Ошибся. Решето Эратосфена - это именно «заглядывание» вперед - можно
интерпретировать как «запоминание» предыдущих результатов.
У меня же - тупое деление от 2 до i/2.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

> Ошибся. Решето Эратосфена - это именно «заглядывание» вперед - можно интерпретировать как «запоминание» предыдущих результатов. У меня же - тупое деление от 2 до i/2.

есть же еще быстрый, не на 100%, но весьма надежный метод быстрой проверки, является число простым или нет, забыл название. в SICP вроде упоминался

korvin_ ★★★★★
()
Ответ на: комментарий от pacify

> У меня же - тупое деление от 2 до i/2.

ыыы!!! и еще раз!!!

чувак, достаточно делить на числа от 2 до корня квадратного из i

это даже детям в расширенных курсах математики в 5-м классе рассказывали

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> чувак, достаточно делить на числа от 2 до корня квадратного из i

Точно. Ошибся. Я над такими простыми вещами уже не задумываюсь. :)

pacify ★★★★★
() автор топика
Ответ на: комментарий от adriano32

www_linux_org_ru, огромное спасибо. Моему компьютеру наступило огромное облегчение :)

adriano32, если получится - так и сделаю.

pacify ★★★★★
() автор топика

первый миллион простых чисел: проверяется за 29 секунд?



так получается доказано, что джава быстрее процессора?

Karapuz ★★★★★
()
Ответ на: комментарий от adriano32

> Причём делить только на найденные перед этим простые числа.

буст (ускорение проги) при этом не больше 7 раз на задаче ТС, не удивлюсь, если реально там 4

при этом буст в 3 (а то и более) раза можно гораздо проще сделать, рассматривая делимость только на числа 6n+1, 6n-1

www_linux_org_ru ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.