O(n) 比 O(nlogn) 花费更多的时间
O(n) takes more time than O(nlogn)
我在spoj上尝试了这个问题。首先,我想出了一种微不足道的o(blogb)算法(参考问题b)。但是由于问题的作者提到了约束,因为 b 属于 [0,10^7],我不相信它是否会通过。无论如何,出于剪切信念,我将其编码如下
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#define PR(x) cout<<#x"="<<x<<endl
#define READ2(x,y) scanf("%d %d",&x,&y)
#define REP(i,a) for(long long i=0;i<a;i++)
#define READ(x) scanf("%d",&x)
#define PRARR(x,n) for(long long i=0;i<n;i++)printf(#x"[%d]=t%dn",i,x[i])
using namespace std;
#include <stdio.h>
struct node {
int val;
int idx;
};
bool operator<(node a,node b){ return a.val<b.val;}
node contain[10000001];
int main(){
int mx=1,count=1,t,n;
scanf("%d",&t);
while(t--){
count=1;mx=1;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&contain[i].val);
contain[i].idx=i;
}
sort(contain,contain+n);
for(int j=1;j<n;j++){
if(contain[j].idx>contain[j-1].idx)
count++;
else count=1;
mx=max(count,mx);
}
printf("%dn",n-mx);
}
}
它在 SPOJ 服务器上以 0.01 秒的速度通过(已知很慢)但我很快就想出了一个O(b)算法,代码如下
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<string>
#include<cstring>
#define PR(x) printf(#x"=%dn",x)
#define READ2(x,y) scanf("%d %d",&x,&y)
#define REP(i,a) for(int i=0;i<a;i++)
#define READ(x) scanf("%d",&x)
#define PRARR(x,n) for(int i=0;i<n;i++)printf(#x"[%d]=t%dn",i,x[i])
using namespace std;
int val[1001];
int arr[1001];
int main() {
int t;
int n;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int mn=2<<29,count=1,mx=1;
for(int i=0;i<n;i++){
scanf("%d",&arr[i]);
if(arr[i]<mn) { mn=arr[i];}
}
for(int i=0;i<n;i++){
val[arr[i]-mn]=i;
}
for(int i=1;i<n;i++){
if(val[i]>val[i-1]) count++;
else {
count=1;
}
if(mx<count) mx=count;
}
printf("%dn",n-mx);
}
}
但令人惊讶的是,它花了 0.14s :O
现在我的问题是 o(b) 不是比 o(blogb) 对于 b> 2 更好吗?那为什么时间差异这么大呢?社区的一位成员建议这可能是由于缓存未命中。与o(blogb)相比,o(b)代码的本地化程度较低。但是我不认为这会导致 0.10 秒的差异,对于 <1000 次代码运行也是如此?(是的b实际上小于1000.不知道为什么问题设置者如此夸张)
编辑:我看到所有答案都指向渐近符号中的隐藏常量值,这通常会导致算法运行时间的差异。但是如果你看一下代码,你会发现我所做的只是用另一个循环遍历来替换排序的调用。现在我假设排序至少访问一次数组的每个元素。如果我们考虑执行的行数,这不会使两个程序更加接近吗?除此之外,我过去使用 spoj 的经验告诉我 I/O 对程序的运行时间产生了巨大影响,但我在两个代码中都使用相同的 I/O 例程。
Big O 表示法描述了当输入集接近无限大小时函数需要多长时间。如果你有足够大的数据集,O(n) 将始终击败 O(n log n)。
在实践中,由于大O公式中的其他隐藏变量,一些"性能较差"的算法更快。一些更具可扩展性的算法可能会更慢。随着输入集变小,差异变得更加任意。
当我花了几个小时实施可扩展的解决方案并在测试时,我以艰难的方式学到了所有这些,发现它只会对大型数据集更快。
编辑:
关于具体情况,有些人提到同一行代码在性能方面可能会有很大差异。这里的情况很可能就是这种情况。这意味着大O公式中的"隐藏变量"非常相关。您越了解计算机的内部工作原理,您拥有的优化技术就越多。
如果你只记得一件事,记住这一点。切勿仅通过读取代码来比较两种算法的性能。如果它很重要,请在实际数据集上安排实际实现的时间。
I/O 操作(scanf()
、printf()
)都会偏向结果。
众所周知,这些操作非常慢,并且在计时时显示出很大的差异。 您永远不应使用(包括任何 I/O 操作)来测量代码的性能,除非这些操作是您尝试测量的。
因此,请删除这些调用,然后重试。
我还要指出,0.1s非常小。 0.1s 的差异可能是指加载可执行文件和准备代码执行所需的时间。
Big-O 表示法不是可以插入任意n
值的公式。 它只是将函数的增长描述为n
走向无穷大。
这是一个比人们想象的更有趣的问题。 O() 概念可能很有用,但它并不总是像某些人认为的那样有用。 对于对数阶尤其如此。 代数上,对数实际上有一个零阶,也就是说log(n)/n^epsilon收敛于任何正ε。
通常,顺序计算中的对数因素并不重要。
然而,肯德尔·弗雷是对的。 对于足够大的数据集,O(n*log(n)) 最终会丢失。 只是数据集可能必须非常大才能显示对数差异。
我在 SPOj 中查看了您的解决方案。我注意到您的 O(nlogn) 解决方案占用 79M 内存,而 O(n) 占用非常少量的内存,显示为 0K。我也看了其他解决方案。我看过的大多数最快的解决方案都使用了大量内存。现在我能想到的最明显的原因就是std::sort()
函数的实现。它实施得非常好,使您的解决方案非常快。对于 O(n) 解决方案,我认为它可能很慢,因为if() {...} else {...}
.尝试将其更改为三元运算符,并让我们知道它是否有任何区别。
希望有帮助!!
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 从文本文件中读取时钟时间和事件时间并进行处理
- 具有未知值时的时间复杂性
- 如何减少花费的时间
- C++在变量给定的指定时间内关闭电脑
- rcpp函数中的清理时间很长
- C++:floor unix时间戳到UTC月份
- 如何在c++中录制具有精确帧时间戳的视频
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 如果我使用minPts为1的DBSCAN算法,它还会在O(nlogn)时间内运行吗
- 如何证明以下算法具有 O(nlogn) 时间复杂度
- O(n) 比 O(nlogn) 花费更多的时间