一个小小的算法题

写在前面
我们对一个算法优化的操作,除了是对思维角度的改变,还有针对具体情况进行分类细化。

/*题目:一个整数,它加上100后是一个完全平方数,在加上168又是一个完全平方数,请该数为多少?
现在一共有两个条件:1.加上100是一个数的苹方 2.加上268又是一个数的平方
所以,最大范围就是他俩是相邻俩个数的苹方 得到式子(x+1)^2 -x^2 =168;(x+1)^2 =num+268;x^2 =num+100;
解得x=83.5,所以取x为84,得到num=6956;所以在6956范围内找这个数
*/

这边我是用js来写的。

解法一

var timestamp1 = (new Date()).valueOf();
    var x,y;
    for(var num=0;num<=6956;num++){
      x=Math.sqrt(num+100);
      y=Math.sqrt(num+268);
      if(x*x==num+100&&y*y==num+268){
        var str=/\./;
        x=String(x);
        y=String(y);
       if(x.match(str)==null&&y.match(str)==null){
         console.log(num);
       }
        } 
      }
      var timestamp2 = (new Date()).valueOf();
      var consume1 = timestamp2-timestamp1
      console.log("消耗时间:"+consume1)

这个解法很明显,就是遍历6956,然后这个数加上100和268,再平方根,取整,验证这两个整数。这个想法是从题目本身出发,每一个数的循环,然后找到四个数,下面是运行结果,
在这里插入图片描述
但是仔细分析一个这个解法,循环了七千次,而我们的答案只有三个,绝大多数是无效的循环,

解法二

var timestamp7 = (new Date()).valueOf();
var count = 0;
      for(let i = 84;i>=10;i--){
        for(let j = i-1;j>=1;j--){
			count++;
         	 if((i*i-j*j)==168)
           		 console.log(j*j-100)
        }
      }
      var timestamp8 = (new Date()).valueOf();
      var consume4 = timestamp8-timestamp7
      console.log("消耗时间:"+consume4)
	  console.log("循环次数:"+count)

这个解法的出发角度是从两个平方数本身出发,两个平方数是相差168的,我们约定大数为A,小数为B,只要将两数排列组合一下,只要两数相差168就符合条件,
在这里插入图片描述
计算结果如下,多出一个数是因为上面一个解法没有把负数包含进去,可以看到这里用的时间减小了4倍,这里循环的次数也大概是3500次,减少了大概一半,并且里面不需要平方根操作,所以时间减少了很多
在这里插入图片描述

解法三

仔细分析上面的算法,在第二个循环中,如果打印出了答案,那么接下来的循环就是没有必要的,就可以直接进入下一个i循环,
在这里插入图片描述

 var count1 = 0;
	  var timestamp3 = (new Date()).valueOf();
      for(let i = 84;i>=10;i--){
        for(let j = i-1;j>=1;j--){
			count1++;
          if((i*i-j*j)<168)
            continue;
          if((i*i-j*j)==168)
            console.log(j*j-100)
          if((i*i-j*j)>168)
            j=0
        }
      }
      var timestamp4 = (new Date()).valueOf();
      var consume2 = timestamp4-timestamp3
      console.log("消耗时间:"+consume2)
	  console.log("循环次数:"+count1)

运行结果如下所示,可以看到循环次数大大的减小了,理论上时间会缩短不到十倍,

在这里插入图片描述

解法四

解法三是将正解之后的无用循环去掉,那么再想想,正解之前的循环是不是也可以去掉呢?因为A是递减循环的,所以我们设A的值为x1,然后B循环了y1次发现差值等于168,我们记录下这个y1,然后A的值为X2,然后循环了y2次发现差值等于168,记录y2。所以得到以下结果:

x1>x2
x1>y1>0
x2>y2>0
x1^2-(x1-y1)^2=x2^2-(x2-y2)^2
证:y1<y2

在这里插入图片描述
所以说在每次循环之前也是有好多次是肯定是找不到正解的,所以也需要在循环之前就快速定位到正解的大概区间

在这里插入图片描述
代码如下:

var timestamp5 = (new Date()).valueOf();
var count2 = 0
var reduce=1
      for(let i = 84;i>=1;i--){
        for(let j = i-reduce;j>=1;j--){
		count2++
          if((i*i-j*j)<168){
            continue;
          }else
          if((i*i-j*j)==168){
            reduce=i-j-1;
            console.log(j*j-100)
            j=0
          }else
          if((i*i-j*j)>168){
            reduce=i-j-1;
            j=0
          }
        }
      }
      var timestamp6 = (new Date()).valueOf();
      var consume3 = timestamp5-timestamp6
      console.log("消耗时间:"+consume3)
	  console.log("循环次数:"+count2)

可以看到结果是只循环了155次,减少了一小半的时间,也就是说,每次j循环两轮就可以判断是否有解,

在这里插入图片描述

总结

在分析一个算法的时候,进入算法内部,分析哪些操作是每次都去做而没用的,就需要去掉,而最后一种解法可以称之为动态规划,记忆每次循环的次数,为下次循环的最小次数。

最后

在这85个中其实也只有四个是有正解的,能不能把这85个也精简呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小丸子呢

致力于源码分析,期待您的激励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值