什么是二次筛法提取阶段最高效的分解算法?

huangapple go评论82阅读模式
英文:

What is the most efficient factoring algorithm for quadratic sieve extraction phase?

问题

在二次筛选算法中,使用对数近似找到 bSmooth 值后,您需要对数字(称为 B)进行因数分解,以构建 bSmooth 向量。

常见的解决方案是使用因子基中的素数进行试除法。与随机数不同,在这种情况下,试除法非常高效,因为大多数因子将在素数基中。我之所以说“大多数”,是因为常见的优化将允许一个小阈值,以包括1-3个乘积达到 2^30 左右,这称为部分关系。

在我的当前实现中,这个向量提取阶段占用了大部分时间。我一直尝试的另一个解决方案是,重新接收、再次遍历素数基,并在已知为 b-平滑的索引中记录向量,但结果却更慢。

以下是我当前的代码,我为试除法添加了四个优化,请告诉我是否有更好的解决方案。

  1. 对于素数2,我检查 B 的最后一位设置位,然后向右移动以提取它。
  2. 我使用 BigInteger 的 divideAndRemainder,它通过将除法和取模操作合并为1次,既优化了内存,又提升了性能。
  3. 如果 B 小于因子基中的最大素数,那么它必定在因子基中,所以我使用哈希映射来定位它的索引。
  4. 如果没有小于 B.bitLength() / 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%。

英文:

In the quadratic sieve algorithm, after finding bSmooth values using logarithmic approximation you need to factor the number, let's call it B, to construct the bSmooth vector.

A common solution is to use a trial division using the primes in the factor base. Unlike random numbers, in this case, the trial division is super efficient since most of the factors will be in the prime base. I am saying "most" because a common optimization will allow a small threshold to include 1-3 prims with a product of up to 2^30 or so, it's called a partial relation.

In my current implementation, this vector extraction phase takes most of the time. Another solution that I have been trying to do is receiving, walking again over the prime base, and record the vectors in the indexes knowen to be b-smooth., but that turned to be even slower.

Below is my current code, I added 4 optimizations for the trial division, please tell me if there are better solutions for it.

  1. For the prime 2, I check the last set bit of B and shift right to extract it.
  2. I am using BigInteger divideAndRemainder it's optimizing both the memory and the performance by combining the division and the mod actions into 1
  3. if B is smaller then the max prime in the factor base, then it must be in the factor base, so I use a hash map to locate it's index
  4. if no prime up to B.bitLenght() / 2 is dividing B then it must be a partial relation, I will include it only if it's a prime.
    private VectorData extractVector(BigInteger value) {
BitSet vector = new BitSet(PrimeBase.instance.primeBase.size());
if(value.compareTo(BigInteger.ZERO) &lt; 0){
vector.set(0);
value = value.abs();
}
value = extractPower2(value, vector);
for (int i = 2; i &lt; 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) &lt;= 0) {
int index = PrimeBase.instance.primeBaseMap.get(value);
vector.set(index);
bSmoothVectorData.vector = vector;
return bSmoothVectorData;
} else if (value.bitLength() / 2 &lt; p.bitLength()) {
if (isPrime(value.longValue())) {
return new VectorData(vector, value);
}
return null;
}
}
}
return null;
}

bSmoothVectorData is used to differentiate between full and partial relations. The last else-if case that calls for isPrime is rare and takes less than 0.001% overall performance of this method, the bottleneck is in the call to divideAndRemainder that takes about 72% of the performance.

答案1

得分: 1

我通过将试除法与接收法进行切换,成功提高了近80%的性能。现在,我在问题中已经提到我之前尝试过这个方法,但没有成功。不过,这一次却奏效了。

我用整数操作替换了BigInteger.mod(x).equals(ZERO)的测试,改成了(bSmoothData.localX - delta) % prime == startingPosition,这可能非常特定于我的实现,但核心思想是检查素数是否应该被用来划分筛选数组中的bSmooth索引。

接下来,我构建了所有这些素数的乘积,并将实际的bSmooth值除以它,然后剩下一个可以适应Java long的余数。然后,我继续使用试除法提取它。如果你对我的实现感兴趣,我制作了一个关于它的视频,你可以在这里观看:链接

英文:

I was able to achieve a nearly 80% boost in performance by switching the trial division with receiving. Now, I already mentioned in the question that I tried this before with no success. Well, this time it worked.

I replaced the BigInteger.mod(x).equals(ZERO) test with integer operations (bSmoothData.localX - delta) % prime == startingPosition, it's probably very specific to my implemintation, but the idea is to check if the prime is supposed to divide the bSmooth index in the sieving array.

Next, I construct a product of all those primes and divide the actual bSmooth value by it, I left then with a reminder that can feet into Java long. And I continue extracting it using trial division. If you are interested in my implementation I made a video about it here

huangapple
  • 本文由 发表于 2020年8月23日 05:40:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/63541365.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定