I recently implemented Karatsuba Multiplication as a personal exercise. I wrote my implementation in Python following the pseudocode provided on wikipedia:
procedure karatsuba(num1, num2)
if (num1 < 10) or (num2 < 10)
return num1*num2
/* calculates the size of the numbers */
m = max(size_base10(num1), size_base10(num2))
m2 = m/2
/* split the digit sequences about the middle */
high1, low1 = split_at(num1, m2)
high2, low2 = split_at(num2, m2)
/* 3 calls made to numbers approximately half the size */
z0 = karatsuba(low1, low2)
z1 = karatsuba((low1+high1), (low2+high2))
z2 = karatsuba(high1, high2)
return (z2*10^(2*m2)) + ((z1-z2-z0)*10^(m2)) + (z0)
Here is my python implementation:
def karat(x,y):
if len(str(x)) == 1 or len(str(y)) == 1:
return x*y
else:
m = max(len(str(x)),len(str(y)))
m2 = m / 2
a = x / 10**(m2)
b = x % 10**(m2)
c = y / 10**(m2)
d = y % 10**(m2)
z0 = karat(b,d)
z1 = karat((a+b),(c+d))
z2 = karat(a,c)
return (z2 * 10**(2*m2)) + ((z1 - z2 - z0) * 10**(m2)) + (z0)
My question is about final merge of z0, z1, and z2.
z2 is shifted m digits over (where m is the length of the largest of two multiplied numbers).
Instead of simply multiplying by 10^(m), the algorithm uses *10^(2*m2)* where m2 is m/2.
I tried replacing 2*m2 with m and got incorrect results. I think this has to do with how the numbers are split but I'm not really sure what's going on.
Depending on your Python version you must or should replace / with the explicit floor division operator // which is the appropriate here; it rounds down ensuring that your exponents remain entire numbers.
This is essential for example when splitting your operands in high digits (by floor dividing by 10^m2) and low digits (by taking the residual modulo 10^m2) this would not work with a fractional m2.
It also explains why 2 * (x // 2) does not necessarily equal x but rather x-1 if x is odd.
In the last line of the algorithm 2 m2 is correct because what you are doing is giving a and c their zeros back.
If you are on an older Python version your code may still work because / used to be interpreted as floor division when applied to integers.
def karat(x,y):
if len(str(x)) == 1 or len(str(y)) == 1:
return x*y
else:
m = max(len(str(x)),len(str(y)))
m2 = m // 2
a = x // 10**(m2)
b = x % 10**(m2)
c = y // 10**(m2)
d = y % 10**(m2)
z0 = karat(b,d)
z1 = karat((a+b),(c+d))
z2 = karat(a,c)
return (z2 * 10**(2*m2)) + ((z1 - z2 - z0) * 10**(m2)) + (z0)
i have implemented the same idea but i have restricted to the 2 digit multiplication as the base case because i can reduce float multiplication in function
import math
def multiply(x,y):
sx= str(x)
sy= str(y)
nx= len(sx)
ny= len(sy)
if ny<=2 or nx<=2:
r = int(x)*int(y)
return r
n = nx
if nx>ny:
sy = sy.rjust(nx,"0")
n=nx
elif ny>nx:
sx = sx.rjust(ny,"0")
n=ny
m = n%2
offset = 0
if m != 0:
n+=1
offset = 1
floor = int(math.floor(n/2)) - offset
a = sx[0:floor]
b = sx[floor:n]
c = sy[0:floor]
d = sy[floor:n]
print(a,b,c,d)
ac = multiply(a,c)
bd = multiply(b,d)
ad_bc = multiply((int(a)+int(b)),(int(c)+int(d)))-ac-bd
r = ((10**n)*ac)+((10**(n/2))*ad_bc)+bd
return r
print(multiply(4,5))
print(multiply(4,58779))
print(int(multiply(4872139874092183,5977098709879)))
print(int(4872139874092183*5977098709879))
print(int(multiply(4872349085723098457,597340985723098475)))
print(int(4872349085723098457*597340985723098475))
print(int(multiply(4908347590823749,97098709870985)))
print(int(4908347590823749*97098709870985))
I tried replacing 2*m2 with m and got incorrect results. I think this has to do with how the numbers are split but I'm not really sure what's going on.
This goes to the heart of how you split your numbers for the recursive calls.
If you choose to use an odd n then n//2 will be rounded down to the nearest whole number, meaning your second number will have a length of floor(n/2) and you would have to pad the first with the floor(n/2) zeros.
Since we use the same n for both numbers this applies to both. This means if you stick to the original odd n for the final step, you would be padding the first term with the original n zeros instead of the number of zeros that would result from the combination of the first padding plus the second padding (floor(n/2)*2)
You have used m2 as a float. It needs to be an integer.
def karat(x,y):
if len(str(x)) == 1 or len(str(y)) == 1:
return x*y
else:
m = max(len(str(x)),len(str(y)))
m2 = m // 2
a = x // 10**(m2)
b = x % 10**(m2)
c = y // 10**(m2)
d = y % 10**(m2)
z0 = karat(b,d)
z1 = karat((a+b),(c+d))
z2 = karat(a,c)
return (z2 * 10**(2*m2)) + ((z1 - z2 - z0) * 10**(m2)) + (z0)
Your code and logic is correct, there is just issue with your base case. Since according to the algo a,b,c,d are 2 digit numbers you should modify your base case and keep the length of x and y equal to 2 in the base case.
I think it is better if you used math.log10 function to calculate the number of digits instead of converting to string, something like this :
def number_of_digits(number):
"""
Used log10 to find no. of digits
"""
if number > 0:
return int(math.log10(number)) + 1
elif number == 0:
return 1
else:
return int(math.log10(-number)) + 1 # Don't count the '-'
The base case if len(str(x)) == 1 or len(str(y)) == 1: return x*y is incorrect. If you run either of the python code given in answers against large integers, the karat() function will not produce the correct answer.
To make the code correct, you need to change the base case to if len(str(x) < 3 or len(str(y)) < 3: return x*y.
Below is a modified implementation of Paul Panzer's answer that correctly multiplies large integers.
def karat(x,y):
if len(str(x)) < 3 or len(str(y)) < 3:
return x*y
n = max(len(str(x)),len(str(y))) // 2
a = x // 10**(n)
b = x % 10**(n)
c = y // 10**(n)
d = y % 10**(n)
z0 = karat(b,d)
z1 = karat((a+b), (c+d))
z2 = karat(a,c)
return ((10**(2*n))*z2)+((10**n)*(z1-z2-z0))+z0
Related
Given two sequences A and B of the same length: one is strictly increasing, the other is strictly decreasing.
It is required to find an index i such that the absolute value of the difference between A[i] and B[i] is minimal. If there are several such indices, the answer is the smallest of them. The input sequences are standard Python arrays. It is guaranteed that they are of the same length. Efficiency requirements: Asymptotic complexity: no more than the power of the logarithm of the length of the input sequences.
I have implemented index lookup using the golden section method, but I am confused by the use of floating point arithmetic. Is it possible to somehow improve this algorithm so as not to use it, or can you come up with a more concise solution?
import random
import math
def peak(A,B):
def f(x):
return abs(A[x]-B[x])
phi_inv = 1 / ((math.sqrt(5) + 1) / 2)
def cal_x1(left,right):
return right - (round((right-left) * phi_inv))
def cal_x2(left,right):
return left + (round((right-left) * phi_inv))
left, right = 0, len(A)-1
x1, x2 = cal_x1(left, right), cal_x2(left,right)
while x1 < x2:
if f(x1) > f(x2):
left = x1
x1 = x2
x2 = cal_x1(x1,right)
else:
right = x2
x2 = x1
x1 = cal_x2(left,x2)
if x1 > 1 and f(x1-2) <= f(x1-1): return x1-2
if x1+2 < len(A) and f(x1+2) < f(x1+1): return x1+2
if x1 > 0 and f(x1-1) <= f(x1): return x1-1
if x1+1 < len(A) and f(x1+1) < f(x1): return x1+1
return x1
#value check
def make_arr(inv):
x = set()
while len(x) != 1000:
x.add(random.randint(-10000,10000))
x = sorted(list(x),reverse = inv)
return x
x = make_arr(0)
y = make_arr(1)
needle = 1000000
c = 0
for i in range(1000):
if abs(x[i]-y[i]) < needle:
c = i
needle = abs(x[i]-y[i])
print(c)
print(peak(x,y))
Approach
The poster asks about alternative, simpler solutions to posted code.
The problem is a variant of Leetcode Problem 852, where the goal is to find the peak index in a moutain array. We convert to a peak, rather than min, by computing the negative of the abolute difference. Our aproach is to modify this Python solution to the Leetcode problem.
Code
def binary_search(x, y):
''' Mod of https://walkccc.me/LeetCode/problems/0852/ to use function'''
def f(m):
' Absoute value of difference at index m of two arrays '
return -abs(x[m] - y[m]) # Make negative so we are looking for a peak
# peak using binary search
l = 0
r = len(arr) - 1
while l < r:
m = (l + r) // 2
if f(m) < f(m + 1): # check if increasing
l = m + 1
else:
r = m # was decreasing
return l
Test
def linear_search(A, B):
' Linear Search Method '
values = [abs(ai-bi) for ai, bi in zip(A, B)]
return values.index(min(values)) # linear search
def make_arr(inv):
random.seed(10) # added so we can repeat with the same data
x = set()
while len(x) != 1000:
x.add(random.randint(-10000,10000))
x = sorted(list(x),reverse = inv)
return x
# Create data
x = make_arr(0)
y = make_arr(1)
# Run search methods
print(f'Linear Search Solution {linear_search(x, y)}')
print(f'Golden Section Search Solution {peak(x, y)}') # posted code
print(f'Binary Search Solution {binary_search(x, y)}')
Output
Linear Search Solution 499
Golden Section Search Solution 499
Binary Search Solution 499
I made a program to multiply two strings and I expected 1000*10 = 10000, but I am getting 100000. I don't know where my logic is wrong. I also tried replacing m2 with m1 in the expression ((val3+val4) * 10**(m2)) but nothing works, and when I try to multiply 120 * 10, I get 300.
def Multiply_Recursive(a, b):
if len(a) == 1 or len(b) == 1:
return str(int(a)*int(b))
else:
m = max(len(a),len(b))
m2 = m // 2
m1 = len(b) // 2
A = int(a[0:m2])
B = int(a[m2:len(a)])
C = int(b[0:m1])
D = int(b[m1:len(b)])
val1 = int(Multiply_Recursive(str(A),str(C)))
val2 = int(Multiply_Recursive(str(B),str(D)))
val3 = int(Multiply_Recursive(str(A),str(D)))
val4 = int(Multiply_Recursive(str(B),str(C)))
return str(val1 * 10**(2*m2) + ((val3+val4) * 10**(m2)) + val2)
num = Multiply_Recursive("1000","10")
print(num)
In the final formula you assume that m2 digits occur at the right of the split point, and that this number of digits is the same for both splits. Neither is generally true.
Also, as the definition of m depends on the larger input, it could lead to an out-of-range number of digits represented by m2, when a is much smaller than b.
You could fix this like this:
Define m2 like this:
m2 = len(a) // 2
Split the input in such a way that m1 and m2 are the number of digits after the split point, not before. So split like this:
A = int(a[:-m2])
B = int(a[-m2:])
C = int(b[:-m1])
D = int(b[-m1:])
Change the final formula taking into account that m1 and m2 can be different. So for instance there should not be 2*m2, but m1+m2, ...Etc:
return str(val1 * 10**(m1+m2) + val3 * 10**m2 + val4 * 10**m1 + val2)
The real Karatsuba algorithm will make sure to choose m1 and m2 so they are the same, and it will not convert strings to integer unless it is certain the size of the strings is limited. It also needs one less recursive call.
The naming is not helpful - just using ah, al, bh, bl:
def multiply_recursive(a, b):
""" Return the product of a and b.
All numbers are passed as their decimal string representations.
"""
if not a or not b:
return "0"
if len(a) == 1 or len(b) == 1:
return str(int(a)*int(b))
#
m = max(len(a), len(b))
m2 = m // 2
# different split points not suitable for common scaling of "mixed products" below
# m1 = len(b) // 2
# low parts are remainders: split from least significant end!
ah, al = a[0:-m2], a[-m2:].lstrip("0")
bh, bl = b[0:-m2], b[-m2:].lstrip("0")
# print(ah, al, bh, bl, sep=',')
ahbh = int(multiply_recursive(ah, bh))
albl = int(multiply_recursive(al, bl))
ahbl = int(multiply_recursive(ah, bl))
albh = int(multiply_recursive(al, bh))
product = str(ahbh * 100**m2 + (ahbl+albh) * 10**m2 + albl)
# print(product)
return product
num = multiply_recursive("1000","10")
print(num)
I am doing the Stanford's Algorithms MOOC and got stuck with Karatsuba multiplication algorithm programming assignment.
Karatsuba multiplication is simply an algorithm for multiplication of two integer that is asymptotically faster than usual multiplication.
RESTRICTIONS
I restricted myself to only use single-digit multiplication and padding numbers (adding zeros at the end, i.e. multiplying by 10 to some power), so there are 3 base cases
I also decided to convert the numbers into strings and take several numbers instead of dividing it by 10 to some power, but I tried the other way, it does not help
I also decided to generalise the algorithm, i.e. do not assume that number1 and number2 have similar length therefore I use both n1 and n2 (see the code)
Because of the point above, I also decided not to use the Gauss's trick
I know, the restrictions might see meaningless, but I used it as a programming exercise rather than some practical solution, hence I am mainly interesting in spotting my mistake rather than finding some "simpler solution".
Here is my code:
def karatsuba(number1, number2):
n1 = len(str(number1)) # number of digits in the first number
n2 = len(str(number2)) # number of digits in the second number
if n1 == 1 and n2 == 1: # base case number 1 - both numbers are single-digit
kara = number1*number2
return kara
elif n1 == 1: # base case number 2 - only one number is single-digit
c = int(str(number2)[:(n2//2)])
d = int(str(number2)[(n2//2):])
kara = 10**((n2+1)//2)*c*number2 + d*number2
return kara
elif n2 == 1: # base case number 3 - only one number is single digit
a = int(str(number1)[:(n1//2)])
b = int(str(number1)[(n1//2):])
kara = 10**((n2+1)//2)*a*number2 + b*number2
return kara
elif n1 != 1 and n2 != 1: # loop
a = int(str(number1)[:(n1 // 2)])
b = int(str(number1)[(n1 // 2):])
c = int(str(number2)[:(n2 // 2)])
d = int(str(number2)[(n2 // 2):])
z1 = karatsuba(a, c)
z2 = karatsuba(a, d)
z3 = karatsuba(b, c)
z4 = karatsuba(b, d)
kara = 10**((n1+1)//2+(n2+1)//2)*z1 + 10**((n1+1)//2)*z2 + 10**((n2+1)//2)*z3 + z4
return kara
This is not a Karatzuba algorithm. The point of Karatzuba is to make only 3 recursive invocations; you do 4 of them. The recursive invocations, in your notation, should be
karatzuba(a, c)
karatzuba(b, d)
karatzuba(a + b, c + d)
Besides that, there is a problem with base case 2: number1 does not participate in it at all.
These are some mistakes to be corrected if you haven't yet.
kara = 10**((n2+1)//2)*c*number1 + d*number1 #in base case 2
kara = 10**((n1+1)//2)*a*number2 + b*number2 #in base case 3. your code has n2+1
Conventional Karatsuba has 3 recursions. but I can see why are you making 4 recursions. can't say which is faster though.
working code for the example you've given above in the comments
def karatsuba(number1, number2):
n1 = len(str(number1)) # number of digits in the first number
n2 = len(str(number2)) # number of digits in the second number
if n1 == 1 and n2 == 1: # base case number 1 - both numbers are single-digit
kara = number1*number2
return kara
elif n1 == 1: # base case number 2 - only one number is single-digit
c = int(str(number2)[:(n2//2)])
d = int(str(number2)[(n2//2):])
kara = 10**((n2+1)//2)*c*number1 + d*number1 #a mistake here
return kara
elif n2 == 1: # base case number 3 - only one number is single digit
a = int(str(number1)[:(n1//2)])
b = int(str(number1)[(n1//2):])
kara = 10**((n1+1)//2)*a*number2 + b*number2 #a mistake here
return kara
elif n1 != 1 and n2 != 1: # loop
a = int(str(number1)[:(n1 // 2)])
b = int(str(number1)[(n1 // 2):])
c = int(str(number2)[:(n2 // 2)])
d = int(str(number2)[(n2 // 2):])
z1 = karatsuba(a, c)
z2 = karatsuba(a, d)
z3 = karatsuba(b, c)
z4 = karatsuba(b, d)
kara = 10**((n1+1)//2+(n2+1)//2)*z1 + 10**((n1+1)//2)*z2 + 10**((n2+1)//2)*z3 + z4
return kara
num1 = 3141592653589793238462643383279502884197169399375105820974944592
num2 = 2718281828459045235360287471352662497757247093699959574966967627
k_res = karatsuba(num1,num2)
ac_res = num1*num2
print(k_res)
print(ac_res)
assert k_res==ac_res
I can't use inner loops
I can't use if-else
I need to compute the following series:
x - x^3/3! + x^5/5! - x^7/7! + x^9/9! ...
I am thinking something like the following:
n =1
x =0.3
one=1
fact1=1
fact2=1
term =0
sum =0
for i in range(1, n+1, 2):
one = one * (-1)
fact1 = fact1*i
fact2 = fact2*i+1
fact = fact1*fact2
x = x * x
term = x/fact
sum = sum + term
But, I am finding hard times in keeping the multiplications of both fact and x.
You want to compute a sum of terms. Each term is the previous term mutiplied by -1 * x * x and divided by n * (n+1). Just write it:
def func(x):
eps = 1e-6 # the expected precision order
term = x
sum = term
n = 1
while True:
term *= -x * x
term /= (n+1) * (n+2)
if abs(term) < eps: break
sum += term
n += 2
return sum
Demo:
>>> func(math.pi / 6)
0.4999999918690232
giving as expected 0.5 with a precision of 10e-6
Note: the series is the well known development of the sin function...
Isn't that a Taylor series for sin(x)? And can you use list comprehension? With list comprehension that could be something like
x = 0.3
sum([ (-1)**(n+1) * x**(2n-1) / fact(2n-1) for n in range(1, numOfTerms)])
If you can't use list comprehension you could simply loop that like this
x=0.3
terms = []
for n in range(1, numberOfTerms):
term = (-1)**(n+1)*x**(2n-1)/fact(2n-1)
terms.append(term)
sumOfTerms = sum(terms)
Then calculating the factorial by recursion:
def fact(k):
if (k == 1):
return n
else:
return fact(k-1)*k
Calcualting the factorial using Striling's approximation:
fact(k) = sqrt(2*pi*k)*k**k*e**(-k)
No if-else here nor inner loops. But then there will be precision errors and need to use math lib to get the constants or get even more precision error and use hard coded values for pi and e.
Hope this can help!
n = NUMBER_OF_TERMS
x = VALUE_OF_X
m = -1
sum = x # Final sum
def fact(i):
f = 1
while i >= 1:
f = f * i
i = i - 1
return f
for i in range(1, n):
r = 2 * i + 1
a = pow (x , r)
term = a * m / fact(r);
sum = sum + term;
m = m * (-1)
Say I have a 1D array x with positive and negative values in Python, e.g.:
x = random.rand(10) * 10
For a given positive value of K, I would like to find the offset c that makes the sum of positive elements of the array y = x + c equal to K.
How can I solve this problem efficiently?
How about binary search to determine which elements of x + c are going to contribute to the sum, followed by solving the linear equation? The running time of this code is O(n log n), but only O(log n) work is done in Python. The running time could be dropped to O(n) via a more complicated partitioning strategy. I'm not sure whether a practical improvement would result.
import numpy as np
def findthreshold(x, K):
x = np.sort(np.array(x))[::-1]
z = np.cumsum(np.array(x))
l = 0
u = x.size
while u - l > 1:
m = (l + u) // 2
if z[m] - (m + 1) * x[m] >= K:
u = m
else:
l = m
return (K - z[l]) / (l + 1)
def test():
x = np.random.rand(10)
K = np.random.rand() * x.size
c = findthreshold(x, K)
assert np.abs(K - np.sum(np.clip(x + c, 0, np.inf))) / K <= 1e-8
Here's a randomized expected O(n) variant. It's faster (on my machine, for large inputs), but not dramatically so. Watch out for catastrophic cancellation in both versions.
def findthreshold2(x, K):
sumincluded = 0
includedsize = 0
while x.size > 0:
pivot = x[np.random.randint(x.size)]
above = x[x > pivot]
if sumincluded + np.sum(above) - (includedsize + above.size) * pivot >= K:
x = above
else:
notbelow = x[x >= pivot]
sumincluded += np.sum(notbelow)
includedsize += notbelow.size
x = x[x < pivot]
return (K - sumincluded) / includedsize
You can sort x in descending order, loop over x and compute the required c thus far. If the next element plus c is positive, it should be included in the sum, so c gets smaller.
Note that it might be the case that there is no solution: if you include elements up to m, c is such that m+1 should also be included, but when you include m+1, c decreases and a[m+1]+c might get negative.
In pseudocode:
sortDescending(x)
i = 0, c = 0, sum = 0
while i < x.length and x[i] + c >= 0
sum += x[i]
c = (K - sum) / i
i++
if i == 0 or x[i-1] + c < 0
#no solution
The running time is obviously O(n log n) because it is dominated by the initial sort.