整数模式的因子快速计算

时间:2015-02-14 14:58:17

标签: algorithm

是否可以在不循环整个表达链的情况下计算Factorial(x)mod m

((1 % m) * (2 %m) * (3 % m) * ... (x % m)) % m?

更准确地说,m可以是1< = m< = 10 ^ 7并且x:1< = x<米

1 个答案:

答案 0 :(得分:1)

Factorial的快速​​算法很少

  • 所以答案是:是的,您可以在不循环所有值的情况下计算阶乘
  • 我所看到的都是使用素数分解(包括我的算法)
  • 因此,只需使用mod乘法而不是正常乘法
  • 看这里:Fast exact bigint factorial是我的快速算法
  • ,另一个答案还包含摆动素数算法的链接......

[注释]

  • 对于N!,您需要一份最多N
  • 的素数列表
  • 但其余代码可以处理能够持有N,m
  • 的算术
  • 所以不需要大量的数字......

[edit1]挖掘32位C ++实现

//---------------------------------------------------------------------------
DWORD modmul(DWORD a,DWORD b,DWORD n)
    {
    DWORD _a,_b,_n;
    _a=a;
    _b=b;
    _n=n;
    asm {
        mov eax,_a
        mov ebx,_b
        mul ebx     // H(edx),L(eax) = eax * ebx
        mov ebx,_n
        div ebx     // eax = H(edx),L(eax) / ebx
        mov _a,edx  // edx = H(edx),L(eax) % ebx
        }
    return _a;
    }
//---------------------------------------------------------------------------
DWORD modfact0(DWORD n,DWORD m)         // (n!) mod m (naive approach)
    {
    DWORD i,f;
    for (f=1,i=2;i<=n;i++) f=modmul(f,i,m);
    return f;
    }
//---------------------------------------------------------------------------
DWORD modfact1(DWORD n,DWORD m)         // (n!) mod m (mine fast approach)
    {
    if (n<=4)
        {
        if (n==4) return 24;
        if (n==3) return  6;
        if (n==2) return  2;
        if (n==1) return  1;
        if (n==0) return  1;
        }
    int N4,N2,p,i,j,e; DWORD c,pp;
    N4=(n>>2)<<2;
    N2=N4>>1;
    c=modfact1(N2,m); c=modmul(c,c,m);  // c=((2N)!)^2;
    for (i=0;;i++)                      // c*= T2
        {
        p=primes_i32.dat[i];
        if (!p) break;
        if (p>N4) break;
        for (e=0,j=N4;j;e+=j&1,j/=p);
        if (e)                          // c*=p^e
            {
            if (p==2) c<<=e;
            else for (pp=p;;)
                {
                if (int(e&1)) c=modmul(c,pp,m);
                e>>=1; if (!e) break;
                pp=modmul(pp,pp,m);
                }
            }
        }
    for (i=N4+1;i<=n;i++) c=modmul(c,i,m);
    return c;
    }
//---------------------------------------------------------------------------

素数:

  • DWORD primes_i32.dat[]是预先计算的所有素数的排序(升序)列表,最多为n

结果如下:

[  18.529 ms] slow modfact0(1000000,1299721) = 195641
[   2.995 ms] fast modfact1(1000000,1299721) = 195641
[  96.242 ms] slow modfact0(5000000,9999991) = 2812527
[  13.305 ms] fast modfact1(5000000,9999991) = 2812527
  • 1299721是接近1000000的第一个素数我发现
  • 如果m不是素数并且子结果达到零,那么你可以忽略其余的乘法到大规模加速......

希望结果没有可比性......