使用Python有效地发现原始根模数?

时间:2016-10-22 10:07:47

标签: python performance python-3.x optimization simplify

我使用以下代码在 Python 中查找primitive rootsn

代码:

def gcd(a,b):
    while b != 0:
        a, b = b, a % b
    return a

def primRoots(modulo):
    roots = []
    required_set = set(num for num in range (1, modulo) if gcd(num, modulo) == 1)

    for g in range(1, modulo):
        actual_set = set(pow(g, powers) % modulo for powers in range (1, modulo))
        if required_set == actual_set:
            roots.append(g)           
    return roots

if __name__ == "__main__":
    p = 17
    primitive_roots = primRoots(p)
    print(primitive_roots)

输出:

[3, 5, 6, 7, 10, 11, 12, 14]   

从以下网址提取的代码片段: Diffie-Hellman (Github)

{<1}}方法可以在内存使用性能 /效率方面进行简化或优化吗?

5 个答案:

答案 0 :(得分:5)

根据 Pete 的评论和 Kasramvd 的回答,我可以建议:

from math import gcd as bltin_gcd

def primRoots(modulo):
    required_set = {num for num in range(1, modulo) if bltin_gcd(num, modulo) }
    return [g for g in range(1, modulo) if required_set == {pow(g, powers, modulo)
            for powers in range(1, modulo)}]

print(primRoots(17))

<强>输出:

[3, 5, 6, 7, 10, 11, 12, 14]

<强>的变化:

  • 现在使用pow方法的3-rd参数作为模数。
  • 切换到math 3.5中定义的gcd内置函数(对于Python {{1}})以提高速度。

有关内置 gcd 的其他信息,请访问: Co-primes checking

答案 1 :(得分:4)

您可以在此处进行一次快速更改(效率不佳)是使用列表和设置理解:

def primRoots(modulo):
    coprime_set = {num for num in range(1, modulo) if gcd(num, modulo) == 1}
    return [g for g in range(1, modulo) if coprime_set == {pow(g, powers, modulo)
            for powers in range(1, modulo)}]

现在,您可以在此处进行一项功能强大且有趣的算法更改,即使用gcd function优化memoization。或者甚至更好,您可以在Python-3.5 +或以前版本的gcd模块中使用内置的math函数形式fractions模块:

from functools import wraps
def cache_gcd(f):
    cache = {}

    @wraps(f)
    def wrapped(a, b):
        key = (a, b)
        try:
            result = cache[key]
        except KeyError:
            result = cache[key] = f(a, b)
        return result
    return wrapped

@cache_gcd
def gcd(a,b):
    while b != 0:
        a, b = b, a % b
    return a
# or just do the following (recommended)
# from math import gcd

然后:

def primRoots(modulo):
    coprime_set = {num for num in range(1, modulo) if gcd(num, modulo) == 1}
    return [g for g in range(1, modulo) if coprime_set == {pow(g, powers, modulo)
            for powers in range(1, modulo)}]

正如评论中所提到的,作为更多pythoinc优化器方式,您可以使用fractions.gcd(或者用于Python-3.5 + math.gcd)。

答案 2 :(得分:1)

在p为素数的特殊情况下,以下是更快的一点:

string Value = ((grdAppSetting.Rows[e.RowIndex].Cells[2].Controls[0]) as TextBox).Text;

答案 3 :(得分:1)

通过使用更高效的算法,您可以极大地改善isNotPrime功能。你可以通过对偶数进行特殊测试来加倍速度,然后只测试奇数直到平方根,但与米勒拉宾测试等算法相比,这仍然是非常低效的。 Rosetta Code网站中的此版本将始终为少于​​25位左右的任何数字提供正确答案。对于大质数,这将在使用试验分工所用的一小部分时间内运行。

此外,在处理整数时,应避免使用浮点指数运算符**,就像在这种情况下一样(即使我刚刚链接的Rosetta代码也做同样的事情!)。在特定情况下,事情可能会正常工作,但是当Python必须从浮点转换为整数时,或者当一个整数太大而无法准确表示浮点时,它可能是一个微妙的错误来源。您可以使用有效的整数平方根算法。这是一个简单的:

