I implemented a RSA Cryptography program, using python, and it works perfectly using prime numbers with aproximally 10 digits. But when I use numbers with 25 digits or more, for example, it does not work.
It worked with the following keys:
p = 2324731
q = 186647
e = 433899328297
n = 433904066957
It not worked with:
p = 3673864730662357928718503
q = 2127738717256957618781057
e = 7817024229395103552360986476332293342120062315901
n = 7817024229395103552360993847944520620136941797671
here's the code:
inverse (d key):
#classmethod
def __linearOperation(cls, a, b, mdc, i):
t = -int(a / b)
r = a % b
mdc.append([1, a, t, b])
if r == 1:
return mdc
inverseLine = cls.__linearOperation(b, r, mdc, i + 1)
s = inverseLine[i][0]
t = inverseLine[i][2]
inverseLine[i - 1][0] *= t
inverseLine[i - 1][2] *= t
inverseLine[i - 1][2] += s
inverseLine.remove(inverseLine[i])
return inverseLine
def __inverse(self, e, φ):
inverseLine = self.__linearOperation(e, φ, [], 1)
inverse = inverseLine[0][0]
if inverse < 0:
return inverse + φ
if inverse > φ:
return inverse % φ
else:
return inverse
Modular Exponentiation:
#staticmethod
def __QuickMod(base, exp, n):
result = 1
while exp > 0:
if exp & 1:
result = (result * base) % n
base = (base ** 2) % n
exp = exp >> 1
return result
encrypt/decrypt:
def encryptChar(self, n: int, e: int, M: int) -> int:
C = self.__QuickMod(M, e, n) # C = M^e mod n
return C
def decryptChar(self, p: int, q: int, e: int, C: int) -> int:
d = self.__inverse(e, (p - 1) * (q - 1))
M = self.__QuickMod(C, d, p * q) # M = C^d mod n
return M
By trying to encrypt the number 109 ("m" char in ascII), the encryptChar function returns 6825028446539883496812231478440519650519629664279.
By trying to decrypt the number above the decryptChar function should returns the number 109 back. But that's not what happens.
In my conception, by using python we do not have number size restriction.
Is there something i'm not seeing? Thank you for your help.
The problem is in my inverse algorithm. I'll try to re-implement it. But using pow() function with -1 as expoent works.
I'm writing a program where the user enters a, b, c, d, e, and f and displays the result. If ad - bc = 0, I'm supposed to report that there is no solution. But even when I include the part of code where:
if denominator == 0: print("The equation has no solution")
I keep getting a dividing by zero error. The numbers I use for the prompt are 1.0, 2.0, 2.0, 4.0, 4.0, 5.0 respectively. Here's my code:
def cramersRule():
a = float(input("Enter a: "))
b = float(input("Enter b: "))
c = float(input("Enter c: "))
d = float(input("Enter d: "))
e = float(input("Enter e: "))
f = float(input("Enter f: "))
denominator = ((a * d) - (b * c))
x = (((e * d) - (b * f)) / denominator)
y = (((a * f) - (e * c)) / denominator)
e = ((a * x) + (b * y))
f = ((c * x) + (d * y))
if denominator == 0:
print("The equation has no solution.")
else:
print("x is", x , "and y is" , y)
Please help!
You are performing calculations with it:
x = (((e * d) - (b * f)) / denominator)
y = (((a * f) - (e * c)) / denominator)
That's why you get the error. You must first check if the denominator is zero.
def cramersRule():
a = float(input("Enter a: "))
b = float(input("Enter b: "))
c = float(input("Enter c: "))
d = float(input("Enter d: "))
e = float(input("Enter e: "))
f = float(input("Enter f: "))
denominator = ((a * d) - (b * c))
if denominator == 0:
print("The equation has no solution.")
else:
x = (((e * d) - (b * f)) / denominator)
y = (((a * f) - (e * c)) / denominator)
e = ((a * x) + (b * y))
f = ((c * x) + (d * y))
print("x is", x , "and y is" , y)
Everyone else has solved the problem you asked about; here's the problem you didn't ask about, better code organization!
from __future__ import division # all division is floating-point
import sys
# version compatibility
if sys.hexversion < 0x3000000:
inp = raw_input # Python 2.x
else:
inp = input # Python 3.x
def get_float(prompt):
while True:
try:
return float(inp(prompt))
except ValueError:
pass
def cramers_rule(a, b, c, d, e, f):
denominator = a*d - b*c
if denominator:
x = (e*d - b*f) / denominator
y = (a*f - e*c) / denominator
e = a*x + b*y
f = c*x + d*y
return x, y, e, f
else:
return None # no solution
def main():
prompt = "Enter {0}: ".format
kwargs = {ch:get_float(prompt(ch)) for ch in 'abcdef'}
res = cramers_rule(**kwargs)
if res is None:
print("The equation has no solution.")
else:
print("x is {0} and y is {1}".format(*res))
if __name__=="__main__":
main()
.
Each function should only do one thing: cramers_rule() should calculate Cramer's Rule, it should not be doing input and output. Having cramers_rule() accept arguments and return the result means it can be reused (ie in actual calculations).
Error checking on inputs - it's nice if it doesn't crash if, when you ask the user for a float, they enter 'potato' (you know someone will). Delegated to get_float(), where it belongs.
if x == 0: is unPythonic; PEP-8 says if not x: is preferred because it allows duck-typing - the function will now work on any type that knows how to do elementary operations. I don't know if it makes sense to apply Cramer's Law to tensors, but if it does, now you can.
Python operators obey order of operations; I got rid of a bunch of extraneous parentheses.
Hope that helps ;-)
You should use the try-except error handling pattern. Here's a basic example:
try:
x = (((e * d) - (b * f)) / denominator)
except ZeroDivisionError:
print("The equation has no solution.")
else:
print("Success!")
Move your check for zero to earlier in your function like so:
...
denominator = ((a * d) - (b * c)
if denominator == 0:
print("The equation has no solution.")
else:
x = (((e * d) - (b * f)) / denominator)
y = (((a * f) - (e * c)) / denominator)
e = ((a * x) + (b * y))
f = ((c * x) + (d * y))
print("x is", x , "and y is" , y)
I want to code an "application" that will plot the nth reagent's concentration in time function of a chain reaction: A->B->C->D->...
The thing is, c_n(t) contains 2^n - 1 exponential functions - which are nested based on a pattern I've found:
c_1(t) = c_0_1 * exp(-k_1 * t)
c_2(t) = c_0_2 * exp(-k_2 * t) + c_0_1 * k_1 * {[exp(-k_1 * t) - exp(-k_2 * t)]/[k_2 - k_1]}
c_3(t) = c_0_3 * exp(-k_3 * t) + c_0_2 * k_2 * {[exp(-k_2 * t) - exp(-k_3 * t)]/[k_3 - k_2]} + c_0_1 * k_1 * k_2 * [1/(k_2-k_1)] * <{[exp(-k_1 * t) - exp(-k_3 * t)]/[k_3 - k_1]} - {[exp(-k_2 * t) - exp(-k_3 * t)]/[k_3 - k_2]}>
As you can see, each equation is a sum of reappearing elements. The number of nestings is dependent on the degree of relationship: 0-th degree (A to A) - simple exponential function, 1st degree (A to B, B to C, etc.) - 1 nesting, 2nd degree (A to C, B to D, etc.) - 2 nestings, etc.
Each equation can be divided into reappearing parts:
the 'independent' unit: c_0_n * exp(-k_n * t),
the basic unit: f(a,b) = (exp(- k_n[b - 1] * t) - exp(- k_n[a - 1] * t)) / (k_n[a - 1] - k_n[b - 1]),
the nested unit based on the basic unit,
the product of the multiplication of constants (parameters) before each nested unit.
Each nested unit of the nth equation derives from the nested units of the (n-1)th equation. The equations themselves can be obtained through iterated integration. The number of possible equations (based on the number of independent kinetic constants k) for the nth reagent is given by Bell number B(n).
Each such equation can be obtained from the equation with n independent kinetic constants for the nth reagent (all are independent of one another). One simply has to find the limes of such equation. E.g. if k_3 = k_4 and k_7 = k_2, then we are looking for lim k_4->k_3 [lim k_7->k_2 (f(t))].
The working code:
print
print ("Commands: komendy() - list of commands, test() - sets initial parameters, zakres() - asks for the number of reagents, tabela() - displays the table, stez() - asks for the initial concentrations, kin() - asks for the kinetic constants.")
print
n = 0
import matplotlib.pyplot as plt
import numpy as np
def komendy(): # displays the list of commands
print
print ("Commands: komendy() - list of commands, test() - sets initial parameters, zakres() - asks for the number of reagents, tabela() - displays the table, stez() - asks for the initial concentrations, kin() - asks for the kinetic constants.")
print
return
def zakres(): # number of reagents query
global n, zakres_n, c_0_n, k_n
n = int(raw_input("Define the number of n reagents: "))
zakres_n = range(1, n + 1)
c_0_n = [int(0)] * n
k_n = [int(0)] * n
return
def stez(): # initial concentrations query
while True:
y = int(raw_input("Define the value of c_0_n for n equal to (press 0 to break): "))
if y == 0:
break
x = raw_input("Define the value of c_0_" + str(y) + ": ")
if "." in x:
c_0_n[y - 1] = float(x)
else:
c_0_n[y - 1] = int(x)
return
def kin(): # kinetic constants query
while True:
q = int(raw_input("Define the value of k_n for n equal to (press 0 to break): "))
if q == 0:
break
p = raw_input("Define the value of k_" + str(q) + ": ")
if "." in p:
k_n[q - 1] = float(p)
else:
k_n[q - 1] = int(p)
return
def tabela(): # displays the table with the initial data
if n == 0:
zakres()
print
print "n: ", zakres_n
print "c_0_n: ", c_0_n
print "k_n: ", k_n
print
else:
print
print "n: ", zakres_n
print "c_0_n: ", c_0_n
print "k_n: ", k_n
print
return
def wykres(): # plots the basic unit
global f_t, t_k, t, t_d
a = int(raw_input("a = "))
b = int(raw_input("b = "))
reag = map(int, raw_input("Provide the reagents to plot (separate with spacebar): ").split(" "))
t_k = float(raw_input("Define time range from 0 to: "))
t_d = float(raw_input("Set the precision of the time axis: "))
t = np.arange(0,t_k,t_d)
p = []
def f_t(t):
return (np.exp(- k_n[b - 1] * t) - np.exp(- k_n[a - 1] * t)) / (k_n[a - 1] - k_n[b - 1])
f_t = f_t(t)
for i in reag:
p += plt.plot(t,i*f_t)
And the code that doesn't work [yet] (the only difference is the new wykres() function I'm trying to build):
print
print ("Commands: komendy() - list of commands, test() - sets initial parameters, zakres() - asks for the number of reagents, tabela() - displays the table, stez() - asks for the initial concentrations, kin() - asks for the kinetic constants.")
print
n = 0
import matplotlib.pyplot as plt
import numpy as np
def komendy(): # displays the list of commands
print
print ("Commands: komendy() - list of commands, test() - sets initial parameters, zakres() - asks for the number of reagents, tabela() - displays the table, stez() - asks for the initial concentrations, kin() - asks for the kinetic constants.")
print
return
def zakres(): # number of reagents query
global n, zakres_n, c_0_n, k_n
n = int(raw_input("Define the number of n reagents: "))
zakres_n = range(1, n + 1)
c_0_n = [int(0)] * n
k_n = [int(0)] * n
return
def stez(): # initial concentrations query
while True:
y = int(raw_input("Define the value of c_0_n for n equal to (press 0 to break): "))
if y == 0:
break
x = raw_input("Define the value of c_0_" + str(y) + ": ")
if "." in x:
c_0_n[y - 1] = float(x)
else:
c_0_n[y - 1] = int(x)
return
def kin(): # kinetic constants query
while True:
q = int(raw_input("Define the value of k_n for n equal to (press 0 to break): "))
if q == 0:
break
p = raw_input("Define the value of k_" + str(q) + ": ")
if "." in p:
k_n[q - 1] = float(p)
else:
k_n[q - 1] = int(p)
return
def tabela(): # displays the table with the initial data
if n == 0:
zakres()
print
print "n: ", zakres_n
print "c_0_n: ", c_0_n
print "k_n: ", k_n
print
else:
print
print "n: ", zakres_n
print "c_0_n: ", c_0_n
print "k_n: ", k_n
print
return
def wykres(): # plots the requested functions
global t_k, t, t_d, f, constans
reag = map(int, raw_input("Provide the reagents to plot (separate with spacebar): ").split(" "))
t_k = float(raw_input("Define the time range from 0 to: "))
t_d = float(raw_input("Define the precision of the time axis: "))
t = np.arange(0,t_k,t_d)
p = []
def f(a,b): # basic unit
return (np.exp(- k_n[b - 1] * t) - np.exp(- k_n[a - 1] * t)) / (k_n[a - 1] - k_n[b - 1])
def const(l,r): # products appearing before the nested parts
const = 1
constans = 1
for h in range(l,r):
const = const * k_n[h]
constans = c_0_n[l] * const
return
def czlonF(g): # nested part
czlonF = 0
for u in range(g):
czlonF = czlonF + npoch(f(a,b),g)
if g == 1:
czlonF(g) = 0
return
def npoch(f(a,b),n):
f = f(a,b)
for x in range(b+1, n+1):
f = npoch(f(a,b),x)
return
def c(j): # final result, concentration in time function
return
def czlon0(m): # 'independent' part
return (c_0_n[m - 1] * np.exp(- k_n[m - 1] * t))
for i in reag: # the actual plot command
p += plt.plot(t,c(i))
plt.show()
return
def test():
global n, zakres_n, k_n, c_0_n
n = 5
zakres_n = range(1, n + 1)
k_n = [1,2,3,4,5]
c_0_n = [2,3,4,5,6]
return
plt.show()
return
def test():
global n, zakres_n, k_n, c_0_n
n = 5
zakres_n = range(1, n + 1)
k_n = [1,2,3,4,5]
c_0_n = [2,3,4,5,6]
return
How do I fix the wykres() function so that it plots c(n)? How do I build it so that it can be plotted? I want Python to automatically build c_n(t) for whichever n I desire and plot all of them.
I need a way to compute the nth root of a long integer in Python.
I tried pow(m, 1.0/n), but it doesn't work:
OverflowError: long int too large to convert to float
Any ideas?
By long integer I mean REALLY long integers like:
11968003966030964356885611480383408833172346450467339251
196093144141045683463085291115677488411620264826942334897996389
485046262847265769280883237649461122479734279424416861834396522
819159219215308460065265520143082728303864638821979329804885526
557893649662037092457130509980883789368448042961108430809620626
059287437887495827369474189818588006905358793385574832590121472
680866521970802708379837148646191567765584039175249171110593159
305029014037881475265618958103073425958633163441030267478942720
703134493880117805010891574606323700178176718412858948243785754
898788359757528163558061136758276299059029113119763557411729353
915848889261125855717014320045292143759177464380434854573300054
940683350937992500211758727939459249163046465047204851616590276
724564411037216844005877918224201569391107769029955591465502737
961776799311859881060956465198859727495735498887960494256488224
613682478900505821893815926193600121890632
If it's a REALLY big number. You could use a binary search.
def find_invpow(x,n):
"""Finds the integer component of the n'th root of x,
an integer such that y ** n <= x < (y + 1) ** n.
"""
high = 1
while high ** n <= x:
high *= 2
low = high/2
while low < high:
mid = (low + high) // 2
if low < mid and mid**n < x:
low = mid
elif high > mid and mid**n > x:
high = mid
else:
return mid
return mid + 1
For example:
>>> x = 237734537465873465
>>> n = 5
>>> y = find_invpow(x,n)
>>> y
2986
>>> y**n <= x <= (y+1)**n
True
>>>
>>> x = 119680039660309643568856114803834088331723464504673392511960931441>
>>> n = 45
>>> y = find_invpow(x,n)
>>> y
227661383982863143360L
>>> y**n <= x < (y+1)**n
True
>>> find_invpow(y**n,n) == y
True
>>>
Gmpy is a C-coded Python extension module that wraps the GMP library to provide to Python code fast multiprecision arithmetic (integer, rational, and float), random number generation, advanced number-theoretical functions, and more.
Includes a root function:
x.root(n): returns a 2-element tuple (y,m), such that y is the
(possibly truncated) n-th root of x; m, an ordinary Python int,
is 1 if the root is exact (x==y**n), else 0. n must be an ordinary
Python int, >=0.
For example, 20th root:
>>> import gmpy
>>> i0=11968003966030964356885611480383408833172346450467339251
>>> m0=gmpy.mpz(i0)
>>> m0
mpz(11968003966030964356885611480383408833172346450467339251L)
>>> m0.root(20)
(mpz(567), 0)
You can make it run slightly faster by avoiding the while loops in favor of setting low to 10 ** (len(str(x)) / n) and high to low * 10. Probably better is to replace the len(str(x)) with the bitwise length and using a bit shift. Based on my tests, I estimate a 5% speedup from the first and a 25% speedup from the second. If the ints are big enough, this might matter (and the speedups may vary). Don't trust my code without testing it carefully. I did some basic testing but may have missed an edge case. Also, these speedups vary with the number chosen.
If the actual data you're using is much bigger than what you posted here, this change may be worthwhile.
from timeit import Timer
def find_invpow(x,n):
"""Finds the integer component of the n'th root of x,
an integer such that y ** n <= x < (y + 1) ** n.
"""
high = 1
while high ** n < x:
high *= 2
low = high/2
while low < high:
mid = (low + high) // 2
if low < mid and mid**n < x:
low = mid
elif high > mid and mid**n > x:
high = mid
else:
return mid
return mid + 1
def find_invpowAlt(x,n):
"""Finds the integer component of the n'th root of x,
an integer such that y ** n <= x < (y + 1) ** n.
"""
low = 10 ** (len(str(x)) / n)
high = low * 10
while low < high:
mid = (low + high) // 2
if low < mid and mid**n < x:
low = mid
elif high > mid and mid**n > x:
high = mid
else:
return mid
return mid + 1
x = 237734537465873465
n = 5
tests = 10000
print "Norm", Timer('find_invpow(x,n)', 'from __main__ import find_invpow, x,n').timeit(number=tests)
print "Alt", Timer('find_invpowAlt(x,n)', 'from __main__ import find_invpowAlt, x,n').timeit(number=tests)
Norm 0.626754999161
Alt 0.566340923309
If you are looking for something standard, fast to write with high precision. I would use decimal and adjust the precision (getcontext().prec) to at least the length of x.
Code (Python 3.0)
from decimal import *
x = '11968003966030964356885611480383408833172346450467339251\
196093144141045683463085291115677488411620264826942334897996389\
485046262847265769280883237649461122479734279424416861834396522\
819159219215308460065265520143082728303864638821979329804885526\
557893649662037092457130509980883789368448042961108430809620626\
059287437887495827369474189818588006905358793385574832590121472\
680866521970802708379837148646191567765584039175249171110593159\
305029014037881475265618958103073425958633163441030267478942720\
703134493880117805010891574606323700178176718412858948243785754\
898788359757528163558061136758276299059029113119763557411729353\
915848889261125855717014320045292143759177464380434854573300054\
940683350937992500211758727939459249163046465047204851616590276\
724564411037216844005877918224201569391107769029955591465502737\
961776799311859881060956465198859727495735498887960494256488224\
613682478900505821893815926193600121890632'
minprec = 27
if len(x) > minprec: getcontext().prec = len(x)
else: getcontext().prec = minprec
x = Decimal(x)
power = Decimal(1)/Decimal(3)
answer = x**power
ranswer = answer.quantize(Decimal('1.'), rounding=ROUND_UP)
diff = x - ranswer**Decimal(3)
if diff == Decimal(0):
print("x is the cubic number of", ranswer)
else:
print("x has a cubic root of ", answer)
Answer
x is the cubic number of 22873918786185635329056863961725521583023133411
451452349318109627653540670761962215971994403670045614485973722724603798
107719978813658857014190047742680490088532895666963698551709978502745901
704433723567548799463129652706705873694274209728785041817619032774248488
2965377218610139128882473918261696612098418
Oh, for numbers that big, you would use the decimal module.
ns: your number as a string
ns = "11968003966030964356885611480383408833172346450467339251196093144141045683463085291115677488411620264826942334897996389485046262847265769280883237649461122479734279424416861834396522819159219215308460065265520143082728303864638821979329804885526557893649662037092457130509980883789368448042961108430809620626059287437887495827369474189818588006905358793385574832590121472680866521970802708379837148646191567765584039175249171110593159305029014037881475265618958103073425958633163441030267478942720703134493880117805010891574606323700178176718412858948243785754898788359757528163558061136758276299059029113119763557411729353915848889261125855717014320045292143759177464380434854573300054940683350937992500211758727939459249163046465047204851616590276724564411037216844005877918224201569391107769029955591465502737961776799311859881060956465198859727495735498887960494256488224613682478900505821893815926193600121890632"
from decimal import Decimal
d = Decimal(ns)
one_third = Decimal("0.3333333333333333")
print d ** one_third
and the answer is: 2.287391878618402702753613056E+305
TZ pointed out that this isn't accurate... and he's right. Here's my test.
from decimal import Decimal
def nth_root(num_decimal, n_integer):
exponent = Decimal("1.0") / Decimal(n_integer)
return num_decimal ** exponent
def test():
ns = "11968003966030964356885611480383408833172346450467339251196093144141045683463085291115677488411620264826942334897996389485046262847265769280883237649461122479734279424416861834396522819159219215308460065265520143082728303864638821979329804885526557893649662037092457130509980883789368448042961108430809620626059287437887495827369474189818588006905358793385574832590121472680866521970802708379837148646191567765584039175249171110593159305029014037881475265618958103073425958633163441030267478942720703134493880117805010891574606323700178176718412858948243785754898788359757528163558061136758276299059029113119763557411729353915848889261125855717014320045292143759177464380434854573300054940683350937992500211758727939459249163046465047204851616590276724564411037216844005877918224201569391107769029955591465502737961776799311859881060956465198859727495735498887960494256488224613682478900505821893815926193600121890632"
nd = Decimal(ns)
cube_root = nth_root(nd, 3)
print (cube_root ** Decimal("3.0")) - nd
if __name__ == "__main__":
test()
It's off by about 10**891
Possibly for your curiosity:
http://en.wikipedia.org/wiki/Hensel_Lifting
This could be the technique that Maple would use to actually find the nth root of large numbers.
Pose the fact that x^n - 11968003.... = 0 mod p, and go from there...
I may suggest four methods for solving your task. First is based on Binary Search. Second is based on Newton's Method. Third is based on Shifting n-th Root Algorithm. Fourth is called by me Chord-Tangent method described by me in picture here.
Binary Search was already implemented in many answers above. I just introduce here my own vision of it and its implementation.
As alternative I also implement Optimized Binary Search method (marked Opt). This method just starts from range [hi / 2, hi) where hi is equal to 2^(num_bit_length / k) if we're computing k-th root.
Newton's Method is new here, as I see it wasn't implemented in other answers. It is usually considered to be faster than Binary Search, although my own timings in code below don't show any speedup. Hence this method here is just for reference/interest.
Shifting Method is 30-50% faster than optimized binary search method, and should be even faster if implemented in C++, because C++ has fast 64 bit arithemtics which is partially used in this method.
Chord-Tangent Method:
Chord-Tangent Method is invented by me on piece of paper (see image above), it is inspired and is an improvement of Newton method. Basically I draw a Chord and a Tangent Line and find intersection with horizontal line y = n, these two intersections form lower and upper bound approximations of location of root solution (x0, n) where n = x0 ^ k. This method appeared to be fastest of all, while all other methods do more than 2000 iterations, this method does just 8 iterations, for the case of 8192-bit numbers. So this method is 200-300x times faster than previous (by speed) Shifting Method.
As an example I generate really huge random integer of 8192 bits in size. And measure timings of finding cubic root with both methods.
In test() function you can see that I passed k = 3 as root's power (cubic root), you can pass any power instead of 3.
Try it online!
def binary_search(begin, end, f, *, niter = [0]):
while begin < end:
niter[0] += 1
mid = (begin + end) >> 1
if f(mid):
begin = mid + 1
else:
end = mid
return begin
def binary_search_kth_root(n, k, *, verbose = False):
# https://en.wikipedia.org/wiki/Binary_search_algorithm
niter = [0]
res = binary_search(0, n + 1, lambda root: root ** k < n, niter = niter)
if verbose:
print('Binary Search iterations:', niter[0])
return res
def binary_search_opt_kth_root(n, k, *, verbose = False):
# https://en.wikipedia.org/wiki/Binary_search_algorithm
niter = [0]
hi = 1 << (n.bit_length() // k - 1)
while hi ** k <= n:
niter[0] += 1
hi <<= 1
res = binary_search(hi >> 1, hi, lambda root: root ** k < n, niter = niter)
if verbose:
print('Binary Search Opt iterations:', niter[0])
return res
def newton_kth_root(n, k, *, verbose = False):
# https://en.wikipedia.org/wiki/Newton%27s_method
f = lambda x: x ** k - n
df = lambda x: k * x ** (k - 1)
x, px, niter = n, 2 * n, [0]
while abs(px - x) > 1:
niter[0] += 1
px = x
x -= f(x) // df(x)
if verbose:
print('Newton Method iterations:', niter[0])
mini, minv = None, None
for i in range(-2, 3):
v = abs(f(x + i))
if minv is None or v < minv:
mini, minv = i, v
return x + mini
def shifting_kth_root(n, k, *, verbose = False):
# https://en.wikipedia.org/wiki/Shifting_nth_root_algorithm
B_bits = 64
r, y = 0, 0
B = 1 << B_bits
Bk_bits = B_bits * k
Bk_mask = (1 << Bk_bits) - 1
niter = [0]
for i in range((n.bit_length() + Bk_bits - 1) // Bk_bits - 1, -1, -1):
alpha = (n >> (i * Bk_bits)) & Bk_mask
B_y = y << B_bits
Bk_yk = (y ** k) << Bk_bits
Bk_r_alpha = (r << Bk_bits) + alpha
Bk_yk_Bk_r_alpha = Bk_yk + Bk_r_alpha
beta = binary_search(1, B, lambda beta: (B_y + beta) ** k <= Bk_yk_Bk_r_alpha, niter = niter) - 1
y, r = B_y + beta, Bk_r_alpha - ((B_y + beta) ** k - Bk_yk)
if verbose:
print('Shifting Method iterations:', niter[0])
return y
def chord_tangent_kth_root(n, k, *, verbose = False):
niter = [0]
hi = 1 << (n.bit_length() // k - 1)
while hi ** k <= n:
niter[0] += 1
hi <<= 1
f = lambda x: x ** k
df = lambda x: k * x ** (k - 1)
# https://i.stack.imgur.com/et9O0.jpg
x_begin, x_end = hi >> 1, hi
y_begin, y_end = f(x_begin), f(x_end)
for icycle in range(1 << 30):
if x_end - x_begin <= 1:
break
niter[0] += 1
if 0: # Do Binary Search step if needed
x_mid = (x_begin + x_end) >> 1
y_mid = f(x_mid)
if y_mid > n:
x_end, y_end = x_mid, y_mid
else:
x_begin, y_begin = x_mid, y_mid
# (y_end - y_begin) / (x_end - x_begin) = (n - y_begin) / (x_n - x_begin) ->
x_n = x_begin + (n - y_begin) * (x_end - x_begin) // (y_end - y_begin)
y_n = f(x_n)
tangent_x = x_n + (n - y_n) // df(x_n) + 1
chord_x = x_n + (n - y_n) * (x_end - x_n) // (y_end - y_n)
assert chord_x <= tangent_x, (chord_x, tangent_x)
x_begin, x_end = chord_x, tangent_x
y_begin, y_end = f(x_begin), f(x_end)
assert y_begin <= n, (chord_x, y_begin, n, n - y_begin)
assert y_end > n, (icycle, tangent_x - binary_search_kth_root(n, k), y_end, n, y_end - n)
if verbose:
print('Chord Tangent Method iterations:', niter[0])
return x_begin
def test():
import random, timeit
nruns = 3
bits = 8192
n = random.randrange(1 << (bits - 1), 1 << bits)
a = binary_search_kth_root(n, 3, verbose = True)
b = binary_search_opt_kth_root(n, 3, verbose = True)
c = newton_kth_root(n, 3, verbose = True)
d = shifting_kth_root(n, 3, verbose = True)
e = chord_tangent_kth_root(n, 3, verbose = True)
assert abs(a - b) <= 0 and abs(a - c) <= 1 and abs(a - d) <= 1 and abs(a - e) <= 1, (a - b, a - c, a - d, a - e)
print()
print('Binary Search timing:', round(timeit.timeit(lambda: binary_search_kth_root(n, 3), number = nruns) / nruns, 3), 'sec')
print('Binary Search Opt timing:', round(timeit.timeit(lambda: binary_search_opt_kth_root(n, 3), number = nruns) / nruns, 3), 'sec')
print('Newton Method timing:', round(timeit.timeit(lambda: newton_kth_root(n, 3), number = nruns) / nruns, 3), 'sec')
print('Shifting Method timing:', round(timeit.timeit(lambda: shifting_kth_root(n, 3), number = nruns) / nruns, 3), 'sec')
print('Chord Tangent Method timing:', round(timeit.timeit(lambda: chord_tangent_kth_root(n, 3), number = nruns) / nruns, 3), 'sec')
if __name__ == '__main__':
test()
Output:
Binary Search iterations: 8192
Binary Search Opt iterations: 2732
Newton Method iterations: 9348
Shifting Method iterations: 2752
Chord Tangent Method iterations: 8
Binary Search timing: 0.506 sec
Binary Search Opt timing: 0.05 sec
Newton Method timing: 2.09 sec
Shifting Method timing: 0.03 sec
Chord Tangent Method timing: 0.001 sec
I came up with my own answer, which takes #Mahmoud Kassem's idea, simplifies the code, and makes it more reusable:
def cube_root(x):
return decimal.Decimal(x) ** (decimal.Decimal(1) / decimal.Decimal(3))
I tested it in Python 3.5.1 and Python 2.7.8, and it seemed to work fine.
The result will have as many digits as specified by the decimal context the function is run in, which by default is 28 decimal places. According to the documentation for the power function in the decimal module, "The result is well-defined but only “almost always correctly-rounded”.". If you need a more accurate result, it can be done as follows:
with decimal.localcontext() as context:
context.prec = 50
print(cube_root(42))
In older versions of Python, 1/3 is equal to 0. In Python 3.0, 1/3 is equal to 0.33333333333 (and 1//3 is equal to 0).
So, either change your code to use 1/3.0 or switch to Python 3.0 .
Try converting the exponent to a floating number, as the default behaviour of / in Python is integer division
n**(1/float(3))
Well, if you're not particularly worried about precision, you could convert it to a sting, chop off some digits, use the exponent function, and then multiply the result by the root of how much you chopped off.
E.g. 32123 is about equal to 32 * 1000, the cubic root is about equak to cubic root of 32 * cubic root of 1000. The latter can be calculated by dividing the number of 0s by 3.
This avoids the need for the use of extension modules.