优化Totient功能

时间:2017-02-28 15:16:58

标签: python function

我正在尝试在Python上最大化Euler Totient函数,因为它可以使用大的任意数字。问题是该程序在一段时间后被杀死,因此它没有达到所需的比例。我曾想过将起始数字增加到更大的数字,但我认为这样做并不谨慎。我试图得到一个数字除以总数得到高于10.本质上我试图找到一个符合这个标准的稀疏数字。

这是我的phi功能:

def phi(n):
    amount = 0

    for k in range(1, n + 1):
        if fractions.gcd(n, k) == 1:
            amount += 1

    return amount

3 个答案:

答案 0 :(得分:1)

高 N/phi(N) 比率的最可能候选者是素数的乘积。如果您只是在寻找一个比率 > 10 的数字,那么您可以生成素数并只检查素数的乘积,直到您获得所需的比率

def totientRatio(maxN,ratio=10):
    primes    = []
    primeProd = 1
    isPrime   = [1]*(maxN+1)
    p         = 2
    while p*p<=maxN:
        if isPrime[p]:
            isPrime[p*p::p] = [0]*len(range(p*p,maxN+1,p))
            primes.append(p)
            primeProd *= p
            tot = primeProd
            for f in primes:
                tot -= tot//f
            if primeProd/tot >= ratio:
                return primeProd,primeProd/tot,len(primes)
        p += 1 + (p&1)

输出:

totientRatio(10**6)
16516447045902521732188973253623425320896207954043566485360902980990824644545340710198976591011245999110, 
10.00371973209101, 
55

这会为您提供具有该比率的最小数字。该数字的倍数将具有相同的比率。

n = 16516447045902521732188973253623425320896207954043566485360902980990824644545340710198976591011245999110

n*2/totient(n*2) = 10.00371973209101

n*11*13/totient(n*11*13) = 10.00371973209101

在您达到下一个素数乘积(即该数乘以下一个素数)之前,没有任何数字具有更高的比率。

n*263/totient(n*263) = 10.041901868473037

从乘积中去除一个素数会按 (1-1/P) 的比例影响比值。
例如如果 m = n/109,则 m/phi(m) = n/phi(n) * (1-1/109)

(n//109) / totient(n//109)    = 9.91194248684247

10.00371973209101 * (1-1/109) = 9.91194248684247

这应该能让您有效地浏览比率并找到满足您需要的数字。

例如,要获得比率 >= 10 但接近 10 的数字,您可以转到下一个质数乘积并删除一个或多个较小的质数以降低比率。这可以使用组合(来自 itertools)来完成,并且可以让您找到非常具体的比率:

m = n*263/241

m/totient(m) = 10.000234225865265

m = n*(263...839) / (7 * 61 * 109 * 137)  # 839 is 146th prime 

m/totient(m) = 10.000000079805726

答案 1 :(得分:0)

我有一个部分解决方案,但结果看起来不太好..(这个解决方案可能无法给你一个现代计算机硬件的答案(ram的数量目前有限制))我从{{得到了答案3}} pcg挑战并修改它以吐出n / phi(n)与特定n的比率

import numba as nb
import numpy as np
import time

n = int(2**31)

@nb.njit("i4[:](i4[:])", locals=dict(
    n=nb.int32, i=nb.int32, j=nb.int32, q=nb.int32, f=nb.int32))

def summarum(phi):
    #calculate phi(i) for i: 1 - n
    #taken from <a>https://codegolf.stackexchange.com/a/26753/42652</a>
    phi[1] = 1
    i = 2
    while i < n:
        if phi[i] == 0:
            phi[i] = i - 1
            j = 2
            while j * i < n:
                if phi[j] != 0:
                    q = j
                    f = i - 1
                    while q % i == 0:
                        f *= i
                        q //= i
                    phi[i * j] = f * phi[q]
                j += 1
        i += 1
    #divide each by n to get ratio n/phi(n)
    i = 1
    while i < n: #jit compiled while loop is faster than: for i in range(): blah blah blah
        phi[i] = i//phi[i]
        i += 1
    return phi

if __name__ == "__main__":
    s1 = time.time()
    a = summarum(np.zeros(n, np.int32))
    locations = np.where(a >= 10)
    print(len(locations))

我的工作比赛中只有足够的公羊。测试约为0 < n < 10^8,最大比例约为6.你可能会或者可能没有任何运气上升到更大的n,虽然10 ^ 8已经花了几秒钟(不确定是什么开销... spyder已经最近表现得很奇怪)

答案 2 :(得分:0)

p 55 #是满足所需条件的稀疏数字。

此外,由于 p n #/ phi( p n #)是严格增加的顺序:

p 1 #/ phi( p 1 #)为2。对于n> 1, p n #/ phi( p n #)等于 p n-1 p n / phi( p n-1 p n ),由于 p n p < sub> n-1 #是互素数,等于( p n-1 #/ phi( p n-1 #))*( p n / phi( p n ))。我们都知道 p n > phi( p n )> 0,所以 p < / em> n / phi( p n )>1。因此我们具有序列 p n #/ phi( p n #)严格增加。

我不认为这些是满足您要求的唯一很少使用的数字,但是我没有一种有效的方法来吸引其他人。相比之下,生成原始数等于生成前n个素数并将列表相乘(无论是使用3.8+中的functools.reduce(),math.prod(),还是循环使用)。

关于编写phi(n)函数的一般问题,我可能首先会找到n的素因子,然后对phi(n)使用Euler乘积公式。另外,请确保不要使用浮点除法。即使通过试验除法找到n的素数因子也应该比计算gcd的性能好n倍,但是当使用较大的n时,用有效的素因数分解算法替换它会带来很多好处。除非您想留下一个不错的十字架,否则请不要编写自己的十字架。我知道一个问题,鉴于这个问题的普遍性,可能还有很多其他问题。需要的时间。

说到时间,如果这对您(或未来的读者)仍然足够有意义,想想时间...肯定也把前面的答案也混了。