二次筛提取阶段最有效的因式分解算法是什么?
What is the most efficient factoring algorithm for quadratic sieve extraction phase?
在二次筛算法中,在使用对数近似找到 bSmooth 值后,您需要对数字进行因式分解,我们称之为 B
,以构建 bSmooth 向量。
一个常见的解决方案是使用因子基中的素数进行试验除法。与随机数不同,在这种情况下,试验除法非常有效,因为大多数因子都位于素数基中。我说“最”是因为一个常见的优化将允许一个小阈值包含 1-3 个素数,乘积高达 2^30 左右,这称为部分关系。
在我的 current implementation 中,这个向量提取阶段花费了大部分时间。我一直在尝试做的另一个解决方案是接收,再次遍历素数基,并在已知为 b-smooth 的索引中记录向量。但结果变得更慢。
下面是我目前的代码,我为试用版添加了4个优化,如果有更好的解决方案请告诉我。
- 对于素数 2,我检查
B
的最后一个设置位并右移以提取它。
- 我正在使用 BigInteger
divideAndRemainder
它通过将除法和 mod 操作合并为 1 来优化内存和性能
- 如果
B
小于因子基中的最大素数,那么它一定在因子基中,所以我使用哈希映射来定位它的索引
- 如果
B.bitLenght() / 2
之前没有素数整除 B
那么它一定是偏关系,只有当它是素数时我才会包括它。
private VectorData extractVector(BigInteger value) {
BitSet vector = new BitSet(PrimeBase.instance.primeBase.size());
if(value.compareTo(BigInteger.ZERO) < 0){
vector.set(0);
value = value.abs();
}
value = extractPower2(value, vector);
for (int i = 2; i < PrimeBase.instance.primeBase.size(); i++) {
BigInteger p = PrimeBase.instance.primeBaseBigInteger.get(i);
int count = 1;
BigInteger[] results = value.divideAndRemainder(p);
if (results[1].equals(BigInteger.ZERO)) {
value = results[0];
while (true) {
results = value.divideAndRemainder(p);
if(!results[1].equals(BigInteger.ZERO)){
break;
}
value = results[0];
count++;
}
if(count % 2 == 1) {
vector.set(i);
}
if (value.equals(BigInteger.ONE)) {
bSmoothVectorData.vector = vector;
return bSmoothVectorData;
} else if (value.compareTo(PrimeBase.instance.maxPrimeBigInteger) <= 0) {
int index = PrimeBase.instance.primeBaseMap.get(value);
vector.set(index);
bSmoothVectorData.vector = vector;
return bSmoothVectorData;
} else if (value.bitLength() / 2 < p.bitLength()) {
if (isPrime(value.longValue())) {
return new VectorData(vector, value);
}
return null;
}
}
}
return null;
}
bSmoothVectorData
用于区分完全关系和部分关系。调用 isPrime
的最后一个 else-if 情况很少见,占用该方法的整体性能不到 0.001%,瓶颈在于对 divideAndRemainder
的调用,它占用了大约 72% 的性能。
通过将试用部门切换为接收,我能够实现近 80% 的性能提升。现在,我已经在问题中提到我之前尝试过但没有成功。嗯,这次成功了。
我用整数运算 (bSmoothData.localX - delta) % prime == startingPosition
替换了 BigInteger.mod(x).equals(ZERO)
测试,这可能非常适合我的实现,但我的想法是检查素数是否应该在筛分中划分 bSmooth 指数数组。
接下来,我构建了所有这些素数的乘积,并将实际的 bSmooth 值除以它,然后我留下了一个可以进入 Java 英尺长的提醒。然后我继续使用试用版提取它。如果您对我的实现感兴趣,我制作了一个视频 here
在二次筛算法中,在使用对数近似找到 bSmooth 值后,您需要对数字进行因式分解,我们称之为 B
,以构建 bSmooth 向量。
一个常见的解决方案是使用因子基中的素数进行试验除法。与随机数不同,在这种情况下,试验除法非常有效,因为大多数因子都位于素数基中。我说“最”是因为一个常见的优化将允许一个小阈值包含 1-3 个素数,乘积高达 2^30 左右,这称为部分关系。
在我的 current implementation 中,这个向量提取阶段花费了大部分时间。我一直在尝试做的另一个解决方案是接收,再次遍历素数基,并在已知为 b-smooth 的索引中记录向量。但结果变得更慢。
下面是我目前的代码,我为试用版添加了4个优化,如果有更好的解决方案请告诉我。
- 对于素数 2,我检查
B
的最后一个设置位并右移以提取它。 - 我正在使用 BigInteger
divideAndRemainder
它通过将除法和 mod 操作合并为 1 来优化内存和性能
- 如果
B
小于因子基中的最大素数,那么它一定在因子基中,所以我使用哈希映射来定位它的索引 - 如果
B.bitLenght() / 2
之前没有素数整除B
那么它一定是偏关系,只有当它是素数时我才会包括它。
private VectorData extractVector(BigInteger value) {
BitSet vector = new BitSet(PrimeBase.instance.primeBase.size());
if(value.compareTo(BigInteger.ZERO) < 0){
vector.set(0);
value = value.abs();
}
value = extractPower2(value, vector);
for (int i = 2; i < PrimeBase.instance.primeBase.size(); i++) {
BigInteger p = PrimeBase.instance.primeBaseBigInteger.get(i);
int count = 1;
BigInteger[] results = value.divideAndRemainder(p);
if (results[1].equals(BigInteger.ZERO)) {
value = results[0];
while (true) {
results = value.divideAndRemainder(p);
if(!results[1].equals(BigInteger.ZERO)){
break;
}
value = results[0];
count++;
}
if(count % 2 == 1) {
vector.set(i);
}
if (value.equals(BigInteger.ONE)) {
bSmoothVectorData.vector = vector;
return bSmoothVectorData;
} else if (value.compareTo(PrimeBase.instance.maxPrimeBigInteger) <= 0) {
int index = PrimeBase.instance.primeBaseMap.get(value);
vector.set(index);
bSmoothVectorData.vector = vector;
return bSmoothVectorData;
} else if (value.bitLength() / 2 < p.bitLength()) {
if (isPrime(value.longValue())) {
return new VectorData(vector, value);
}
return null;
}
}
}
return null;
}
bSmoothVectorData
用于区分完全关系和部分关系。调用 isPrime
的最后一个 else-if 情况很少见,占用该方法的整体性能不到 0.001%,瓶颈在于对 divideAndRemainder
的调用,它占用了大约 72% 的性能。
通过将试用部门切换为接收,我能够实现近 80% 的性能提升。现在,我已经在问题中提到我之前尝试过但没有成功。嗯,这次成功了。
我用整数运算 (bSmoothData.localX - delta) % prime == startingPosition
替换了 BigInteger.mod(x).equals(ZERO)
测试,这可能非常适合我的实现,但我的想法是检查素数是否应该在筛分中划分 bSmooth 指数数组。
接下来,我构建了所有这些素数的乘积,并将实际的 bSmooth 值除以它,然后我留下了一个可以进入 Java 英尺长的提醒。然后我继续使用试用版提取它。如果您对我的实现感兴趣,我制作了一个视频 here