0

I'm generating prime numbers from Fibonacci as follows (using Python, with mpmath and sympy for arbitrary precision):

from mpmath import *

def GCD(a,b):
    while a:
        a, b = fmod(b, a), a
    return b

def generate(x):
    mp.dps = round(x, int(log10(x))*-1)
    if x == GCD(x, fibonacci(x-1)):
        return True
    if x == GCD(x, fibonacci(x+1)):
        return True

    return False

for x in range(1000, 2000)
    if generate(x)
        print(x)

It's a rather small algorithm but seemingly generates all primes (except for 5 somehow, but that's another question). I say seemingly because a very little percentage (0.5% under 1000 and 0.16% under 10K, getting less and less) isn't prime. For instance under 1000: 323, 377 and 442 are also generated. These numbers are not prime.

Is there something off in my script? I try to account for precision by relating the .dps setting to the number being calculated. Can it really be that Fibonacci and prime numbers are seemingly so related, but then when it's get detailed they aren't? :)

Ropstah
  • 17,538
  • 24
  • 120
  • 194

2 Answers2

2

For this type of problem, you may want to look at the gmpy2 library. gmpy2 provides access to the GMP multiple-precision library which includes gcd() and fib() functions which calculate the greatest common divisor and the n-th fibonacci numbers quickly, and only using integer arithmetic.

Here is your program re-written to use gmpy2.

import gmpy2

def generate(x):
    if x == gmpy2.gcd(x, gmpy2.fib(x-1)):
        return True
    if x == gmpy2.gcd(x, gmpy2.fib(x+1)):
        return True
    return False

for x in range(7, 2000):
    if generate(x):
        print(x)

You shouldn't be using any floating-point operations. You can calculate the GCD just using the builtin % (modulo) operator.

Update

As others have commented, you are checking for Fibonacci pseudoprimes. The actual test is slightly different than your code. Let's call the number being tested n. If n is divisible by 5, then the test passes if n evenly divides fib(n). If n divided by 5 leaves a remainder of either 1 or 4, then the test passes if n evenly divides fib(n-1). If n divided by 5 leaves a remainder of either 2 or 3, then the test passes if n evenly divides fib(n+1). Your code doesn't properly distinguish between the three cases.

If n evenly divides another number, say x, it leaves a remainder of 0. This is equivalent to x % n being 0. Calculating all the digits of the n-th Fibonacci number is not required. The test just cares about the remainder. Instead of calculating the Fibonacci number to full precision, you can calculate the remainder at each step. The following code calculates just the remainder of the Fibonacci numbers. It is based on the code given by @pts in Python mpmath not arbitrary precision?

def gcd(a,b):
    while b:
        a, b = b, a % b
    return a

def fib_mod(n, m):
    if n < 0:
        raise ValueError

    def fib_rec(n):
        if n == 0:
            return 0, 1
        else:
            a, b = fib_rec(n >> 1)
            c = a * ((b << 1) - a)
            d = b * b + a * a
            if n & 1:
                return d % m, (c + d) % m
            else:
                return c % m, d % m

    return fib_rec(n)[0]

def is_fib_prp(n):
    if n % 5 == 0:
        return not fib_mod(n, n)
    elif n % 5 == 1 or n % 5 == 4:
        return not fib_mod(n-1, n)
    else:
        return not fib_mod(n+1, n)

It's written in pure Python and is very quick.

The sequence of numbers commonly known as the Fibonacci numbers is just a special case of a general Lucas sequence L(n) = p*L(n-1) - q*L(n-2). The usual Fibonacci numbers are generated by (p,q) = (1,-1). gmpy2.is_fibonacci_prp() accepts arbitrary values for p,q. gmpy2.is_fibonacci(1,-1,n) should match the results of the is_fib_pr(n) given above.

Disclaimer: I maintain gmpy2.

Community
  • 1
  • 1
