为了解决高中留下的一些整数分解问题而进行必要的学习
为了解决高中留下的一些整数分解问题而进行必要的学习
🕹️来源于一道题having a lunch(想了好久的做不出)
这是三元姐姐区(习题区名称)的一道题,而且还给出了解释:
作者写成了这样,发现测试用例也只有前面两例通过,后面的用例由于复杂度太高了根本解不出来:
->到这里运行
#include<stdio.h>
long long kinds(int N1) {
long long cnt = 0;
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 3; k++) {
for (int l = 0; l <= N1 + 1; l++) {
for (int m = 0; m <= N1 / 2 + 1; m++) {
for (int n = 0; n <= N1 / 3 + 1; n++) {
if ( i + j + k + l + 2 * m + 3 * n == N1) {
cnt++;
// printf("(%d,%d,%d,%d,%d,%d)\n", i, j, k, l, m, n);
}
}
}
}
}
}
}
// for(long long n=0;n<=N1/3;n++){
// for(long long m=0;m<=N1/2;m++){
// for(long long l=0;l<=N1+6;l++){
// if(l + 2 * m + 3 * n == N1){
// if(l==0||l==N1+6){
// cnt+=1;
// }else if(l==1||l==N1+5){
// cnt+=4;
// }else if(l==2||l==N1+4){
// cnt+=9;
// }else if(l==3||l==N1+3){
// cnt+=15;
// }else if(l==4||l==N1+2){
// cnt+=20;
// }else if(l==5||l==N1+1){
// cnt+=23;
// }else if(l>=6||l<=N1){
// cnt+=24;
// }
// }
// }
// }
// }
return cnt;
}
int main() {
int N1 = 0, N2 = 0;
scanf("%d%d", &N1, &N2);
printf("%lld %lld\n", kinds(N1), kinds(N2));
// int N = 1000;
// printf("%lld", kinds(N));
return 0;
}
后来想办法写成三个,结果还是无济于事
->到这里运行
#include<stdio.h>
long long kinds(int N1) {
long long cnt = 0;
// for (int i = 0; i <= 1; i++) {
// for (int j = 0; j <= 2; j++) {
// for (int k = 0; k <= 3; k++) {
// for (int l = 0; l <= N1 + 1; l++) {
// for (int m = 0; m <= N1 / 2 + 1; m++) {
// for (int n = 0; n <= N1 / 3 + 1; n++) {
// if ( i + j + k + l + 2 * m + 3 * n == N1) {
// cnt++;
//// printf("(%d,%d,%d,%d,%d,%d)\n", i, j, k, l, m, n);
// }
// }
// }
// }
// }
// }
// }
for(long long n=0;n<=N1/3;n++){
for(long long m=0;m<=N1/2;m++){
for(long long l=0;l<=N1+6;l++){
if(l + 2 * m + 3 * n == N1){
if(l==0||l==N1+6){
cnt+=1;
}else if(l==1||l==N1+5){
cnt+=4;
}else if(l==2||l==N1+4){
cnt+=9;
}else if(l==3||l==N1+3){
cnt+=15;
}else if(l==4||l==N1+2){
cnt+=20;
}else if(l==5||l==N1+1){
cnt+=23;
}else if(l>=6||l<=N1){
cnt+=24;
}
}
}
}
}
return cnt;
}
int main() {
int N1 = 0, N2 = 0;
scanf("%d%d", &N1, &N2);
printf("%lld %lld\n", kinds(N1), kinds(N2));
// int N = 1000;
// printf("%lld", kinds(N));
return 0;
}
原因如下:(测试用例)
输入 | 输出 |
---|---|
4 96 | 34 18434 |
5 6 | 52 74 |
1009876 7 | 2039699070754 100 |
1000000000 776543 | 2000000000000000002 1206038061700 |
900500400 999500400 | 1621801940800320002 1998002099200320002 |
后面三个数据太大,用三个for显然不正确,复杂度太高;
经过一系列的查找,了解到有生成函数这个东西,就是与标题所说有关的整数分解问题,然后自己后来的想法是这样的,先构造一个生成函数:
\[f(x)=(x^0+x^1)(x^0+x^1+x^2)(x^0+x^1+x^2+x^3)(\sum_{a=0}^{inf} x^a)(\sum_{b=0}^{inf} x^{2b})(\sum_{c=0}^{inf} x^{3c})
\]
因为我们需要得到的是\(x^n\)的系数,所以理所当然地想到求n次导,即系数K为
\[K=\frac{f^{(n)}(0) }{n!}
\]
但问题也随之而来,在C语言中自己没学过如何求导。。。
后面学指答疑群(一个知识交流群)上面的大佬给出解释,再根据自己的理解写一下
大佬说:第一行是根据生成函数写出来的,第一步化简是麦克劳林 ,第二步是公因式 ,第三步是组合数, 最后一步就是得出答案了
🚶自己理解
由排列组合的知识,我们可以知道,此问题可转化为:
求下式中\(x^n\)的系数,
\[(x^0+x^1)(x^0+x^1+x^2)(x^0+x^1+x^2+x^3)(\sum_{i=0}^{inf} x^i)(\sum_{i=0}^{inf} x^{2i})(\sum_{i=0}^{inf} x^{3i})
\]
自己先自行初步化解:
\[(x^0+x^1)(x^0+x^1+x^2)(x^0+x^1+x^2+x^3)(\sum_{i=0}^{inf} x^i)(\sum_{i=0}^{inf} x^{2i})(\sum_{i=0}^{inf} x^{3i})\\
=(x^0+x^1)(x^0+x^1+x^2)(x^0+x^1+x^2+x^3)\lim_{i \to \inf} (\frac{1-x^{i+1}}{1-x})(\frac{1-x^{2i+2}}{1-x^2})(\frac{1-x^{3i+3}}{1-x^3})\\
=(x^3+x^2+x+1)*(\sum_{i=0}^{inf} x^i)^3(\lim_{i \to 0} (1+x^{i+1} )(1+x^{i+1}x^{i+2}))\]
做到这一步发现自己不会做了,
然后发现一个问题就是,由麦克劳林公式可得
\[\frac{1}{1-x}=(\sum_{i=0}^{inf} x^i)\\
\frac{1}{1-x^{2}}=(\sum_{i=0}^{inf} x^{2i})\\
\frac{1}{1-x^{3}}=(\sum_{i=0}^{inf} x^{3i})\\\]
则有返回上面的重新计算
\[(x^0+x^1)(x^0+x^1+x^2)(x^0+x^1+x^2+x^3)(\sum_{i=0}^{inf} x^i)(\sum_{i=0}^{inf} x^{2i})(\sum_{i=0}^{inf} x^{3i})\\\\
=\frac{(x^0+x^1)(x^0+x^1+x^2)(x^0+x^1+x^2+x^3)}{(1-x)(1-x^2)(1-x^3)}\\\\
=\frac{(x^3+x^2+x+1)}{(1-x)^{3}}\\\\
=(x^3+x^2+x+1)*(\sum_{i=0}^{inf} x^i)^3\]
下面的多项式相乘建议画一下就可以明白,
\[(\sum_{i=0}^{inf} x^i)^2=\sum_{i=0}^{inf} (i+1)x^i
\]
\[(\sum_{i=0}^{inf} x^i)^3=(\sum_{i=0}^{inf} x^i)^2*(\sum_{i=0}^{inf} x^i)=(\sum_{i=0}^{inf} (i+1)x^i)*(\sum_{i=0}^{inf} x^i)=\sum_{i=0}^{inf} \frac{(1+i)*(2+i)}{2}x^i
\]
则
\[(x^3+x^2+x+1)*(\sum_{i=0}^{inf} x^i)^3\\
=(x^3+x^2+x+1)*(\sum_{i=0}^{inf} \frac{(1+i)*(2+i)}{2}x^i)\]
又需要满足n>=4,我们可以通过下面这种形式计算:
则有x^n的系数K刚好为
\[K=\frac{(n+2)*(n+1)}{2}+\frac{(n)*(n+1)}{2}+\frac{(n)*(n-1)}{2}+\frac{(n-1)*(n-2)}{2}
=2(n^2+1)\]
最终代码,虽然写的很简洁,但是下面这段代码背后的很多东西都是通过数学推导出来的;
->到这里运行
#include<stdio.h>
int main() {
long long N1 = 0, N2 = 0;
scanf("%lld%lld", &N1, &N2);
printf("%lld %lld\n", 2*(N1*N1+1), 2*(N2*N2+1));
return 0;
}
最后的最后,还是得感谢群里的大佬们🌹🌹🌹
另外还想分享一种方法:从测试用例找规律
Cross-validation(同时也不失为一种好方法来充分利用乐学系统来验证数学模型的正确性🐶)
我们其实还可以利用matlab来对我们可以输出的代码结果进行拟合,从而达到通过小数据预测大数据的效果,在matlab命令行中输入如下语句:
>> x=[4 5 6 7 8 9 11 16 50 99];
>> y=[34 52 74 100 130 164 244 514 5002 19604];
>> cftool
在图左方我们同样可以得到拟合曲线的公式是\(2*x^2+2\),需要注意的是图上方的拟合模型和类型要选对,这样我们可以通过仅仅一个公式的计算便解决了计算机需要遍历的次数问题(虽然还有大佬说这是小学奥数问题但我真不会🥀)
打算接下来学一下生成函数再写一篇文章吧。