I am trying to implement Karatsuba multiplication algorithm for binary (base 2) numbers.
A requirement is that the intermediate / final results must also be in binary so as to assist in educative purposes.
This is my implementation so far. (I am using bittstring library as a container for binary digits)
Convertion to BitArray:
This class is created so that signed (2's complement represented binary strings) as well as unsigned / Python signed binary (-0b111) can all be converted to BitArray
quickly without handling the same at the spot of usage.
Could this be improved further to make it faster and cleaner?
class BitTools(object):
# DEV NOTE : BitArray is the class name so pep8 naming ( bit_array ) is
# not used
@staticmethod
def to_BitArray(input_data, bits, signed=False):
"""
Returns a bit array object, from the input_data.
"""
result = None
if type(input_data) not in [str, int, BitArray]:
raise TypeError(
"Input must be given as integer or binary strings or bitarray objects")
# Convert to Bit Array Objects
if isinstance(input_data, int):
result = BitArray(int=input_data, length=bits)
# Sign is taken care by the sign of input_data
elif isinstance(input_data, str):
# Sign is decided by the "signed" parameter or - in the input_data
# string
input_data = input_data.replace("0b", "")
if len(input_data) == 0:
return BitArray(int=0, length=bits)
# First priority to - in the string "-0b111" ( -7 )
if "-" in input_data or ((input_data[0] == "1") and not signed) or (input_data[0] == "0"):
result = BitArray(int=int(input_data, 2), length=bits)
# Next priority to 2s complement signed binary explicitly mentined
# as signed
else:
input_data = input_data.replace("-", "")
length = len(input_data)
mask = int(("1" * length), 2)
input_data = (int(input_data, 2) ^ mask) + 1
result = BitArray(int=-input_data, length=bits)
else:
raise TypeError(
"Input must be given as binary strings or integers.")
return result
Karatsuba Multiplication Algorithm.
class Multipliers(object):
"""
This class implements various types of mulipliers using different algorithms used in study, analysis
or practical implementation of ALU's in various Computer architectures.
"""
@staticmethod
def karatsuba_multiply(multiplier, multiplicand, bits = None, signed=False):
# Use bit array only to calculate 2's complement of signed binaries.
if bits is None:
multiplier = multiplier.replace('0b','')
if not signed:
multiplier = multiplier.lstrip("0")
multiplicand = multiplicand.replace('0b','')
if not signed:
multiplicand = multiplicand.lstrip("0")
bits = max(len(multiplier), len(multiplicand)) + 1
len_input = bits
if (bits % 2) == 0:
bits += 1
multiplicand = BitTools.to_BitArray(multiplicand, bits, signed)
multiplier = BitTools.to_BitArray(multiplier, bits, signed)
sign_bit = None
if ( signed or (multiplicand.int < 0) or (multiplier.int < 0)):
# Calculating the sign of the product
if ( ( multiplicand.bin[0] == "1" ) ^ ( multiplier.bin[0] == "1" ) ):
sign_bit = 1
else:
sign_bit = 0
# Strip off the sign bit
multiplicand.int = abs(multiplicand.int)
multiplier.int = abs(multiplier.int)
# Binary without the sign bit
multiplier_abs = multiplier.bin[1:]
multiplicand_abs = multiplicand.bin[1:]
if len(multiplier_abs) == 0 or len(multiplicand_abs) == 0:
return "0"
# Base case of 1 bit multiplication
if len(multiplier_abs) == 1:
return "1" if ( multiplier_abs == "1" and multiplicand_abs == "1" ) else "0"
# Base case for 2 bit multiplication
if len(multiplier_abs) == 2:
return bin( multiplicand.int * multiplier.int ).replace("0b","")
m = (bits-1) / 2
# x = x1*(2**m) + x0
# y = y1*(2**m) + y0
x1 = multiplicand_abs[:m]
x0 = multiplicand_abs[m:]
y1 = multiplier_abs[:m]
y0 = multiplier_abs[m:]
#print x1, x0
#print y1, y0
#print "m ", m
# Upper half of the bits
z2 = Multipliers.karatsuba_multiply(x1, y1)
# Lower half of the bits
z0 = Multipliers.karatsuba_multiply(x0, y0)
# ( x1 + x0 )( y1 + y0 )
sum_term1 = int(x1,2) + int(x0,2)
sum_term1 = bin(sum_term1)
sum_term2 = int(y1,2) + int(y0,2)
sum_term2 = bin(sum_term2)
#print "sum terms: ", sum_term1.replace('0b',''), sum_term2.replace('0b','0')
z1 = Multipliers.karatsuba_multiply(sum_term1, sum_term2)
z1 = bin ( int(z1,2) - int(z2,2) - int(z0,2) )
#print "z1: ", z1
# The "0" padding at the right is binary equivalent of left shift or muliply with 2**bits
abs_result = int((z2 + "0"*(2*m)),2) + int((z1 + "0"*(m)),2) + int(z0,2)
# len_result = 2*length of multiplicand / multiplier
len_result = 2*len_input
# Converting to binary of 2ce the bit length of inputs
abs_result = BitTools.to_BitArray(abs_result, len_result)
if sign_bit == 1:
abs_result.int *= -1
return abs_result.bin