I have a PHP script that calculates and counts the number of prime numbers in 1 to 1,000,000 correctly.
I am trying to convert the script into Ruby_2.7.0. on Ubuntu_20.04.``
The full script is at http://davekimble.net/primes.rb
Line 37: if n > sqrt(x) then break end
which the interpreter errors:

Traceback (most recent call last):
4: from ./simple/primes/primes.rb:33:in `<main>'
3: from ./simple/primes/primes.rb:33:in `each'
2: from ./simple/primes/primes.rb:35:in `block in <main>'
1: from ./simple/primes/primes.rb:35:in `each'
./simple/primes/primes.rb:37:in `block (2 levels) in <main>': undefined method `>' for nil:NilClass (NoMethodError)

I don’t think “>” is a method, and I don’t think “n” is a nil:NilClass, so I can’t fix it.
What is going wrong ?

#!/usr/bin/ruby
TIME = Time.now
# primes
# standard functions
# cast numeric to string
def str(a)
return a.to_s
end
# a to the power of b
def pow(a, b)
return a ** b
end
# square root of a
def sqrt(a)
return Math.sqrt(a)
end
# modulo
def mod(a, b)
return a % b
end
alias display print
alias input gets
require "date"
eol = "\n"
xstart = DateTime.now
primes = [2]
primecount = 1
for x in 3..1_000_000 do
prime = true
for y in 0..primecount do
n = primes[y]
if n > sqrt(x) then break end
if mod(x, n) == 0 then prime = false end
end
if prime then
primes[primecount] = x
primecount = primecount+1
end
end
xend = DateTime.now
display str(primecount)+" in "+str(xstart - xend)+" seconds"+eol
puts "\n\e[2K#{primecount} primes found in #{Time.now.-(TIME).round(4)}s"

Your code is terrible and slow. Fast brute force one:

Faster Eratosthenes:

TIME = Time.now
N = 1_000_000
PRIMES = (2..N).to_a.unshift(nil, nil)
i = -1
while (i += 1) < N
next unless x = PRIMES[i]
if (sqr = x ** 2) > N
PRIMES.tap(&:compact!)
break
end
j = sqr - x
PRIMES[j] = nil while (j += x) <= N
end
puts "\n\e[2K#{PRIMES.count} primes found in #{Time.now.-(TIME).round(4)}s"

Benchmarks

10K

Your Code

1229 in -56702693/86400000000000 seconds
1229 primes found in 0.0582s

My Code

1229 primes found in 0.0013s

100K

Your Code

9592 in -322906859/21600000000000 seconds
9592 primes found in 1.293s

Thanks for your reply, but unfortunately you didn’t answer my question: what is wrong with “if n > sqrt(x) then break end” ? What did you do to it to fix it before benchmarking it ? I haven’t made it run yet.

As I understand it a NilClass is not an empty class, it is a non-existing class, and yet n seems to be a member of it.

“Your code is terrible and slow”

I agree it is slow, it has never even run, but “terrible” ? It was not written to be fast, but to be easy to understand and free of weird syntax. On that basis,

PRIMES.tap(&:compact!)

is not acceptable.

Also when I run your script it produces

./simple/primes/primes2.rb: line 1: TIME: command not found
./simple/primes/primes2.rb: line 2: N: command not found
./simple/primes/primes2.rb: line 3: syntax error near unexpected token `('
./simple/primes/primes2.rb: line 3: `PRIMES = (2..N).to_a.unshift(nil, nil)'

also your code needs a N-sized array, and doesn’t have any way of dealing with N = 1_000_000_000_000, while my code only needs a x sized array.

So it could be argued that it’s YOUR code that is terrible.
Obviously it is a matter of opinion.

a sub-adult Southern Cassowary, presumably temporarily separated from his father and cautiously looking for food.

If I reduce the script further:

#!/usr/bin/ruby
def sqrt(a)
return Math.sqrt(a)
end
x = 16
n = 1
for i in 0..10 do
for j in 0..10 do
if n > sqrt(x) then break end
end
end

It works - which makes me look harder at my previous line, line 36, “n = primes[y]”.
This is the PHP way of doing it, the Ruby way is “n = primes.take(y)”.
Also line 42, “primes[] = x” is the PHP way of doing it, the Ruby way is “primes.push(x)” .

So fixing that up we have:

max = 1_000_000
primes = [2]
for x in 3..max do
prime = true
for y in 0..primes.count do
n = primes.take(y)
if n > sqrt(x) then break end
if mod(x, n) == 0 then prime = false end
end
if prime then
primes.push(x)
end
end

Running that gives:

Traceback (most recent call last):
4: from ./simple/primes/primes.rb:33:in `<main>'
3: from ./simple/primes/primes.rb:33:in `each'
2: from ./simple/primes/primes.rb:35:in `block in <main>'
1: from ./simple/primes/primes.rb:35:in `each'
./simple/primes/primes.rb:38:in `block (2 levels) in <main>': undefined method `>' for []:Array (NoMethodError)

Finally, there must be something wrong in my last post. It is ‘the Ruby way is “n = primes.take(y)”’. That should read 'the Ruby way is “n = primes.at(y)” ’
So fixing that up, it works. And the benchmarking against PHP:

For 1_000_000 on i7-8565:
PHP: 78498 in 4.2546830177307 seconds
Ruby: 78498 in 16.602168063 seconds

What are you trying to do ? Comparing and array vs a sqrt doesnt make sense.
Either you want to iterate over the array and compare individual values, or compare vs the last element etc or compare vs the length of the array etc ?

Sorry dont have much insight into this solution so not sure what you are trying to do.

If the interpreter had said it that clearly, I would have guessed what was going wrong. n was not supposed to have been an array but an element of an array, hence n = primes[y] was the problem. Changing that to n = primes.at[y] fixes it. All caused by inadvertently doing things in the PHP style, which is prettier I think. If the common methods were .get() and .put() it would be more intuitive. I can alias those.

What I was really after was some code which ran at 100% CPU time for several seconds without interruptions.

There are hundreds of methods for the array class, some of which are so specific to one problem, that they should be omitted.

There’s no #!/usr/bin/env ruby in my code, so it runs in BASH when you make it executable and execute it. Prepend that line in the beginning to get it working. But anyway it’s getting off topic.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.