def int_sqrt(n):
   if n == 0:
      return 0
   x = n
   y = (x + n//x)//2

   while (y<x):
      x=y
      y = (x + n//x)//2

   return x

答案 4 :(得分:0)

那些代码都是低效的,在很多方面,首先你不需要迭代 n 的所有互质提醒,你只需要检查欧拉函数与 n 的除数的幂。在 n 是素数的情况下,欧拉函数是 n-1。如果 n i 素数,则需要对 n-1 进行因式分解并仅检查那些除法器,而不是所有除法器。这背后有一个简单的数学原理。

第二。你需要更好的函数来为一个数字供电,想象功率太大了,我认为在 python 中你有函数 pow(g, powers, modulo) 它在每个步骤进行除法并只得到余数( _ % modulo )。

如果您要实现 Diff & Helman 算法,最好使用安全素数。它们是这样的素数,p 是素数,2p+1 也是素数,所以 2p+1 被称为安全素数。如果你得到 n = 2*p+1,那么 n-1 的除法器(n 是素数,欧拉函数从 n 是 n-1)是 1、2、p 和 2p,你只需要检查数字是否g 在 2 次幂和 g 在 p 次幂中,如果其中一个给出 1,那么那个 g 不是原始根,你可以扔掉那个 g 并选择另一个 g,下一个 g+1,如果 g^2 和 g^ p 的模 n 不等于 1,那么 g 是一个原始根,该检查保证除 2p 之外的所有幂将给出与模 n 不同的数字。

示例代码使用 Sophie Germain 素数 p 和相应的安全素数 2p+1,并计算该安全素数 2p+1 的本原根。

您可以轻松地为任何质数或任何其他数字重新编写代码,方法是添加一个函数来计算欧拉函数并找到该值的所有除数。但这只是一个演示,而不是完整的代码。并且可能有更好的方法。

class SGPrime :
    '''
    This object expects a Sophie Germain prime p, it does not check that it accept that as input.
    Euler function from any prime is n-1, and the order (see method get_order) of any co-prime 
    remainder of n could be only a divider of Euler function value.  
    '''
    def __init__(self, pSophieGermain ):
        self.n = 2*pSophieGermain+1
        #TODO! check if pSophieGermain is prime 
        #TODO! check if n is also prime.
        #They both have to be primes, elsewhere the code does not work!

        # Euler's function is n-1, #TODO for any n, calculate Euler's function from n
        self.elrfunc = self.n-1
    
        # All divisors of Euler's function value, #TODO for any n, get all divisors of the Euler's function value.
        self.elrfunc_divisors = [1, 2, pSophieGermain, self.elrfunc]
    
        
    def get_order(self, r):
        ''' 
        Calculate the order of a number, the minimal power at which r would be congruent with 1 by modulo p.
        '''
        r = r % self.n
        for d in self.elrfunc_divisors:
           if ( pow( r, d, self.n) == 1 ):
               return d
        return 0 # no such order, not possible if n is prime, - see small Fermat's theorem
    
    def is_primitive_root(self, r):
        '''
        Check if r is a primitive root by modulo p. Such always exists if p is prime.
        '''
        return ( self.get_order(r) == self.elrfunc )
    
    def find_all_primitive_roots(self, max_num_of_roots = None):
        '''
        Find all primitive roots, only for demo if n is large the list is large for DH or any other such algorithm 
        better to stop at first primitive roots.
        '''
        primitive_roots = []
        for g in range(1, self.n):
            if ( self.is_primitive_root(g) ):
                primitive_roots.append(g)
                if (( max_num_of_roots != None ) and (len(primitive_roots) >= max_num_of_roots)):
                    break
        return primitive_roots

#demo, Sophie Germain's prime
p =  20963
sggen = SGPrime(p)
print (f"Safe prime : {sggen.n}, and primitive roots of {sggen.n} are : " )
print(sggen.find_all_primitive_roots())

问候