Karatsuba multiplication error for large integers in Python - python

I've been trying to implement the Karatsuba algorithm in Python3 in the following way:
def karatsuba(num1,num2):
n_max = max(len(str(int(num1))), len(str(int(num2))))
if n_max == 1:
return int(num1*num2)
n = n_max + n_max%2
a = num1//10**(n/2)
b = num1%10**(n/2)
c = num2//10**(n/2)
d = num2%10**(n/2)
t1 = karatsuba(a,c)
t3 = karatsuba(b,d)
t2 = karatsuba(a+b,c+d) - t1 - t3
return int(t1*10**n + t2*10**(n/2) + t3)
While the function works for small products, it fails for ones that exceed 18 digits. One can see this by running, say,
import random
for i in range(1,12):
a = random.randint(10**i, 10**(i+1)-1)
b = random.randint(10**i, 10**(i+1)-1)
print(f"{len(str(a*b))} digits, error: {abs(a*b - karatsuba(a,b))}")
I would appreciate if someone could explain what is the root of this problem and, if possible, how could this code be modified to fix it. My best guess is that some round-off error is committed by Python at some point. That said, I don't really know how int fundamentally works in this language.

Use n//2 instead of n/2 to stay with ints and avoid that precision loss due to that float value.

Related

Julia code not finishing while Python code does

I am very new to Julia and was trying to implement/rewrite some of my previous Python code as practice. I was using the Project Euler problem 25 as practice
In Python I have
def fibonacci(N):
"""Returns the Nth Fibonacci Number"""
F = [0, 1]
i = 0
while i <= N-2:
F_new = F[i] + F[i+1]
F.append(F_new)
i += 1
return F[N]
N = 0
x = 1000
while len(str(fibonacci(N))) <= x:
if len(str(fibonacci(N))) == x:
print(N)
break
N = N + 1
Which runs and gives me the correct answer in about 6.5 seconds. When trying to do this in Julia below
function fib(N)
F = [0, 1]
global i = 1
while i <= N-2
F_new = F[i] + F[i+1]
append!(F, F_new)
global i += 1
end
return F[N]
end
N = 1
x = 1000
while length(string(fib(N))) <= x
if length(string(fib(N))) == x
print(N-1)
break
end
global N += 1
end
The code seems to run "forever". However in the Julia code only when x<= 20 will the code finish and produce the correct answer. In the Julia code when x>20 the program never ends.
I'm not sure where something could go wrong if it runs for all values below 21? Could somebody explain where the error is happening and why?
Python integers are by default unbounded in size and will grow as needed. Julia on the other hand will default to a signed 64 bit integer if on a 64 bit system. (See docs) This begins to overflow when trying to calculate values above around 19 digits long, hence why this starts around x=20. In order to get the same behavior in Julia, you should use the BigInt type for any values or arguments which can get above this size.
The main problem with your code is what #duckboycool has described. The second advice is to always write functions in Julia. Read the Julia performance tips page for a good start.
Note that you can make the function by #Bill 2X faster by removing the unnecessary if like this:
function test(x = 1000)
N = 0
while ndigits(fib(N)) < x
N += 1
end
return N
end
But if you really want a 16000X faster Julia version, then you can do this:
function euler25()
limit = big(10)^999
a, b = big(1), big(1)
N = 2
while b <= limit
a, b = b, a + b
N += 1
end
return N
end
#btime euler25() = 4782
377.700 μs (9573 allocations: 1.15 MiB)
This runs in 377 μs, because we avoid calculating fib(N) at every step from the beginning. And instead of comparing with the length of a string of the output at each iteration, we just compare with 10^999.
In addition to the earlier answer, note that you should avoid globals if looking at performance, so this is much faster than your global i and x code:
function fib(N)
F = [big"0", big"1"]
for i in 1:N-2
F_new = F[i] + F[i+1]
push!(F, F_new)
end
return F[N]
end
function test(x = 1000)
N = 1
while length(string(fib(N))) <= x
if length(string(fib(N))) == x
print(N-1)
break
end
N += 1
end
end
test()
#AboAmmar shows probably the best "normal" way of writing this. But if you want something even a bit more optimized, you can use in-place BigInt calls. I'm not sure whether I would recommend this, but it's nice to be aware of it.
using Base.GMP.MPZ: add!, set!
function euler25_(limit=big(10)^999)
a, b = big(1), big(1)
N = 2
c = big(0)
while b <= limit
add!(c, a, b)
set!(a, b)
set!(b, c)
N += 1
end
return N
end
This uses the special BigInt functions in the GMP.MPZ library, and writes values in-place, avoiding most of the allocations, and running 2.5x faster on my laptop.

