Кубический корень по модулю P, как мне это сделать?

Я пытаюсь вычислить кубический корень многозначного числа по модулю P в Python и терплю неудачу.

Я нашел код для алгоритма Тонелли-Шенкса, который предположительно легко преобразовать из квадратных корней в кубические, но это ускользает от меня. Я обыскал Интернет, математические библиотеки и несколько книг, но безрезультатно. Код был бы замечательным, как и алгоритм, объясненный простым английским языком.

Вот код Python (2.6?) для поиска квадратных корней:

def modular_sqrt(a, p):
    """ Find a quadratic residue (mod p) of 'a'. p
        must be an odd prime.

        Solve the congruence of the form:
            x^2 = a (mod p)
        And returns x. Note that p - x is also a root.

        0 is returned is no square root exists for
        these a and p.

        The Tonelli-Shanks algorithm is used (except
        for some simple cases in which the solution
        is known from an identity). This algorithm
        runs in polynomial time (unless the
        generalized Riemann hypothesis is false).
    """
    # Simple cases
    #
    if legendre_symbol(a, p) != 1:
        return 0
    elif a == 0:
        return 0
    elif p == 2:
        return n
    elif p % 4 == 3:
        return pow(a, (p + 1) / 4, p)

    # Partition p-1 to s * 2^e for an odd s (i.e.
    # reduce all the powers of 2 from p-1)
    #
    s = p - 1
    e = 0
    while s % 2 == 0:
        s /= 2
        e += 1

    # Find some 'n' with a legendre symbol n|p = -1.
    # Shouldn't take long.
    #
    n = 2
    while legendre_symbol(n, p) != -1:
        n += 1

    # Here be dragons!
    # Read the paper "Square roots from 1; 24, 51,
    # 10 to Dan Shanks" by Ezra Brown for more
    # information
    #

    # x is a guess of the square root that gets better
    # with each iteration.
    # b is the "fudge factor" - by how much we're off
    # with the guess. The invariant x^2 = ab (mod p)
    # is maintained throughout the loop.
    # g is used for successive powers of n to update
    # both a and b
    # r is the exponent - decreases with each update
    #
    x = pow(a, (s + 1) / 2, p)
    b = pow(a, s, p)
    g = pow(n, s, p)
    r = e

    while True:
        t = b
        m = 0
        for m in xrange(r):
            if t == 1:
                break
            t = pow(t, 2, p)

        if m == 0:
            return x

        gs = pow(g, 2 ** (r - m - 1), p)
        g = (gs * gs) % p
        x = (x * gs) % p
        b = (b * g) % p
        r = m

def legendre_symbol(a, p):
    """ Compute the Legendre symbol a|p using
        Euler's criterion. p is a prime, a is
        relatively prime to p (if p divides
        a, then a|p = 0)

        Returns 1 if a has a square root modulo
        p, -1 otherwise.
    """
    ls = pow(a, (p - 1) / 2, p)
    return -1 if ls == p - 1 else ls

Источник: Вычисление модульных квадратных корней в Python


person Seth    schedule 19.07.2011    source источник
comment
Мне любопытно просмотреть полный алгоритм, но пока не было возможности. В качестве сокращения для корней, отличных от квадратного, вы всегда можете использовать 6 ** (1.0/3). Возведение в степень 1/3 -> корень кубический. Возможно, вы можете немного потерять точность: (5 ** (1.0/3)) ** 3 -> 4.9999999999999982   -  person g.d.d.c    schedule 19.07.2011
comment
Вы изучали это: scipy.special.cbrt (docs.scipy. org/doc/scipy/reference/generated/)?   -  person yasouser    schedule 19.07.2011
comment
Вы уверены, что p простое число? pow(2,p-1,p) != 1, поэтому либо pow-функция не работает (сомнительно), либо p не простое число. Пари также считает, что p составное.   -  person deinst    schedule 20.07.2011
comment
Ммммммммммммммммммммммммммммммм... это может быть композиция из 3 простых чисел. Это плохо? Мне нужно проверить мои алгоритмы.   -  person Seth    schedule 21.07.2011
comment
Я идиот, ты гений, и твой код работает отлично (когда я ввел правильный номер)!!! Еще раз спасибо!   -  person Seth    schedule 21.07.2011


Ответы (2)


