并查集效率测试
pre
全文基础设定
-----------------------------
n 集合总数(即并查集个体数)
m 总共操作数
f find操作数
Ackerman(k,n) = {n+1/k==0 | Ackerman.iter(n+1,k=k-1)(k-1,n)} / 具体定义可以看wiki
alpha(n) = min{k:Ackerman(k,1)>=n}
引自《算法导论》
并查集(disjoint set)有两种优化: 按秩合并(union by rank,UR) 和 路径压缩(path compression,PC)
其中,
仅用按秩合并的时间复杂度$\text{O} ( m \log n)$
仅用路径压缩的时间复杂度$\Theta ( n + f \cdot ( 1 + \log_{2 + f / n} n))$
两个都用的时间复杂度$\text{O} ( m \alpha ( n))$
测试程序
并查集程序
#define sizex 100000000 int f[sizex],rk[sizex]; struct ds{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1; } void find(int t){ return ~f[t]?t:find(f[t]); } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; f[b]=a; } } djs; struct dsU{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1,rk[i]=1; } void find(int t){ return ~f[t]?t:find(f[t]); } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; if(rk[a]>=rk[b]){ f[b]=a; rk[a]+=rk[b]; }else{ f[a]=b; rk[b]+=rk[a]; } } } djsU; struct dsP{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1; } void find(int t){ return ~f[t]?t:f[t]=find(f[t]); } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; f[b]=a; } } djsP; struct dsUP{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1,rk[i]=1; } void find(int t){ return ~f[t]?t:f[t]=find(f[t]); } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; if(rk[a]>=rk[b]){ f[b]=a; rk[a]+=rk[b]; }else{ f[a]=b; rk[b]+=rk[a]; } } } djsUP;
测试程序
#define sizex 100000000 int f[sizex],rk[sizex]; struct ds{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1; } int find(int t){ return ~f[t]?find(f[t]):t; } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; f[b]=a; } } djs; struct dsU{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1,rk[i]=1; } int find(int t){ return ~f[t]?find(f[t]):t; } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; if(rk[a]>=rk[b]){ f[b]=a; rk[a]+=rk[b]; }else{ f[a]=b; rk[b]+=rk[a]; } } } djsU; struct dsP{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1; } int find(int t){ return ~f[t]?f[t]=find(f[t]):t; } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; f[b]=a; } } djsP; struct dsUP{ inline void init(int size){ for(int i=0;i<size;++i) f[i]=-1,rk[i]=1; } int find(int t){ return ~f[t]?f[t]=find(f[t]):t; } inline void join(int a,int b){ a=find(a),b=find(b); if(a==b) return; if(rk[a]>=rk[b]){ f[b]=a; rk[a]+=rk[b]; }else{ f[a]=b; rk[b]+=rk[a]; } } } djsUP; #include <cstdio> #include <random> #include <malloc.h> #include <sys/time.h> using namespace std; struct cmd{ bool type; int a,b; }temp; cmd* data; long long mytic(){ long long result = 0.0; struct timeval tv; gettimeofday( &tv, NULL ); result = ((long long)tv.tv_sec)*1000000 + (long long)tv.tv_usec; return result; } #define dic1() disA(generator) #define dic2() disB(generator) void genData(int a,int b,int c){ mt19937 generator; uniform_int_distribution<int> disA(0,a-1); uniform_int_distribution<int> disB(0,b+c-1); int k=b+c,i=0,j; for(;i<b;++i) data[i].type=0,data[i].a=dic1(),data[i].b=dic1(); for(;i<k;++i) data[i].type=1,data[i].a=dic1(); for(i=0;i<k;++i){ j=dic2(); if(i==j) continue; temp=data[i]; data[i]=data[j]; data[j]=temp; } } void testN(int a,int b,int c){ int k=b+c,i; printf("none opt\n"); long long start=mytic(); djs.init(a); for(i=0;i<k;++i){ switch(data[i].type){ case 0: djs.join(data[i].a,data[i].b); case 1: djs.find(data[i].a); } } start=mytic()-start; printf("Time usage: %lld us\n",start); } void testU(int a,int b,int c){ int k=b+c,i; printf("union by rank opt\n"); long long start=mytic(); djsU.init(a); for(i=0;i<k;++i){ switch(data[i].type){ case 0: djsU.join(data[i].a,data[i].b); case 1: djsU.find(data[i].a); } } start=mytic()-start; printf("Time usage: %lld us\n",start); } void testP(int a,int b,int c){ int k=b+c,i; printf("path compression opt\n"); long long start=mytic(); djsP.init(a); for(i=0;i<k;++i){ switch(data[i].type){ case 0: djsP.join(data[i].a,data[i].b); case 1: djsP.find(data[i].a); } } start=mytic()-start; printf("Time usage: %lld us\n",start); } void testUP(int a,int b,int c){ int k=b+c,i; printf("both opt\n"); long long start=mytic(); djsUP.init(a); for(i=0;i<k;++i){ switch(data[i].type){ case 0: djsUP.join(data[i].a,data[i].b); case 1: djsUP.find(data[i].a); } } start=mytic()-start; printf("Time usage: %lld us\n",start); } int main(){ int a,b,c,i,j,k,l,m,n,N,U,P,UP; data=(cmd*)malloc(200000000*sizeof(cmd)); while(printf("0 to quit> "),scanf("%d",&a),a){ printf("N U P UP\n"); scanf("%d%d%d%d",&N,&U,&P,&UP); printf("set size: "); scanf("%d",&a); printf("op join num: "); scanf("%d",&b); printf("op find num: "); scanf("%d",&c); if(a>100000000||(b+c)>200000000) continue; i=b+c; printf("total %d ops\n", b+c); genData(a,b,c); if(N) testN(a,b,c); if(U) testU(a,b,c); if(P) testP(a,b,c); if(UP) testUP(a,b,c); } free(data); return 0; }
raw data
set size: 500 join operations: 500 find operations: 500 total 1000 ops none opt Time usage: 383 us Union by Rank opt Time usage: 44 us Path Compression opt Time usage: 48 us both opt(s) Time usage: 39 us set size: 10000 join operations: 7000 find operations: 10000 total 17000 ops none opt Time usage: 1159 us Union by Rank opt Time usage: 246 us Path Compression opt Time usage: 274 us both opt(s) Time usage: 245 us set size: 100000 join operations: 70000 find operations: 100000 total 170000 ops none opt Time usage: 134035 us Union by Rank opt Time usage: 3096 us Path Compression opt Time usage: 3291 us both opt(s) Time usage: 2967 us set size: 400000 join operations: 280000 find operations: 400000 total 680000 ops none opt Time usage: 5293524 us Union by Rank opt Time usage: 14099 us Path Compression opt Time usage: 14466 us both opt(s) Time usage: 12509 us set size: 1000000 join operations: 700000 find operations: 1000000 total 1700000 ops Union by Rank opt Time usage: 48863 us Path Compression opt Time usage: 40933 us both opt(s) Time usage: 41501 us set size: 10000000 join operations: 7000000 find operations: 10000000 total 17000000 ops Union by Rank opt Time usage: 882355 us Path Compression opt Time usage: 1118981 us both opt(s) Time usage: 814115 us set size: 100000000 join operations: 70000000 find operations: 100000000 total 170000000 ops Union by Rank opt Time usage: 13025344 us Path Compression opt Time usage: 19499277 us both opt(s) Time usage: 12493686 us
(-O2)
=================================================== set size: 100000000 join operations: 60000000 find operations: 120000000 total 180000000 ops Union by Rank opt Time usage: 10360505 us Path Compression opt Time usage: 11255217 us both opt(s) Time usage: 9452136 us
(-O3) ===================================================== bug fixed: add `break' set size: 100000000 join operations: 60000000 find operations: 120000000 total 180000000 ops Union by Rank opt Time usage: 12039494 us Path Compression opt Time usage: 13178722 us both opt(s) Time usage: 13395750 us set size: 100000000 join operations: 50000000 find operations: 140000000 total 190000000 ops Union by Rank opt Time usage: 11454054 us Path Compression opt Time usage: 10056804 us both opt(s) Time usage: 11322394 us set size: 100000000 join operations: 100000000 find operations: 100000000 total 200000000 ops Union by Rank opt Time usage: 20773924 us Path Compression opt Time usage: 46671332 us both opt(s) Time usage: 19270817 us (-O3)