此程序中的完整查找器的说明

时间:2015-07-09 13:27:04

标签: python math

我需要在here的第一个答案中对编辑中建议的程序进行说明。这是一个程序,可以找到一系列数字的总和。有人可以提供一个简单的解释吗? (暂时忽略求和部分,我需要找出 init 方法如何找到总数。)我知道答案中有一个解释,但这是对不同程序的解释,我需要这个特定的解释。

class Totient:
def __init__(self, n):
    self.totients = [1 for i in range(n)]
    for i in range(2, n):
        if self.totients[i] == 1:
            for j in range(i, n, i):
                self.totients[j] *= i - 1
                k = j / i
                while k % i == 0:
                    self.totients[j] *= i
                    k /= i
def __call__(self, i):
    return self.totients[i]
if __name__ == '__main__':
    from itertools import imap
    totient = Totient(10000)
    print sum(imap(totient, range(10000)))

2 个答案:

答案 0 :(得分:1)

这是Eratosthenes筛选的一个变种,用于寻找素数。

如果您想知道单个数字 n 的总数,找到它的最佳方法是计算 n 并将每个因子的乘积减去1 ;例如,30 = 2 * 3 * 5,并从每个因子中减1,然后乘以,给出1 * 2 * 4 = 8的总数。但是如果你想找到小于给定 n ,比分解每种方法更好的方法是筛分。想法很简单:设置一个数组 X 从0到 n ,在每个 X i中存储 i ,然后从0开始遍历数组,每当 X i = i 循环遍历的倍数时我,将每个乘以 i - 1。

my blog的进一步讨论和代码。

答案 1 :(得分:1)

我不完全确定代码在做什么 - 但坦率地说它看起来很糟糕。它显然试图使用Euler的totient函数是乘法的,这意味着a,b是相对素数,然后t(a,b)= t(a)* t(b),以及如果p是素数然后t(p)= p-1。但是 - 它似乎正在使用原始的试验部门来确定这些事情。如果你真的想要计算给定范围内所有数字的总数,那么你应该使用一个算法来筛选数字。

这是一个版本,它随着它的进行筛选并利用了剑柄的倍增性质。在每次通过主循环时,它以一个尚未处理的素数p开始。它确定p <= n的所有幂,然后使用这些幂的直接公式(见https://en.wikipedia.org/wiki/Euler%27s_totient_function)。一旦添加了这些总数,它就形成所有可能的产品&lt; = n这些权力以及先前已计算过总数的数字。这样就可以将大量数字添加到先前确定的数字列表中。最多需要通过主循环进行sqrt(n)次传递。它几乎立即运行n = 10000.它返回一个列表,其中第i个值是i的总和(为方便起见,t(0)= 0):

def allTotients(n):

    totients = [None]*(n+1) #totients[i] will contain the t(i)
    totients[0] = 0 
    totients[1] = 1

    knownTotients = [] #known in range 2 to n
    p = 2
    while len(knownTotients) < n - 1:
        powers = [p]
        k = 2
        while p ** k <= n:
            powers.append(p ** k)
            k +=1
        totients[p] = p - 1

        for i in range(1,len(powers)):
            totients[powers[i]] = powers[i] - powers[i-1]

        #at this stage powers represent newly discovered totients
        #combine with previously discovered totients to get still more
        newTotients = powers[:]
        for m in knownTotients:
            for pk in powers:
                if m*pk > n: break
                totients[m*pk] = totients[m]*totients[pk]
                newTotients.append(m*pk)
        knownTotients.extend(newTotients)

        #if there are any unkown totients -- the smallest such will be prime

        if len(knownTotients) < n-1:
            p = totients.index(None)
    return totients

为了完整起见,这里有一个算法的Python实现,用于计算用户448810在答案中描述的单个数字的总数:

from math import sqrt

#crude factoring algorithm: 

small_primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,
                53,59,61,67,71,73,79,83,89,97]

def factor(n):
    #returns a list of prime factors
    factors = []
    num = n
    #first pull out small prime factors
    for p in small_primes:
        while num % p == 0:
            factors.append(p)
            num = num // p
        if num == 1: return factors
    #now do trial division, starting at 101
    k = 101
    while k <= sqrt(num):
        while num % k == 0:
            factors.append(k)
            num = num // k
        k += 2
    if num == 1:
        return factors
    else:
        factors.append(num)
        return factors

def totient(n):
    factors = factor(n)
    unique_factors = set()
    t = 1
    for p in factors:
        if p in unique_factors:
            t *= p
        else:
            unique_factors.add(p)
            t *= (p-1)
    return t