Примечание добавлено позже: в алгоритме Тонелли-Шенкса и здесь предполагается, что p является простым числом. Если бы мы могли быстро вычислять модульные квадратные корни из составных модулей, мы могли бы быстро разлагать числа на множители. Прошу прощения за то, что предположил, что вы знали, что p простое число.

См. здесь или здесь. Обратите внимание, что числа по модулю p - это конечное поле с p элементами.

Изменить: см. также это (это дедушка этих документов. )

Простая часть заключается в том, что когда p = 2 mod 3, тогда все является кубом, а кубический корень из a равен просто a**((2*p-1)/3) %p.

Добавлено: Вот код, чтобы сделать все, кроме простых чисел 1 мод 9. Я постараюсь добраться до него на этих выходных. Если никто другой не доберется до него первым

#assumes p prime returns cube root of a mod p
def cuberoot(a, p):
    if p == 2:
        return a
    if p == 3:
        return a
    if (p%3) == 2:
        return pow(a,(2*p - 1)/3, p)
    if (p%9) == 4:
        root = pow(a,(2*p + 1)/9, p)
        if pow(root,3,p) == a%p:
            return root
        else:
            return None
    if (p%9) == 7:
        root = pow(a,(p + 2)/9, p)
        if pow(root,3,p) == a%p:
            return root
        else:
            return None
    else:
        print "Not implemented yet. See the second paper"
person deinst    schedule 19.07.2011
comment
Ты великолепен! Я распечатал эти бумаги и планировал провести долгую, утомительную ночь, пытаясь понять их, и, вероятно, потерпев неудачу. Я использовал ваш код на некоторых меньших числах, и он работал отлично! Когда я использовал большие числа, я превысил предел рекурсии в Python. Я понял, как увеличить предел, и в результате Python 3.2 дает сбой, а Python 2.6 возвращает результат, который кажется неверным, и при кубе не становится моим исходным числом. Нужно ли мне попробовать новую версию Python или другой компьютер? Должен ли я дать вам свой номер? Еще раз спасибо! - person Seth; 20.07.2011
comment
Функция python pow() может принимать третий аргумент для модуля, что делает его эквивалентным вашему powerMod(). - person phkahler; 20.07.2011
comment
@phkahler Спасибо. Я не знал этого. - person deinst; 20.07.2011
comment
@seth Во второй статье написан довольно простой алгоритм. Если вы замените powerMod на pow и по предложению phkahler, проблемы с рекурсией должны исчезнуть. Я не уверен в ошибках, я проверял только на маленьких простых числах. Мне нужно идти на работу. - person deinst; 20.07.2011
comment
@seth, не могли бы вы привести большой пример, который терпит неудачу? - person deinst; 20.07.2011
comment
Я добавил набор чисел к моему вопросу. Если я действительно что-то не напортачил, что возможно, это число представляет собой куб, за которым следует простое/конечное поле. В вашей последней версии нет проблем с рекурсией. Я действительно не смог посмотреть на это сегодня, но я пройдусь по нему снова завтра и в пятницу и перепроверю все свои предположения. Спасибо за вашу помощь! - person Seth; 21.07.2011
comment
Неважно, как только я использовал правильный номер, все сработало отлично, спасибо! - person Seth; 21.07.2011
comment
Я перейду к сложной части в эти выходные, если у меня будет время. - person deinst; 21.07.2011
comment
Этот код предоставляет один из трех кубических корней. Как получить два других? - person Shebla Tsama; 18.01.2018
comment
@SheblaTsama умножить на примитивный кубический корень из 1. - person deinst; 18.01.2018
comment
@deinst, пожалуйста, объясните, что делать, если эта функция возвращает None. Значит ли это, что число не имеет кубических корней? - person Denis Leonov; 21.06.2018
comment
@ Денис Да. В большинстве случаев, когда p равно 1 по модулю 3, кубического корня нет. - person deinst; 21.06.2018
comment
Кто-нибудь знает, как получить два других корня?! Когда я переборщил для p = 31, я вижу, что в некоторых случаях есть 3 корня - person PouJa; 25.12.2018
comment
@PouJa, если r равно 1 из 3 кубических корней, а m является мультипликативным порядком r, 2 других кубических корня будут r ^ (1 + m/3) и r ^ (1 + 2 * m/3) - person toongeorges; 30.05.2020

Вот полный код на чистом питоне. Если сначала рассмотреть особые случаи, то он почти так же быстр, как алгоритм Peralta.

