同时复杂但运行时间变化很大

时间:2018-01-04 13:48:15

标签: c++ algorithm primes

对于这个问题:给定偶数(大于2),返回两个素数,其总和将等于给定数。

以下算法分别具有时间复杂度O(A 2.5 )和O(Alog(log(A))。对于A(整数)的值仍然大到73939138,第二个是真的很慢。我尝试了很多输入,第一个更快。你能解释一下原因吗?

int ul=A/2;
vector <int> answer;
for (int i=1; i<=ul; i++)
{
    if (check(i)==1 && check(A-i)==1 ) //check(i) checks primality of i in O(i^1.5)
   {
       int myint[2] ={ i,A-i };
       answer.assign( myint, myint+2);
       return answer;
   }
}
vector<bool> primes(A+1,true);
int i,j;
//Sieve of Eratosthenes O(Alog(log(A)))
for(i=2;i*i<A+1;i++)
{
    if(primes[i])
    {
        for(j=2;i*j<A+1;j++)
            primes[i*j]=0;
    }
}
vector<int> arr,answer;
//arr is vector containing all primes from 2 to A; O(A)
for(i=2;i<A+1;i++)
{
    if(primes[i])
        arr.push_back(i);
}
i=0;j=arr.size()-1;
//Algorithm to find 2 numbers summing up to a value
while(i<=j)
{
    if(arr[i]+arr[j]>A)
        j--;
    else if(arr[i]+arr[j]<A)
        i++;
    else
    {
        answer.push_back(arr[i]);
        answer.push_back(arr[j]);
        return answer;
    }
}

编辑:check(n)定义如下:

int check(int n)
{
    int ul=sqrt(n);
    if(n<2)
        return 0;
    for(int i=2; i<=ul; i++)
    {
        if(n%i==0)
            return 0;
    }
    return 1;    
}

2 个答案:

答案 0 :(得分:8)

时间复杂度关于算法如何运行,但随着问题变得越来越大,它的速度如何扩展。在每个元素上花费1秒的算法与在每个元素上花费1微秒的算法具有相同的时间复杂度:O(n)。在这两种情况下,如果你有10倍的元素,算法将花费10倍的时间来运行。

答案 1 :(得分:3)

您考虑的复杂性并未提供有关算法性能的即时信息,而是有关渐近行为的信息,通常用于最坏情况方案。

最坏情况与平均情况

看看A = 73939138的答案:

73939138 = 5 + 73939133

基本上,你的第一个算法会对check进行约10次调用,而第二个算法会通过一个巨大的循环来填充数组primes ..

第一种算法的平均值复杂度可能远低于O(A^2.5),而第二种算法的平均情况复杂度接近或等于O(A log(log(A))

注意:关于平均情况的复杂性仅仅是猜测,不要将它们用于合理的结果。

第二种算法:

在此算法中,无论A是什么,您都必须使用筛选的Eratosthenes 填充数组primes,即O(A log(log(A)))。由于这是算法中最耗时的部分,因此该算法的平均情况复杂度可能接近其最坏情况复杂度,因此O(A log(log(A)))

第一种算法:

这里更复杂......基本上,它取决于算法的结果。根据{{​​3}},A作为两个素数之和的平均写作方式为A / (2 * log(A) ^ 2)

由于素数不能对两种不同的方式做出贡献,这意味着平均2 * A / (2 * log(A) ^ 2) = A / (log(A) ^ 2)素数有助于其中一种方式。

如果我们假设* 1 这些素数是均匀分布的,那么较小的素数应该接近A / (A / log(A) ^ 2) = log(A)^2

因此,您只需检查大约log(A)^2的数字。

1 我有严格不知道如果这是真的,我只是在猜测......

渐近行为

检查Wikipedia's page on Goldbach's conjecture

当你说O(A log(log(A)))复杂性时,你隐藏了很多东西 - f(A) = C * (A log(log(A))) + g(A) g(A) O(A log(log(A))) O(A log(log(A)))的任何函数f(A) = c1 * A * log(log(A)) + c2 * A + c3 * log(A) 也是O(A log(log(A)))

例如:

c1

...是c2

系数c3primesarr决定了算法实现的实际行为,不幸的是,这些通常很难找到(或复杂)。

例如,快速浏览一下您的实现会显示以下内容:

  • 第一种算法不使用任何类型的容器,因此内存需求很少(只有一些局部变量)。
  • 第二种算法使用两个相对较大的数组A = 73939138primes - 如果73939139
    • std::vector<bool>包含arr“实体” - 这可能是int的专业化所优化的,但您仍然需要~9MB,这可能不适合L1缓存,也许L2,你需要为每次访问进行一些按位操作。
    • push_back应包含~4000000 [HPM] Error occurred while trying to proxy request /rest/open/test/test-por tal-url=abcdsdf/ui-settings from localhost:9000 to https://localhost:9443 (DEPTH_ZERO_SELF_SIGNED_CERT) (https://nodejs.org/api/errors.html#errors_common_system_errors) (请参阅@PeterBecker's answer and comments),您需要多次分配,因为您使用的是{{1}}。