有效地计算阶乘

时间:2019-06-16 05:21:22

标签: c++ algorithm data-structures combinations

我有一个N最多为10 ^ 5的数组。 我需要使用给定的QL解决R范围查询。 Q <= 10 ^ 5

对于每个查询,我需要在L到R范围内找到不同的元素 然后找到他们的阶乘。

例如,如果数组为{5, 4, 2, 4, 5, 5, 7}

如果L = 2R = 4

那么我们有了{ 4 : 2 times, 2: 1 time },答案是(2!)*(1!) = 2

如果L = 1R = 5 那么我们有了{5 : 2 times, 4: 2 times, 2 : 1 time},答案是(2!)*(2!).(1!) = 4

O((N)*(Q))显然可以解决此问题。 我该如何优化它。

注意:所有阶乘均以1000000007为模计算

2 个答案:

答案 0 :(得分:3)

好的,因为(根据您的评论),数组中只能有10 5 个元素,这也是数组中每个可能值的最大 count

因此,您可以做的一件事就是将所有这些阶乘因子预先计算到一个数组中,然后将其用于计算。

可能最好的方法是编写一个元程序,将值构造成C ++结构,然后将其包含在自己的代码中(a)。结构如下所示:

unsigned int facMod10p7[] = {
    0,
    1,
    2,
    :
    /* whatever (10^5)! % 1000000007 is */
};

一旦有了,每个阶乘的查找就是O(1)。要进行查询,您只需遍历数组(从LR),计算唯一值的数量即可。

最好用map<unsigned int, unsigned int>来完成,第一个字段是值(假设此处为未签名的值,但您可以很容易地使该符号签名),第二个字段为出现的次数。

对于L2/R4的{​​{1}}情况,您最终会得到一张地图,因此:

{4, 2, 4}

然后,只需简单地遍历一下,即可为每个计数查找阶乘并取所有乘积。

由于它是O(n)循环中的O(1)查找/乘法,因此复杂度将为O(n)。


(a)例如,用于输出前10,000个阶乘的Python程序在我的盒子上花费大约30秒的时间来生成整个表(在我的WSL环境中,不一定以其盲目性而闻名。 / O速度,至少要等到即将发布的下一个版本为止):

{ [2] = 1, [4] = 2 }

如果要进行自己的测试,则代码为:

real 0m29.137s
user 0m28.438s
sys  0m0.547s

答案 1 :(得分:3)

让我们考虑一下您的示例:

n = 7
A[n] = {5, 4, 2, 4, 5, 5, 7}
L = 2
R = 4
p = 1000000007
  1. 计算H[]的直方图A[]O(n)

    大小为n的数组中位于L,R之间的所有值,其中m是您的任何值的最大计数。

     H[2] = 1
     H[3] = 0
     H[4] = 2
    

    在代码中类似:

    int H[R-L+1],i;
    for (i=L,i<=R;i++) H[i-L]=0;
    for (i=0,i<n;i++)
     if ((A[i]>=L)&&(A[i]<=R)
      H[i-L]++;
    

    我将H的索引移了L,所以我们不会浪费空间,所以:

     H[0] = 1
     H[1] = 0
     H[2] = 2
    
  2. 找到m = max(H[])的{​​{1}}

    简单地:

    O(R-L+1)

    如此:

    int m;
    for (m=H[0],i=L+1;i<=R;i++)
     if (m<H[i-L])
      m=H[i-L];
    
  3. 预先计算所有m = 2 (即m

    O(m)

    如此:

    int F[m+1],j;
    j=1; F[0]=j;
    for (i=1;i<=m;i++)
     {
     j=modmul(j,i,p); // j = j*i mod p
     F[i]=j;
     }
    
  4. 计算阶乘的最终PI为O(R-L + 1)

    很简单:

    F[] = { 0!,1!,2! }
    F[] = { 0 ,1 ,2  }
    

    如此:

    for (j=1,i=L;i<=R;i++)
     j=modmul(j,F[H[i-L]],p);
    // here j is your result
    

您可以看到整个过程是j = F[H[0]]*F[H[1]]*F[H[2]] j = F[1]*F[0]*F[2] j = 1!*0!*2! j = 2 ,这比您的O(n+m+R-L)

好得多

如果您多次执行此操作,则应考虑将O(N*Q)预计算为最大F[]值...

如果我选择n,则内容如下:

L=1,R=5

您的直方图中有错误,因为数组中有5个错误是3倍而不是2个!但是,如果将范围应用于数组的索引而不是其值,则不是错误,而我的方法需要对索引稍作更改...周期的所有// 1 2 3 4 5 H[] = { 0,1,0,2,3 } m = 3 // 0 1 2 3 F[] = { 1,1,2,6 } PI(F[H]) = F[0]*F[1]*F[0]*F[2]*F[3] = 1 * 1 * 1 * 2! * 3! = 2*6 = 12 将从i到改为L