-1
Q(x)=[Q(x−1)+Q(x−2)]^2
Q(0)=0, Q(1)=1

I need to find Q(29). I wrote a code in python but it is taking too long. How to get the output (any language would be fine)?

Here is the code I wrote:

a=0
b=1
for i in range(28):
    c=(a+b)*(a+b)
    a=b
    b=c
print(b)
kloe
  • 21
  • 10

4 Answers4

2

I don't think this is a tractable problem with programming. The reason why your code is slow is that the numbers within grow very rapidly, and python uses infinite-precision integers, so it takes its time computing the result.

Try your code with double-precision floats:

a=0.0
b=1.0
for i in range(28):
    c=(a+b)*(a+b)
    a=b
    b=c
print(b)

The answer is inf. This is because the answer is much much larger than the largest representable double-precision number, which is rougly 10^308. You could try using finite-precision integers, but those will have an even smaller representable maximum. Note that using doubles will lead to loss of precision, but surely you don't want to know every single digit of your huuuge number (side note: I happen to know that you do, making your job even harder).

So here's some math background for my skepticism: Your recurrence relation goes

Q[k] = (Q[k-2] + Q[k-1])^2

You can formulate a more tractable sequence from the square root of this sequence:

P[k] = sqrt(Q[k])
P[k] = P[k-2]^2 + P[k-1]^2

If you can solve for P, you'll know Q = P^2.

Now, consider this sequence:

R[k] = R[k-1]^2

Starting from the same initial values, this will always be smaller than P[k], since

P[k] = P[k-2]^2 + P[k-1]^2 >= P[k-1]^2

(but this will be a "pretty close" lower bound as the first term will always be insignificant compared to the second). We can construct this sequence:

R[k] = R[k-1]^2 = R[k-2]^4 = R[k-3]^6 = R[k-m]^(2^m) = R[0]^(2^k)

Since P[1 give or take] starts with value 2, we should consider

R[k] = 2^(2^k)

as a lower bound for P[k], give or take a few exponents of 2. For k=28 this is

P[28] > 2^(2^28) = 2^(268435456) = 10^(log10(2)*2^28) ~ 10^80807124

That's at least 80807124 digits for the final value of P, which is the square root of the number you're looking for. That makes Q[28] larger than 10^1.6e8. If you printed that number into a text file, it would take more than 150 megabytes.

If you imagine you're trying to handle these integers exactly, you'll see why it takes so long, and why you should reconsider your approach. What if you could compute that huge number? What would you do with it? How long would it take python to print that number on your screen? None of this is trivial, so I suggest that you try to solve your problem on paper, or find a way around it.


Note that you can use a symbolic math package such as sympy in python to get a feeling of how hard your problem is:

import sympy as sym
a,b,c,b0 = sym.symbols('a,b,c,b0')
a = 0
b = b0
for k in range(28):
    c = (a+b)**2
    a = b
    b = c
print(c)

This will take a while, but it will fill your screen with the explicit expression for Q[k] with only b0 as parameter. You would "only" have to substitute your values into that monster to obtain the exact result. You could also try sym.simplify on the expression, but I couldn't wait for that to return anything meaningful.


During lunch time I let your loop run, and it finished. The result has

>>> import math
>>> print(math.log10(c))
49287457.71120789

So my lower bound for k=28 is a bit large, probably due to off-by-one errors in the exponent. The memory needed to store this integer is

>>> import sys
>>> sys.getsizeof(c)
21830612

that is roughly 20 MB.

Community
  • 1
  • 1
1

This can be solved with brute force but it is still an interesting problem since it uses two different "slow" operations and there are trade-offs in choosing the correct approach.

There are two places where the native Python implementation of algorithm is slow: the multiplication of large numbers and the conversion of large numbers to a string.

Python uses the Karatsuba algorithm for multiplication. It has a running time of O(n^1.585) where n is the length of the numbers. It does get slower as the numbers get larger but you can compute Q(29).