#assumes p prime, it returns all cube roots of a mod p
def cuberoots(a, p):

    #Non-trivial solutions of x**r=1
    def onemod(p,r):
        sols=set()
        t=p-2
        while len(sols)<r:        
            g=pow(t,(p-1)//r,p)
            while g==1: t-=1; g=pow(t,(p-1)//r,p)
            sols.update({g%p,pow(g,2,p),pow(g,3,p)})
            t-=1
        return sols

    def solutions(p,r,root,a): 
        todo=onemod(p,r)
        return sorted({(h*root)%p for h in todo if pow(h*root,3,p)==a})

#---MAIN---
a=a%p

if p in [2,3] or a==0: return [a]
if p%3 == 2: return [pow(a,(2*p - 1)//3, p)] #One solution

#There are three or no solutions 

#No solution
if pow(a,(p-1)//3,p)>1: return []

if p%9 == 7:                                #[7, 43, 61, 79, 97, 151]   
    root = pow(a,(p + 2)//9, p)
    if pow(root,3,p) == a: return solutions(p,3,root,a)
    else: return []

if p%9 == 4:                                #[13, 31, 67, 103, 139]
    root = pow(a,(2*p + 1)//9, p) 
    print(root)
    if pow(root,3,p) == a: return solutions(p,3,root,a)        
    else: return []        
            
if p%27 == 19:                              #[19, 73, 127, 181]
    root = pow(a,(p + 8)//27, p)
    return solutions(p,9,root,a)

if p%27 == 10:                              #[37, 199, 307]
    root = pow(a,(2*p +7)//27, p)  
    return solutions(p,9,root,a) 

#We need a solution for the remaining cases
return tonelli3(a,p,True)

Расширение алгоритма Тонелли-Шэнка.

def tonelli3(a,p,many=False):

    def solution(p,root):
        g=p-2
        while pow(g,(p-1)//3,p)==1: g-=1  #Non-trivial solution of x**3=1
        g=pow(g,(p-1)//3,p)
        return sorted([root%p,(root*g)%p,(root*g**2)%p])

#---MAIN---
a=a%p
if p in [2,3] or a==0: return [a]
if p%3 == 2: return [pow(a,(2*p - 1)//3, p)] #One solution

#No solution
if pow(a,(p-1)//3,p)>1: return []

#p-1=3**s*t
s=0
t=p-1
while t%3==0: s+=1; t//=3
 
#Cubic nonresidu b
b=p-2
while pow(b,(p-1)//3,p)==1: b-=1

c,r=pow(b,t,p),pow(a,t,p)    
c1,h=pow(c,3**(s-1),p),1    
c=pow(c,p-2,p) #c=inverse modulo p

for i in range(1,s):
    d=pow(r,3**(s-i-1),p)
    if d==c1: h,r=h*c,r*pow(c,3,p)
    elif d!=1: h,r=h*pow(c,2,p),r*pow(c,6,p)           
    c=pow(c,3,p)
    
if (t-1)%3==0: k=(t-1)//3
else: k=(t+1)//3

r=pow(a,k,p)*h
if (t-1)%3==0: r=pow(r,p-2,p) #r=inverse modulo p

if pow(r,3,p)==a: 
    if many: 
        return solution(p,r)
    else: return [r]
else: return [] 

Вы можете протестировать его, используя:

test=[(17,1459),(17,1000003),(17,10000019),(17,1839598566765178548164758165715596714561757494507845814465617175875455789047)]

for a,p in test:
    print "y^3=%s modulo %s"%(a,p)
    sol=cuberoots(a,p)
    print "p%s3=%s"%("%",p%3),sol,"--->",map(lambda t: t^3%p,sol)

который должен дать (быстро):

y^3=17 по модулю 1459
p%3=1 [483, 329, 647] ---› [17, 17, 17]
y^3=17 по модулю 1000003
p%3= 1 [785686, 765339, 448981] ---› [17, 17, 17]
y^3=17 по модулю 10000019
p%3=2 [5188997] ---› [17]
у ^ 3 = 17 по модулю 1839598566765178548164758165715596714561757494507845814465617175875455789047
р% 3 = 1 [753801617033579226225229608063663938352746555486783903392457865386777137044, 655108821219252496141403783945148550782812009720868259303598196387356108990, 430688128512346825798124773706784225426198929300193651769561114101322543013] ---> [17, 17, 17]

person Rolandb    schedule 17.11.2019