if condition using sympy equation solver/ sympy very slow

I want to solve this equation witht the following parameters:
gamma = 0.1
F = 0.5
w = 0
A = symbols('A')
a = 1 + w**4 -w**2 + 4*(gamma**2)*w**2
b = 1 - w**2
sol = solve(a*A**2 + (9/16)*A**6 + (3/2)*b*A**4 -F**2)
list_A = []
for i in range(len(sol)):
if(type( solutions[i] )==float ):
print(sol[i])
list_A = sol[i]
However, as supposed, I am getting some real and complex values, and I want to remove the complex ones and only keep the floats. But this condition I implemented is not valid due to the type of sol[i] is either sympy.core.add.Add for complex or sympy.core.numbers.Float for floats.
My question is, how can I modify my condition so that it works for getting only the float values?
In addition, is there a way to speed it up? it is very slow if I put it in a loop for many values of omega.
this is my first time working with sympy
When it is able to validate solutions relative to assumptions on symbols, it will; so if you tell SymPy that A is real then -- if it can verify the solutions -- it will only show the real ones:
>>> A = symbols('A',real=True)
>>> sol = solve(a*A**2 + (9/16)*A**6 + (3/2)*b*A**4 -F**2)
>>> sol
[-0.437286658108243, 0.437286658108243]

Improving perfomance(speed) of exponent problem

I'm currently learning Python on repl.it and I have a problem with one of my work.
My code is supposed to:
1.Input a given integer X
2.Find the greatest integer n where 2ⁿ is less than or equal to X.
3.Print the exponent value(n) and the result of the expression 2ⁿ.
But my code fail as the machine insert too big number like 10^8+2. The program completely failed
Here is the piece of code that I'm working on:
X = int(input())
a = X//2
while a > -1:
if (2**a) < =x:
print(a)
print(2**a)
break
else:
a -= 1
Can anyone find me another solution to this problem, or improve the bit of code I'm working on by its runtime? It works with small number(less than 10^6) but otherwise the program freeze.
Thanks in advance!
Of course, I can't refer to the "too big input" that you mention (since you didn't provide it), but as for the problem itself, it could be easier solved in the following way:
import numpy as np
a = int(np.log2(your_input))
The first issue I see is that in you code
if (2**a) < =x:
print(a)
print(2**a)
you calculate the value of 2**a twice. A good start could be to save the value of 2**a into a variable. However, since you are only doing powers of 2 you could also take a look at bitwise operations. So instead of doing a = X//2 you could also write
a= X >> 2
and instead of doing 2**a write
temp = 1 << a
When working with powers of 2 it can be significantly faster to work with bitwise operations.
I did it! (using some of your solutions of course)
This is my teachers code :
x = int(input())
n = 1
while 2 ** n <= x:
n += 1
print(n - 1, 2 ** (n - 1))

calculation of pi, getting different value than book

