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
Related
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 an assessment that is asking by the given "n" as input which is a length of a stick; how many triangles can you make? (3 < n < 1,000,000)
For example:
input: N=8
output: 1
explanation:
(3,3,2)
input: N=12
output: 3
explanation:
(4,4,4) (4,5,3) (5,5,2)
Now the codes I wrote are returning 33 % accuracy as the web assessment is throwing time limit error.
ans = 0
n = int(input())
for a in range(1, n + 1):
for b in range(a, n - a + 1):
c = n - a - b
if a + b > c >= b:
ans += 1
print(ans)
code b:
ans = 0
n = int(input())
for i in range(1,n):
for j in range(i,n):
for c in range(j,n):
if(i+j+c==n and i+j>c):
ans+=1
print(ans)
How can this be made faster?
This is an intuitive O(n) algorithm I came up with:
def main():
n = int(input())
if n < 3:
print(0)
return
ans = n % 2
for a in range(2, n//2+1):
diff = n - a
if diff // 2 < a:
break
if diff % 2 == 0:
b = diff // 2
else:
b = diff // 2 + 1
b = max(b - a // 2, a)
c = n - b - a
if abs(b - c) >= a:
b += 1
c -= 1
ans += abs(b-c)//2 + 1
print(ans)
main()
I find the upper bound and lower bound for b and c and count the values in that range.
I thought of a completely different way to do it:
We take the smallest side and call it a. It can never be more than n/3, otherwise a different side would be the smallest.
We try to figure out what is the next smallest side (b):
We see what's left after reducing our a.
We divide it by 2 in order to find the middle where we'll start advancing from
We'll see how far we can get before the difference between the lengths is a (or the difference from the middle is a/2) as that's the minimum b side length that is possible and satisfies a+b>c. Basically, the second smallest side is a/2 less than the middle.
The smallest side is the maximum between our calculation or a, in caseb==a. b can never be lower than a as it violates our first rule that a is the smallest.
We figure out the difference from the middle and the smallest side. That's how many possible solutions we have for the other 2 sides.
Add everything together for every a and that's our solution.
The floor, ceil and % are fixes for when a is odd, the middle is .5, or +1 in case b+c is even, cause b==c is then possible.
Code:
import math
n = int(input("Enter a number: "))
total = 0
# a is the shortest side
for a in range(1, (n//3)+1):
length_left = n-a
middle_number = length_left/2
# Shortest potential side b where the distance between b and c is smaller than a (c-b < a)
b = middle_number-(math.ceil(a/2)-1)-((length_left % 2)/2)
# We calculate how far it is from the middle
max_distance_from_middle = middle_number - max(b, a)
# Add another 1 if the length is even, in case b==c
adding = math.floor(max_distance_from_middle) + (1 if length_left % 2 == 0 else 0)
total += adding
print(total)
Or in an ugly one-liner:
n = int(input("Enter a number: "))
print(sum(math.floor((n-a)/2 - max((n-a)/2 - math.ceil(a/2) + 1 - (((n-a) % 2)/2), a)) + 1 - ((n-a) % 2) for a in range(1, (n//3)+1)))
Alcuin's sequence expansion: O(1)
Alcuin's sequence [See: https://en.wikipedia.org/wiki/Alcuin%27s_sequence] is a series expansion of the polynomial below, where the nth coefficient corresponds to the nth answer, that is, the maximum amount of unique integer triangles with perimeter n.
The algorithmic implementation of this is simply a formula. The Online Encyclopaedia of Integer Sequences (OEIS) provides many formulas that achieve this, the simplest of which is:
round(n^2 / 48) (Even)
round((n+3)^2 / 48) (Odd)
[See: https://oeis.org/A005044]
This evidently has a constant time complexity, given that the only functions required are modulo 2, integer squared and round, each of which are constant time (under certain definitions).
Implementation
Expanded:
def triangles(n):
if n % 2 == 0:
return round(n ** 2 / 48)
else:
return round((n + 3) ** 2 / 48)
1-Liner:
def triangles(n): return round(n ** 2 / 48) if n%2==0 else round((n + 3) ** 2 / 48)
Or even:
def triangles(n): return round((n + 3 * n%2) ** 2 / 48)
Extra
No imports are needed.
As the OP questioned, why do we divide by 48? While I can't answer that explicitly, let's get an intuitive understanding. We are squaring numbers, so it is evidently going to expand greatly. By the time we get to 5, that would give 64 (8^2). So, there must be a constant (albeit a reciprocal) to restrict the growth of the parabola, thus the / 48.
When we graph the OP's method, it gives an alternating parabola. This explains why there is a back-and-forth with the +3 and +0.
https://mathworld.wolfram.com/AlcuinsSequence.html
import math
n = int(input())
print(round(n ** 2 / 48)) if n % 2 == 0 else print(round((n + 3)** 2 / 48))
I have written a fraction adder in Python for my computer science class. However, I am running into problems with the final answer reduction procedure.
The procedure uses the "not equal" comparison operator != at the start of a for loop to test whether, when dividing the numerator and denominator, there will be a remainder. If there will be a remainder (numerator % denominator ≠ 0), the procedure executes: each gets divided by n, then n increments and the for loop runs again. This continues until they divide evenly into each other.
Firstly, I am recieving a syntax error:
python FractionAdder.py 2 4 6 8
File "FractionAdder.py", line 23
for ansnum % n != 0 and ansdenom % n != 0:
^
SyntaxError: invalid syntax
Secondly, the for loop is not fully robust. My intended purpose was to have it reduce the final answer to its simplest form, but right now, it is only continuing to increment n and reduce until the numerator and denominator divide into each other evenly. This is a problem: 3 divides evenly into 6, but 3/6 is not in its simplest form. May I have some suggestions as to how to improve the robustness of my procedure, such that n continues to increment and the loop keeps cycling until the simplest form has been achieved? (Is there a better way to structure my conditional to achieve this?)
Full Code:
import sys
num1 = int(sys.argv[1])
denom1 = int(sys.argv[2])
num2 = int(sys.argv[3])
denom2 = int(sys.argv[4])
n = 1
# Find common denominators and adjust both fractions accordingly.
while denom1 != denom2:
denom1 = denom1 * denom2
num1 = num1 * denom2
denom2 = denom2 * denom1
num2 = num2 * denom2
# Add the numerators and set the ansdenom (denom1 and denom2 should be equal by this point if LCD function worked)
ansnum = num1 + num2
ansdenom = denom1
# Reduce the answer.
n = 2
for ansnum % n != 0 and ansdenom % n != 0:
ansnum = ansnum / n
ansdenom = ansdenom / n
n += 1
print("The sum of the two fractions is:" + str(ansnum) + "//" + str(ansdenom))
Thanks in advance!
The error you see is derived by the wrong usage of for where while is the right type of loop (for is for iteration, while for condition).
Nevertheless, your logic at deciding the common denominators is flawed, and leads to an infinite loop. Please read about least common multiple, and consider the following pseudocode for determining the "new" numerators:
lcm = lcm(den1, den2)
num1 *= lcm / den1
num2 *= lcm / den2
You are trying to write a greatest-common-denominator finder, and your terminating condition is wrong. Euclid's Algorithm repeatedly takes takes the modulo difference of the two numbers until the result is 0; then the next-to-last result is the GCD. The standard python implementation looks like
def gcd(a, b):
while b:
a, b = b, a % b
return a
There is an implementation already in the standard library, math.gcd.
from math import gcd
import sys
def add_fractions(n1, d1, n2, d2):
"""
Return the result of n1/d1 + n2/d2
"""
num = n1 * d2 + n2 * d1
denom = d1 * d2
div = gcd(num, denom)
return num // div, denom // div
if __name__ == "__main__":
if len(sys.argv) != 5:
print("Usage: {} num1 denom1 num2 denom2".format(sys.argv[0]))
else:
n1, d1, n2, d2 = [int(i) for i in sys.argv[1:]]
num, denom = add_fractions(n1, d1, n2, d2)
print("{}/{} + {}/{} = {}/{}".format(n1, d1, n2, d2, num, denom))
I am trying to create a program that will find every number with a square root, cube root, quarter root, and quintuple root under 2^60. Every time I run the command I get only every square number, which is what I programmed variable Num1 to be.
code:
Num = 1
Num1 = 1
while Num1 < 1152921504606846976:
Num += 2
Num1 += Num
Num2 = Num1 ** 0.5
Num3 = Num1 ** 0.33333333333333333333
Num4 = Num1 ** 0.25
Num5 = Num1 ** 0.2
if Num1 > Num:
float(Num2).is_integer()and float(Num3).is_integer()and float(Num4).is_integer() and float(Num5).is_integer()
print Num1
else:
null
Sorry for bad code I am REALLY new to this.
Num2 - Num5 are the answers of the number Num1 being rooted, and if they are all integers my goal is to have a print command give the original number, Num1
As Julien pointed out using floats in this probem is problematic due to the precision issues. Furthermore, you are doing an iteration until 2 ^ 60, which may be pretty slow.
Simple but slow approach
A simple approach would be generate all the integers that have square roots, then all the integers that have cubic roots, and so on. After that, do an intersection of all the numbers we generated so far.
That process can be done easily, we need to iterate from 1 until n^(1/k) to generate numbers that have kth-roots, if i^k is less or equal than our max number, then we have found an kth-root. The code:
def kth_roots(n, k):
i = 1
ans = []
while i ** k <= n:
ans.append(i ** k)
i += 1
return ans
n = 2 ** 60
two = kth_roots(n, 2)
three = kth_roots(n, 3)
four = kth_roots(n, 4)
five = kth_roots(n, 5)
answer = set(two) & set(three) & set(four) & set(five)
print(answer)
An approach based on divisibility
I will propose an answer asuming that you will have your maximum number expressed as a power in the form x ^ y.
Note that, a number will have an integer square root if it can be expressed as b ^ e, such that e is divisible by two, it will have a integer cube root if e is divisible by three, and so on. So a better approach, is to check which exponents will satisfy your conditions (divisibility by 2, 3, 4, and 5). Finally we must determine the value of b, we can brute force, and stop whenever it is greater than x ^ y.
In this way we do not have to play with float numbers that may be a headache here. Some code:
max_exp = 60
max_base = 2
maxm = max_base ** max_exp
ans = 0
e = 1
print(1) # Trivial case
while True:
if e % 2 == 0 and e % 3 == 0 and e % 4 == 0 and e % 5 == 0:
b = 2
flag = False
while b ** e <= maxm:
flag = True
print(b ** e)
b += 1
if flag is False:
break
e += 1
EDIT: As Hugh Bothwel mentioned, the divisibility check on the powers can be reduced to compute the LCM of [2,3,4,5], that would be 60, so any number a^60 have the mentioned integer roots. All that remains is to brute force the values of a. The code:
from fractions import gcd
def _lcm(x, y):
return (x * y) // gcd(x, y)
maxm = 2 ** 60
lcm = reduce(_lcm, [2, 3, 4, 5], 1)
a = 1
while a ** lcm <= maxm:
print(a ** lcm)
a += 1
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