Сито Эратосфена в рубине

Вместо того, чтобы копировать Ruby-версию этого алгоритма из сети, я хотел создать свою собственную на основе его описания здесь. Однако я не могу понять две вещи

def primeSieve(n)
  primes = Array.new

  for i in 0..n-2
   primes[i] = i+2
  end

  index = 0
  while Math.sqrt(primes.last).ceil > primes[index]
    (primes[index] ** 2).step(primes.length - 1, primes[index]) 
      {|x| x % primes[index] == 0 ? primes.delete(x) : ""}
    index += 1
  end

  primes
end
  1. Почему он не повторяется до конца массива?
  2. Согласно описанию в приведенной выше ссылке, цикл должен прерываться, когда квадратный корень последнего элемента в массиве больше текущего простого числа - мой делает это раньше.

Я вполне уверен, что это как-то связано с операцией удаления, изменяющей длину массива. Например, моя функция в настоящее время дает 2,3,5,7,9,10, когда я ввожу n=10, что явно неверно. Любые предложения о том, как я могу изменить это, чтобы заставить его работать так, как предполагалось?


person Damian    schedule 27.10.2008    source источник
comment
как насчет пробелов, чтобы я мог разобрать ваш код?   -  person Dustin Getz    schedule 28.10.2008
comment
Что ж, этот небольшой опыт на какое-то время оттолкнул меня от Ruby. Кажется, что он обладает всеми выразительными возможностями Perl с ужасной читабельностью Perl, но, по крайней мере, я уже понимаю Perl.   -  person paxdiablo    schedule 28.10.2008
comment
Вы, вероятно, не должны судить Ruby по этому примеру. Я думаю, что Дамиан новичок в Ruby, и это не обычный способ реализации Решета Эратосфена.   -  person Andru Luvisi    schedule 28.10.2008


Ответы (5)


Следующее, кажется, работает. Я убрал арифметику с плавающей запятой и возвел в квадрат вместо извлечения квадратного корня. Я также заменил цикл удаления вызовом «выбрать».

while primes[index]**2 <= primes.last
      prime = primes[index]
      primes = primes.select { |x| x == prime || x%prime != 0 }
      index += 1
end

Изменить: я думаю, что понял, как вы пытаетесь это сделать. Следующее, похоже, работает и, похоже, больше соответствует вашему первоначальному подходу.

while Math.sqrt(primes.last).ceil >= primes[index]
    (primes[index] * 2).step(primes.last, primes[index]) do
      |x|
      primes.delete(x)
    end
    index += 1
end
person Andru Luvisi    schedule 27.10.2008
comment
Высоко ценим Гломек! Я думаю, что первое опубликованное вами решение, безусловно, имеет больше смысла, поскольку ранее я не знал, что операция select является новой для Ruby, и мне пришлось искать ее. Еще раз спасибо :D - person Damian; 28.10.2008
comment
Второй вариант ужасно медленный - первый примерно в 80 раз лучше, но все же примерно на 50% медленнее, чем лучшее, что у меня есть на данный момент. - person Mike Woodhouse; 11.01.2009
comment
Я пытался придумать что-то максимально близкое к исходному коду плаката, но это сработало. - person Andru Luvisi; 12.01.2009
comment
Второй фрагмент - дерьмо. Вызовите удаление на свой страх и риск. Вы попробуйте запустить это для N = 10 миллионов - person nes1983; 05.03.2012
comment
@ nes1983 nes1983 Я не знаком с Ruby, возможно, a.delete(x) требует времени, линейного по длине a? Это действительно нарушило бы всю предпосылку решета, которое не должно сравнивать значения, а скорее использует адреса напрямую в качестве замены значений. Первый фрагмент тоже не годится, проверяя каждый элемент в массиве на делимость — вместо того, чтобы перепрыгивать через него с шагом p (или четным 2p — для шансов;)) . - person Will Ness; 10.03.2012
comment
мы не должны на самом деле удалять что-либо из последовательности, просто отмечаем это - статья WP на момент вашего ответа (и еще много лет после этого) была запутанной и вводящей в заблуждение - неправильно. - person Will Ness; 10.03.2012
comment
Уилл Несс прав. Ни одно из этих двух решений не реализует сито. Это просто вариации пробного деления, которое имеет худшую временную сложность. - person Jørgen Fogh; 11.12.2014

