1

I'm working on creating a method, gap(g, m, n).

All 3 parameters are integers.


gap finds the first 2 consecutive prime numbers, between m and n, that have a difference of g.

For example, gap(2, 1, 10) => [3, 5]

In the range from m to n, 1..10, the first 2 consecutive prime numbers with a gap of 2 is [3,5].

If instead, it was gap(1, 1, 10) => [2,3]

and if it was gap(6, 1, 10) => nil

https://repl.it/BbGo/1


# method to check if a number is prime

def prime?(num)
  (2..Math.sqrt(num).floor).each do |m|
    if num % m == 0
      return false
    end
  end
  true
end

this method works by iterating through each number from 2, the smallest prime, to the square root of the parameter, checking to see if the parameter is evenly divisible by anything in that range. If it is, the method returns false.


# gap method
def gap(g, m, n)

  if g.odd? && g > 1
    return nil
  end

  primes =  (m..n).select do |num|
              num.odd? && prime?(num)
            end

  first =   primes[0..-2].find_index do |x| 
              primes[primes.index(x) + 1] - x == g
            end

  [
    primes[first], 
    primes[first+1]
  ] unless first.nil?

end


gap(2, 10000000, 11000000)

All prime numbers have a gap of either 2, 4, or a number made up of 2 and 4's added together. The only exception is the gap from 2-3.

So if the gap argument, g, given is a number like 3, which is both odd and greater than 1, the method automatically returns nil because no such prime gap exists.


Problem

My issue is that the method is too slow. By using the replit link above, you get a time of about 20 seconds. Apparently it is possible to get it to about 1 second.

I've tried optimizing by filtering out the even numbers already from m..n, which helped. But I'm just not sure how I can get this to go even faster.

What I'm thinking is before finding out every single prime number in m..n, I should check each iteration if the gap is correct, so once I find it I can just terminate the method without looking at unnecessary primes, but I'm not sure how to implement this.

Thanks for the help, and any general criticism of my code is welcome as well.

John Chen
  • 199
  • 7
  • Did you try to profile your code? It's hard to optimize without profiling first, but my bet is you should use some faster method of finding prime numbers, sieve of Eratosthenes perhaps? – cthulhu Dec 31 '15 at 00:47
  • I'm not too sure how to fully profile, but I know the bottleneck of the code is this part primes = (m..n).select { |num| num.odd? && prime?(num)} – John Chen Dec 31 '15 at 01:27
  • I think that your idea ("What I'm thinking is...") would be a significant speed-up. Think about how you would do it if you were working it out on paper. You would probably write down the most recent prime and the current prime somewhere and check the gap before moving on to the next prime. You only need one loop for this. – Jordan Running Dec 31 '15 at 01:30

1 Answers1

0

Answer using Jordan's advice:

def gap(g, m, n)

  if g.odd? && g > 1
    return nil
  end

  recent = 0
  current = 0

  (m..n).each do |num|
    if num.odd? && prime?(num)
      current = num  
      if current - recent == g
        break
      else 
        recent = current
      end
    end
  end

  [recent, current] unless current - recent != g

end

gap(2, 10000000, 11000000) 
#=> [10000139, 10000141] 

completes in 3 ms.

https://repl.it/BbGo/3

Thanks!

John Chen
  • 199
  • 7