2

谁能向我解释为什么下面的算法是一个无错误的整数分解方法,它总是返回一个非平凡的 N 因子。我知道这听起来很奇怪,但我在 2 年前设计了这个方法,但仍然不明白它背后的数学逻辑,这让我很难改进它。它非常简单,只涉及加法和减法。

public static long factorX( long N )
{
    long x=0, y=0;
    long b = (long)(Math.sqrt(N));
    long a = b*(b+1)-N;
    if( a==b ) return a;

    while ( a!= 0 )
    {
        a-= ( 2+2*x++ - y);
        if( a<0 ) { a+= (x+b+1); y++; }
    }

return ( x+b+1 );
}

看来上述方法实际上是通过对丢番图方程的迭代找到了解:

f(x,y) = a - x(x+1) + (x+b+1)y
where b = floor( sqrt(N) ) and a = b(b+1) - N
that is, when a = 0, f(x,y) = 0 and (x+b+1) is a factor of N.

Example: N = 8509  
b = 92, a = 47  
f(34,9) = 47 - 34(34+1) + 9(34+92+1) = 0  
and so x+b+1 = 127 is a factor of N.  

重写方法:

public static long factorX(long N)
{
long x=1, y=0, f=1;
long b = (long)(Math.sqrt(N));
long a = b*(b+1)-N;
if( a==b ) return a;

  while( f != 0 )
  {
    f = a - x*(x+1) + (x+b+1)*y;
      if( f < 0 ) y++;
    x++;
  }

return x+b+1;
}  

我非常感谢有关如何改进此方法的任何建议。

这是 10 个 18 位随机半素数的列表:

349752871155505651 = 666524689 x 524741059   in 322 ms
259160452058194903 = 598230151 x 433211953   in 404 ms
339850094323758691 = 764567807 x 444499613   in 1037 ms
244246972999490723 = 606170657 x 402934339   in 560 ms
285622950750261931 = 576888113 x 495109787   in 174 ms
191975635567268981 = 463688299 x 414018719   in 101 ms
207216185150553571 = 628978741 x 329448631   in 1029 ms
224869951114090657 = 675730721 x 332780417   in 1165 ms
315886983148626037 = 590221057 x 535201141   in 110 ms
810807767237895131 = 957028363 x 847213937   in 226 ms
469066333624309021 = 863917189 x 542952889   in 914 ms
4

3 回答 3

2

好的,我用 Matlab 看看这里发生了什么。这是 N=100000 的结果: 在此处输入图像描述x在每次迭代中都在增加,变量的有趣模式a与余数密切相关N % x+b+1(正如您在图中的灰线中看到的那样a + (N % (x+b+1)) - x = floor(sqrt(N)))。因此,我认为您只是找到了比sqrt(N)简单迭代更大的第一个因素,但是有一个相当模糊的标准来确定它确实是一个因素:D(对不起,半答案......我必须离开,我可能会稍后继续)。

这是 matlab 代码,以防万一您希望它自己测试:

clear all
close all

N = int64(100000);

histx = [];
histDiffA = [];
histy = [];
hista = [];
histMod = [];
histb = [];

x=int64(0);
y=int64(0);
b = int64(floor(sqrt(double(N))));
a = int64(b*(b+1)-N);
if( a==b ) 
    factor = a;
else
    while ( a ~= 0 )
        a = a - ( 2+2*x - y);
        histDiffA(end+1) = ( 2+2*x - y);
        x = x+1;
        if( a<0 ) 
            a = a + (x+b+1); 
            y = y+1;
        end
        hista(end+1) = a;
        histb(end+1) = b;
        histx(end+1) = x;
        histy(end+1) = y;
        histMod(end+1) = mod(N,(x+b+1));
    end
    factor = x+b+1;
end

figure('Name', 'Values');
hold on
    plot(hista,'-or')
    plot(hista+histMod-histx,'--*', 'Color', [0.7 0.7 0.7])
    plot(histb,'-ob')
    plot(histx,'-*g')
    plot(histy,'-*y')
    legend({'a', 'a+mod(N,x+b+1)-x', 'b', 'x', 'y'}); % 'Input',
hold off

fprintf( 'factor is %d \n', factor );
于 2013-02-08T11:39:35.820 回答
1

您的方法是 (na)*(n+b) 的试乘法的变体,其中 n=floor(sqrt(N)) 和 b==1。

然后该算法迭代 a-- / b++ 直到 (na)*(n+b) - N == 0 的差值。

部分差异(关于 a 和 b)分别与 2b 和 2a 成比例。因此,不需要真正的乘法。

复杂度是 |a| 的线性函数 或 |b| -- N 越“方形”,方法收敛得越快。总之,有更快的方法,最容易理解的方法之一是二次残差筛。

于 2013-02-12T11:33:44.870 回答
0

请原谅我的 c#,我不懂 Java。将 x 和 y 步进 2 可提高算法速度。

using System.Numerics;                      // needed for BigInteger
    /* Methods ************************************************************/
    private static BigInteger sfactor(BigInteger k) // factor odd integers
    {
        BigInteger x, y;
        int flag;
        x = y = iSqrt(k);                   // Integer Square Root
        if (x % 2 == 0) { x -= 1; y += 1; } // if even make x & y odd
        do
        { 
            flag = BigInteger.Compare((x*y), k);
            if (flag > 0) x -= 2;
            y += 2;
        } while(flag != 0);
        return x;                           // return x
    } // end of sfactor()

    // Finds the integer square root of a positive number
    private static BigInteger iSqrt(BigInteger num)
    {
        if (0 == num) { return 0; }           // Avoid zero divide
        BigInteger n = (num / 2) + 1;         // Initial estimate, never low
        BigInteger n1 = (n + (num / n)) >> 1; // right shift to divide by 2
        while (n1 < n)
        {
            n = n1;
            n1 = (n + (num / n)) >> 1;        // right shift to divide by 2
        }
        return n;
    } // end iSqrt()

于 2015-11-15T00:10:57.970 回答