1、今天遇到了一道有意思的数学题:
政府采购一批IT配件,总预算10000元,条件如下:
- 移动硬盘每个500,普通硬盘每个300,U盘3个100;
- 所有配件个数加起来100个,并且钱刚好花完;
问题来了: 移动硬盘、普通硬盘、U盘分别买多少个?
这里原理并不复杂,用a、b、c分别表示3种设备的数量,得到如下等式:
a+b+c=100;
a*500+b*300+c*100/3 = 10000;
2、根据上述的限制条件,普通的开发人员不动脑立刻就能想到for循环的解决办法如下:
这么做的时间复杂度是0(N^3);这里还好每次循环只有100次,所以3次循环一共只有10^6次。如果每次循环1000次了,3层嵌套就是10^9=1亿次了,电脑明显会卡顿。如果这样面试大厂,分分钟被面试官教育人生,offer想都不用想了!
3、第一种优化思路:移动硬盘500元一个,10000元最多买20个;普通硬盘300元一个,10000元最多买33个;U盘100元3个,那么U盘个数肯定是3的倍数,换句话说除以3余数是0,所以新的表达式如下:
a+b+c=100;
a*500+b*300+c*100/3 = 10000;
其中: 0=<a<=20; 0=<b<=33; c%3=0&c>=0; 根据这种思路的代码如下:
这次结果是对的,但是循环次数已经降到660次,时间复杂度是O(n^2),少了一个数量级;这种代码普通电脑计算时已经毫无压力!
4、其实还有优化空间,过程推导如下:
- a+b+c=100; ----- 表达式1
- a*500+b*300+c*100/3 = 10000; ----- 表达式2
- 表达式2两边同时除以100,乘以3得到:15a+9b+c=300; -----表达式3
- 由表达式1得到:c=100-a-b, 带入表达式3替换c,最终得到:7a+4b=100 => b = 25-7*a/4;-----表达式4
- 因为b必须是正整数,所以a必须是4的倍数,并且a<=12, 这里记作a=4*i(0=<i<=3);
- 所以b=25-7*(4*i)/4=25-7*i
- 又所以c=100-a-b=100-4*i-(25-7*i)=75+3*i
所以整个代码优化如下:这里只循环4次就搞定了! 本质上是:人为已经把a、b、c的取值计算方式想好,只是通过计算机落实而已!前面两种方式纯粹是通过计算机的算力暴力求解,后面这种是人为优化解法!
循环次数进一步降到4次,时间复杂度降低到O(n);空间复杂度降低到O(1);
完整代码如下:
#include <iostream> #include <ctime> #include <chrono> using std::chrono::high_resolution_clock; using std::chrono::milliseconds; int main() { high_resolution_clock::time_point beginTime1 = high_resolution_clock::now(); for (int a=0; a<=100;a++) { for (int b=0;b<=100;b++) { for (int c = 0; c <= 100; c++) { if ((a*500+b*300+c*100/3 == 10000)&&(a+b+c==100)) { printf("移动硬盘:%d; 普通硬盘:%d; U盘:%d ",a,b,c); } } } } high_resolution_clock::time_point endTime1 = high_resolution_clock::now(); milliseconds timeInterval1 = std::chrono::duration_cast<milliseconds>(endTime1 - beginTime1); std::cout << "运行耗时:"<< timeInterval1.count() << "ms "; printf("===================================================================> "); high_resolution_clock::time_point beginTime2 = high_resolution_clock::now(); for (int a = 0; a <= 10000/500; a++) { for (int b = 0; b <= 10000/300; b++) { int c = 100 - a - b; if (c % 3 != 0) continue; if (a * 500 + b * 300 + c * 100 / 3 == 10000) { printf("移动硬盘:%d; 普通硬盘:%d; U盘:%d ", a, b, c); } } } high_resolution_clock::time_point endTime2 = high_resolution_clock::now(); milliseconds timeInterval2 = std::chrono::duration_cast<milliseconds>(endTime2 - beginTime2); std::cout << "运行耗时:" << timeInterval2.count() << "ms "; printf("===================================================================> "); for (int i = 0; i <= 3; i++) { printf("移动硬盘:%d; 普通硬盘:%d; U盘:%d ", 4*i, 25-7*i, 75+3*i); } }