Skip to main content
updated the code and added the description
Source Link
fedja
  • 61.9k
  • 11
  • 160
  • 302

Edit: This is the best and the fastest version. $n$ is gone now and the guaranteed relative precision is $1/N^2$ (the constant $1$ is correct, so if you want $10^{-3}$ accuracy (to compare with Mathematica time), just set $N=34$ and get $10^6$ pairs in under 10 seconds. The time is essentially proportional to $N$. For $10^{-5}$ accuracy $N=340$ and 83 seconds suffice. I'll explain the algorithm a bit later; now it makes sense :-)

Edit 2: The outline of the algorithm.

We shall use the averaging over the projections. If we take the discrete set of $N$ equally spaced lines $L_j$, then the approximate formula is $$ |z|\approx \frac{\pi}{2}\frac 1N\sum_{j=1}^N |P_j z| $$
where $P_j$ is the orthogonal projection operator to the line $L_j$. The relative accuracy of this approximation can be easily computed and is, as I said, $1\pm N^{-2}$. The computation of the average projection is going to be exact.

For each projection, we need to evaluate the convolution of $A(z)=|z|$ with four normalized characteristic functions $F_j$ of intervals $[-U_j,U_j]$ at some point $x$. We arrange $U_j$ in the increasing order, so that $U_0<U_1<U_2<U_3$ and do the honest convolution of the absolute value with the third and the fourth function, so we have an explicit formula for $A*F_2*F_3$, which is a cubic spline with partition points $\pm U_3\pm U_2$. The convolution $F_0*F_1$ is just a linear spline, which, when shifted to $x$, has the partition points $x\pm U_0\pm U_1$. We thus need to integrate the product of the two splines, which is the fourth degree spline with known partition points. This is done by arranging the partition points in the increasing order and applying the 3-node Gauss quadrature on each partition interval in the support of $F_0*F_1$. That's it.

I tried to implement it in the fastest way possible, so some parts may look a bit strange. The function $gghh()$ is essentially the product of $A*F_3*F_2$ and $F_1*F_0$, the function $F()$ does the integration job over a single partition interval (up to a constant) and $D()$ takes care of setting the projections and determining the partition intervals of interest. However, once the idea of the program is clear, you can certainly try to see if your code writing skills are better than mine :-)

