以下是 Java 中的示例算法(它不是带有 GMP 的 C++,但转换应该非常简单):
- 生成一个随机数
x
的位长Nbits
- 尝试排除所有小于 100 的素因数,保留除以 x 的素因数列表。
isProbablePrime
使用 Java 的方法测试剩余因子是否为素数
- 如果剩余的因子乘积是具有足够概率的素数,我们就成功地分解了 x。(停止)
- 否则,剩余因子乘积肯定是复合的(参见 isProbablePrime 文档)。
- 虽然我们还有时间,但我们运行Pollard rho 算法,直到找到除数 d。
- 如果我们没有时间,我们就失败了。(停止)
- 我们找到了一个除数 d。所以我们将 d 分解,将 d 的素因子添加到 x 的素因子列表中,然后转到步骤 4。
该算法的所有参数都在程序列表的开头附近。我寻找 1024 位随机数,超时时间为 250 毫秒,然后我继续运行程序,直到我得到一个具有至少 4 个质因数的数字 x(有时程序会找到一个具有 1、2 或 3 个质因数的数字第一的)。使用此参数设置,在我的 2.66Ghz iMac 上通常需要大约 15-20 秒。
Pollard 的 rho 算法并不是那么有效,但与二次筛(QS) 或通用数域筛(GNFS) 相比,它很简单——我只是想看看这个简单算法是如何工作的。
为什么会这样:(尽管你们中的许多人声称这是一个难题)
显而易见的事实是,素数并不罕见。对于 1024 位数字,素数定理表明每 1024 ln 2(= 约 710)个数字中约有 1 个是素数。
因此,如果我生成一个素数的随机数 x,并且我接受概率素数检测,我就成功地分解了 x。
如果它不是素数,但我很快会分解出一些小因数,而剩下的因数是(概率上)素数,那么我已经成功分解了 x。
否则我就放弃并生成一个新的随机数。(OP说这是可以接受的)
大多数成功分解的数字将具有 1 个大素因数和一些小的素因数。
难以分解的数字是那些具有不小的质因数和至少 2 个大质因数的数字(这些包括作为两个大数乘积的密码密钥;OP 对密码学只字未提),我只能当我没时间时跳过它们。
package com.example;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class FindLargeRandomComposite {
final static private int[] smallPrimes = {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};
final static private int maxTime = 250;
final static private int Nbits = 1024;
final static private int minFactors = 4;
final static private int NCERTAINTY = 4096;
private interface Predicate { public boolean isTrue(); }
static public void main(String[] args)
{
Random r = new Random();
boolean found = false;
BigInteger x=null;
List<BigInteger> factors=null;
long startTime = System.currentTimeMillis();
while (!found)
{
x = new BigInteger(Nbits, r);
factors = new ArrayList<BigInteger>();
Predicate keepRunning = new Predicate() {
final private long stopTime = System.currentTimeMillis() + maxTime;
public boolean isTrue() {
return System.currentTimeMillis() < stopTime;
}
};
found = factor(x, factors, keepRunning);
System.out.println((found?(factors.size()+" factors "):"not factored ")+x+"= product: "+factors);
if (factors.size() < minFactors)
found = false;
}
long stopTime = System.currentTimeMillis();
System.out.println("Product verification: "+(x.equals(product(factors))?"passed":"failed"));
System.out.println("elapsed time: "+(stopTime-startTime)+" msec");
}
private static BigInteger product(List<BigInteger> factors) {
BigInteger result = BigInteger.ONE;
for (BigInteger f : factors)
result = result.multiply(f);
return result;
}
private static BigInteger findFactor(BigInteger x, List<BigInteger> factors,
BigInteger divisor)
{
BigInteger[] qr = x.divideAndRemainder(divisor);
if (qr[1].equals(BigInteger.ZERO))
{
factors.add(divisor);
return qr[0];
}
else
return x;
}
private static BigInteger findRepeatedFactor(BigInteger x,
List<BigInteger> factors, BigInteger p) {
BigInteger xprev = null;
while (xprev != x)
{
xprev = x;
x = findFactor(x, factors, p);
}
return x;
}
private static BigInteger f(BigInteger x, BigInteger n)
{
return x.multiply(x).add(BigInteger.ONE).mod(n);
}
private static BigInteger gcd(BigInteger a, BigInteger b) {
while (!b.equals(BigInteger.ZERO))
{
BigInteger nextb = a.mod(b);
a = b;
b = nextb;
}
return a;
}
private static BigInteger tryPollardRho(BigInteger n,
List<BigInteger> factors, Predicate keepRunning) {
BigInteger x = new BigInteger("2");
BigInteger y = x;
BigInteger d = BigInteger.ONE;
while (d.equals(BigInteger.ONE) && keepRunning.isTrue())
{
x = f(x,n);
y = f(f(y,n),n);
d = gcd(x.subtract(y).abs(), n);
}
if (d.equals(n))
return x;
BigInteger[] qr = n.divideAndRemainder(d);
if (!qr[1].equals(BigInteger.ZERO))
throw new IllegalStateException("Huh?");
// d is a factor of x. But it may not be prime, so run it through the factoring algorithm.
factor(d, factors, keepRunning);
return qr[0];
}
private static boolean factor(BigInteger x0, List<BigInteger> factors,
Predicate keepRunning) {
BigInteger x = x0;
for (int p0 : smallPrimes)
{
BigInteger p = new BigInteger(Integer.toString(p0));
x = findRepeatedFactor(x, factors, p);
}
boolean done = false;
while (!done && keepRunning.isTrue())
{
done = x.equals(BigInteger.ONE) || x.isProbablePrime(NCERTAINTY);
if (!done)
{
x = tryPollardRho(x, factors, keepRunning);
}
}
if (!x.equals(BigInteger.ONE))
factors.add(x);
return done;
}
}