提问人:mercutio 提问时间:8/23/2008 最后编辑:smcimercutio 更新时间:10/4/2023 访问量:237091
求数最大质因数的算法
Algorithm to find Largest prime factor of a number
问:
计算数字最大质因数的最佳方法是什么?
我认为最有效的方法如下:
- 找到除以干净的最小素数
- 检查除法结果是否为素数
- 如果没有,请查找下一个最低值
- 转到 2。
我之所以做出这个假设,是因为计算小质因数更容易。这是对的吗?我还应该研究哪些其他方法?
编辑:我现在已经意识到,如果有超过 2 个质因数在起作用,我的方法是徒劳的,因为当结果是另外两个素数的乘积时,第 2 步会失败,因此需要递归算法。
再次编辑:现在我意识到这仍然有效,因为最后找到的素数必须是最高的素数,因此对步骤 2 的非素数结果的任何进一步测试都会导致更小的素数。
答:
我认为最好将所有可能的素数都存储在比 n 小的地方,然后迭代它们以找到最大的除数。您可以从 prime-numbers.org 获得素数。
当然,我认为你的数字不会太大:)
这可能并不总是更快,但更乐观的是,你找到了一个大的质数除数:
N
是您的号码- 如果它是素数,那么
return(N)
- 计算素数直到
Sqrt(N)
- 按降序遍历素数(最大的在前)
- 如果那么
N is divisible by Prime
Return(Prime)
- 如果那么
编辑:在第 3 步中,您可以使用埃拉托色尼筛子或阿特金斯筛子或任何您喜欢的东西,但筛子本身不会发现您是最大的质因数。(这就是为什么我不会选择 SQLMenace 的帖子作为官方答案......
评论
n = abs(number);
result = 1;
if (n mod 2 == 0) {
result = 2;
while (n mod 2 = 0) n /= 2;
}
for(i=3; i<sqrt(n); i+=2) {
if (n mod i == 0) {
result = i;
while (n mod i = 0) n /= i;
}
}
return max(n,result)
有一些模检验是肤浅的,因为如果去除了所有因子 2 和 3,n 就永远不能被 6 整除。你只能允许 i 的素数,这在这里的其他几个答案中都有显示。
你实际上可以在这里将埃拉托色尼的筛子交织在一起:
- 首先创建整数列表
自。
sqrt(n)
- 在 for 循环中标记所有倍数
的 I 到 新的 as not
prime,并改用 while 循环。
sqrt(n)
- 将 i 设置为 列表。
另请参阅此问题。
实际上,有几种更有效的方法来找到大数的因数(对于较小的因数,试除法效果相当好)。
如果输入数字有两个非常接近其平方根的因子,则有一种非常快的方法称为费马因式分解。它利用恒等式 N = (a + b)(a - b) = a^2 - b^2,易于理解和实现。不幸的是,它通常不是很快。
对长达 100 位的数字进行因式分解的最著名方法是二次筛。作为奖励,部分算法可以通过并行处理轻松完成。
我听说过的另一种算法是 Pollard 的 Rho 算法。它通常不如二次筛高效,但似乎更容易实现。
一旦你决定了如何将一个数字分成两个因子,这是我能想到的最快的算法,可以找到一个数字的最大质因数:
创建一个优先级队列,该队列最初存储号码本身。每次迭代,您都会从队列中删除最大的数字,并尝试将其拆分为两个因子(当然,不允许 1 成为这些因子之一)。如果这一步失败了,这个数字是素数,你有你的答案!否则,将这两个因素添加到队列中并重复。
评论
最简单的解决方案是一对相互递归的函数。
第一个函数生成所有质数:
- 从大于 1 的所有自然数的列表开始。
- 删除所有非素数。也就是说,没有质因数(除了它们本身)的数字。见下文。
第二个函数按递增顺序返回给定数的质因数。n
- 列出所有素数(见上文)。
- 删除所有不是 的因式数的数字。
n
的最大质因数是第二个函数给出的最后一个数字。n
此算法需要惰性列表或具有按需调用语义的语言(或数据结构)。
为了澄清起见,以下是 Haskell 中上述的一个(低效)实现:
import Control.Monad
-- All the primes
primes = 2 : filter (ap (<=) (head . primeFactors)) [3,5..]
-- Gives the prime factors of its argument
primeFactors = factor primes
where factor [] n = []
factor xs@(p:ps) n =
if p*p > n then [n]
else let (d,r) = divMod n p in
if r == 0 then p : factor xs d
else factor ps n
-- Gives the largest prime factor of its argument
largestFactor = last . primeFactors
要使这更快,只需更聪明地检测哪些数字是质数和/或因数,但算法保持不变。n
所有数字都可以表示为素数的乘积,例如:
102 = 2 x 3 x 17
712 = 2 x 2 x 2 x 89
你可以通过简单地从 2 开始并继续除法,直到结果不是你的数字的倍数来找到这些:
712 / 2 = 356 .. 356 / 2 = 178 .. 178 / 2 = 89 .. 89 / 89 = 1
使用这种方法,您不必实际计算任何素数:它们都是素数,因为您已经尽可能多地使用前面的所有数字对数字进行了分解。
number = 712;
currNum = number; // the value we'll actually be working with
for (currFactor in 2 .. number) {
while (currNum % currFactor == 0) {
// keep on dividing by this number until we can divide no more!
currNum = currNum / currFactor // reduce the currNum
}
if (currNum == 1) return currFactor; // once it hits 1, we're done.
}
评论
currFactor = 3513642
在我看来,给定算法的步骤 #2 不会是那么有效的方法。你没有合理的期望它是素数。
此外,先前的答案暗示了埃拉托色尼的筛子是完全错误的。我刚刚写了两个程序来分解123456789。一个基于筛子,一个基于以下内容:
1) Test = 2
2) Current = Number to test
3) If Current Mod Test = 0 then
3a) Current = Current Div Test
3b) Largest = Test
3c) Goto 3.
4) Inc(Test)
5) If Current < Test goto 4
6) Return Largest
这个版本比 Sieve 快 90 倍。
问题是,在现代处理器上,操作类型远不如操作数量重要,更不用说上面的算法可以在缓存中运行,而 Sieve 不能。Sieve 使用了很多操作来剔除所有合数。
另请注意,我在确定因素时对因素进行划分减少了必须测试的空间。
评论
这是我所知道的最好的算法(在 Python 中)
def prime_factors(n):
"""Returns all the prime factors of a positive integer"""
factors = []
d = 2
while n > 1:
while n % d == 0:
factors.append(d)
n /= d
d = d + 1
return factors
pfs = prime_factors(1000)
largest_prime_factor = max(pfs) # The largest element in the prime factor list
上述方法在最坏的情况下运行(当输入是质数时)。O(n)
编辑:
以下是评论中建议的版本。这里又是代码。O(sqrt(n))
def prime_factors(n):
"""Returns all the prime factors of a positive integer"""
factors = []
d = 2
while n > 1:
while n % d == 0:
factors.append(d)
n /= d
d = d + 1
if d*d > n:
if n > 1: factors.append(n)
break
return factors
pfs = prime_factors(1000)
largest_prime_factor = max(pfs) # The largest element in the prime factor list
评论
O(sqrt(n))
O(n)
n
我的答案是基于三联画的,但改进了很多。它基于这样一个事实,即超过 2 和 3,所有素数都是 6n-1 或 6n+1 的形式。
var largestPrimeFactor;
if(n mod 2 == 0)
{
largestPrimeFactor = 2;
n = n / 2 while(n mod 2 == 0);
}
if(n mod 3 == 0)
{
largestPrimeFactor = 3;
n = n / 3 while(n mod 3 == 0);
}
multOfSix = 6;
while(multOfSix - 1 <= n)
{
if(n mod (multOfSix - 1) == 0)
{
largestPrimeFactor = multOfSix - 1;
n = n / largestPrimeFactor while(n mod largestPrimeFactor == 0);
}
if(n mod (multOfSix + 1) == 0)
{
largestPrimeFactor = multOfSix + 1;
n = n / largestPrimeFactor while(n mod largestPrimeFactor == 0);
}
multOfSix += 6;
}
我最近写了一篇博客文章,解释了这个算法是如何工作的。
我敢说,一种不需要测试原始性(并且没有筛子结构)的方法会比使用这些方法的方法运行得更快。如果是这样的话,这可能是这里最快的算法。
评论
while (multOfSix - 1 <= n)
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include <time.h>
factor(long int n)
{
long int i,j;
while(n>=4)
{
if(n%2==0) { n=n/2; i=2; }
else
{ i=3;
j=0;
while(j==0)
{
if(n%i==0)
{j=1;
n=n/i;
}
i=i+2;
}
i-=2;
}
}
return i;
}
void main()
{
clock_t start = clock();
long int n,sp;
clrscr();
printf("enter value of n");
scanf("%ld",&n);
sp=factor(n);
printf("largest prime factor is %ld",sp);
printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);
getch();
}
我知道这不是一个快速的解决方案。发布希望更容易理解的慢速解决方案。
public static long largestPrimeFactor(long n) {
// largest composite factor must be smaller than sqrt
long sqrt = (long)Math.ceil(Math.sqrt((double)n));
long largest = -1;
for(long i = 2; i <= sqrt; i++) {
if(n % i == 0) {
long test = largestPrimeFactor(n/i);
if(test > largest) {
largest = test;
}
}
}
if(largest != -1) {
return largest;
}
// number is prime
return n;
}
这是与生成器相同的function@Triptych,也略有简化。
def primes(n):
d = 2
while (n > 1):
while (n%d==0):
yield d
n /= d
d += 1
然后可以使用以下方法找到 MAX Prime:
n= 373764623
max(primes(n))
以及使用以下方法发现的因素列表:
list(primes(n))
首先计算一个存储质数的列表,例如 2 3 5 7 11 13 ...
每次对一个数进行质因式分解时,请使用 Triptych 的实现,但要迭代这个素数列表而不是自然整数。
这是我在 c# 中的尝试。最后的打印输出是数字的最大质因数。我检查了一下,它有效。
namespace Problem_Prime
{
class Program
{
static void Main(string[] args)
{
/*
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
*/
long x = 600851475143;
long y = 2;
while (y < x)
{
if (x % y == 0)
{
// y is a factor of x, but is it prime
if (IsPrime(y))
{
Console.WriteLine(y);
}
x /= y;
}
y++;
}
Console.WriteLine(y);
Console.ReadLine();
}
static bool IsPrime(long number)
{
//check for evenness
if (number % 2 == 0)
{
if (number == 2)
{
return true;
}
return false;
}
//don't need to check past the square root
long max = (long)Math.Sqrt(number);
for (int i = 3; i <= max; i += 2)
{
if ((number % i) == 0)
{
return false;
}
}
return true;
}
}
}
使用 Java:
对于值:int
public static int[] primeFactors(int value) {
int[] a = new int[31];
int i = 0, j;
int num = value;
while (num % 2 == 0) {
a[i++] = 2;
num /= 2;
}
j = 3;
while (j <= Math.sqrt(num) + 1) {
if (num % j == 0) {
a[i++] = j;
num /= j;
} else {
j += 2;
}
}
if (num > 1) {
a[i++] = num;
}
int[] b = Arrays.copyOf(a, i);
return b;
}
对于值:long
static long[] getFactors(long value) {
long[] a = new long[63];
int i = 0;
long num = value;
while (num % 2 == 0) {
a[i++] = 2;
num /= 2;
}
long j = 3;
while (j <= Math.sqrt(num) + 1) {
if (num % j == 0) {
a[i++] = j;
num /= j;
} else {
j += 2;
}
}
if (num > 1) {
a[i++] = num;
}
long[] b = Arrays.copyOf(a, i);
return b;
}
//this method skips unnecessary trial divisions and makes
//trial division more feasible for finding large primes
public static void main(String[] args)
{
long n= 1000000000039L; //this is a large prime number
long i = 2L;
int test = 0;
while (n > 1)
{
while (n % i == 0)
{
n /= i;
}
i++;
if(i*i > n && n > 1)
{
System.out.println(n); //prints n if it's prime
test = 1;
break;
}
}
if (test == 0)
System.out.println(i-1); //prints n if it's the largest prime factor
}
评论
while
i
2,2,2,2,2,2,2,2,2,2,2,2, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5, 2,3,4,5
i=2,3,4,5,6,...,10^12+39
long n = 2*1000000000039L
return;
#python implementation
import math
n = 600851475143
i = 2
factors=set([])
while i<math.sqrt(n):
while n%i==0:
n=n/i
factors.add(i)
i+=1
factors.add(n)
largest=max(factors)
print factors
print largest
评论
在 C++ 中使用递归计算数字的最大质因数。代码的工作原理解释如下:
int getLargestPrime(int number) {
int factor = number; // assumes that the largest prime factor is the number itself
for (int i = 2; (i*i) <= number; i++) { // iterates to the square root of the number till it finds the first(smallest) factor
if (number % i == 0) { // checks if the current number(i) is a factor
factor = max(i, number / i); // stores the larger number among the factors
break; // breaks the loop on when a factor is found
}
}
if (factor == number) // base case of recursion
return number;
return getLargestPrime(factor); // recursively calls itself
}
Python 迭代方法,从数字中删除所有质因数
def primef(n):
if n <= 3:
return n
if n % 2 == 0:
return primef(n/2)
elif n % 3 ==0:
return primef(n/3)
else:
for i in range(5, int((n)**0.5) + 1, 6):
#print i
if n % i == 0:
return primef(n/i)
if n % (i + 2) == 0:
return primef(n/(i+2))
return n
这是我快速计算最大质因数的方法。
它基于这样一个事实,即修改不包含非质因数。为了实现这一点,一旦找到一个因素,我们就会进行除法。然后,剩下的唯一事情就是返回最大的因子。它已经是黄金了。x
x
代码 (Haskell):
f max' x i | i > x = max'
| x `rem` i == 0 = f i (x `div` i) i -- Divide x by its factor
| otherwise = f max' x (i + 1) -- Check for the next possible factor
g x = f 2 x 2
评论
JavaScript 代码:
'option strict';
function largestPrimeFactor(val, divisor = 2) {
let square = (val) => Math.pow(val, 2);
while ((val % divisor) != 0 && square(divisor) <= val) {
divisor++;
}
return square(divisor) <= val
? largestPrimeFactor(val / divisor, divisor)
: val;
}
使用示例:
let result = largestPrimeFactor(600851475143);
以下 C++ 算法不是最好的算法,但它适用于十亿以下的数字,而且速度非常快
#include <iostream>
using namespace std;
// ------ is_prime ------
// Determines if the integer accepted is prime or not
bool is_prime(int n){
int i,count=0;
if(n==1 || n==2)
return true;
if(n%2==0)
return false;
for(i=1;i<=n;i++){
if(n%i==0)
count++;
}
if(count==2)
return true;
else
return false;
}
// ------ nextPrime -------
// Finds and returns the next prime number
int nextPrime(int prime){
bool a = false;
while (a == false){
prime++;
if (is_prime(prime))
a = true;
}
return prime;
}
// ----- M A I N ------
int main(){
int value = 13195;
int prime = 2;
bool done = false;
while (done == false){
if (value%prime == 0){
value = value/prime;
if (is_prime(value)){
done = true;
}
} else {
prime = nextPrime(prime);
}
}
cout << "Largest prime factor: " << value << endl;
}
我正在使用的算法继续将数字除以当前的质因数。
我在python 3中的解决方案:
def PrimeFactor(n):
m = n
while n%2==0:
n = n//2
if n == 1: # check if only 2 is largest Prime Factor
return 2
i = 3
sqrt = int(m**(0.5)) # loop till square root of number
last = 0 # to store last prime Factor i.e. Largest Prime Factor
while i <= sqrt :
while n%i == 0:
n = n//i # reduce the number by dividing it by it's Prime Factor
last = i
i+=2
if n> last: # the remaining number(n) is also Factor of number
return n
else:
return last
print(PrimeFactor(int(input())))
输入 : 输出 :10
5
输入 : 输出 :600851475143
6857
与三联画的答案相似,但也不同。在此示例中,不使用列表或字典。代码是用 Ruby 编写的
更新
由于 Cooper Wolfe 的建议,该算法得到了增强和优化。
def largest_prime_factor(number)
i = 2
while number > 1
if number % i == 0
number /= i
elsif i > Math.sqrt(number)
i = number
else
i += 1
end
end
return i
end
largest_prime_factor(600851475143)
# => 6857
评论
i = number
return number
“James Wang”在网络上找到了这个解决方案
public static int getLargestPrime( int number) {
if (number <= 1) return -1;
for (int i = number - 1; i > 1; i--) {
if (number % i == 0) {
number = i;
}
}
return number;
}
使用筛子的质因数:
#include <bits/stdc++.h>
using namespace std;
#define N 10001
typedef long long ll;
bool visit[N];
vector<int> prime;
void sieve()
{
memset( visit , 0 , sizeof(visit));
for( int i=2;i<N;i++ )
{
if( visit[i] == 0)
{
prime.push_back(i);
for( int j=i*2; j<N; j=j+i )
{
visit[j] = 1;
}
}
}
}
void sol(long long n, vector<int>&prime)
{
ll ans = n;
for(int i=0; i<prime.size() || prime[i]>n; i++)
{
while(n%prime[i]==0)
{
n=n/prime[i];
ans = prime[i];
}
}
ans = max(ans, n);
cout<<ans<<endl;
}
int main()
{
ll tc, n;
sieve();
cin>>n;
sol(n, prime);
return 0;
}
这是我在 Clojure 中的尝试。只走几率和素数的素数,即。.使用惰性序列有助于在需要值之前生成值。prime?
sieve
(defn prime?
([n]
(let [oddNums (iterate #(+ % 2) 3)]
(prime? n (cons 2 oddNums))))
([n [i & is]]
(let [q (quot n i)
r (mod n i)]
(cond (< n 2) false
(zero? r) false
(> (* i i) n) true
:else (recur n is)))))
(def primes
(let [oddNums (iterate #(+ % 2) 3)]
(lazy-seq (cons 2 (filter prime? oddNums)))))
;; Sieve of Eratosthenes
(defn sieve
([n]
(sieve primes n))
([[i & is :as ps] n]
(let [q (quot n i)
r (mod n i)]
(cond (< n 2) nil
(zero? r) (lazy-seq (cons i (sieve ps q)))
(> (* i i) n) (when (> n 1) (lazy-seq [n]))
:else (recur is n)))))
(defn max-prime-factor [n]
(last (sieve n)))
猜猜看,除了执行因式分解之外,没有直接的方法,就像上面的例子一样,即
在迭代中,您确定数字 N 的“小”因子 f,然后继续简化问题“找到 N':=N/f 的最大质因数,候选因子为 >=f”。
从一定大小的 f 开始,如果你对约简的 N' 进行质数检验,预期的搜索时间会更短,这证实了你的 N' 已经是初始 N 的最大质因数。
第 1 部分。Pollard-Rho + 审判部门。
受到您的问题的启发,我决定在 Python 中实现我自己的因式分解版本(并找到最大质因数)。
据我所知,最容易实现但非常有效的分解算法可能是 Pollard 的 Rho 算法。它的运行时间最多比试除算法的运行时间快得多。这两种算法仅在复合数(非素数)的情况下才有这些运行时间,这就是为什么应该使用素数检验来过滤掉素数(不可因式)数的原因。O(N^(1/4))
O(N^(1/2))
我在代码中使用了以下算法:费马素数测试...,Pollard的Rho算法...,Trial Division算法。在运行 Pollard 的 Rho 之前使用费马素数检验,以过滤掉素数。Trial Division 被用作后备,因为在极少数情况下,Pollard 的 Rho 可能无法找到因子,尤其是对于一些小数字。
显然,在将一个数字完全分解到质因数排序列表中后,最大的质因数将是该列表中的最后一个元素。在一般情况下(对于任何随机数),除了完全分解一个数字之外,我不知道任何其他方法可以找出最大的质因数。
例如,在我的代码中,我以 Pi 的前 190 个小数位分解,代码在 1 秒内分解这个数字,并显示最大的质因数,大小为 165 位(545 位)!
def is_fermat_probable_prime(n, *, trials = 32):
# https://en.wikipedia.org/wiki/Fermat_primality_test
import random
if n <= 16:
return n in (2, 3, 5, 7, 11, 13)
for i in range(trials):
if pow(random.randint(2, n - 2), n - 1, n) != 1:
return False
return True
def pollard_rho_factor(N, *, trials = 16):
# https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
import random, math
for j in range(trials):
i, stage, y, x = 0, 2, 1, random.randint(1, N - 2)
while True:
r = math.gcd(N, x - y)
if r != 1:
break
if i == stage:
y = x
stage <<= 1
x = (x * x + 1) % N
i += 1
if r != N:
return [r, N // r]
return [N] # Pollard-Rho failed
def trial_division_factor(n, *, limit = None):
# https://en.wikipedia.org/wiki/Trial_division
fs = []
while n & 1 == 0:
fs.append(2)
n >>= 1
d = 3
while d * d <= n and limit is None or d <= limit:
q, r = divmod(n, d)
if r == 0:
fs.append(d)
n = q
else:
d += 2
if n > 1:
fs.append(n)
return fs
def factor(n):
if n <= 1:
return []
if is_fermat_probable_prime(n):
return [n]
fs = trial_division_factor(n, limit = 1 << 12)
if len(fs) >= 2:
return sorted(fs[:-1] + factor(fs[-1]))
fs = pollard_rho_factor(n)
if len(fs) >= 2:
return sorted([e1 for e0 in fs for e1 in factor(e0)])
return trial_division_factor(n)
def demo():
import time, math
# http://www.math.com/tables/constants/pi.htm
# pi = 3.
# 1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679
# 8214808651 3282306647 0938446095 5058223172 5359408128 4811174502 8410270193 8521105559 6446229489 5493038196
# 4428810975 6659334461 2847564823 3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273
# 7245870066 0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951 9415116094
# 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735 1885752724 8912279381 8301194912
# 9833673362 4406566430 8602139494 6395224737 1907021798 6094370277 0539217176 2931767523 8467481846 7669405132
#
# n = first 190 fractional digits of Pi
n = 1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164_0628620899_8628034825_3421170679_8214808651_3282306647_0938446095_5058223172_5359408128_4811174502_8410270193_8521105559_6446229489
print('Number:', n)
tb = time.time()
fs = factor(n)
print('All Prime Factors:', fs)
print('Largest Prime Factor:', f'({math.log2(fs[-1]):.02f} bits, {len(str(fs[-1]))} digits)', fs[-1])
print('Time Elapsed:', round(time.time() - tb, 3), 'sec')
if __name__ == '__main__':
demo()
输出:
Number: 1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489
All Prime Factors: [3, 71, 1063541, 153422959, 332958319, 122356390229851897378935483485536580757336676443481705501726535578690975860555141829117483263572548187951860901335596150415443615382488933330968669408906073630300473]
Largest Prime Factor: (545.09 bits, 165 digits) 122356390229851897378935483485536580757336676443481705501726535578690975860555141829117483263572548187951860901335596150415443615382488933330968669408906073630300473
Time Elapsed: 0.593 sec
第 2 部分。椭圆曲线法。
一段时间后,我决定通过从头开始实现更高级的椭圆曲线分解方法(称为 ECM)来改进我的帖子,请参阅维基百科文章 Lenstra 椭圆曲线分解。
这种方法比我的回答帖子的第 1 部分中描述的 Pollard Rho 要快得多。椭圆 ECM 方法的时间复杂度为 ,其中表示一个数的最小质因数。而 Pollard Rho 的时间复杂度是 .因此,对于足够大的最小 P 因子,ECM 要快得多。O(exp[(Sqrt(2) + o(1)) Sqrt(ln p ln ln p)])
p
O(Sqrt(p))
ECM方法的步骤:
检查数字是否小于 2^16,然后通过试除法进行因式分解。返回结果。
检查数字是否可能是具有高条件的素数,为此我使用费马测试进行 32 次试验。要克服卡迈克尔数,您可以改用米勒拉宾测试。如果 number 是质数,则将其作为唯一的因数返回。
随机生成曲线参数,由曲线方程推导。检查曲线是否正常,值应为非零。
A, X, Y
B
Y^2 = X^3 + AX + B (mod N)
4 * A ** 3 - 27 * B ** 2
通过埃拉托色尼的筛子产生小素数,素数低于我们的边界。每个素数提高到一些小功率,这个提高的素数将被称为 K。我确实在它比某些 Bound2 小的时候提升了力量,就我而言。
Sqrt(Bound)
计算椭圆点乘法,其中 K 取自上一步,P 为 (X, Y)。根据 Wiki 进行计算。
P = k * P
点乘法使用模块化逆法,根据 Wiki 进行计算。如果此 GCD 不为 1,则它给出非 1 的 N 因子,因此在本例中,I 通过 Exception 并从 ECM 分解算法返回该因子。
GCD(SomeValue, N)
如果直到 Bound 的所有素数都相乘并且没有给出任何因子,则使用另一条随机曲线和更大的 Bound 再次重新运行 ECM 因式分解算法(如上所述)。在我的代码中,我通过将 256 添加到旧边界来获取新边界。
1.-6.
我的 ECM 代码中使用了以下子算法,所有提到的子算法都链接到相应的维基百科文章:试除因式分解、费马概率测试、埃拉托色尼筛(素数生成器)、欧几里得算法(计算最大公约数,GCD)、扩展欧几里得算法(具有贝祖系数的 GCD)、模乘法逆、从右到左的二进制指数 (用于椭圆点乘法)、椭圆曲线算术(点加法和乘法)、Lenstra 椭圆曲线因式分解。
与我的答案的第 1 部分不同,我在 Pi 数的位数中分解,在第 2 部分中,我创建了一个由 组成的特殊数,这意味着数字是 24 位和 35 位的随机素数以及 37 等的乘积......此自定义数字在视觉上更令人印象深刻,以显示算法功能。n = Prime(24) * Prime(35) * Prime(37) * ... etc
与我答案的第 1 部分一样,我还使用几种方法来比较速度,并用更简单的方法分解出较小的因素。因此,在下面的代码中,我使用 Trial Division + Pollard Rho + 椭圆曲线方法。
在下面的代码之后,请参阅控制台输出以找出我的代码输出的内容。
def is_fermat_probable_prime(n, *, trials = 32):
# https://en.wikipedia.org/wiki/Fermat_primality_test
import random
if n <= 16:
return n in (2, 3, 5, 7, 11, 13)
for i in range(trials):
if pow(random.randint(2, n - 2), n - 1, n) != 1:
return False
return True
def pollard_rho_factor(N, *, trials = 8, limit = None):
# https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
import random, math
if N <= 1:
return []
if is_fermat_probable_prime(N):
return [N]
for j in range(trials):
i, stage, y, x = 0, 2, 1, random.randint(1, N - 2)
while True:
r = math.gcd(N, x - y)
if r != 1:
break
if i == stage:
y = x
stage <<= 1
x = (x * x + 1) % N
i += 1
if limit is not None and i >= limit:
return [N] # Pollard-Rho failed
if r != N:
return sorted(pollard_rho_factor(r, trials = trials, limit = limit) +
pollard_rho_factor(N // r, trials = trials, limit = limit))
return [N] # Pollard-Rho failed
def trial_division_factor(n, *, limit = None):
# https://en.wikipedia.org/wiki/Trial_division
fs = []
while n & 1 == 0:
fs.append(2)
n >>= 1
d = 3
while d * d <= n and limit is None or d <= limit:
q, r = divmod(n, d)
if r == 0:
fs.append(d)
n = q
else:
d += 2
if n > 1:
fs.append(n)
return fs
def ecm_factor(N0, *, verbose = False):
# https://en.wikipedia.org/wiki/Lenstra_elliptic-curve_factorization
import math, random, time; gmpy2 = None
#import gmpy2
def GenPrimes_SieveOfEratosthenes(end):
# https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
composite = [False] * (end // 2) # Allocate for odd numbers only
for p in range(3, int(end ** 0.5 + 3), 2):
if composite[p >> 1]:
continue
for i in range(p * p, end, p * 2):
composite[i >> 1] = True
yield 2
for p in range(3, end, 2):
if not composite[p >> 1]:
yield p
def GCD(a, b):
# https://en.wikipedia.org/wiki/Euclidean_algorithm
# return math.gcd(a, b)
while b != 0:
a, b = b, a % b
return a
def EGCD(a, b):
# https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
if gmpy2 is None:
ro, r, so, s = a, b, 1, 0
while r != 0:
ro, (q, r) = r, divmod(ro, r)
so, s = s, so - q * s
return ro, so, (ro - so * a) // b
else:
return tuple(map(int, gmpy2.gcdext(a, b)))
def ModularInverse(a, n):
# https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
# return pow(a, -1, n)
g, s, t = EGCD(a, n)
if g != 1:
raise ValueError(a)
return s % n
def EllipticCurveAdd(N, A, B, X0, Y0, X1, Y1):
# https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication
if X0 == X1 and Y0 == Y1:
# Double
l = ((3 * X0 ** 2 + A) * ModularInverse(2 * Y0, N)) % N
x = (l ** 2 - 2 * X0) % N
y = (l * (X0 - x) - Y0) % N
else:
# Add
l = ((Y1 - Y0) * ModularInverse(X1 - X0, N)) % N
x = (l ** 2 - X0 - X1) % N
y = (l * (X0 - x) - Y0) % N
return x, y
def EllipticCurveMul(N, A, B, X, Y, k):
# https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
assert k >= 2, k
k -= 1
BX, BY = X, Y
while k != 0:
if k & 1:
X, Y = EllipticCurveAdd(N, A, B, X, Y, BX, BY)
BX, BY = EllipticCurveAdd(N, A, B, BX, BY, BX, BY)
k >>= 1
return X, Y
bound_start = 1 << 9
def Main(N, *, bound = bound_start, icurve = 0):
def NextFactorECM(x):
return Main(x, bound = bound + bound_start, icurve = icurve + 1)
def PrimePow(p, *, bound2 = int(bound ** 0.5 + 1.01)):
mp = p
while True:
mp *= p
if mp >= bound2:
return mp // p
if N <= 1:
return []
if N < (1 << 16):
fs = trial_division_factor(N)
if verbose and len(fs) >= 2:
print('Factors from TrialDiv:', fs, flush = True)
return fs
if is_fermat_probable_prime(N):
return [N]
if verbose:
print(f'Curve {icurve:>4}, bound 2^{math.log2(bound):>7.3f}', flush = True)
while True:
X, Y, A = [random.randrange(N) for i in range(3)]
B = (Y ** 2 - X ** 3 - A * X) % N
if 4 * A ** 3 - 27 * B ** 2 == 0:
continue
break
for p in GenPrimes_SieveOfEratosthenes(bound):
k = PrimePow(p)
try:
X, Y = EllipticCurveMul(N, A, B, X, Y, k)
except ValueError as ex:
g = GCD(ex.args[0], N)
assert g > 1, g
if g != N:
if verbose:
print(f'Factor from ECM: {g} ({math.log2(g):.1f} bits)', flush = True)
return sorted(NextFactorECM(g) + NextFactorECM(N // g))
else:
return NextFactorECM(N)
return NextFactorECM(N)
return Main(N0)
def factor(n):
if n <= 1:
return []
if is_fermat_probable_prime(n):
return [n]
fs1 = trial_division_factor(n, limit = 1 << 12)
fs1, n2 = fs1[:-1], fs1[-1]
print(len(fs1), 'factors from TrialDivision')
fs2 = pollard_rho_factor(n2, limit = 1 << 17)
fs2, n3 = fs2[:-1], fs2[-1]
print(len(fs2), 'factors from PollardRho')
fs3 = ecm_factor(n3, verbose = True)
print(len(fs3), 'factors from ECM')
return sorted(fs1 + fs2 + fs3)
def demo():
import time, math, random
def Prime(bits):
while True:
x = random.randrange(1 << (bits - 1), 1 << bits)
if is_fermat_probable_prime(x):
return x
# http://www.math.com/tables/constants/pi.htm
# pi = 3.
# 1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679
# 8214808651 3282306647 0938446095 5058223172 5359408128 4811174502 8410270193 8521105559 6446229489 5493038196
# 4428810975 6659334461 2847564823 3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273
# 7245870066 0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951 9415116094
# 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735 1885752724 8912279381 8301194912
# 9833673362 4406566430 8602139494 6395224737 1907021798 6094370277 0539217176 2931767523 8467481846 7669405132
#
# n = 1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164_0628620899_8628034825_3421170679_8214808651_3282306647_0938446095_5058223172_5359408128_4811174502_8410270193_8521105559_6446229489_5493038196_4428810975_6659334461_2847564823_3786783165_2712019091_4564856692_3460348610_4543266482_1339360726_0249141273_7245870066_0631558817_4881520920_9628292540_9171536436_7892590360_0113305305_4882046652_1384146951_9415116094_3305727036_5759591953_0921861173_8193261179_3105118548_0744623799_6274956735_1885752724_8912279381_8301194912_9833673362_4406566430_8602139494_6395224737_1907021798_6094370277_0539217176_2931767523_8467481846_7669405132
# 141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132
n = Prime(9) * Prime(10) * Prime(11) * Prime(20) * Prime(24) * Prime(35) * Prime(37) * Prime(38) * Prime(120)
print('Number:', n)
tb = time.time()
fs = factor(n)
print('All Prime Factors:', fs)
print('Largest Prime Factor:', f'({math.log2(fs[-1]):.02f} bits, {len(str(fs[-1]))} digits)', fs[-1])
if len(fs) >= 2:
print('2nd-largest Prime Factor:', f'({math.log2(fs[-2]):.02f} bits, {len(str(fs[-2]))} digits)', fs[-2])
print('Time Elapsed:', round(time.time() - tb, 3), 'sec')
if __name__ == '__main__':
demo()
控制台输出:
Number: 3020823358956369790763854998578637168366763837218991014777892420353187988302225517459334041
3 factors from TrialDivision
2 factors from PollardRho
Curve 0, bound 2^ 9.000
Curve 1, bound 2^ 10.000
Curve 2, bound 2^ 10.585
Factor from ECM: 20028139561 (34.2 bits)
Curve 3, bound 2^ 11.000
Curve 4, bound 2^ 11.322
Curve 5, bound 2^ 11.585
Curve 6, bound 2^ 11.807
Curve 7, bound 2^ 12.000
Curve 8, bound 2^ 12.170
Factor from ECM: 96583780901 (36.5 bits)
Curve 9, bound 2^ 12.322
Curve 10, bound 2^ 12.459
Curve 11, bound 2^ 12.585
Curve 12, bound 2^ 12.700
Curve 13, bound 2^ 12.807
Curve 14, bound 2^ 12.907
Curve 15, bound 2^ 13.000
Curve 16, bound 2^ 13.087
Curve 17, bound 2^ 13.170
Factor from ECM: 239171423261 (37.8 bits)
4 factors from ECM
All Prime Factors: [397, 1021, 1459, 754333, 16156687, 20028139561,
96583780901, 239171423261, 905908369146483365552973334921981697]
Largest Prime Factor: (119.45 bits, 36 digits) 905908369146483365552973334921981697
2nd-largest Prime Factor: (37.80 bits, 12 digits) 239171423261
Time Elapsed: 17.156 sec
C 语言中的递归
算法可以是
- 检查 n 是因子还是 t
- 检查 n 是否为素数。如果是这样,请记住 n
- 递增 n
- 重复直到 n > sqrt(t)
下面是 C 语言中问题的(尾部)递归解决方案的示例:
#include <stdio.h>
#include <stdbool.h>
bool is_factor(long int t, long int n){
return ( t%n == 0);
}
bool is_prime(long int n0, long int n1, bool acc){
if ( n1 * n1 > n0 || acc < 1 )
return acc;
else
return is_prime(n0, n1+2, acc && (n0%n1 != 0));
}
int gpf(long int t, long int n, long int acc){
if (n * n > t)
return acc;
if (is_factor(t, n)){
if (is_prime(n, 3, true))
return gpf(t, n+2, n);
else
return gpf(t, n+2, acc);
}
else
return gpf(t, n+2, acc);
}
int main(int argc, char ** argv){
printf("%d\n", gpf(600851475143, 3, 0));
return 0;
}
该解决方案由三个功能组成。一个用于测试候选因子是否为一个因子,另一个用于测试该因子是否为素数,最后一个用于将这两者组合在一起。
以下是一些关键思想:
1- 在 sqrt(600851475143) 处停止递归
2- 仅测试因数的奇数
3- 仅测试奇数质数的候选因子
评论
1.
找到任何除以清楚的数(因为 i = 2 到 int(sqr(num)) ),除以该数 (num = num/i),然后重复,直到在 1 中找不到任何东西。s 区间 num 是最大的因子2.
3.