针对给定问题的优化算法?

时间:2019-05-06 10:23:14

标签: c++ optimization

我正在解决一个问题,该问题表明我们有一个包含从1到N的整数的列表L。我们必须执行以下操作N-1次:

  1. 选择列表中的两个元素,让我们用X和Y表示它们。
  2. 删除L中的选定元素。
  3. 将数字X + Y + X * Y附加到L。 最后,L恰好包含一个整数。找到这个整数。 由于答案可能很大,我们必须以10 ^ 9 + 7
  4. 为模进行计算

约束: 1≤N≤1,000,000

时间限制: 1秒

我已经编写了这段代码,它给出了线性时间的正确答案,但它表示此方法超出了时间限制。有人可以提供更好的优化解决方案

inline ull cal(ull x, ull y){
  ull ans, i, modno;
  modno = 1000000007;
  i = 1;

  ans = (x + y);
  i = (i*x) % modno;
  i = (i*y) % modno;
  ans = ans + i;
  ans = ans % modno;
  return ans;
}

int main(){
    ull n;
    cin>>n;

    ull sum, modno;
    sum = 0;
    modno = 1000000007;

    if(n == 1)
        cout<<1<<endl;
    else
    {
        sum = n + (n-1) + (n*(n-1));
        n -= 2;
        do
        {
            if(n <= 0)
                break;

            sum = cal(sum, n);
            n -= 1;
        }while(1);
        cout<<ans<<endl;
     }

   return 0;
}

最终代码:

    ull n;
    cin>>n;

    if(n == 1)
        cout<<1<<endl;
    else
    {
        ull modno = 1000000007;
        ull ans = 1;
        ull no = n+1;

        while(no >= 1)
        {
            ans = (ans*no);
            if(ans > modno)
                ans = ans%modno;
            no--;
        }

        ans = ans - 1;
        ans = ans % modno;
        cout<<ans<<endl;

4 个答案:

答案 0 :(得分:3)

有一个总和的封闭式解决方案:L = (N+1)!-1

总和遵循此递归方程L_N = N + L_(n-1) + N*L_(n-1), L_0=0,可以通过始终选择X=L_(N-1)Y=N(=要添加的下一个数字)来获得。

派生:

derivation

编辑:

在您发布最终代码时,我正在发布基准测试:

#include <iostream>
#include <cstdint>
#include <chrono>

std::uint64_t
factorial(std::uint64_t n) {
    std::uint64_t x = 1;
    while (n > 1)
        x = (x * n--) % 1'000'000'007;
    return x;
}
int
main() {
    std::uint64_t n;
    std::cin >> n;
    std::uint64_t numMicro = 0;
    for (std::size_t i = 0; i < 1'000; ++i) {
        auto start = std::chrono::high_resolution_clock::now();
        volatile std::uint64_t res = factorial(n);
        auto end = std::chrono::high_resolution_clock::now();
        numMicro +=
            std::chrono::duration_cast<std::chrono::microseconds>(end - start)
                .count();
    }
    std::cout << "On average: " << numMicro / 1000.0 << "microseconds";
    return 0;
}

使用-O3进行编译,volatile只是为了确保编译器不会优化计算。 您的解决方案几乎相同,不到1秒。不知道要进一步优化什么。

答案 1 :(得分:0)

算法应如下所示:

总和<-1 对于指数<-2,n     总和=(总和+索引+总和*索引)mod 1000000007 结束于

说明:由于+和*是可交换和关联的,因此处理项目的顺序无关紧要,因此您在实现此循环方面做得很好,但不必要地使cal函数过于复杂。

其他答案告诉您计算((n + 1)!-1)mod modno,如果我们忘记模部分,这是正确的,但我怀疑计算((n + 1)!-1)mod无论n的值如何,modno都会产生与逐步计算结果相同的结果,因为每个步骤中都有+和*。如果其他回答者是正确的,则可以极大地优化算法。如果没有,那么优化起来就不那么容易了。

答案 2 :(得分:0)

问题仅显示“ Choose two elements of the list, let's denote them by X and Y.”,并且没有说明需要选择元素的顺序

因此可以将其重写为:

  1. 将列表分成每个CPU一个子列表

  2. 使用SIMD;为每个CPU的每一对计算(X+1)*(Y+1) 子列表并将结果存储为64位整数的新列表,因此 可以避免进行昂贵的模运算

  3. 使用SIMD;为中的每一对计算(X*Y - 1) % 1000000007 每个CPU的新子列表,并将结果存储为32位整数。

  4. 重复前面的2个步骤,直到剩下一个值 每个CPU(并在需要时执行最后的R = (R - 1) % 1000000007以使其恢复为32位)。存放这些 值并终止一个列表中的所有线程。

  5. 使用SIMD;为每对计算(X+1)*(Y+1)

  6. 使用SIMD;为每对计算(X+*Y - 1) % 1000000007

  7. 重复前面的2个步骤,直到剩下一个值

答案 3 :(得分:0)

正如其他人提到的那样,问题归结为计算((n + 1)!-1)%p。您可以搜索有关执行此操作的快速方法(快速阶乘模质数)。 here

就是其中之一。

更新:刚刚检查了Codechef的问题链接。与往常一样,诀窍在于您没有准确描述的约束条件。最多100000个案例,您必须执行相同的任务。由于n很小,因此可以使用标准for循环在1秒内获得单个fact(n)mod p。

不起作用的是为每个测试用例计算fact(n)mod p。像许多其他问题一样,您可以使用预计算受益:建立一个数组,其中arr [i]是i! mod p到i =最大值n可以取+1。使用此信息,您只需返回(arr [n + 1]-1)%p,就可以回答O(1)中的每个查询(测试用例)。

只需尝试一下并被接受。下次,请在您的描述中添加问题链接,通常情况是您认为某些问题不相关,而这部分是问题的整体答案。