我在spoj上尝试this问题。 首先,我提出了一种简单的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%d\n",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("%d\n",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"=%d\n",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%d\n",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("%d\n",n-mx);
}
}
但令人惊讶的是, 0.14s :O
现在我的问题是 o(b)优于o(blogb)对于b&gt; 2 ?那么为什么时间差异如此之大?来自社区的一位成员建议可能是由于缓存未命中。与o(blogb)相比,o(b)代码本地化程度较低。但是我没有看到导致 0.10s的差异那也是<1000运行的代码? (是的b实际上小于1000.不知道为什么问题设定者夸大了)
编辑:我看到所有的答案都是渐近符号中隐藏的常量值,这些常常会导致算法运行时间的不一致。但是如果你看一下代码,你就会意识到我的一切。正在做的是通过循环的另一次遍历来取代对排序的调用。现在我假设排序访问数组的每个元素至少一次。如果我们考虑数量的话,这不会使两个程序更接近那些被执行的行?是的,我过去使用spoj的经历告诉我I / O对程序的运行时间产生了极大的影响,但我在两个代码中都使用了相同的I / O例程。
答案 0 :(得分:5)
Big O表示法描述了当输入集接近无限大小时函数需要多长时间。如果你有足够大的数据集,O(n)总是会超过O(n log n)。
在实践中,由于大O公式中的其他隐藏变量,一些“性能较差”的算法更快。一些更可扩展的算法可能更慢。随着输入集变小,差异变得更加随意。
当我花费数小时实施可扩展的解决方案时,以及在测试时发现只有大型数据集才会更快的时候,我学到了所有这些。
编辑:
就具体情况而言,有些人提到同一行代码在性能方面可能有很大差异。这可能就是这种情况。这意味着大O公式中的“隐藏变量”非常相关。你越了解计算机内部的工作方式,你就越有优化技术。
如果你只记得一件事,请记住这一点。永远不要只通过阅读代码来比较两种算法的性能。如果这很重要,请在实际数据集上实际实施。
答案 1 :(得分:4)
I / O操作(scanf()
,printf()
)会偏向结果。
这些操作非常缓慢,并且在计时时表现出很大的差异。你永远不会使用包括任何i / o操作来衡量代码的性能,除非那些操作是你想要测量的。
所以,删除这些电话再试一次。
我还要指出0.1s非常小。 0.1s的差异可能是指加载可执行文件和准备执行代码所需的时间。
答案 2 :(得分:3)
Big-O表示法不是可以将n
的任意值插入的公式。它只是将函数的增长描述为n
朝向无穷大。
答案 3 :(得分:1)
这是一个比人们怀疑的更有趣的问题。 O()概念可能很有用,但它并不总是像有些人想的那样有用。对数订单尤其如此。在代数上,对数实际上具有零阶,也就是说log(n)/ n ^ epsilon收敛于任何正ε。
比我们想象的更频繁,订单计算中的对数因子并不重要。
然而,肯德尔弗雷是对的。对于足够大的数据集,O(n * log(n))最终会丢失。只有数据集可能必须非常大才能显示对数差异。
答案 4 :(得分:0)
我在SPOj中查看了您的解决方案。我注意到你的O(nlogn)解决方案需要79M内存,而O(n)需要非常少量的内存,它显示为0K。我也看了其他解决方案。我看过的大多数最快的解决方案都使用了大量的内存。现在我能想到的最明显的原因是std::sort()
函数的实现。它实现得非常好,使您的解决方案速度惊人。对于O(n)解决方案,我认为由于if() {...} else {...}
可能会很慢。尝试将其更改为三元运算符,并告知我们是否有任何区别。
希望它有所帮助!!