注:不标明题目出处
'''
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
最大素因子
'''
分析:
求一个数的素因子,那么有这么几种做法:
一,列出所有的素数,然后逐个计算,计算得到最大的那个素数即是最大素因子(从前往后,与从后往前的计算量是不一样的)
二,列出当前的素数,然后用目标除尽素数,得到一个新的数,继续如此,最后得到的非1数即为最大的素因子
三,从2开始,依次递增,然后判断是否能被目标整除,若能,那么除尽此数,并添加到列表,然后继续。
最后得到一个素因子列表,列表最后一项即为目标的最大素因子
解法一:
列出所有可能的素数,从后往前依然计算,若可以整除,那么返回
def max_prime(target):
<span style="white-space:pre"> </span>if target < 2: return 0
<span style="white-space:pre"> </span>middle = int(math.sqrt(target)) + 1
<span style="white-space:pre"> </span>primelist = create_prime_list(middle)
<span style="white-space:pre"> </span>for x in xrange(1, middle):
<span style="white-space:pre"> </span>index = middle - x
<span style="white-space:pre"> </span>if primelist[index] == 1:
<span style="white-space:pre"> </span>if target % index == 0:
<span style="white-space:pre"> </span>return index
<span style="white-space:pre"> </span>return target
print max_prime(n)
若当前这个数非素数,那么一定可以分解成素数相乘,且最大的那个素数不会超过它的平方根?
对于21 = 3 * 7,所以此方法宣告失败
那进一步说,非素数分解成素数相乘,最大那个素数肯定不会超过它的一半,于是有:
def max_prime(target):
if target < 2: return 0
middle = target / 2 + 1
primelist = create_prime_list(middle)
for x in xrange(1, middle):
index = middle - x
if primelist[index] == 1:
if target % index == 0:
return index
return target
若是这样,那么对于此题,它需要生成一个300425737572长度的列表,太大了,显然不合适。
所以此方法宣告失败
在这里用到了生成素数列表,这个方法还不错,虽然不是最好的,但肯定不是差的。
def create_prime_list(n = 10 ** 6):
other = n % 6
total = n / 6
primelist = [0, 1, 0, 0, 0, 1] * total + [0, 1, 0, 0, 0, 1][:other]
for i in xrange(2, n):
if primelist[i]:
primelist[i+i :: i] = [0] * ((n - i - 1) / i)
primelist[0:4] = [0, 0, 1, 1]
return primelist
生成自然序列,若当前列表下标为素数,那么下标所示的位置的值为1,否则为0
<pre name="code" class="python">def max_prime(target):
if target < 2: return 0
middle = target / 2 + 1
primelist = create_prime_list(middle)
maxprime = 0
for x in xrange(1, middle):
index = x
if primelist[index] == 1:
if target % index == 0:
maxprime = index
if maxprime == 0:
maxprime = target
return maxprime 这个算法和前面的一样,所以也可以说是 失败的。
解法二:
列出可能的素数,依然去除,最后得到的非1整数,即为最大素因子
def max_prime(target):
<span style="white-space:pre"> </span>if target < 2: return 0
<span style="white-space:pre"> </span>middle = int(math.sqrt(target)) + 1
<span style="white-space:pre"> </span>primelist = create_prime_list(middle)
<span style="white-space:pre"> </span>for x in xrange(1, middle):
<span style="white-space:pre"> </span>if primelist[x] == 1:
<span style="white-space:pre"> </span>while target % x == 0:
<span style="white-space:pre"> </span>target /= x
<span style="white-space:pre"> </span>if target == 1:
<span style="white-space:pre"> </span>return x
<span style="white-space:pre"> </span>if target < middle and primelist[target] == 1:
<span style="white-space:pre"> </span>return target
<span style="white-space:pre"> </span>return target
这个解法和上面的有所区别,那就是若这个数分解后,得到的素数并不在生成的素数列表里面,也可以返回正确的结果。
原因是:在计算素数因子的过程中将所有小的互生数因子都去掉了,若它含有一个素因子大于它的平方根,那么它不会被除掉,会保留下来。
那么计算后,返回计算后的target值,即为最大的素因子。
若还有一个素因子大于它的平方根,那么这两个都大于平方根的素因子相乘,显然大于这个数,所以它最多只有一个大于平方根的素因子。
而所有小于平方根的素因子都去掉了,肯定只剩下这个素因子了。
若没有一个大于平方根的素因子,那么会在遍历的过程中出现,当除尽时,target的值为1,此时的除数即为最大的素因子。
为了加快计算,若target在列表范围内,且在素数列表中找到其值为1,那么说明target为素数,且为最大,因为比它小的已经除掉了。
从2开始,依次递增,(这是从一个朋友那里学来的,不过,没学完全)
def max_prime(target):
<span style="white-space:pre"> </span>if target < 2: return 0
<span style="white-space:pre"> </span>primelist = []
<span style="white-space:pre"> </span>f = 2
<span style="white-space:pre"> </span>while 1:
<span style="white-space:pre"> </span>while f ** 2 < target:
<span style="white-space:pre"> </span>while target % f == 0:<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>target //= f
<span style="white-space:pre"> </span>primelist.append(f)
<span style="white-space:pre"> </span>f += 1
<span style="white-space:pre"> </span>else:
<span style="white-space:pre"> </span>if target == f ** 2:
<span style="white-space:pre"> </span>target //= f
<span style="white-space:pre"> </span>primelist.append(target)
<span style="white-space:pre"> </span>return primelist
对于这个方法,若是只关注于本题,那么可以设置f=3,然后在循环的时候,f+=2,这样会更快。
上面的方法通用,但是不是足够快的。
若是希望更通用一点,那么需要处理一下后,才能这样赋值。
<span style="white-space:pre"> </span>while target % 2 == 0: target = target >> 1
if target == 1: return 2
在第3行添加如上两行,即可以使用f = 3, f += 2这个方法。
我不知道这样讲,是否可以让你明白。是否这样就是秩序渐进。希望得到你的评论。谢谢。