The algorithm for converting a Python integer to its decimal representation is much slower. It has running time of O(n^2). For large numbers, it is much slower than multiplication.

Note: the times for conversion to a string also include the actual calculation time.

On my computer, computing Q(25) requires ~2.5 seconds but conversion to a string requires ~3 minutes 9 seconds. Computing Q(26) requires ~7.5 seconds but conversion to a string requires ~12 minutes 36 seconds. As the size of the number doubles, multiplication time increases by a factor of 3 and the running time of string conversion increases by a factor of 4. The running time of the conversion to string dominates. Computing Q(29) takes about 3 minutes and 20 seconds but conversion to a string will take more than 12 hours (I didn't actually wait that long).

One option is the gmpy2 module that provides access the very fast GMP library. With gmpy2, Q(26) can be calculated in ~0.2 seconds and converted into a string in ~1.2 seconds. Q(29) can be calculated in ~1.7 seconds and converted into a string in ~15 seconds. Multiplication in GMP is O(n*ln(n)). Conversion to decimal is faster that Python's O(n^2) algorithm but still slower than multiplication.

The fastest option is Python's decimal module. Instead of using a radix-2, or binary, internal representation, it uses a radix-10 (actually of power of 10) internal representation. Calculations are slightly slower but conversion to a string is very fast; it is just O(n). Calculating Q(29) requires ~9.2 seconds but calculating and conversion together only requires ~9.5 seconds. The time for conversion to string is only ~0.3 seconds.

Here is an example program using decimal. It also sums the individual digits of the final value.

import decimal

decimal.getcontext().prec = 200000000
decimal.getcontext().Emax = 200000000
decimal.getcontext().Emin = -200000000

def sum_of_digits(x):
    return sum(map(int, (t for t in str(x))))

a = decimal.Decimal(0)
b = decimal.Decimal(1)
for i in range(28):
    c = (a + b) * (a + b)
    a = b
    b = c


temp = str(b)
print(i, len(temp), sum_of_digits(temp))

I didn't include the time for converting the millions of digits into strings and adding them in the discussion above. That time should be the same for each version.

casevh
  • 11,093
  • 1
  • 24
  • 35
  • I would consider both options "brute force" solutions: the entire integer is calculated, converted to a string, and then the individual digits are summed. What do you consider to be a brute force solution? – casevh Oct 03 '16 at 13:41
0

This WILL take too long, since is a kind of geometric progression which tends to infinity.

Example:

a=0

b=1

c=1*1 = 1

a=1

b=1

c=2*2 = 4

a=1

b=4

c=5*5 = 25

a=4

b=25

c= 29*29 = 841

a=25

b=841

.
.
.
Nelthar
  • 57
  • 9
0

You can check if c%10==0 and then divide it, and in the end multiplyit number of times you divided it but in the end it'll be the same large number. If you really need to do this calculation try using C++ it should run it faster than Python.


Here's your code written in C++

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    long long int a=0;
    long long int b=1;
    long long int c=0;
    for(int i=0;i<28;i++){
            c=(a+b)*(a+b);
            a=b;
            b=c;
    }

    cout << c;
    return 0;
}
Tuc3k
  • 1,032
  • 9
  • 12
  • Did you try this? Using doubles the result is `inf`, so the answer is larger than `realmax`. I'd be surprised if a longlong could hold the answer. The bug is that you're computing `2*(a+b)` instead of `(a+b)*(a+b)`. – Andras Deak -- Слава Україні Oct 01 '16 at 12:02
  • I haven't tried running it but he should probably use bignum library. Fixed the code :) – Tuc3k Oct 01 '16 at 12:09
  • You should try it... if my suspicion is correct, it will quickly tell you the answer is inf, or whatever integer overflow is in C++. If it's so, I'd remove the code from the answer. – Andras Deak -- Слава Україні Oct 01 '16 at 12:22
  • 1
    It doesn't output inf but a negative value (Integer overflow) unfortunately that's not correct. I'll try to modify the code little bit more to see if I can fix it. – Tuc3k Oct 01 '16 at 13:26