Arithmetic precision problems with large numbers - python

I am writing a program that handles numbers as large as 10 ** 100, everything looks good when dealing with smaller numbers but when values get big I get these kind of problems:
>>> N = 615839386751705599129552248800733595745824820450766179696019084949158872160074326065170966642688
>>> ((N + 63453534345) / sqrt(2)) == (N / sqrt(2))
>>> True
Clearly the above comparision is false, why is this happening?
Program code:
from math import *
def rec (n):
r = sqrt (2)
s = r + 2
m = int (floor (n * r))
j = int (floor (m / s))
if j <= 1:
return sum ([floor (r * i) for i in range (1, n + 1)])
assert m >= s * j and j > 1, "Error: something went wrong"
return m * (m + 1) / 2 - j * (j + 1) - rec (j)
print rec (1e100)
Edit:
I don't think my question is a duplicate of the linked question above because the decimal points in n, m and j are not important to me and I am looking for a solution to avoid this precision issue.

You can’t retain the precision you want while dividing by standard floating point numbers, so you should instead divide by a Fraction. The Fraction class in the fractions module lets you do exact rational arithmetic.
Of course, the square root of 2 is not rational. But if the error is less than one part in 10**100, you’ll get the right result.
So, how to compute an approximation to sqrt(2) as a Fraction? There are several ways to do it, but one simple way is to compute the integer square root of 2 * 10**200, which will be close to sqrt(2) * 10**100, then just make that the numerator and make 10**100 the denominator.
Here’s a little routine in Python 3 for integer square root.
def isqrt(n):
lg = -1
g = (1 >> n.bit_length() // 2) + 1
while abs(lg - g) > 1:
lg = g
g = (g + n//g) // 2
while g * g > n:
g -= 1
return g
You should be able to take it from there.

Related

How to find Log10(x) without Math library [duplicate]

I have tried to create a program to calculate the base-10 logarithm based on the Taylor series-based algorithm described in "The Mathematical-Function Computation Handbook" (I found an online copy via my University's library).
A similar algorithm is given on another question on StackOverflow for which I cannot find the link right now.
10.3.2 Computing logarithms in a decimal base
For a decimal base, the base-10 logarithm is the natural choice, and the decomposition of the argument into an
exponent and a fraction gives us a decimal representation:
x = (−1)^s × f × 10^n, either f = 0 exactly, or f is in [1/10, 1).
If f ≤√1/10, set f = 10 × f and n = n − 1, so that f is now in the interval (√1/10,√10]. Then introduce a change of variable, a Taylor-series expansion, and a polynomial representation of that expansion:
z = (f − 1)/( f + 1),
f = (1 + z)/(1 − z),
D = 2 log10(e)
= 2/ log(10)
log10( f) = D × (z + z3/3 + z5/5 + z7/7 + z9/9 + z11/11 + · · · )
≈ D × z + z3Q(z2), polynomial fit incorporates D in Q(z2).
For f in (√1/10,√10], we have z in roughly [−0.5195,+0.5195]. The wider range of z requires longer polynomials compared to the binary case, and also makes the correction term z3Q(z2) relatively larger. Its magnitude does not exceed |0.35z|, so it provides barely one extra decimal digit of precision, instead of two. Accurate computation of z is easier than in the binary case: just set z = fl(fl(f−12)−12)/fl(f+1).
For this I wrote this program in Python:
def log10(x):
n = 0.0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
if(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 4.60517018598809 #2*log10(e)
sum = z
for k in range(3,111,2):
sum+=(z**k)/k
return D*n*sum
I compared the results to the math.log10 function, and the results are not as expected. My biggest issue when debugging is understanding the algorithm and why it works.
Here is my source code after the suggested corrections (changed return statement to D*sum+n fixed the value of D, and changed if(x<=0.316227766016838) to while(x<=0.316227766016838). I added some if statements to handle exceptional cases.
The code below works well within my target precision of 6 digits (I tested it with very small input, large input).
def log10(x):
# Handle exceptional cases
if (x == 1):
return 0
if (x == 0):
return float('-Inf')
if (x < 0):
return float('nan')
n = 0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
while(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 0.868588964 #2*log10(e)
#Taylor series
sum = z
for k in range(3,23,2):
sum+=(z**k)/k
return D*sum+n

Taylor series of cos x expansion in python

I want to calculate the summation of cosx series (while keeping the x in radian). This is the code i created:
import math
def cosine(x,n):
sum = 0
for i in range(0, n+1):
sum += ((-1) ** i) * (x**(2*i)/math.factorial(2*i))
return sum
and I checked it using math.cos() .
It works just fine when I tried out small numbers:
print("Result: ", cosine(25, 1000))
print(math.cos(25))
the output:
Result: 0.991203540954667 0.9912028118634736
The number is still similar. But when I tried a bigger number, i.e 40, it just returns a whole different value.
Result: 1.2101433786727471 -0.6669380616522619
Anyone got any idea why this happens?
The error term for a Taylor expansion increases the further you are from the point expanded about (in this case, x_0 = 0). To reduce the error, exploit the periodicity and symmetry by only evaluating within the interval [0, 2 * pi]:
def cosine(x, n):
x = x % (2 * pi)
total = 0
for i in range(0, n + 1):
total += ((-1) ** i) * (x**(2*i) / math.factorial(2*i))
return total
This can be further improved to [0, pi/2]:
def cosine(x, n):
x = x % (2 * pi)
if x > pi:
x = abs(x - 2 * pi)
if x > pi / 2:
return -cosine(pi - x, n)
total = 0
for i in range(0, n + 1):
total += ((-1) ** i) * (x**(2*i) / math.factorial(2*i))
return total
Contrary to the answer you got, this Taylor series converges regardless of how large the argument is. The factorial in the terms' denominators eventually drives the terms to 0.
But before the factorial portion dominates, terms can get larger and larger in absolute value. Native floating point doesn't have enough bits of precision to keep enough information for the low-order bits to survive.
Here's a way that doesn't lose any bits of precision. It's not practical because it's slow. Trust me when I tell you that it typically takes years of experience to learn how to write practical, fast, high-quality math libraries.
def mycos(x, nbits=100):
from fractions import Fraction
x2 = - Fraction(x) ** 2
i = 0
ntries = 0
total = term = Fraction(1)
while True:
ntries += 1
term = term * x2 / ((i+1) * (i+2))
i += 2
total += term
if (total // term).bit_length() > nbits:
break
print("converged to >=", nbits, "bits in", ntries, "steps")
return total
and then your examples:
>>> mycos(25)
converged to >= 100 bits in 60 steps
Fraction(177990265631575526901628601372315751766446600474817729598222950654891626294219622069090604398951917221057277891721367319419730580721270980180746700236766890453804854224688235663001, 179569976498504495450560473003158183053487302118823494306831203428122565348395374375382001784940465248260677204774780370309486592538808596156541689164857386103160689754560975077376)
>>> float(_)
0.9912028118634736
>>> mycos(40)
converged to >= 100 bits in 82 steps
Fraction(-41233919211296161511135381283308676089648279169136751860454289528820133116589076773613997242520904406094665861260732939711116309156993591792484104028113938044669594105655847220120785239949370429999292710446188633097549, 61825710035417531603549955214086485841025011572115538227516711699374454340823156388422475359453342009385198763106309156353690402915353642606997057282914587362557451641312842461463803518046090463931513882368034080863251)
>>> float(_)
-0.6669380616522619
Things to note:
The full-precision results require lots of bits.
Rounded back to float, they identically match what you got from math.cos().
It doesn't require anywhere near 1000 steps to converge.

Algorithm for computing base-10 logarithm in Python

I have tried to create a program to calculate the base-10 logarithm based on the Taylor series-based algorithm described in "The Mathematical-Function Computation Handbook" (I found an online copy via my University's library).
A similar algorithm is given on another question on StackOverflow for which I cannot find the link right now.
10.3.2 Computing logarithms in a decimal base
For a decimal base, the base-10 logarithm is the natural choice, and the decomposition of the argument into an
exponent and a fraction gives us a decimal representation:
x = (−1)^s × f × 10^n, either f = 0 exactly, or f is in [1/10, 1).
If f ≤√1/10, set f = 10 × f and n = n − 1, so that f is now in the interval (√1/10,√10]. Then introduce a change of variable, a Taylor-series expansion, and a polynomial representation of that expansion:
z = (f − 1)/( f + 1),
f = (1 + z)/(1 − z),
D = 2 log10(e)
= 2/ log(10)
log10( f) = D × (z + z3/3 + z5/5 + z7/7 + z9/9 + z11/11 + · · · )
≈ D × z + z3Q(z2), polynomial fit incorporates D in Q(z2).
For f in (√1/10,√10], we have z in roughly [−0.5195,+0.5195]. The wider range of z requires longer polynomials compared to the binary case, and also makes the correction term z3Q(z2) relatively larger. Its magnitude does not exceed |0.35z|, so it provides barely one extra decimal digit of precision, instead of two. Accurate computation of z is easier than in the binary case: just set z = fl(fl(f−12)−12)/fl(f+1).
For this I wrote this program in Python:
def log10(x):
n = 0.0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
if(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 4.60517018598809 #2*log10(e)
sum = z
for k in range(3,111,2):
sum+=(z**k)/k
return D*n*sum
I compared the results to the math.log10 function, and the results are not as expected. My biggest issue when debugging is understanding the algorithm and why it works.
Here is my source code after the suggested corrections (changed return statement to D*sum+n fixed the value of D, and changed if(x<=0.316227766016838) to while(x<=0.316227766016838). I added some if statements to handle exceptional cases.
The code below works well within my target precision of 6 digits (I tested it with very small input, large input).
def log10(x):
# Handle exceptional cases
if (x == 1):
return 0
if (x == 0):
return float('-Inf')
if (x < 0):
return float('nan')
n = 0 #Start exponent of base 10
while (x >= 1.0):
x = x/10.0
n+=1
# if x <= sqrt(1/10)
while(x<=0.316227766016838):
x = x*10.0
n = n-1
#Produce a change of variable
z = (x-1.0)/(x+1.0)
D = 0.868588964 #2*log10(e)
#Taylor series
sum = z
for k in range(3,23,2):
sum+=(z**k)/k
return D*sum+n

How should I optimize this code?

def f(n):
Total_Triangles = 0
for i in range(1,n+1):
term = 3**(i-1)
Total_Triangles+=term
return Total_Triangles
Q = int(input())
for i in range(Q):
n = int(input())
Ans = f(n)*4 +1
print(Ans%1000000007)
How to tackle with Time limit error in this code?
Karan has a good answer. It will speed up your original approach, but you still end up calculating huge numbers. Fortunately, Python's Long type can do that, but I expect that it isn't as efficient as the native 32-bit or 64-bit integer types.
You are told to give the answer modulo a huge number M, 1,000,000,007. You can improve the algorithm by using modular arithmetic throughout, so that your numbers never get very big. In modular arithmetic, this is true:
(a + b) % M == (a % M + b % M) % M
(a * b) % M == (a % M * b % M) % M
One approach could be to calculate all possible Q values up front using modular arithmetic:
M = 1000000007
def makef(m):
"""Generator to create all sum(3**i) mod M"""
n = 1
s = 0
for i in range(m):
yield s
s = (s + n) % M
n = ((n + n) % M + n) % M
f = list(makef(100000))
Q = int(input())
for i in range(Q):
n = int(input())
print (f[n] * 4 + 1) % M
This will do the calculations in a big loop, but only once and should be fast enough for your requirements.
Python offers you a second way: The expression a ** b is mapped to the in-built function pow(a, b). This function can take a third parameter: a base for modular arithmetic, so that pow(a, b, M) will calculate (a ** b) % M without generating huge intermediate results.
Now you can use Karan's neat formula. But wait, there's a pitfall: You have to divide the result of the power by two. The modular relationships above are not true of division. For example, (12 // 2) % M is 6, but if you applied the modulo operator first, as the pow function does, you'd get ((12 % 2) // 2) % M, which is 1 and not what you want. A solution is to calculate the power modulo 2 * M and then divide by 2:
def f(n):
return pow(3, n, 2 * 1000000007) // 2
Q = int(input())
for i in range(Q):
n = int(input())
print (f(n) * 4 + 1) % M
(Note that all powers of 3 are odd, so I have removed the - 1 and let the integer division do the work.)
Side note: The value of M is chosen so that the addition of two numbers that are smaller than M fits in a signed 32-bit integer. That means that users of C, C++ or Java don't have to use bignum libraries. But note that 3 * n can still overflow a signed int, so that you have to take care when multiplying by three: Use ((n + n) % M + n) % M instead.
You want to find 3 ** 0 + 3 ** 1 ... + 3 ** (n - 1), this is just a geometric series with first term a = 1, common ratio r = 3 and number of terms n = n, and using the summation of a geometric series formula, we can find f(n) much faster when defined as so:
def f(n):
return (3 ** n - 1) // 2

Recursion Formula for Integer Partitions

I have written the following code for evaluating integer partitions using the recurrence formula involving pentagonal numbers:
def part(n):
p = 0
if n == 0:
p += 1
else:
k = 1
while ((n >= (k*(3*k-1)/2)) or (n >= (k*(3*k+1)/2))):
i = (k * (3*k-1)/2)
j = (k * (3*k+1)/2)
if ((n-i) >= 0):
p -= ((-1)**k) * part(n-i)
if ((n-j) >= 0):
p -= ((-1)**k) * part(n-j)
k += 1
return p
n = int(raw_input("Enter a number: "))
m = part(n)
print m
The code works fine up until n=29. It gets a bit slow around n=24, but I still get an output within a decent runtime. I know the algorithm is correct because the numbers yielded are in accordance with known values.
For numbers above 35, I don't get an output even after waiting for a long time (about 30 minutes). I was under the impression that python can handle numbers much larger than the numbers used here. Can someone help me improve my runtime and get better results? Also, if there is something wrong with the code, please let me know.
You can use Memoization:
def memo(f):
mem = {}
def wrap(x):
if x not in mem:
mem[x] = f(x)
return mem[x]
return wrap
#memo
def part(n):
p = 0
if n == 0:
p += 1
else:
k = 1
while (n >= (k * (3 * k - 1) // 2)) or (n >= (k * (3 * k + 1) // 2)):
i = (k * (3 * k - 1) // 2)
j = (k * (3 * k + 1) // 2)
if (n - i) >= 0:
p -= ((-1) ** k) * part(n - i)
if (n - j) >= 0:
p -= ((-1) ** k) * part(n - j)
k += 1
return p
Demo:
In [9]: part(10)
Out[9]: 42
In [10]: part(20)
Out[10]: 627
In [11]: part(29)
Out[11]: 4565
In [12]: part(100)
Out[12]: 190569292
With memoization we remember previous calculation so for repeated calculations we just do a lookup in the dict.
Well there are a number of things you can do.
Remove duplicate calculations. - Basically you are calculating "3*k+1" many times for every execution of your while loop. You should calculate it once and assign it to a variable, and then use the variable.
Replace the (-1)**k with a much faster operation like something like -2*(k%2)+1). So instead of the calculation being linear with respect to k it is constant.
Cache the result of expensive deterministic calculations. "part" is a deterministic function. It gets called many times with the same arguments. You can build a hashmap of the inputs mapped to the results.
Consider refactoring it to use a loop rather than recursion. Python does not support tail recursion from what I understand, thus it is burdened with having to maintain very large stacks when you use deep recursion.
If you cache the calculations I can guarantee it will operate many times faster.

Categories

Resources