这是Sieve of Eratosthenes的实现。
class PrimeGenerator
def self.get_primes_between( x, y)
sieve_array = Array.new(y) {|index|
(index == 0 ? 0 : index+1)
}
position_when_we_can_stop_checking = Math.sqrt(y).to_i
(2..position_when_we_can_stop_checking).each{|factor|
sieve_array[(factor).. (y-1)].each{|number|
sieve_array[number-1] = 0 if isMultipleOf(number, factor)
}
}
sieve_array.select{|element|
( (element != 0) && ( (x..y).include? element) )
}
end
def self.isMultipleOf(x, y)
return (x % y) == 0
end
end
现在我这样做是为了'提交解决问题的解决方案,因为你有时间杀死'网站。我选择红宝石作为我的impl语言..然而我被宣布超时。 我做了一些基准测试
require 'benchmark'
Benchmark.bmbm do |x|
x.report ("get primes") { PrimeGenerator.get_primes_between(10000, 100000)}
end
ruby 1.9.1p0(2009-01-30修订版21907)[i386-mswin32]
L:\Gishu\Ruby>ruby prime_generator.rb
Rehearsal ----------------------------------------------
get primes 33.953000 0.047000 34.000000 ( 34.343750)
------------------------------------ total: 34.000000sec
user system total real
get primes 33.735000 0.000000 33.735000 ( 33.843750)
ruby 1.8.6(2007-03-13 patchlevel 0)[i386-mswin32]
Rehearsal ----------------------------------------------
get primes 65.922000 0.000000 65.922000 ( 66.110000)
------------------------------------ total: 65.922000sec
user system total real
get primes 67.359000 0.016000 67.375000 ( 67.656000)
所以我在C#2.0 / VS 2008中重新编写了这个东西 - > 722毫秒
所以现在这让我觉得我的实现是一个问题,还是这种语言之间的差异? (我对1.9 Ruby Ruby感到惊讶......直到我不得不将它与C#进行比较:)
更新 结果是我的“put-eratosthenes-to-shame-adapt”毕竟:) 消除不必要的循环迭代是主要的优化。如果有人对细节感兴趣,你可以阅读here;反正这个问题太长了。
答案 0 :(得分:4)
我首先看一下你的内循环。 sieve_array[(factor).. (y-1)]
每次执行时都会创建一个新数组。相反,尝试用普通的索引循环替换它。
答案 1 :(得分:4)
显然,每台计算机都会以不同的方式对其进行基准测试,但是通过使用每个块删除数组上的循环,并通过引起内部,我能够在我的机器(Ruby 1.8.6)上使运行速度提高约50倍。循环检查更少的数字。
factor=2
while factor < position_when_we_can_stop_checking
number = factor
while number < y-1
sieve_array[number-1] = 0 if isMultipleOf(number, factor)
number = number + factor; # Was incrementing by 1, causing too many checks
end
factor = factor +1
end
答案 2 :(得分:2)
Eratosthenes的Sieve作为寻找素数的说明性方法很好,但我会实现它有点不同。实质是你不必检查已知素数的倍数的数字。现在,您可以创建一个列表,列出所有顺序素数的列表,直到您正在检查的数字的平方根,然后只需通过素数列表来检查素数就足够了,而不是使用数组来存储这些信息。
如果你想到它,这就是你对图像所做的,但是采用更“虚拟”的方式。
修改:快速入侵我的意思(不是从网上复制而来);)
public class Sieve {
private readonly List<int> primes = new List<int>();
private int maxProcessed;
public Sieve() {
primes.Add(maxProcessed = 2); // one could add more to speed things up a little, but one is required
}
public bool IsPrime(int i) {
// first check if we can compare against known primes
if (i <= primes[primes.Count-1]) {
return primes.BinarySearch(i) >= 0;
}
// if not, make sure that we got all primes up to the square of i
int maxFactor = (int)Math.Sqrt(i);
while (maxProcessed < maxFactor) {
maxProcessed++;
bool isPrime = true;
for (int primeIndex = 0; primeIndex < primes.Count; primeIndex++) {
int prime = primes[primeIndex];
if (maxProcessed % prime == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.Add(maxProcessed);
}
}
// now apply the sieve to the number to check
foreach (int prime in primes) {
if (i % prime == 0) {
return false;
}
if (prime > maxFactor) {
break;
}
}
return true;
}
}
在我的慢速机器上使用大约67毫秒....测试应用程序:
class Program {
static void Main(string[] args) {
Stopwatch sw = new Stopwatch();
sw.Start();
Sieve sieve = new Sieve();
for (int i = 10000; i <= 100000; i++) {
sieve.IsPrime(i);
}
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds);
}
}
答案 3 :(得分:2)
我不知道它如何比较速度,但这是一个相当小而简单的SoE实现,对我来说很好用:
def sieve_to(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
还有一些可能的加速,但我认为它非常好。
它们不完全等效,所以你的10_000到1_000_000等于
sieve_to(1_000_000) - sieve_to(9_999)
或近似的东西。
无论如何,在WinXP上,使用Ruby 1.8.6(和相当大的Xeon CPU),我得到了这个:
require 'benchmark'
Benchmark.bm(30) do |r|
r.report("Mike") { a = sieve_to(10_000) - sieve_to(1_000) }
r.report("Gishu") { a = PrimeGenerator.get_primes_between( 1_000, 10_000) }
end
给出了
user system total real
Mike 0.016000 0.000000 0.016000 ( 0.016000)
Gishu 1.641000 0.000000 1.641000 ( 1.672000)
(我因为无聊等待而停止了一百万个案件的运行。)
所以我说这是你的算法。 ; - )
C#解决方案几乎可以保证快几个数量级。
答案 4 :(得分:0)
我还要注意,根据我的经验,Ruby在Windows系统上比在* nix上慢得多。当然,我不确定你有什么速度处理器,但是在Ruby 1.9中我的Ubuntu盒子上运行这段代码需要大约10秒,而1.8.6需要30秒。
答案 5 :(得分:0)
用ruby-prof对它进行基准测试。它可以吐出像kcachegrind这样的工具,可以查看代码缓慢的位置。
然后,一旦你快速制作ruby,使用RubyInline为你优化方法。