Edit: This is the best and the fastest version. $n$ is gone now and the guaranteed relative precision is $1/N^2$ (the constant $1$ is correct, so if you want $10^{-3}$ accuracy (to compare with Mathematica time), just set $N=34$ and get $10^6$ pairs in under 10 seconds. The time is essentially proportional to $N$ For $10^{-5}$ accuracy $N=340$ and 83 seconds suffice. I'll explain the algorithm a bit later; now it makes sense :-)

Edit: This is the best and the fastest version. $n$ is gone now and the guaranteed relative precision is $1/N^2$ (the constant $1$ is correct, so if you want $10^{-3}$ accuracy (to compare with Mathematica time), just set $N=34$ and get $10^6$ pairs in under 10 seconds. The time is essentially proportional to $N$. For $10^{-5}$ accuracy $N=340$ and 83 seconds suffice. I'll explain the algorithm a bit later; now it makes sense :-)

Edit 2: The outline of the algorithm.

We shall use the averaging over the projections. If we take the discrete set of $N$ equally spaced lines $L_j$, then the approximate formula is $$ |z|\approx \frac{\pi}{2}\frac 1N\sum_{j=1}^N |P_j z| $$
where $P_j$ is the orthogonal projection operator to the line $L_j$. The relative accuracy of this approximation can be easily computed and is, as I said, $1\pm N^{-2}$. The computation of the average projection is going to be exact.

For each projection, we need to evaluate the convolution of $A(z)=|z|$ with four normalized characteristic functions $F_j$ of intervals $[-U_j,U_j]$ at some point $x$. We arrange $U_j$ in the increasing order, so that $U_0<U_1<U_2<U_3$ and do the honest convolution of the absolute value with the third and the fourth function, so we have an explicit formula for $A*F_2*F_3$, which is a cubic spline with partition points $\pm U_3\pm U_2$. The convolution $F_0*F_1$ is just a linear spline, which, when shifted to $x$, has the partition points $x\pm U_0\pm U_1$. We thus need to integrate the product of the two splines, which is the fourth degree spline with known partition points. This is done by arranging the partition points in the increasing order and applying the 3-node Gauss quadrature on each partition interval in the support of $F_0*F_1$. That's it.

I tried to implement it in the fastest way possible, so some parts may look a bit strange. The function $gghh()$ is essentially the product of $A*F_3*F_2$ and $F_1*F_0$, the function $F()$ does the integration job over a single partition interval (up to a constant) and $D()$ takes care of setting the projections and determining the partition intervals of interest. However, once the idea of the program is clear, you can certainly try to see if your code writing skills are better than mine :-)

updated the code
Source Link
fedja
  • 61.9k
  • 11
  • 160
  • 302

Edit: I optimized for speed, soThis is the best and the fastest version. $n$ is gone now and the guaranteed relative precision is $0.1\%$ for$1/N^2$ (the constant $1$ is correct, so if you want $10^{-3}$ accuracy (to compare with Mathematica time), just set $N=34$ and get $10^6$ pairs in 20under 10 seconds but the code is somewhat less readable. The average precision on random pairs of rectangles seems to be much higher but the worst case scenariotime is about thatessentially proportional to $N$ For $10^{-5}$ accuracy $N=340$ and 83 seconds suffice. I'll explain the algorithm a bit later; now it makes sense :-)

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

const double pi=3.141592653589;


141592653589, ppi=pi/57.6, pi2=pi/2, dl=sqrt(0.6)/2;

double gggghh(double a, double yb, double c, double d, double x, double t)
{
double g;y=fabs(t), g=y*a, h=2*d;
if(y<=1y<=a-ab) g=(1+y*y+a*aa*a+y*y+b*b/3)/2*0.0;5; 
else
{
g=y;
 if(y<1+ay<a+b) {double z=1+az=a+b-y; g+=z*z*z/12/a;(12*b);}
}
y=fabs(t-x); 
if(y>c-d) h-=(y-c+d);
return g;g*h;
}

double F(double a, double b, double c, double xd, int n)
{
double s1=0x, s2=0;
double hb=(b-c)/n, hc=c/n, x1=x-c-b, x2=x-b+caa, x3=x+b+c;
for(int k=0;double k<=2*n;++kbb) 
{
double y1=fabst2=(x1aa+bb)*0.5, y2=fabs(x2)bbaa=bb-aa, y3=fabs(x3);dt=dl*bbaa;
doublereturn g1=ggbbaa*(gghh(a,y2)b, g2=(gg(ac,y1d,x,t2-dt)+gg+gghh(a,y3))*k;
double cckc=(k%2==0? (k==0 || k==2*n?1:2):4b,c,d,x,t2+dt);
x1+=hc; x2+=hb; x3-=hc;
s1+=cckc*g1; s2+=cckc*g2;
}
return (s1*+1.6*gghh(a,b-,c,d,x,t2)+s2*hc/2)/b/n/6;(a*c*d);
}

double D(double a1,double b1, double c1, double d1, double a2,double b2, double c2, double d2, int N, int n)
{
double s=0.0;
double X1=b1-a1, Y1=d1-c1, X2=b2-a2, Y2=d2-c2, S1=(a2+b2-a1-b1)/2, S2=(c2+d2-c1-d1)/2;

double S=sqrt(S1*S1+S2*S2); 

double t0=pi/2t0=pi2/N, cs=cos(t0), ss=sin(t0), dcs=2*cs*cs-1, dss=2*cs*ss; 
double SS=fabs(S1)+fabs(S2)+fabs(X1)+fabs(X2)+fabs(Y1)+fabs(Y2);
SS*=0.00000001;
for(int k=0; k<N;++k)
{ 
double csnew=cs*dcs-ss*dss;
ss=ss*dcs+cs*dss; cs=csnew;
double U[4]={fabs(X1*cs)+SS, fabs(Y1*ss)+SS, fabs(X2*cs)+SS, fabs(Y2*ss)+SS};
double x=fabsx=-fabs(S1*cs+S2*ss); 


for(int kk=0;kk<3;++kk)
{
int kkk=3-kk;
for(int j=0;j<3;++jj=0;j<kkk;++j)
if(U[j]>U[j+1]) {double u=U[j]; U[j]=U[j+1]; U[j+1]=u;}
}
for(int

double kk=0;kk<4;++kk)U0=U[0], U[kk]+=0.0000000001*(U[3]+S);U1=U[1], 
 U2=U[2], U3=U[3];

double S=U[3]/2;V[4]={-U3-U2,-U3+U2,U3-U2,U3+U2}, 
VV[4]={x/=S;-U1-U0,x-U1+U0,x+U1-U0,x+U1+U0};
for(
double W[8]; 
int j=0;j<4;++ji=0, ii=0, kstart=-1, kfinish=-1;
while(ii<4)
{
++kfinish; U[j]/=S;
if(V[i]<VV[ii]) {W[kfinish]=V[i]; ++i;}
else {W[kfinish]=VV[ii]; if(ii==0) kstart=kfinish; ++ii;} 
s+=S*F}

for(U[2]/2int kk=kstart;kk<kfinish;++kk)
s+=F(U3,U[1]/2U2,U[0]/2U1,U0,x,nW[kk],W[kk+1]);
}
return pi/2*sppi*s/N;
} 



double unitrand()
{
return (rand()+0.0)/RAND_MAX;
}


int main()
{
time_t now=time(0);
srand(now); 

int N=50,n=4;N=1000;

double m=100,M=0;

for(int k=0; k<1000000;++k)
{
if(k%10000==0) {printf("%d %f%.12f %f\n"%.12f\n",k/10000,m,M);}
double 
a1=unitrand(),b1=a1+unitrand(),
a2=unitrand(),b2=a2+unitrand(),
c1=unitrand(),d1=c1+unitrand(),
c2=unitrand(),d2=c2+unitrand();

double r=D(a1,b1,c1,d1,a2,b2,c2,d2,N,n);

if(k%1000==0)
{
r/=D(a1,b1,c1,d1,a2,b2,c2,d2,300,15600);
if(r<m) m=r;
if(r>M) M=r;
}
}
printf("\n%.12f",D(1,2,3,5,4,6,7,8,4000));
printf("\n%f %f""\n%.12f",D(1,2,3,5,4,6,7,8,N,n), /D(1,2,3,5,4,6,7,8,2000,40)-1);
printf("\n%f %f""\n%.12f",D(0,2,0,2,0,2,0,2,N,n), /D(0,2,0,2,0,2,0,2,2000,40)-1);
printf("\n%f %f""\n%.12f",D(0,0,0,2,0,0,0,2,N,n)3, D(0,0.0001,0,2,03,0,0,2,2000.0001,40N)-1);
printf("\n%f %f""\n%.12f",D(0,2,0,0,0,0,0,2,N,n), /D(0,2,0,0,0,0,0,2,2000,40)-1);
printf("\n%f %f""\n%.12f",D(0,0,0,0,3,3,4,4,N,n), D(0,0,0,0,3,3,4,4,2000,40)/5-1);
return 0;
}

Edit: I optimized for speed, so now the guaranteed precision is $0.1\%$ for $10^6$ pairs in 20 seconds but the code is somewhat less readable. The average precision on random pairs of rectangles seems to be much higher but the worst case scenario is about that.

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

const double pi=3.141592653589;




double gg(double a, double y)
{
double g;
if(y<=1-a) g=(1+y*y+a*a/3)/2.0; 
else
{
g=y;
if(y<1+a) {double z=1+a-y; g+=z*z*z/12/a;}
}
return g;
}

double F(double a, double b, double c, double x, int n)
{
double s1=0, s2=0;
double hb=(b-c)/n, hc=c/n, x1=x-c-b, x2=x-b+c, x3=x+b+c;
for(int k=0; k<=2*n;++k) 
{
double y1=fabs(x1), y2=fabs(x2), y3=fabs(x3);
double g1=gg(a,y2), g2=(gg(a,y1)+gg(a,y3))*k;
double cckc=(k%2==0? (k==0 || k==2*n?1:2):4);
x1+=hc; x2+=hb; x3-=hc;
s1+=cckc*g1; s2+=cckc*g2;
}
return (s1*(b-c)+s2*hc/2)/b/n/6;
}

double D(double a1,double b1, double c1, double d1, double a2,double b2, double c2, double d2, int N, int n)
{
double s=0.0;
double X1=b1-a1, Y1=d1-c1, X2=b2-a2, Y2=d2-c2, S1=(a2+b2-a1-b1)/2, S2=(c2+d2-c1-d1)/2;

double S=sqrt(S1*S1+S2*S2);
double t0=pi/2/N, cs=cos(t0), ss=sin(t0), dcs=2*cs*cs-1, dss=2*cs*ss; 


for(int k=0; k<N;++k)
{ 
double csnew=cs*dcs-ss*dss;
ss=ss*dcs+cs*dss; cs=csnew;
double U[4]={fabs(X1*cs), fabs(Y1*ss), fabs(X2*cs), fabs(Y2*ss)};
double x=fabs(S1*cs+S2*ss);

for(int kk=0;kk<3;++kk)
for(int j=0;j<3;++j)
if(U[j]>U[j+1]) {double u=U[j]; U[j]=U[j+1]; U[j+1]=u;}

for(int kk=0;kk<4;++kk) U[kk]+=0.0000000001*(U[3]+S); 
 

double S=U[3]/2; x/=S;
for(int j=0;j<4;++j) U[j]/=S; 

s+=S*F(U[2]/2,U[1]/2,U[0]/2,x,n);
}
return pi/2*s/N;
}

double unitrand()
{
return (rand()+0.0)/RAND_MAX;
}


int main()
{
time_t now=time(0);
srand(now); 

int N=50,n=4;

double m=100,M=0;

for(int k=0; k<1000000;++k)
{
if(k%10000==0) {printf("%d %f %f\n",k/10000,m,M);}
double 
a1=unitrand(),b1=a1+unitrand(),
a2=unitrand(),b2=a2+unitrand(),
c1=unitrand(),d1=c1+unitrand(),
c2=unitrand(),d2=c2+unitrand();

double r=D(a1,b1,c1,d1,a2,b2,c2,d2,N,n);

if(k%1000==0)
{
r/=D(a1,b1,c1,d1,a2,b2,c2,d2,300,15);
if(r<m) m=r;
if(r>M) M=r;
}
}

printf("\n%f %f",D(1,2,3,5,4,6,7,8,N,n), D(1,2,3,5,4,6,7,8,2000,40));
printf("\n%f %f",D(0,2,0,2,0,2,0,2,N,n), D(0,2,0,2,0,2,0,2,2000,40));
printf("\n%f %f",D(0,0,0,2,0,0,0,2,N,n), D(0,0,0,2,0,0,0,2,2000,40));
printf("\n%f %f",D(0,2,0,0,0,0,0,2,N,n), D(0,2,0,0,0,0,0,2,2000,40));
printf("\n%f %f",D(0,0,0,0,3,3,4,4,N,n), D(0,0,0,0,3,3,4,4,2000,40));
return 0;
}

Edit: This is the best and the fastest version. $n$ is gone now and the guaranteed relative precision is $1/N^2$ (the constant $1$ is correct, so if you want $10^{-3}$ accuracy (to compare with Mathematica time), just set $N=34$ and get $10^6$ pairs in under 10 seconds. The time is essentially proportional to $N$ For $10^{-5}$ accuracy $N=340$ and 83 seconds suffice. I'll explain the algorithm a bit later; now it makes sense :-)

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

const double pi=3.141592653589, ppi=pi/57.6, pi2=pi/2, dl=sqrt(0.6)/2;

double gghh(double a, double b, double c, double d, double x, double t)
{
double y=fabs(t), g=y*a, h=2*d;
if(y<=a-b) g=(a*a+y*y+b*b/3)*0.5; 
else if(y<a+b) {double z=a+b-y; g+=z*z*z/(12*b);}

y=fabs(t-x); 
if(y>c-d) h-=(y-c+d);
return g*h;
}

double F(double a, double b, double c, double d, double x, double aa, double bb)
{
double t2=(aa+bb)*0.5, bbaa=bb-aa, dt=dl*bbaa;
return bbaa*(gghh(a,b,c,d,x,t2-dt)+gghh(a,b,c,d,x,t2+dt)+1.6*gghh(a,b,c,d,x,t2))/(a*c*d);
}

double D(double a1,double b1, double c1, double d1, double a2,double b2, double c2, double d2, int N)
{
double s=0.0;
double X1=b1-a1, Y1=d1-c1, X2=b2-a2, Y2=d2-c2, S1=(a2+b2-a1-b1), S2=(c2+d2-c1-d1); 

double t0=pi2/N, cs=cos(t0), ss=sin(t0), dcs=2*cs*cs-1, dss=2*cs*ss; 
double SS=fabs(S1)+fabs(S2)+fabs(X1)+fabs(X2)+fabs(Y1)+fabs(Y2);
SS*=0.00000001;
for(int k=0; k<N;++k)
{ 
double csnew=cs*dcs-ss*dss;
ss=ss*dcs+cs*dss; cs=csnew;
double U[4]={fabs(X1*cs)+SS, fabs(Y1*ss)+SS, fabs(X2*cs)+SS, fabs(Y2*ss)+SS};
double x=-fabs(S1*cs+S2*ss); 


for(int kk=0;kk<3;++kk)
{
int kkk=3-kk;
for(int j=0;j<kkk;++j)
if(U[j]>U[j+1]) {double u=U[j]; U[j]=U[j+1]; U[j+1]=u;}
}


double U0=U[0], U1=U[1], U2=U[2], U3=U[3];

double V[4]={-U3-U2,-U3+U2,U3-U2,U3+U2}, 
VV[4]={x-U1-U0,x-U1+U0,x+U1-U0,x+U1+U0};

double W[8]; 
int i=0, ii=0, kstart=-1, kfinish=-1;
while(ii<4)
{
++kfinish; 
if(V[i]<VV[ii]) {W[kfinish]=V[i]; ++i;}
else {W[kfinish]=VV[ii]; if(ii==0) kstart=kfinish; ++ii;} 
}

for(int kk=kstart;kk<kfinish;++kk)
s+=F(U3,U2,U1,U0,x,W[kk],W[kk+1]);
}
return ppi*s/N;
} 



double unitrand()
{
return (rand()+0.0)/RAND_MAX;
}


int main()
{
time_t now=time(0);
srand(now); 

int N=1000;

double m=100,M=0;

for(int k=0; k<1000000;++k)
{
if(k%10000==0) {printf("%d %.12f %.12f\n",k/10000,m,M);}
double 
a1=unitrand(),b1=a1+unitrand(),
a2=unitrand(),b2=a2+unitrand(),
c1=unitrand(),d1=c1+unitrand(),
c2=unitrand(),d2=c2+unitrand();

double r=D(a1,b1,c1,d1,a2,b2,c2,d2,N);

if(k%1000==0)
{
r/=D(a1,b1,c1,d1,a2,b2,c2,d2,600);
if(r<m) m=r;
if(r>M) M=r;
}
}
printf("\n%.12f",D(1,2,3,5,4,6,7,8,4000));
printf("\n%.12f",D(1,2,3,5,4,6,7,8,N)/D(1,2,3,5,4,6,7,8,2000)-1);
printf("\n%.12f",D(0,2,0,2,0,2,0,2,N)/D(0,2,0,2,0,2,0,2,2000)-1);
printf("\n%.12f",D(0,3,0,0.0001,0,3,0,0.0001,N)-1);
printf("\n%.12f",D(0,2,0,0,0,0,0,2,N)/D(0,2,0,0,0,0,0,2,2000)-1);
printf("\n%.12f",D(0,0,0,0,3,3,4,4,N)/5-1);
return 0;
}
added 602 characters in body
Source Link
fedja
  • 61.9k
  • 11
  • 160
  • 302

Edit: I optimized for speed, so now the guaranteed precision is $0.1\%$ for $10^6$ pairs in 20 seconds but the code is somewhat less readable. The average precision on random pairs of rectangles seems to be much higher but the worst case scenario is about that.

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

const double pi=3.141592653589; 




double Absgg(double xa, double y)
{
double g;
if(x<0y<=1-a) returng=(1+y*y+a*a/3)/2.0; 
else
{
g=y;
if(y<1+a) {double z=1+a-x;y; g+=z*z*z/12/a;}
}
return x;g;
}
 

double F(double a, double b, double c, double x, int n)
{
double s=0;s1=0, s2=0;
double hb=b/2hb=(b-c)/n, hc=c/2/n;
for(intn, kb=0;x1=x-c-b, kb<=2*n;++kb)x2=x-b+c, x3=x+b+c;
for(int kc=0;k=0; kc<=2*n;++kck<=2*n;++k) 
{
double y=Absy1=fabs(x-b/2-c/2+kb*hb+kc*hcx1);
double, g=y;
ify2=fabs(y<1+ax2) g=(, ((y+a)*(y+a)-1)/2.0+y3=fabs(1+a-yx3)/2.0+(1-;
double g1=gg(y-a,y2)*, g2=(y-a)*gg(y-a))/6.0 ,y1)/2.0/a;
if+gg(y<=1-a,y3) g=(1+y*y+a*a/3)/2.0;
*k;
double cckb=(kb==0 || kb==2*n?1/6.0:cckc=(kb%2==0k%2==0?2/6.0:4/6.0)), cckc=(kc==0k==0 || kc==2*nk==2*n?1/6.0:(kc%2==0?2/6.0):4/6.0));
x1+=hc; x2+=hb; x3-=hc;
s+=cckb*cckc*g/n/n;s1+=cckc*g1; s2+=cckc*g2;
}
return s;(s1*(b-c)+s2*hc/2)/b/n/6;
}

double D(double a1,double b1, double c1, double d1, double a2,double b2, double c2, double d2, int N, int n)
{
double s=0.0;
double X1=b1-a1, Y1=d1-c1, X2=b2-a2, Y2=d2-c2, S1=(a2+b2-a1-b1)/2, S2=(c2+d2-c1-d1)/2;

double S=sqrt(S1*S1+S2*S2);
double t0=pi/2/N;N, cs=cos(t0), ss=sin(t0), dcs=2*cs*cs-1, dss=2*cs*ss; 


for(int k=0; k<N;++k)
{ 
double t=t0+k*pi/N;csnew=cs*dcs-ss*dss;
ss=ss*dcs+cs*dss; cs=csnew;
double x1=Abs(X1*cos(t)), y1=Abs(Y1*sinU[4]={fabs(t)X1*cs), x2=Abs(X2*cosfabs(t)Y1*ss), y2=Abs(Y2*sinfabs(t)X2*cs), x=Abs(S1*cos(t)+S2*sinfabs(t)Y2*ss)};
 
double U[4]={x1,x2,y1,y2}x=fabs(S1*cs+S2*ss);

for(int kk=0;kk<2;++kkkk=0;kk<3;++kk)
for(int j=0;j<3;++j)
if(U[j]>U[j+1]) {double u=U[j]; U[j]=U[j+1]; U[j+1]=u;}

for(int kk=0;kk<4;++kk) U[kk]+=0.0000000001*(U[3]+S); 


double S=U[3]/2; x/=S;
for(int j=0;j<4;++j) U[j]/=S; 

s+=S*F(U[2]/2.0,U[0],U[1]/2,U[0]/2,x,n);
}
return pi/2*s/N;
}

double unitrand()
{
return (rand()+0.0)/RAND_MAX;
}


int main()
{
time_t now=time(0);
srand(now); 

int N=50,n=4;

double m=100,M=0;

for(int k=0; k<1000000;++k)
{
if(k%10000==0) {printf("%d %f %f\n",k/10000,m,M);}
double 
a1=unitrand(),b1=a1+unitrand(),
a2=unitrand(),b2=a2+unitrand(),
c1=unitrand(),d1=c1+unitrand(),
c2=unitrand(),d2=c2+unitrand(); 

double r=D(a1,b1,c1,d1,a2,b2,c2,d2,15N,2n);/

if(k%1000==0)
{
r/D=D(a1,b1,c1,d1,a2,b2,c2,d2,48300,715);
if(r<m) m=r;
if(r>M) M=r;
}
}

printf("\n%f %f",D(1,2,3,5,4,6,7,8,15N,2n), D(1,2,3,5,4,6,7,8,2000,2040));
printf("\n%f %f",D(0,2,0,2,0,2,0,2,15N,2n), D(0,2,0,2,0,2,0,2,2000,2040));
printf("\n%f %f",D(0,0,0,2,0,0,0,2,15N,2n), D(0,0,0,2,0,0,0,2,2000,2040));
printf("\n%f %f",D(0,2,0,0,0,0,0,2,15N,2n), D(0,2,0,0,0,0,0,2,2000,2040));
printf("\n%f %f",D(0,0,0,0,3,3,4,4,N,n), D(0,0,0,0,3,3,4,4,2000,40));
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

const double pi=3.141592653589;


double Abs(double x)
{
if(x<0) return -x;
return x;
}
 

double F(double a, double b, double c, double x, int n)
{
double s=0;
double hb=b/2/n, hc=c/2/n;
for(int kb=0; kb<=2*n;++kb)
for(int kc=0; kc<=2*n;++kc) 
{
double y=Abs(x-b/2-c/2+kb*hb+kc*hc);
double g=y;
if(y<1+a) g=( ((y+a)*(y+a)-1)/2.0+(1+a-y)/2.0+(1-(y-a)*(y-a)*(y-a))/6.0 )/2.0/a;
if(y<=1-a) g=(1+y*y+a*a/3)/2.0;

double cckb=(kb==0 || kb==2*n?1/6.0:(kb%2==0?2/6.0:4/6.0)), cckc=(kc==0 || kc==2*n?1/6.0:(kc%2==0?2/6.0:4/6.0));

s+=cckb*cckc*g/n/n;
}
return s;
}

double D(double a1,double b1, double c1, double d1, double a2,double b2, double c2, double d2, int N, int n)
{
double s=0.0;
double X1=b1-a1, Y1=d1-c1, X2=b2-a2, Y2=d2-c2, S1=(a2+b2-a1-b1)/2, S2=(c2+d2-c1-d1)/2;

double t0=pi/2/N;
for(int k=0; k<N;++k)
{
double t=t0+k*pi/N; 
double x1=Abs(X1*cos(t)), y1=Abs(Y1*sin(t)), x2=Abs(X2*cos(t)), y2=Abs(Y2*sin(t)), x=Abs(S1*cos(t)+S2*sin(t));
 
double U[4]={x1,x2,y1,y2};

for(int kk=0;kk<2;++kk)
for(int j=0;j<3;++j)
if(U[j]>U[j+1]) {double u=U[j]; U[j]=U[j+1]; U[j+1]=u;}

double S=U[3]/2; x/=S;
for(int j=0;j<4;++j) U[j]/=S; 

s+=S*F(U[2]/2.0,U[0],U[1],x,n);
}
return pi/2*s/N;
}

double unitrand()
{
return (rand()+0.0)/RAND_MAX;
}


int main()
{
time_t now=time(0);
srand(now); 


double m=100,M=0;

for(int k=0; k<1000000;++k)
{
if(k%10000==0) {printf("%d %f %f\n",k/10000,m,M);}
double 
a1=unitrand(),b1=a1+unitrand(),
a2=unitrand(),b2=a2+unitrand(),
c1=unitrand(),d1=c1+unitrand(),
c2=unitrand(),d2=c2+unitrand();
double r=D(a1,b1,c1,d1,a2,b2,c2,d2,15,2);//D(a1,b1,c1,d1,a2,b2,c2,d2,48,7);
if(r<m) m=r;
if(r>M) M=r;
}

printf("\n%f %f",D(1,2,3,5,4,6,7,8,15,2),D(1,2,3,5,4,6,7,8,2000,20));
printf("\n%f %f",D(0,2,0,2,0,2,0,2,15,2),D(0,2,0,2,0,2,0,2,2000,20));
printf("\n%f %f",D(0,0,0,2,0,0,0,2,15,2),D(0,0,0,2,0,0,0,2,2000,20));
printf("\n%f %f",D(0,2,0,0,0,0,0,2,15,2),D(0,2,0,0,0,0,0,2,2000,20));
return 0;
}

Edit: I optimized for speed, so now the guaranteed precision is $0.1\%$ for $10^6$ pairs in 20 seconds but the code is somewhat less readable. The average precision on random pairs of rectangles seems to be much higher but the worst case scenario is about that.

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

const double pi=3.141592653589; 




double gg(double a, double y)
{
double g;
if(y<=1-a) g=(1+y*y+a*a/3)/2.0; 
else
{
g=y;
if(y<1+a) {double z=1+a-y; g+=z*z*z/12/a;}
}
return g;
}

double F(double a, double b, double c, double x, int n)
{
double s1=0, s2=0;
double hb=(b-c)/n, hc=c/n, x1=x-c-b, x2=x-b+c, x3=x+b+c;
for(int k=0; k<=2*n;++k) 
{
double y1=fabs(x1), y2=fabs(x2), y3=fabs(x3);
double g1=gg(a,y2), g2=(gg(a,y1)+gg(a,y3))*k;
double cckc=(k%2==0? (k==0 || k==2*n?1:2):4);
x1+=hc; x2+=hb; x3-=hc;
s1+=cckc*g1; s2+=cckc*g2;
}
return (s1*(b-c)+s2*hc/2)/b/n/6;
}

double D(double a1,double b1, double c1, double d1, double a2,double b2, double c2, double d2, int N, int n)
{
double s=0.0;
double X1=b1-a1, Y1=d1-c1, X2=b2-a2, Y2=d2-c2, S1=(a2+b2-a1-b1)/2, S2=(c2+d2-c1-d1)/2;

double S=sqrt(S1*S1+S2*S2);
double t0=pi/2/N, cs=cos(t0), ss=sin(t0), dcs=2*cs*cs-1, dss=2*cs*ss; 


for(int k=0; k<N;++k)
{ 
double csnew=cs*dcs-ss*dss;
ss=ss*dcs+cs*dss; cs=csnew;
double U[4]={fabs(X1*cs), fabs(Y1*ss), fabs(X2*cs), fabs(Y2*ss)};
double x=fabs(S1*cs+S2*ss);

for(int kk=0;kk<3;++kk)
for(int j=0;j<3;++j)
if(U[j]>U[j+1]) {double u=U[j]; U[j]=U[j+1]; U[j+1]=u;}

for(int kk=0;kk<4;++kk) U[kk]+=0.0000000001*(U[3]+S); 


double S=U[3]/2; x/=S;
for(int j=0;j<4;++j) U[j]/=S; 

s+=S*F(U[2]/2,U[1]/2,U[0]/2,x,n);
}
return pi/2*s/N;
}

double unitrand()
{
return (rand()+0.0)/RAND_MAX;
}


int main()
{
time_t now=time(0);
srand(now); 

int N=50,n=4;

double m=100,M=0;

for(int k=0; k<1000000;++k)
{
if(k%10000==0) {printf("%d %f %f\n",k/10000,m,M);}
double 
a1=unitrand(),b1=a1+unitrand(),
a2=unitrand(),b2=a2+unitrand(),
c1=unitrand(),d1=c1+unitrand(),
c2=unitrand(),d2=c2+unitrand(); 

double r=D(a1,b1,c1,d1,a2,b2,c2,d2,N,n);

if(k%1000==0)
{
r/=D(a1,b1,c1,d1,a2,b2,c2,d2,300,15);
if(r<m) m=r;
if(r>M) M=r;
}
}

printf("\n%f %f",D(1,2,3,5,4,6,7,8,N,n), D(1,2,3,5,4,6,7,8,2000,40));
printf("\n%f %f",D(0,2,0,2,0,2,0,2,N,n), D(0,2,0,2,0,2,0,2,2000,40));
printf("\n%f %f",D(0,0,0,2,0,0,0,2,N,n), D(0,0,0,2,0,0,0,2,2000,40));
printf("\n%f %f",D(0,2,0,0,0,0,0,2,N,n), D(0,2,0,0,0,0,0,2,2000,40));
printf("\n%f %f",D(0,0,0,0,3,3,4,4,N,n), D(0,0,0,0,3,3,4,4,2000,40));
return 0;
}
Source Link
fedja
  • 61.9k
  • 11
  • 160
  • 302
Loading