#include #include #include #include #include /****** Algorithms for optimum psi voting. Warren D. Smith, Nov. 2015. Compile with gcc -Wall -O6 CleanOptPRVote.c (also -DNDEBUG if want to turn off asserts) Run with time ./a.out *******/ #define real double #define uint8 uint8_t #define uint32 uint32_t #define uint64 uint64_t #define uint unsigned int #define ln(x) log(x) #define Rand01() drand48() #define SeedRand(x) srand48(x) #define PI 3.14159265358979323846 #define EulerMasch 0.57721566490153286061 #define Zeta2 1.64493406684822643647 /* PI^2 / 6 */ #define Zeta3 1.20205690315959428540 #define Zeta4 1.08232323371113819152 /* PI^4 / 90 */ #define AbsPsiHalf 1.96351002602142347944 /* -Psi(1/2) = EulerMasch+ln(4) */ #define LN4 1.38629436111989061883 /* ln(4) */ /************ Simple algorithm to compute Psi(x) accurate to within +-1e-15 for any x>0 (except that the discreteness of the floating point computer-representable subset of real numbers prevents any method from achieving that accuracy for very small x). Worst(?) accuracy at x=0.25. If one also wanted x<0 (which we, for election purposes, do not) then one could use the reflection identity Psi(x)=Psi(1-x)-PI*cot(PI*x) to get there. *****************/ real PsiAcc(real x){ // psi(x) = Gamma'(x) / Gamma(x). We ASSUME x>0. real p, y, r; assert(x>0.0); if(x < 0.000286648){ return((Zeta2-x*(Zeta3-x*Zeta4))*x-EulerMasch-1.0/x); } for(y=x, p=0.0; y<14.9; y += 1.0){ p -= 1.0/y; } r = 0.5/y; p += ln(y) - r; r *= r; p -= r * (1/3.0 - r * (2/15.0 - r * (16/63.0 - r * (16/15.0 - r * 256/33.0)))); return(p); } real FsimmonsAcc(real x){ // [Psi(x+1/2)-Psi(1/2)]/2. Assumes x>=0. Accuracy +-4e-16. real p, y, r; assert(x>=0.0); for(y=x+0.5, p=AbsPsiHalf; y<14.9; y += 1.0){ p -= 1.0/y; } r = 0.5/y; p += ln(y) - r; r *= r; p -= r * (1/3.0 - r * (2/15.0 - r * (16/63.0 - r * (16/15.0 - r * 256/33.0)))); return(0.5*p); } #define C1 0.244417244017665 #define C2 0.133307727351213 #define C3 0.333333316834594 #define C4 3.79344885805465e-12 /************ Simple algorithm to compute Psi(x) accurate to within +-9e-12 for any x>=0.0001 and accurate to within +-7e-12 for any x>=0.0003. Worst(?) accuracy at x=0.644504 and x=0.000212217556989 and x=0.01. PsiFast for uniform random x in [0,5] runs in 120 nanoseconds (240 cycles) average time on 2GHz iMac. *****************/ real PsiFast(real x){ // psi(x) = Gamma'(x) / Gamma(x). We ASSUME x>0. real p, y, r; if(x < 0.00017){ return((Zeta2-Zeta3*x)*x-EulerMasch-1.0/x); } p=C4; for(y=x; y<7.01; y += 1.0){ p = p - 1.0/y; } r = 0.5/y; p = p + ln(y) - r; r = r*r; p = p - r*((C1*r-C2)*r+C3); return(p); } real FsimmonsFast(real x){ // psi(x) = Gamma'(x) / Gamma(x). We ASSUME x>0. Accuracy +-4e-12. real p, y, r; assert(x>=0.0); p=C4+AbsPsiHalf; for(y=x+0.5; y<7.01; y += 1.0){ p = p - 1.0/y; } r = 0.5/y; p = p + ln(y) - r; r = r*r; p = p - r*((C1*r-C2)*r+C3); return(0.5*p); } #undef C1 #undef C2 #undef C3 #undef C4 real Ftab[512]; /********* For even greater accuracy, the Ftab could be precomputed to accuracy exceeding full machine accuracy, and simply typed in. For example, here are the values of Fsimmons(j/9) for j=0,1,2,...,9 to very high accuracy, and values for greater j could be got using the shift-by-1 recurrence F(x+1)=F(x)+1/(2*x+1): j Fsimmons(j/9) *******************************/ #define FK0 0 #define FK1 0.231344398261451616064873648180400480003643139525917758784844 #define FK2 0.402461181265483029158748201667014700281522437624437363175421 #define FK3 0.536390306674581119399124765539722520727431767702493342876392 #define FK4 0.645501525962868133052710550655173858364905640019988029663089 #define FK5 0.737072875488692532109319255818931414457565419789726902211828 #define FK6 0.815691260323254343508007379076488922301332395563382479521565 #define FK7 0.884406562797259071986922242399804666842510686827450062579203 #define FK8 0.945335576927708646448801110074017809479387778256144709861120 #define FK9 1 real FK[] = {FK0,FK1,FK2,FK3,FK4,FK5,FK6,FK7,FK8,FK9}; void CreateFtable(uint TOPSCORE){ uint j; real scal=1.0/TOPSCORE; for(j=0; j<512; j++){ Ftab[j] = FsimmonsAcc(j*scal); } } /***** record error for doubling PsiFast test: 1.30416e-11 at x=10.000033540032444 record error for doubling PsiFast test: 4.65661e-10 at x=0.000000151773634 record error for shift-by-1 PsiFast test: 1.20299e-11 at x=7.010000308187760 record error for shift-by-1 PsiFast test: 5.05734e-10 at x=0.000000115166010 record error for PsiAcc-PsiFast test: 7.27596e-12 at x=0.000170964996759 record error for PsiAcc-PsiFast test: 7.27596e-12 at x=0.000026208026647 record error for PsiAcc-PsiFast test: 8.18545e-12 at x=0.000212217556989 Psi function testing done (300000000 tests of domains x>0 and x>0.0001) time 73 seconds record error for doubling PsiFast test: 1.27471e-11 at x=0.005113554019236 record error for doubling PsiFast test: 1.27471e-11 at x=0.005046858460489 record error for shift-by-1 PsiFast test: 1.106e-12 at x=0.000382260723211 record error for shift-by-1 PsiFast test: 1.43818e-12 at x=0.000463437671663 record error for shift-by-1 PsiFast test: 1.49047e-12 at x=0.000318528751742 record error for PsiAcc-PsiFast test: 6.92069e-12 at x=0.010153554194734 record error for PsiAcc-PsiFast test: 6.9349e-12 at x=0.010033532819176 Psi function testing done (300000000 tests of domain 0.0003=rec1){ rec1=e1; printf("record error for shift-by-1 PsiFast test: %12g at x=%.15f\n", rec1, x); } if(e2>=rec2){ rec2=e2; printf("record error for doubling PsiFast test: %12g at x=%.15f\n", rec2, x); } if(e3>=rec3){ rec3=e3; printf("record error for PsiAcc-PsiFast test: %12g at x=%.15f\n", rec3, x); } } printf("Psi function testing done (%u tests of domain %g Qrecord){ //winner set with highest quality yet seen: Qrecord = Q; //print Q then the indices of the winners: printf("%16.8f:", Q); for(k=0; k 0 ){ prv=scc=x[0]; scc--; x[0]=scc; goto R2; }else{ j=1; goto R5; } } R4: if(x[j] >= j+1){ prv=x[j]; x[j]=x[j-1]; scc=j-1; x[j-1]=scc; goto R2; }else{ j++; } R5: if(x[j]+1 < x[j+1]){ prv=x[j-1]; x[j-1]=x[j]; scc=x[j]; scc++; x[j]=scc; goto R2; } else{ j++; if(j=0); assert(S[k]<512); Q += Ftab[ S[k] ]; } if(Q > Qrecord){ //winner set with highest quality yet seen: Qrecord = Q; //print Q then the indices of the winners: printf("%16.8f:", Q); for(k=0; k 0 ){ prv=scc=x[0]; scc--; x[0]=scc; goto R2; }else{ j=1; goto R5; } } R4: if(x[j] >= j+1){ prv=x[j]; x[j]=x[j-1]; scc=j-1; x[j-1]=scc; goto R2; }else{ j++; } R5: if(x[j]+1 < x[j+1]){ prv=x[j-1]; x[j-1]=x[j]; scc=x[j]; scc++; x[j]=scc; goto R2; } else{ j++; if(j