The mathematical constant π (pi) is an irrational number with value approximately 3.1415928... The precise value of π is equal to the following infinite sum: π = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + ... We can get a good approximation of π by computing the sum of the first few terms. Write a function approxPi() that takes as a parameter a floating point value error and approximates the constant π within error by computing the above sum, term by term, until the absolute value of the difference between the current sum and the previous sum (with one fewer terms) is no greater than error. Once the function finds that the difference is less than error, it should return the new sum. Please note that this function should not use any functions or constants from the math module. You are supposed to use the described algorithm to approximate π, not use the built-in value in Python.
I have done the below program but for some reason I am getting the different value from the one in book.
def pi(error):
prev = 1
current = 4
i = 1
while abs(current - prev) > error:
d = 2.0* i +1
sign = (-1)**i
prev = current
current = current + sign * 4 / d
i = i +1
return current
output In [2]: pi(0.01)
Out[2]: 3.146567747182955
But instead I need to get this value
>>> approxPi(0.01)
3.1611986129870506
>>> approxPi(0.0000001)
3.1415928535897395
The approximation you're using is very poor at converging, that is you have to loop quite a lot of times to get a reasonable value. You see that the difference will be 1/d and that's the accuracy. You'll have to loop 5000 times to get four digits 50k times to get next and 500k to get next and so on (that is exponential time complexity for the digits).
This could be one of the reasons that you see a discrepancy here, that you simply get the situation where rounding errors add up. Since you need to use that many iterations you will never get near the full precision of the floats you're using. Another source of discrepancy is that your reference probably is using another exit condition, with your condition you should get an error less than the provided (ideally), and you've got it (3.146567747182955-pi < 0.01). It actually looks like your reference is using the condition abs(current-prev) > 4*error instead.
The formula you're using is that pi=4arctan(1) and using McLaurin expansion of arctan(x) for a value of x that is on the limit of converging at all. To get better performance one should use lower x in that expansion. For example pi=16arctan(1/5)-4arctan(1/239) could be used (this gives linear time complexity for the digits):
def pi(error):
a = 1.0/5
b = 1.0/239
prev = 1
current = 0.0
i = 0
while abs(current - prev) > error:
d = 2.0* i +1
sign = (-1)**i
prev = current
current = current + sign * (16*a - 4*b)/d
a = a*1.0/(5*5)
b = b*1.0/(239*239)
i = i +1
return current
I guess the stopping rule for your function and approxPi are different. In fact, your estimation is better. If you print out all the values of current, you will see that when i equals 50 your function produces your desired output. But, it goes beyond that and produce a better approximation.
So to get the answer you are looking for then the question as posed is wrong in how it describes the exit condition.
Re-organizing you get Pi = 4*(1/1 + 1/3 + 1/5 + ...), to get 3.1611986129870506 with an error of 0.01, then you looking at the subsequent terms and stopping when the term < error:
from itertools import count, cycle #, izip for Py2
def approxPi(error):
p = 0
for sign, d in zip(cycle([1,-1]), count(1, 2)): # izip for Py2
n = sign / d
p += n
if abs(n) < error:
break
return 4*p
Then you get the correct answers:
>>> approxPi(0.01)
3.1611986129870506
>>> approxPi(0.0000001)
3.1415928535897395
Using your code (from #PaulBoddington: Find the value of pi)
def pi(error):
prev = 0
current = 1
i = 1
while abs(current - prev) > error:
d = 2.0*i + 1
sign = (-1)**i
prev = current
current = current + sign / d
i += 1
return 4*current
Note: This is not the difference between the current sum and the previous sum, so the question is wrong, but it is equal to difference_between_sums < error*4. So to get the correct exit for your original code just multiply the error by 4, e.g.:
>>> pi(0.04)
3.1611986129870506

Solving a system of Quadratic Equations

I am doing a cryptography program in Python.
It consists in reading a random phrase, like HELLO. Then it assigns it's respective ASCII values like:
H = 72, E = 79.
Then, using Pythagoras' Theorem, it creates two numbers C1 and C2, like this:
C1 = sqrt(A^2 + (A+B)^2);
C2 = sqrt(B^2 + (A+B)^2)
where, in this case A = H and B = E. That would be the encryption part, but I am having problems solving the system, which will act as the decryptor.
How can I solve this system using python?
C1 = sqrt(A^2 + (A+B)^2);
C2 = sqrt(B^2 + (A+B)^2);
Of course only C1 and C2 are known.
Do I need a new module? Which one?
If you're only talking about using the two characters for encryption, that's not a good idea.
That only gives 65,536 possible variants (two ASCII characters was mentioned but I'll assume a full 8-bit octet, so 256 multiplied by 256), it's easy enough to brute-force this. First, we know that every value of A and B generates a unique pair C1/C2, as per the following program which generates no duplicates:
lookup = {}
for a in range(256):
for b in range(256):
c1s = a*a + (a+b)*(a+b)
c2s = b*b + (a+b)*(a+b)
lkey = "%d:%d"%(c1s,c2s)
lookup[lkey] = 1
print(len(lookup)) # gives 65536 (256 squared)
Also, since both A and B are integers, so too will be C12 and C22.
So the first step is to work out the squares of the values you're given (since sqrt is a potentially expensive operation), taking into account the possibility of floating point inaccuracies:
c1s = int(c1 * c1 + 0.1)
c2s = int(c2 * c2 + 0.1)
Then, simply brute-force the solution:
for a in range(256):
for b in range(256):
if c1s != a*a + (a+b)*(a+b):
continue
if c2s == b*b + (a+b)*(a+b):
print(a,b)
sys.exit(0)
print("No solution")
On my machine, searching for the slowest solution (both a and b set to 255), it takes just a smidgeon over six hundredths of a second.
But you should keep in mind that, if an attacker has the C1/C2 values, they too can get the results that fast. And, even if they don't have it, the fact that there are only 64K possibilities means that they can try every possible value in a little over one and a quarter hours. So I wouldn't be using this method to store anything that's valuable for very long :-)

Categories

Resources