Ruby: What is the speed difference between While and Until loops? - ruby

I'm learning Ruby right now. Coming from using Javascript the past couple of years, I'm familiar with the While loop. But the until loop? I've looked around but couldn't find a solid reason why one would be better than the other.
Ruby has "until" which is described as another way to phrase a problem. The way I see it, "while" iterates until false, and "until" iterates until true.
I'm sure that most of the programs I write won't really need refactoring for speed. However, I like to get into the little details sometimes.
Is there a speed difference between the two loops? Why is there an "until" syntax in Ruby? Why not just stick with "while?"

There would not be a speed difference between while and until as they mirror each other.
We'll compare a while loop with an until loop:
n = 0
puts n += 1 while n != 3
n = 0
puts n += 1 until n == 3
These will both print 1 through 3.
Here's a diff between the two disassembled human-readable instruction sequences from the Ruby VM:
## -13,7 +13,7 ##
0021 pop
0022 getlocal_OP__WC__0 2
0024 putobject 3
-0026 opt_neq <callinfo!mid:!=, argc:1, ARGS_SIMPLE>, <callcache>, <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
-0031 branchif 8
-0033 putnil
-0034 leave
+0026 opt_eq <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
+0029 branchunless 8
+0031 putnil
+0032 leave
A while loop uses a branchif for its jump, whereas the until loop used a branchunless. So, these loops simply differ in the comparison being made, which you can see by looking at how branchif and branchunless are defined:
DEFINE_INSN
branchif
(OFFSET dst)
(VALUE val)
()
{
if (RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
DEFINE_INSN
branchunless
(OFFSET dst)
(VALUE val)
()
{
if (!RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
Performance between while and until should be nearly identical. Usage should be determined by readability.

Speed differences aside, it's really all about readability, which is something that Ruby prides itself on.
Let's pretend we're making a drink - which do you think reads better?
A) pour_drink until glass.full?
B) pour_drink while !glass.full?

Speed will be more influenced by your choice of comparison operator than your choice of while or until
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n != 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n == 10_000_000
end
end
user system total real
while 0.250000 0.000000 0.250000 ( 0.247949)
until 0.220000 0.000000 0.220000 ( 0.222049)
With while n != 10_000_000 vs. until n == 10_000_000, until appears to be faster.
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n < 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n == 10_000_000
end
end
user system total real
while 0.210000 0.000000 0.210000 ( 0.207265)
until 0.220000 0.000000 0.220000 ( 0.223195)
Change it to while n < 10_000_000 and now while seems to have the edge. To be fair we should give them the more equivalent while n < 10_000_000 vs. until n > 9_999_999
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n < 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n > 9_999_999
end
end
user system total real
while 0.200000 0.000000 0.200000 ( 0.208428)
until 0.200000 0.000000 0.200000 ( 0.206218)
Now they're almost identical. So follow Ruby's lead and gain your satisfaction from code that reads like an English sentence. But make sure you use < or > to gain that extra boost of .0000000001 seconds.

Related

Shorter ways to write this condition n != 1 && n != 2 && n != 3 && n !=4 in Ruby

I want to check if the input is one of four numbers 1, 2, 3, 4. If not, there should be an error message. I start out with this
n = gets.chomp.to_i
if n != 1 && n != 2 && n != 3 && n != 4
puts 'invalid input'
end
This also happens a lot in other languages. How can I shorten the above condition n != 1 && n != 2 && n != 3 && n != 4?
I would use Range#cover? which might be faster than include?:
number = gets.chomp.to_i
puts('invalid input') unless (1..4).cover?(number)
Fastest possible check:
if n < 1 || n > 4
Well, there's a case statement:
case num
when 1,2,3,4
puts "it's ok"
else
puts "it's not ok"
end
For instance:
[0, 1, 4, 5].each do |num|
case num
when 1,2,3,4
puts "#{num} is ok"
else
puts "#{num} is not ok"
end
end
# >> 0 is not ok
# >> 1 is ok
# >> 4 is ok
# >> 5 is not ok
Or a bit DRYer:
[0, 1, 4, 5].each do |num|
puts case num
when 1,2,3,4
"#{num} is ok"
else
"#{num} is not ok"
end
end
# >> 0 is not ok
# >> 1 is ok
# >> 4 is ok
# >> 5 is not ok
And, since when allows ranges, you could use when 1 .. 4 instead.
While I appreciate the benchmark results showing that using case was fastest, it didn't make sense, because Casper's should have been. Here's what I found:
require 'fruity'
n = 1
compare do
casper1 { n < 1 || n > 4 }
casper2 { n >= 1 && n <= 4 }
casper3 { 1 <= n && n <= 4 }
sagarpandya82 {n.between?(1,4)}
spickerman {(1..4).cover?(n)}
reitermarkus {[1, 2, 3, 4].include?(n)}
ttm {case n;when 1,2,3,4;true;else;false;end}
end
# >> Running each test 262144 times. Test will take about 11 seconds.
# >> casper2 is similar to casper3
# >> casper3 is faster than ttm by 2x ± 1.0
# >> ttm is faster than casper1 by 2x ± 1.0 (results differ: true vs false)
# >> casper1 is faster than sagarpandya82 by 3x ± 1.0 (results differ: false vs true)
# >> sagarpandya82 is similar to spickerman
# >> spickerman is faster than reitermarkus by 2x ± 0.1
In running that several times casper2 and casper3 will alternate being fastest and next fastest, followed by using case in ttm.
The results will change as more values are added. In particular, I think the include and when 1,2,3,4 tests would be seriously impacted.
Just to show that it's unnecessary to collapse the space, or, said differently, that whitespace doesn't make a difference:
require 'fruity'
n = 1
compare do
ttm1 {case n;when 1,2,3,4;true;else;false;end}
ttm2 {case n; when 1,2,3,4; true; else; false; end}
ttm3 {
case n
when 1,2,3,4
true
else
false
end
}
end
# >> Running each test 131072 times. Test will take about 2 seconds.
# >> ttm1 is similar to ttm3
# >> ttm3 is similar to ttm2
With Fruity, when you see these sort of "is similar to " results they'll tend to alternate in order because the difference in speed is usually the result of system wobble during testing.
And finally, comparing when 1,2,3,4 with when 1..4:
require 'fruity'
n = 1
compare do
ttm1 {
case n
when 1,2,3,4
true
else
false
end
}
ttm2 {
case n
when 1..4
true
else
false
end
}
end
# >> Running each test 131072 times. Test will take about 2 seconds.
# >> ttm1 is faster than ttm2 by 8x ± 10.0
I would use a Range for this, and an unless-statement instead of an if:
unless (1..4).include?(n)
puts 'invalid input'
end
If your range is joint you can use
n.between?(1,4)
I was curious for the fastest solution and much to my surprise it is the case solution, both when n in the range and outside, at least on MRI 1.9.3 under Windows 7. The only pitty it is also the longest solution..
Benchmark.bm do |x|
x.report {r.times {n < 1 || n > 4}}
x.report {r.times {n.between?(1,4)}}
x.report {r.times {(1..4).cover?(n)}}
x.report {r.times {[1, 2, 3, 4].include?(n)}}
x.report {r.times {case n;when 1,2,3,4;true;else;false;end}}
end
gives
user system total real
0.093000 0.000000 0.093000 ( 0.093600)
0.141000 0.000000 0.141000 ( 0.140400)
0.140000 0.000000 0.140000 ( 0.140400)
0.265000 0.000000 0.265000 ( 0.265200)
0.063000 0.000000 0.063000 ( 0.062400)
You can define your inclusive elements in an array and check if n is any of these with include?:
[1, 2, 3, 4].include?(n)
Answers in other posts give answer to your question in the text, but here is my answer to your question in the title:
Shorter ways to write this condition n != 1 || n != 2 || n != 3 || n !=4 in Ruby
n != 1 || n != 2 || n != 3 || n !=4 in Ruby
An expression: true would always be equivalent to that. And if you want to put that in a condition, the conceptually simplest is to not use any condition at all. Without a condition, just write whatever you wanted to do under that condition.

How to get the rightmost digit from an Integer - Ruby

I am working on a program that requires me to read in and validate a number, but in doing so I need to take a two digit integer (for example we'll say 18) and add it 1 + 8. Now I have it working where I do a conditional check if it is greater than 10 and subtract, but that's kind of an ugly solution in my opinion and was wondering if there is a cleaner way of doing it?
You can use the modulo operator n % 10, to get the rightmost digit. For example:
18 % 10
# => 8
9 % 10
# => 9
0 % 10
# => 0
(-123).abs % 10
# => 3
To handle negative numbers, use Integer#abs.
EDIT
Ruby 2.4 has a Integer#digits method. It will give you the digits as an array, starting from unit's place. And if you want to add the digits, you can use Enumerable#sum.
123.digits
# =>[3, 2, 1]
123.digits.first
# => 3
123.digits.sum
# => 6
To add all digits from a number, you can use the following:
18.to_s.chars.map(&:to_i).reduce(:+)
It transforms the number into a string, splits it into digits, transforms each into an integer and adds them all together.
Works with numbers of any length.
This is how I would do it:
any_number = 1234
# Ensure your input is at most a two digit number (might not be needed)
two_digit_number = any_number % 100 #=> 34
# Calculate the final addition
total = two_digit_number / 10 + two_digit_number % 10 #=> 7
For two digit integers, there is at least another method we could use: Numeric#divmod, which returns the quotient and modulus in an array. And here's a look at the speed of the different approaches to sum up the digits:
b = 18
n = 1_000_000
Benchmark.bmbm do |x|
x.report('to_s:') { n.times { b.to_s.chars.map(&:to_i).reduce(:+) }}
x.report('divmod:') { n.times { b.divmod(10).reduce(:+) }}
x.report('direct division:') { n.times { b/10 + b%10 } }
x.report('digits:') { n.times { a.digits.reduce(:+) } }
end
#####
user system total real
to_s: 0.750000 0.000000 0.750000 ( 0.750700)
divmod: 0.150000 0.000000 0.150000 ( 0.153516)
direct division: 0.080000 0.000000 0.080000 ( 0.076697)
digits: 0.560000 0.020000 0.580000 ( 0.574687)
Here a hands on solution for the fun of it:
def get_digits(n)
res=[]; i=0;
while 10**i < n
res.push( n/10**i%10 )
i += 1
end
return res
end
def digits_sum(number,n)
number_array=number.to_s.chars
n=[n,number_array.length].min
number_array.slice(-n,n).map {|c| c.to_i }.reduce(:+) || 0
end
this method return the sum of N right-digits.
digits_sum 123456789,5 => 35
digits_sum 123456789,2 => 17
This will work if you give N greater then number length but wont work if negative count is provided

Is there an infinite loop in my code for solving Collatz sequence?

My code is trying to find the answer to this problem: The following iterative sequence is defined for the set of positive integers:
n → n/2 (n is even)
n → 3n + 1 (n is odd)
Using the rule above and starting with 13, we generate the following sequence:
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.
Which starting number, under one million, produces the longest chain?
NOTE: Once the chain starts the terms are allowed to go above one million.
And here is my code:
step_count = 1
score = {}
largest_score = 1
(1..1000000).map do |n|
while n >= 1 do
if n%2 == 0 then
n/2
step_count += 1
else
(3*n)+1
step_count += 1
end
end
score = {n => step_count}
end
score.each {|n, step_count| largest_score = step_count if largest_score < step_count}
puts score.key(largest_score)
I ran it for over an hour and still no answer. Is there an infinite loop in my code, or maybe some different problem, and if so what is it?
I am using Ruby 1.8.7
Yes, you've got an infinite loop. It's here:
while n >= 1 do
if n%2 == 0 then
n/2
step_count += 1
else
(3*n)+1
step_count += 1
end
end
The condition in your while loop is testing n, but nothing within the loop is changing its value. What you probably meant to do is this:
while n >= 1 do
if n % 2 == 0
n = n / 2
step_count += 1
else
n = (3 * n) + 1
step_count += 1
end
end
A few sidenotes:
It looks like you mean to be updating the score hash with new key-value pairs, but as written, score = { n => step_count } will replace it entirely on each iteration. To add new pairs to the existing Hash, use score[n] = step_count.
It's much more efficient to look up a value in a Hash by its key than the other way around, so you might want to reverse your Hash storage: score[step_count] = n, finding the largest score with score.each { |step_count, n| #... and reading it out with score[largest_score]. This has the added advantage that you won't have to store all million results; it'll only store the last number you reach that results in a chain of a given length. Of course, it also means that you'll only see one number that results in the largest chain, even if there are multiple numbers that have the same, highest chain length! The problem is worded as though the answer is unique, but if it isn't, you won't find out.
To debug problems like this in the future, it's handy to drop your loop iterations to something tiny (ten, say) and sprinkle some puts statements within your loops to watch what's happening and get a feel for the execution flow.
Try the following solution for your problem:
def solve(n)
max_collatz = 0; max_steps = 0
(1..n).each do |k|
next if k % 2 == 0
next if k % 3 != 1
steps = collatz_sequence_count(k)
if steps > max_steps
max_steps = steps
max_collatz = k
end
end
max_collatz
# answer: 837799 with 525 steps, in nearly 2.2 seconds on my machine
end
def collatz_sequence_count(k)
counter = 1
while true
return counter if k == 1
k = k % 2 == 0 ? k/2 : 3 * k + 1
counter += 1
end
end
# You can then use the above methods to get your answer, like this:
answer = solve 1000000
puts "answer is: #{answer}"
Results (uses a custom home-brewed gem to solve ProjectEuler problems):
nikhgupta at MacBookPro in ~/Code/git/ProjectEuler [ master: ✗ ] 48d
± time euler solve 14 +next: 2 | total: 22 | ▸▸▸▹▹▹▹▹▹▹
0014 | Longest Collatz sequence | It took me: 2.166033 seconds. | Solution: 837799
euler solve 14 3.30s user 0.13s system 99% cpu 3.454 total

Speed up solution for "ProjectEuler 35: Circular primes count below 1 million" [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
A number is called a circular prime if all of it's rotations are primes themselves.
For example the number 197 has two rotations: 971, and 719. Both of them are prime.
There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, and 97.
How many circular primes are there below N?
1 <= N <= 1000000
require 'prime'
require 'benchmark'
def circular_prime_count(kk)
primes = []
count = 0
Prime.each(kk) do |prime|
pa= prime.to_s.split('')
flag = true
pa.count.times do |i|
pa.rotate!
flag = false if !Prime.prime?(pa.join.to_i)
end
count+=1 if flag
end
count
end
[100, 200, 1000, 2000 ,10_000, 100_000, 1_000_000 ].each do |number|
puts "Count of primes below #{number} is: #{circular_prime_count(number)}"
Benchmark.bm do |x|
x.report do
circular_prime_count(number)
end
end
end
My benchmark on ruby 2.0.0p247 is:
count of primes below 100 is: 13
user system total real
0.000000 0.000000 0.000000 ( 0.001891)
count of primes below 200 is: 17
user system total real
0.000000 0.000000 0.000000 ( 0.004775)
count of primes below 1000 is: 25
user system total real
0.010000 0.000000 0.010000 ( 0.005716)
count of primes below 2000 is: 27
user system total real
0.020000 0.000000 0.020000 ( 0.018399)
count of primes below 10000 is: 33
user system total real
0.110000 0.000000 0.110000 ( 0.105365)
count of primes below 100000 is: 43
user system total real
1.790000 0.000000 1.790000 ( 1.789223)
count of primes below 1000000 is: 55
user system total real
43.870000 0.010000 43.880000 ( 43.971832)
Could you help to improve performance to find count for 1 million for 20 seconds?
My notebook model is: HP Probook 4530s
Processor Information:
Family: Core i5
Manufacturer: Intel(R) Corporation
ID: A7 06 02 00 FF FB EB BF
Version: Intel(R) Core(TM) i5-2450M CPU # 2.50GHz
Voltage: 1.2 V
External Clock: 100 MHz
Max Speed: 2500 MHz
Current Speed: 2500 MHz
Status: Populated, Enabled
Core Count: 2
Core Enabled: 2
Thread Count: 4
Thanks everyone, so final solution is:
require 'prime'
require 'benchmark'
require 'set'
def circular_prime_count_v2(search_max)
max = if search_max == 10 ** (search_max.to_s.length - 1)
search_max
else
10 ** search_max.to_s.length - 1
end
primes = Prime.each(max).to_set
count = 0
primes.each do |prime|
break if prime > search_max
s = prime.to_s
l = s.length
l.times { |i| primes.include?(((s * 2)[i, l]).to_i) || break } || next
count+=1
end
count
end
[100, 200, 1000, 2000 ,10_000, 100_000, 1_000_000 ].each do |number|
puts "Count of primes below #{number} is: #{circular_prime_count_v2(number)}"
Benchmark.bm do |x|
x.report('circular_prime_count') do
circular_prime_count_v2(number)
end
end
end
Count of primes below 100 is: 13
user system total real
circular_prime_count 0.000000 0.000000 0.000000 ( 0.000482)
Count of primes below 200 is: 17
user system total real
circular_prime_count 0.000000 0.000000 0.000000 ( 0.002683)
Count of primes below 1000 is: 25
user system total real
circular_prime_count 0.000000 0.000000 0.000000 ( 0.003666)
Count of primes below 2000 is: 27
user system total real
circular_prime_count 0.010000 0.000000 0.010000 ( 0.005241)
Count of primes below 10000 is: 33
user system total real
circular_prime_count 0.000000 0.000000 0.000000 ( 0.007074)
Count of primes below 100000 is: 43
user system total real
circular_prime_count 0.060000 0.000000 0.060000 ( 0.057128)
Count of primes below 1000000 is: 55
user system total real
circular_prime_count 0.720000 0.000000 0.720000 ( 0.727345)
Update
I have created a MUCH faster implementation. For historic value, it was roughly based on this post. From my basic testing(with 1 million records), its about 40 times faster than the original.
require 'prime'
require 'benchmark'
require 'set'
def circular_prime_count(kk)
primes, count = [], 0
Prime.each(kk) do |prime|
pa= prime.to_s.split('')
flag = true
pa.count.times do |i|
pa.rotate!
flag = false if !Prime.prime?(pa.join.to_i)
end
count+=1 if flag
end
count
end
def circular_prime_count_v2(search_max)
primes = Prime.each(search_max).to_set
count = 0
primes.each do |prime|
str = prime.to_s
all_points_match = (0...str.length).collect { |i| primes.include?(((str * 2)[i, str.length]).to_i) || break }
count+=1 if all_points_match
end
count
end
Benchmark.bm do |x|
x.report('circular_prime_count') do
puts " Count: #{circular_prime_count(1000000)}"
end
x.report('circular_prime_count_v2') do
puts " Count: #{circular_prime_count_v2(1000000)}"
end
end
NEW RESULTS
user system total real
circular_prime_count Count: 55
39.430000 0.080000 39.510000 ( 39.603969)
circular_prime_count_v2 Count: 55
0.900000 0.000000 0.900000 ( 0.906725)

Generate a list of primes up to a certain number

I'm trying to generate a list of primes below 1 billion. I'm trying this, but this kind of structure is pretty shitty. Any suggestions?
a <- 1:1000000000
d <- 0
b <- for (i in a) {for (j in 1:i) {if (i %% j !=0) {d <- c(d,i)}}}
That sieve posted by George Dontas is a good starting point. Here's a much faster version with running times for 1e6 primes of 0.095s as opposed to 30s for the original version.
sieve <- function(n)
{
n <- as.integer(n)
if(n > 1e8) stop("n too large")
primes <- rep(TRUE, n)
primes[1] <- FALSE
last.prime <- 2L
fsqr <- floor(sqrt(n))
while (last.prime <= fsqr)
{
primes[seq.int(2L*last.prime, n, last.prime)] <- FALSE
sel <- which(primes[(last.prime+1):(fsqr+1)])
if(any(sel)){
last.prime <- last.prime + min(sel)
}else last.prime <- fsqr+1
}
which(primes)
}
Here are some alternate algorithms below coded about as fast as possible in R. They are slower than the sieve but a heck of a lot faster than the questioners original post.
Here's a recursive function that uses mod but is vectorized. It returns for 1e5 almost instantaneously and 1e6 in under 2s.
primes <- function(n){
primesR <- function(p, i = 1){
f <- p %% p[i] == 0 & p != p[i]
if (any(f)){
p <- primesR(p[!f], i+1)
}
p
}
primesR(2:n)
}
The next one isn't recursive and faster again. The code below does primes up to 1e6 in about 1.5s on my machine.
primest <- function(n){
p <- 2:n
i <- 1
while (p[i] <= sqrt(n)) {
p <- p[p %% p[i] != 0 | p==p[i]]
i <- i+1
}
p
}
BTW, the spuRs package has a number of prime finding functions including a sieve of E. Haven't checked to see what the speed is like for them.
And while I'm writing a very long answer... here's how you'd check in R if one value is prime.
isPrime <- function(x){
div <- 2:ceiling(sqrt(x))
!any(x %% div == 0)
}
This is an implementation of the Sieve of Eratosthenes algorithm in R.
sieve <- function(n)
{
n <- as.integer(n)
if(n > 1e6) stop("n too large")
primes <- rep(TRUE, n)
primes[1] <- FALSE
last.prime <- 2L
for(i in last.prime:floor(sqrt(n)))
{
primes[seq.int(2L*last.prime, n, last.prime)] <- FALSE
last.prime <- last.prime + min(which(primes[(last.prime+1):n]))
}
which(primes)
}
sieve(1000000)
Prime Numbers in R
The OP asked to generate all prime numbers below one billion. All of the answers provided thus far are either not capable of doing this, will take a long a time to execute, or currently not available in R (see the answer by #Charles). The package RcppAlgos (I am the author) is capable of generating the requested output in just over 1 second using only one thread. It is based off of the segmented sieve of Eratosthenes by Kim Walisch.
RcppAlgos
library(RcppAlgos)
system.time(primeSieve(1e9)) ## using 1 thread
user system elapsed
1.099 0.077 1.176
Using Multiple Threads
And in recent versions (i.e. >= 2.3.0), we can utilize multiple threads for even faster generation. For example, now we can generate the primes up to 1 billion in under half a second!
system.time(primeSieve(10^9, nThreads = 8))
user system elapsed
2.046 0.048 0.375
Summary of Available Packages in R for Generating Primes
library(schoolmath)
library(primefactr)
library(sfsmisc)
library(primes)
library(numbers)
library(spuRs)
library(randtoolbox)
library(matlab)
## and 'sieve' from #John
Before we begin, we note that the problems pointed out by #Henrik in schoolmath still exists. Observe:
## 1 is NOT a prime number
schoolmath::primes(start = 1, end = 20)
[1] 1 2 3 5 7 11 13 17 19
## This should return 1, however it is saying that 52
## "prime" numbers less than 10^4 are divisible by 7!!
sum(schoolmath::primes(start = 1, end = 10^4) %% 7L == 0)
[1] 52
The point is, don't use schoolmath for generating primes at this point (no offense to the author... In fact, I have filed an issue with the maintainer).
Let's look at randtoolbox as it appears to be incredibly efficient. Observe:
library(microbenchmark)
## the argument for get.primes is for how many prime numbers you need
## whereas most packages get all primes less than a certain number
microbenchmark(priRandtoolbox = get.primes(78498),
priRcppAlgos = RcppAlgos::primeSieve(10^6), unit = "relative")
Unit: relative
expr min lq mean median uq max neval
priRandtoolbox 1.00000 1.00000 1.000000 1.000000 1.000000 1.0000000 100
priRcppAlgos 12.79832 12.55065 6.493295 7.355044 7.363331 0.3490306 100
A closer look reveals that it is essentially a lookup table (found in the file randtoolbox.c from the source code).
#include "primes.h"
void reconstruct_primes()
{
int i;
if (primeNumber[2] == 1)
for (i = 2; i < 100000; i++)
primeNumber[i] = primeNumber[i-1] + 2*primeNumber[i];
}
Where primes.h is a header file that contains an array of "halves of differences between prime numbers". Thus, you will be limited by the number of elements in that array for generating primes (i.e. the first one hundred thousand primes). If you are only working with smaller primes (less than 1,299,709 (i.e. the 100,000th prime)) and you are working on a project that requires the nth prime, randtoolbox is the way to go.
Below, we perform benchmarks on the rest of the packages.
Primes up to One Million
microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^6),
priNumbers = numbers::Primes(10^6),
priSpuRs = spuRs::primesieve(c(), 2:10^6),
priPrimes = primes::generate_primes(1, 10^6),
priPrimefactr = primefactr::AllPrimesUpTo(10^6),
priSfsmisc = sfsmisc::primes(10^6),
priMatlab = matlab::primes(10^6),
priJohnSieve = sieve(10^6),
unit = "relative")
Unit: relative
expr min lq mean median uq max neval
priRcppAlgos 1.000000 1.00000 1.00000 1.000000 1.00000 1.00000 100
priNumbers 21.550402 23.19917 26.67230 23.140031 24.56783 53.58169 100
priSpuRs 232.682764 223.35847 233.65760 235.924538 236.09220 212.17140 100
priPrimes 46.591868 43.64566 40.72524 39.106107 39.60530 36.47959 100
priPrimefactr 39.609560 40.58511 42.64926 37.835497 38.89907 65.00466 100
priSfsmisc 9.271614 10.68997 12.38100 9.761438 11.97680 38.12275 100
priMatlab 21.756936 24.39900 27.08800 23.433433 24.85569 49.80532 100
priJohnSieve 10.630835 11.46217 12.55619 10.792553 13.30264 38.99460 100
Primes up to Ten Million
microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^7),
priNumbers = numbers::Primes(10^7),
priSpuRs = spuRs::primesieve(c(), 2:10^7),
priPrimes = primes::generate_primes(1, 10^7),
priPrimefactr = primefactr::AllPrimesUpTo(10^7),
priSfsmisc = sfsmisc::primes(10^7),
priMatlab = matlab::primes(10^7),
priJohnSieve = sieve(10^7),
unit = "relative", times = 20)
Unit: relative
expr min lq mean median uq max neval
priRcppAlgos 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 20
priNumbers 30.57896 28.91780 31.26486 30.47751 29.81762 40.43611 20
priSpuRs 533.99400 497.20484 490.39989 494.89262 473.16314 470.87654 20
priPrimes 125.04440 114.71349 112.30075 113.54464 107.92360 103.74659 20
priPrimefactr 52.03477 50.32676 52.28153 51.72503 52.32880 59.55558 20
priSfsmisc 16.89114 16.44673 17.48093 16.64139 18.07987 22.88660 20
priMatlab 30.13476 28.30881 31.70260 30.73251 32.92625 41.21350 20
priJohnSieve 18.25245 17.95183 19.08338 17.92877 18.35414 32.57675 20
Primes up to One Hundred Million
For the next two benchmarks, we only consider RcppAlgos, numbers, sfsmisc, matlab, and the sieve function by #John.
microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^8),
priNumbers = numbers::Primes(10^8),
priSfsmisc = sfsmisc::primes(10^8),
priMatlab = matlab::primes(10^8),
priJohnSieve = sieve(10^8),
unit = "relative", times = 20)
Unit: relative
expr min lq mean median uq max neval
priRcppAlgos 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 20
priNumbers 35.64097 33.75777 32.83526 32.25151 31.74193 31.95457 20
priSfsmisc 21.68673 20.47128 20.01984 19.65887 19.43016 19.51961 20
priMatlab 35.34738 33.55789 32.67803 32.21343 31.56551 31.65399 20
priJohnSieve 23.28720 22.19674 21.64982 21.27136 20.95323 21.31737 20
Primes up to One Billion
N.B. We must remove the condition if(n > 1e8) stop("n too large") in the sieve function.
## See top section
## system.time(primeSieve(10^9))
## user system elapsed
## 1.099 0.077 1.176 ## RcppAlgos single-threaded
## gc()
system.time(matlab::primes(10^9))
user system elapsed
31.780 12.456 45.549 ## ~39x slower than RcppAlgos
## gc()
system.time(numbers::Primes(10^9))
user system elapsed
32.252 9.257 41.441 ## ~35x slower than RcppAlgos
## gc()
system.time(sieve(10^9))
user system elapsed
26.266 3.906 30.201 ## ~26x slower than RcppAlgos
## gc()
system.time(sfsmisc::primes(10^9))
user system elapsed
24.292 3.389 27.710 ## ~24x slower than RcppAlgos
From these comparison, we see that RcppAlgos scales much better as n gets larger.
_________________________________________________________
| | 1e6 | 1e7 | 1e8 | 1e9 |
| |---------|----------|-----------|-----------
| RcppAlgos | 1.00 | 1.00 | 1.00 | 1.00 |
| sfsmisc | 9.76 | 16.64 | 19.66 | 23.56 |
| JohnSieve | 10.79 | 17.93 | 21.27 | 25.68 |
| numbers | 23.14 | 30.48 | 32.25 | 34.86 |
| matlab | 23.43 | 30.73 | 32.21 | 38.73 |
---------------------------------------------------------
The difference is even more dramatic when we utilize multiple threads:
microbenchmark(ser = primeSieve(1e6),
par = primeSieve(1e6, nThreads = 8), unit = "relative")
Unit: relative
expr min lq mean median uq max neval
ser 1.741342 1.492707 1.481546 1.512804 1.432601 1.275733 100
par 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 100
microbenchmark(ser = primeSieve(1e7),
par = primeSieve(1e7, nThreads = 8), unit = "relative")
Unit: relative
expr min lq mean median uq max neval
ser 2.632054 2.50671 2.405262 2.418097 2.306008 2.246153 100
par 1.000000 1.00000 1.000000 1.000000 1.000000 1.000000 100
microbenchmark(ser = primeSieve(1e8),
par = primeSieve(1e8, nThreads = 8), unit = "relative", times = 20)
Unit: relative
expr min lq mean median uq max neval
ser 2.914836 2.850347 2.761313 2.709214 2.755683 2.438048 20
par 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 20
microbenchmark(ser = primeSieve(1e9),
par = primeSieve(1e9, nThreads = 8), unit = "relative", times = 10)
Unit: relative
expr min lq mean median uq max neval
ser 3.081841 2.999521 2.980076 2.987556 2.961563 2.841023 10
par 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10
And multiplying the table above by the respective median times for the serial results:
_____________________________________________________________
| | 1e6 | 1e7 | 1e8 | 1e9 |
| |---------|----------|-----------|-----------
| RcppAlgos-Par | 1.00 | 1.00 | 1.00 | 1.00 |
| RcppAlgos-Ser | 1.51 | 2.42 | 2.71 | 2.99 |
| sfsmisc | 14.76 | 40.24 | 53.26 | 70.39 |
| JohnSieve | 16.32 | 43.36 | 57.62 | 76.72 |
| numbers | 35.01 | 73.70 | 87.37 | 104.15 |
| matlab | 35.44 | 74.31 | 87.26 | 115.71 |
-------------------------------------------------------------
Primes Over a Range
microbenchmark(priRcppAlgos = RcppAlgos::primeSieve(10^9, 10^9 + 10^6),
priNumbers = numbers::Primes(10^9, 10^9 + 10^6),
priPrimes = primes::generate_primes(10^9, 10^9 + 10^6),
unit = "relative", times = 20)
Unit: relative
expr min lq mean median uq max neval
priRcppAlgos 1.0000 1.0000 1.000 1.0000 1.0000 1.0000 20
priNumbers 115.3000 112.1195 106.295 110.3327 104.9106 81.6943 20
priPrimes 983.7902 948.4493 890.243 919.4345 867.5775 708.9603 20
Primes up to 10 billion in Under 6 Seconds
## primes less than 10 billion
system.time(tenBillion <- RcppAlgos::primeSieve(10^10, nThreads = 8))
user system elapsed
26.077 2.063 5.602
length(tenBillion)
[1] 455052511
## Warning!!!... Large object created
tenBillionSize <- object.size(tenBillion)
print(tenBillionSize, units = "Gb")
3.4 Gb
Primes Over a Range of Very Large Numbers:
Prior to version 2.3.0, we were simply using the same algorithm for numbers of every magnitude. This is okay for smaller numbers when most of the sieving primes have at least one multiple in each segment (Generally, the segment size is limited by the size of L1 Cache ~32KiB). However, when we are dealing with larger numbers, the sieving primes will contain many numbers that will have fewer than one multiple per segment. This situation creates a lot of overhead, as we are performing many worthless checks that pollutes the cache. Thus, we observe much slower generation of primes when the numbers are very large. Observe for version 2.2.0 (See Installing older version of R package):
## Install version 2.2.0
## packageurl <- "http://cran.r-project.org/src/contrib/Archive/RcppAlgos/RcppAlgos_2.2.0.tar.gz"
## install.packages(packageurl, repos=NULL, type="source")
system.time(old <- RcppAlgos::primeSieve(1e15, 1e15 + 1e9))
user system elapsed
7.932 0.134 8.067
And now using the cache friendly improvement originally developed by Tomás Oliveira, we see drastic improvements:
## Reinstall current version from CRAN
## install.packages("RcppAlgos"); library(RcppAlgos)
system.time(cacheFriendly <- primeSieve(1e15, 1e15 + 1e9))
user system elapsed
2.258 0.166 2.424 ## Over 3x faster than older versions
system.time(primeSieve(1e15, 1e15 + 1e9, nThreads = 8))
user system elapsed
4.852 0.780 0.911 ## Over 8x faster using multiple threads
Take Away
There are many great packages available for generating primes
If you are looking for speed in general, there is no match to RcppAlgos::primeSieve, especially for larger numbers.
If you are working with small primes, look no further than randtoolbox::get.primes.
If you need primes in a range, the packages numbers, primes, & RcppAlgos are the way to go.
The importance of good programming practices cannot be overemphasized (e.g. vectorization, using correct data types, etc.). This is most aptly demonstrated by the pure base R solution provided by #John. It is concise, clear, and very efficient.
Best way that I know of to generate all primes (without getting into crazy math) is to use the Sieve of Eratosthenes.
It is pretty straightforward to implement and allows you calculate primes without using division or modulus. The only downside is that it is memory intensive, but various optimizations can be made to improve memory (ignoring all even numbers for instance).
This method should be Faster and simpler.
allPrime <- function(n) {
primes <- rep(TRUE, n)
primes[1] <- FALSE
for (i in 1:sqrt(n)) {
if (primes[i]) primes[seq(i^2, n, i)] <- FALSE
}
which(primes)
}
0.12 second on my computer for n = 1e6
I implemented this in function AllPrimesUpTo in package primefactr.
I recommend primegen, Dan Bernstein's implementation of the Atkin-Bernstein sieve. It's very fast and will scale well to other problems. You'll need to pass data out to the program to use it, but I imagine there are ways to do that?
You can also cheat and use the primes() function in the schoolmath package :D
The isPrime() function posted above could use sieve(). One only needs to check if any of
the primes < ceiling(sqrt(x)) divide x with no remainder. Need to handle 1 and 2, also.
isPrime <- function(x) {
div <- sieve(ceiling(sqrt(x)))
(x > 1) & ((x == 2) | !any(x %% div == 0))
}
No suggestions, but allow me an extended comment of sorts. I ran this experiment with the following code:
get_primes <- function(n_min, n_max){
options(scipen=999)
result = vector()
for (x in seq(max(n_min,2), n_max)){
has_factor <- F
for (p in seq(2, ceiling(sqrt(x)))){
if(x %% p == 0) has_factor <- T
if(has_factor == T) break
}
if(has_factor==F) result <- c(result,x)
}
result
}
and after almost 24 hours of uninterrupted computer operations, I got a list of 5,245,897 primes. The π(1,000,000,000) = 50,847,534, so it would have taken 10 days to complete this calculation.
Here is the file of these first ~ 5 million prime numbers.
for (i in 2:1000) {
a = (2:(i-1))
b = as.matrix(i%%a)
c = colSums(b != 0)
if (c == i-2)
{
print(i)
}
}
Every number (i) before (a) is checked against the list of prime numbers (n) generated by checking for number (i-1)
Thanks for suggestions:
prime = function(a,n){
n=c(2)
i=3
while(i <=a){
for(j in n[n<=sqrt(i)]){
r=0
if (i%%j == 0){
r=1}
if(r==1){break}
}
if(r!=1){n = c(n,i)}
i=i+2
}
print(n)
}

Resources