casevh
  • 11,093
  • 1
  • 24
  • 35
  • I really want to use it, but I'm having problems installing because of build errors (cannot find vcvarsall.bat). I understand this has something to do with my VS90COMNTOOLS variable but fixes don't seem to help. Anyway I'm really keen on knowing how fast it is! – Ropstah Sep 09 '14 at 02:19
  • Since you are using Windows, you should just the pre-compiled versions: https://pypi.python.org/pypi/gmpy2/2.0.3 If you install using `pip`, it will try to compile all the C code and that isn't easy. – casevh Sep 09 '14 at 02:28
  • I already downloaded that. But installing pre-compiled versions, how does that work? I can't seem to find a simple flag for the `setup.py` command. Or should i just copy-paste the files in my Python install dir? – Ropstah Sep 09 '14 at 02:39
  • It is an executable file. Just run it like any application installer and it should install. If you are using a version of Python other than those from python.org, the installation may fail. If so, see http://stackoverflow.com/questions/16171658/how-to-install-external-libraries-with-portable-python/16206624#16206624 – casevh Sep 09 '14 at 02:44
  • I downloaded the zip file. I'm gonna try the exe. – Ropstah Sep 09 '14 at 02:48
  • As i suspected. "Python not found" haha. I installed using a custom directory.... DOH! – Ropstah Sep 09 '14 at 02:49
  • Hmm this has to do with the installer i guess. It is 32-bit. Got it installed now. – Ropstah Sep 09 '14 at 02:55
  • @casevh: `for x in range(100000000000000001, 100000000000000002):` seems to fail – Ropstah Sep 09 '14 at 03:01
  • The numbers do get large. Attempting to calculate the much smaller `fib(100000000001)` requires more the 16GB of RAM. I'll update my answer with more information. – casevh Sep 09 '14 at 05:20
  • Thanks you for this extra information, really helps me a lot in understanding all concepts! – Ropstah Sep 09 '14 at 13:37
  • I do get an error stating: "RuntimeError: maximum recursion depth exceeded in comparison" when I use your last solution with really big values: e.g. `1000*2**35000-3`. – Ropstah Sep 09 '14 at 13:46
  • This seems to be around the maximum allowed value: ~`1000*2**985-3` (seems around 300 digits to the eye) – Ropstah Sep 09 '14 at 13:50
  • Python's default recursion depth to limited to ~1000 and 300 digits is roughly 1000 bits so that makes sense. You can increase the recursion depth (see the sys module) but that just delays the issue. fib_mod() can be written to avoid recursion and that would be the best approach. – casevh Sep 09 '14 at 14:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/60907/discussion-between-casevh-and-ropstah). – casevh Sep 09 '14 at 14:59
  • I'd love to, waiting for you there ;) – Ropstah Sep 10 '14 at 22:58
1

This isn't really a Python problem; it's a math/algorithm problem. You may want to ask it on the Math StackExchange instead.

Also, there is no need for any non-integer arithmetic whatsoever: you're computing floor(log10(x)) which can be done easily with purely integer math. Using arbitrary-precision math will greatly slow this algorithm down and may introduce some odd numerical errors too.

Here's a simple floor_log10(x) implementation:

from __future__ import division # if using Python 2.x

def floor_log10(x):
  res = 0
  if x < 1:
    raise ValueError
  while x >= 1:
    x //= 10
    res += 1
  return res
Dan Lenski
  • 76,929
  • 13
  • 76
  • 124
  • I'm solely using that log10 calculation for the calculation of the precision i need for the fibonacci calculation! – Ropstah Sep 09 '14 at 01:49
  • Why do you need any floating point precision at all for a purely integer algorithm? (You didn't describe the algorithm in detail, but I'm assuming you're talking about computing the standard Fibonacci sequence?) – Dan Lenski Sep 09 '14 at 01:51
  • Because I need to calculate the `Nth` position of fibonacci. That includes a `sqrt` – Ropstah Sep 09 '14 at 01:51
  • There's clearly something you're leaving out. The standard [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number) is an integer sequence which can be defined in recursive terms without reference to any operation other than simple addition. – Dan Lenski Sep 09 '14 at 01:53
  • I mentioned using `sympy` for arbitrary precision arithmetic. Their `fibonacci` implementation uses this... – Ropstah Sep 09 '14 at 01:54
  • The [`sympy.fib` docs](http://docs.sympy.org/0.7.1/modules/mpmath/functions/numtheory.html#mpmath.fibonacci) explain that the Fibonacci sequence is defined as an integer recurrence relation. The continuous formula is an asymptotic limit which the Fibonacci sequence approaches as *n* goes to infinity. The docs mention that they only use this formula (1) for complex/non-integer arguments for which the recurrence is not defined, and (2) for very large integer arguments. – Dan Lenski Sep 09 '14 at 01:56
  • If you're trying to do mathematically rigorous number-theoretical tests for prime numbers (which are integers!), then no approach other than a pure-integer or purely-symbolic one is going to cut it. – Dan Lenski Sep 09 '14 at 01:58
  • I understand and I'm just playing around here without a lot of mathematical knowledge. I saw the docs and to my understanding the `1*10^9`th Fibonacci number is fairly big. I could just calculate it with `fibonacci()`. However I needed to increase the precision to get the entire value in a way. Sorry if I'm overlooking anything, I just started Python yesterday. – Ropstah Sep 09 '14 at 02:01
  • Actually, it seems to me like you're doing fine at Python :). The issue is that there is a well-known mathematical relationship between Fibonacci numbers and primes, which others pointed out above. That relationship likely explains the behavior of your script. I'm pointing out that, **additionally**, imprecision in how computers represent numbers may cause subtle errors in your algorithm. – Dan Lenski Sep 09 '14 at 02:04
  • 1
    Fair enough. I was indeed pointed to the pseudoprimes concept which I wasn't aware of. I _am_ gonna look at `casevh`'s library to see where that gets me :). – Ropstah Sep 09 '14 at 02:20
  • 1
    `gmpy2` does include an is_fibonacci_prp() function but using that is probably cheating. ;-) – casevh Sep 09 '14 at 02:37