На www.scriptol.org есть более быстрая реализация:

def sieve_upto(top)
  sieve = []
  for i in 2 .. top
    sieve[i] = i
  end
  for i in 2 .. Math.sqrt(top)
    next unless sieve[i]
    (i*i).step(top, i) do |j|
      sieve[j] = nil
    end
  end
  sieve.compact
end

Я думаю, что его можно немного улучшить следующим образом:

def better_sieve_upto(n)
  s = (0..n).to_a
  s[0] = s[1] = nil
  s.each do |p|
    next unless p
    break if p * p > n
    (p*p).step(n, p) { |m| s[m] = nil }
  end
  s.compact
end

... в основном из-за более быстрой инициализации массива, я думаю, но это незначительно. (Я добавил #compact к обоим, чтобы исключить нежелательные nil)

person Mike Woodhouse    schedule 11.01.2009

Это довольно простая реализация псевдокода статьи Википедии с использованием битовый массив.

#!/usr/bin/env ruby -w

require 'rubygems'
require 'bitarray'

def eratosthenes(n)

   a = BitArray.new(n+1)

   (4..n).step(2) { |i|
      a[i] = 1
   }

   (3..(Math.sqrt(n))).each { |i|
       if(a[i] == 0)
           ((i*i)..n).step(2*i) { |j|
               a[j] = 1
           }
       end
   }
   a
 end

def primes(n)
    primes = Array.new
     eratosthenes(n).each_with_index { |isPrime, idx|
        primes << idx if isPrime == 0
     }
     primes[2..-1]
end
person nes1983    schedule 05.03.2012
comment
если вы посмотрите на сам текст статьи, вы увидите, что на самом деле вы можете остановиться на sqrt(n), начать с (i*i) и использовать шаг 2*i для всех простых чисел, кроме 2. - person Will Ness; 10.03.2012
comment
возможно, вам также следует сжать массив и каким-то образом перевести истинные адреса в прямые числа? - person Will Ness; 10.03.2012
comment
@WillNess Вам нужен битовый массив. Однако я не думаю, что Ruby имеет встроенную поддержку для них. github.com/ingramj/bitarray - person nes1983; 11.03.2012
comment
нет, я имею в виду, что вы возвращаете свой a как массив истинных и ложных значений; разве он не должен быть преобразован в список/массив чисел в конце? - person Will Ness; 11.03.2012
comment
@УиллНесс primes = Array.new; bitarray.each_with_index {|isPrime, index| primes << index if isPrime} - person nes1983; 11.03.2012

Это ссылка для интересующихся. Код взят с этого сайта.

Этот код также использует Решето Эратосфена.

n = 1000000
ns = (n**0.5).to_i + 1
is_prime = [false, false] + [true]*(n-1)
2.upto(ns) do |i|
  next if !is_prime[i]
  (i*i).step(n, i) do |j|
    is_prime[j] = false
  end
end

count = 0
list = (0..n).map do |i|
  count += 1 if is_prime[i]
  count
end

while gets
  puts list[$_.to_i]
end

А вот и еще один.

def eratosthenes(n)
  nums = [nil, nil, *2..n]
  (2..Math.sqrt(n)).each do |i|
    (i**2..n).step(i){|m| nums[m] = nil}  if nums[i]
  end
  nums.compact
end

p eratosthenes(100)
person shin    schedule 11.05.2014

or

x = []
Prime.each(123) do |p|
  x << p
end

Здесь может быть способ использовать инъекцию, но сегодня у меня болит голова.

person pjammer    schedule 17.10.2013
comment
Исходный вопрос касается создания сит, list_of_primes = Prime.each(123).inject([], :‹‹) будет работать, если вы хотите использовать inject/reduce, но просто сказав list_of_primes = Prime.each(123) может быть умнее во многих ситуациях. - person user2251284; 20.11.2014