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
本质上是:人为已经把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\n",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\n";
printf("===================================================================>\n");
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\n", 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\n";
printf("===================================================================>\n");
for (int i = 0; i <= 3; i++)
{
printf("移动硬盘:%d; 普通硬盘:%d; U盘:%d\n", 4*i, 25-7*i, 75+3*i);
}
}