需求:
求第1500个只有2,3,5因子的数
数是从小到大排列
第一个数是1,1=2^0*3^0*5^0
//execute result:
//pos:1500 ==> ans:(2^17*3^8*5^0)
//Calculate time estimate < 172/1000,000 s
//Press any key to continue
//any other infomation: http://www.ohyee.net
#include <stdio.h>
#include <math.h>
#include <windows.h>
const int factor[3]={2,3,5};
int nPos = 1500;
double Cartan(double a, double b, double c)
{
double t1;
t1 = pow(36.*b*a-108.*c-8.*a*a*a+12*sqrt(12.*b*b*b-3.*b*b*a*a-54.*b*a*c+81.*c*c+12.*c*a*a*++a), 1./3.);
return (t1-(12.*b-4.*a*a)/t1-2.*a)/6.;
}
int main()
{
double fWei[3], fmin, fmin1, fTmp, fTmp2;
double a,b,c,t;
int cnt, n, i, j, k, i0, j0, k0, i1,j1,k1;
int repeat;
unsigned long dTic;
for(i=0; i<3; i++)
{
fWei[i] = log(factor[i]);
}
n = nPos;
dTic = GetTickCount();
for(repeat=1; repeat<1000; repeat++, n = nPos)
do
{
cnt = 0;
a=fWei[0]+fWei[1]+fWei[2];
b=fWei[0]*fWei[1]+fWei[0]*fWei[2]+fWei[1]*fWei[2];
c=(1.-3.*(n+.5))*fWei[0]*fWei[1]*fWei[2];
t = Cartan(a,b,c);
a = t/fWei[0];
fmin1 = fmin = n*3+10.;
for(i=0; i<=a; i++)
{
fTmp = t-i*fWei[0];
b = fTmp/fWei[1];
for(j=0; j<=b; j++)
{
fTmp2 = fTmp - j*fWei[1];
k = (int)(fTmp2/fWei[2]);
cnt += k+1;
fTmp2 = fTmp2 - k*fWei[2];
if(fmin > fTmp2)
{
fmin = fTmp2;
i0=i;j0=j;k0=k;
}
else if(fmin1 > fTmp2)
{
fmin1 = fTmp2;
i1=i;j1=j;k1=k;
}
}
}
if(cnt<nPos)
{
n += (nPos-cnt)*1.3;
continue;
}
if(cnt==nPos)
{
break;
}
else if(cnt==nPos+1)
{
i0=i1;j0=j1;k0=k1;
break;
}
else
{
printf("sorry, can't find:(");
return -1;
}
} while(1);
dTic = GetTickCount()-dTic;
printf("pos:%d ==> ans:(2^%d*3^%d*5^%d)\n", nPos, i0,j0,k0);
printf("Calculate time estimate < %d/1000,000 s\n", dTic);
return 0;
}
ax+by+cz < L
不失一般性,假设 0<a<b<c;x,y,z属于R;
其中的满足表达式的x,y,z个数可以认为A平面ax+by+cz=L,x=0,y=0,z=0四个平面包围的体积是
同步变化的。所以就有方程 (x0+1)(y0+1)(z0+1)=P*6, 其中x0=L/a,y0=L/b,z0=L/c. 而P则是
体积值,也就是要求的目标数值(题目中为1500)。将上面两式联合求解,就可以得到x0,y0,z0.求解
该方程使用了标准的一元三次方程求解公式:卡尔丹公式
知道x0,y0,z0就可以知道最接近这个A平面的点(X,Y,Z)的具体数值了,我这用的是遍历,而这个点就是
最终所求的三个幂指数数值。
实际上我并没有用真正的体积公式,而是把P*6改成了P*3,然后根据误差把P放大,逐步贴近真实数值。
这种做法到底能有多大好处,我也没有进行严格推导,只是有一点可以肯定的,不用开过多的空间来
存储中间变量和做过多的排序了。