二分法

二分法的概念

在数学中,[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法。

二分法的引入

二分法作为一个常用的算法,我们可以先通过一个简单的题目进行引入:
在这里插入图片描述
题目是这样的,一组数按照升序进行排序,要求是,在这一组数据中,我么要找到目标数字target,正常的情况下,我们肯定会利用循环的方法,将数据进行遍历,从中找到满足条件的数据,从而输出,但是这样做的话,如果是数据较少的情况下还可以进行处理,但是当数据非常打的时候,在利用遍历的方法就会出现超时的情况。那么我们可以利用二分法对这道题进行求解,我们可以先定义一个变量l,代表最左边的数字的下表,定义一个r代表最右边的元素的下标,再定义一个中间变量m,m=(l+r),此时m就代表中间一个元素的下标,我们将目标数字与中间的那个元素进行对比,如果相等就输出m,如果中间元素比目标书数字大,我们就可以把此时的右边界r变成m左边的一个元素;同理,如果中间的那个元素的值比目标元素小,l就变成m右边的一个元素。如图:
在这里插入图片描述直到l和m和r都相等,此时就可以找到了目标数

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

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner in=new Scanner(System.in);
		int n=in.nextInt();
		int a[]=new int[n];
		for(int i=0;i<a.length;i++) {
			a[i]=in.nextInt();
		}
		int x=in.nextInt();
		int l=0,r=a.length-1,m=(r+l)/2;
		while(l<=r)
		{
			m=(r+l)/2;
					if(a[m]==x)
						break;
					else if(a[m]>x)
						r=m-1;
					else
						l=m+1;
		}
		if(a[m]==x)
			System.out.println(m);
		else
			System.out.println("NOT FOUND!");
	}
}

例题一:二分查找

输入n值(1<=n<=1000)、n个非降序排列的整数以及要查找的数x,使用二分查找算法查找x,输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。

思路分析:从题目中的意思我们可以发现:这是一组升序排列的数据,每检测一次,记录一次,如果最终能够找到目标数据,则输出该数据的下标和次数,与上面的引入类似,不同的是如果查找不到需要输出-1和查找的次数。

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int sum=0;//定义一个sum用来计算查找的次数
        int n=in.nextInt();
        int a[]=new int[n];
        for(int i=0;i<n;i++)
        	a[i]=in.nextInt();
        int x=in.nextInt();
        int  l=0,m,r=a.length-1;
        m=(l+r)/2;
    	while(r>=l){//因为r是右边界,l是左边界,所以要想有中值,则必须要保证r>l,也就是循环的条件
			sum++;//每进行一次循环(查找)都自增一次
			m=(l+r)/2;
			if(a[m]==x)
				break;//循环结束的另一个条件,此时的目标值被找到
			else if(a[m]>x)
				r=m-1;//否则就通过比较中值与目标值的大小,判断此时的中值应该作为“右边界”还是“左边界”
			else
				l=m+1;
		}
        if(a[m]==x) {//目标值被找到
    		System.out.println(m);
    			System.out.println(sum);
    		}
    		else if(a[m]!=x) {
    			System.out.println(-1);
    			System.out.println(sum);
    		}
	}
}

我们发现,循环结束的条件有两个,一个是目标值被找到,这样就可以直接确定它的下标,第二种是经过二分查找之后没有找到目标值,就需要左边界小于等于右边界来结束。

例题二:二分法求多项式单根

二分法求函数根的原理为:如果连续函数f(x)在区间[a,b]的两个端点取值异号,即f(a)f(b)<0,则它在这个区间内至少存在1个根r,即f®=0。

二分法的步骤为:

检查区间长度,如果小于给定阈值,则停止,输出区间中点(a+b)/2;否则
如果f(a)f(b)<0,则计算中点的值f((a+b)/2);
如果f((a+b)/2)正好为0,则(a+b)/2就是要求的根;否则
如果f((a+b)/2)与f(a)同号,则说明根在区间[(a+b)/2,b],令a=(a+b)/2,重复循环;
如果f((a+b)/2)与f(b)同号,则说明根在区间[a,(a+b)/2],令b=(a+b)/2,重复循环。
本题目要求编写程序,计算给定3阶多项式
在这里插入图片描述

​​ 在给定区间[a,b]内的根。

上代码

import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner in=new Scanner(System.in);
		double a3,a2,a1,a0;
		a3=in.nextDouble();
		a2=in.nextDouble();
		a1=in.nextDouble();
		a0=in.nextDouble();
		double a=in.nextDouble();
		double b=in.nextDouble();
		double m=(a+b)/2;
		while((b-a)>0.001) {//循环结束的条件
			m=(a+b)/2;
			if(f(a3,a2,a1,a0,m)==0)//循环结束的条件(找到函数的零点)
				break;
			else if(f(a3,a2,a1,a0,m)*f(a3,a2,a1,a0,a)>0)//完全根据题意,如果中值点的函数的符号与a点的函数的符号相同
				a=m;
			else//与b的函数值符号相同
				b=m;
	 }
		System.out.printf("%.2f",m);
	}
	private static double f(double a3,double a2,double a1,double a0,double n)//多项式函数
	{
		return a3*n*n*n+a2*n*n+a1*n+a0;
	}
}

例题三:剪绳子

有 N 根绳子,第 i 根绳子长度为 Li,现在需要 M 根等长的绳子,你可以对 N 根绳子进行任意裁剪(不能拼接),请你帮忙计算出这 M 根绳子最长的长度是多少。
第一行包含 2 个正整数 N、M,表示原始绳子的数量和需求绳子的数量。

第二行包含 N 个整数,其中第 i 个整数 Li 表示第 i 根绳子的长度。

输出一个数字,表示裁剪后最长的长度,保留两位小数。

1≤N,M≤100000,
0<Li<109


import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
      Scanner in= new Scanner(System.in);
      int n=in.nextInt();//绳子的个数
      int k=in.nextInt();//将绳子分成几段
      int L[]=new int[n];
      for (int i=0;i<n; i++)
          L[i]=in.nextInt();
      double l=0,r=1e9;//设置左边界和右边界
      while (r>=l) {
      double mid = (l+r)/2;//此时的mid在这里的意思代表剪完之后的绳子的最大值是多少
      int sum=0;//在这里sum代表最多能剪掉多少段绳子
      for (int i=0;i<n; i++) {
    	  sum+=L[i]/mid;//如果某一段绳子比mid短,就不满足,所以sum=sum+0
    	  //如果大于mid则就看该段绳子能切成几段mid长度的绳子
          }
        if(sum>=k)//切割的段数太多了,肯定不满足绳子最长的要求,所以要把中值作为左边界从而提高中值(每段绳子)的长度 
        	l=mid;
        else//绳子足够长了,但是段数太少了,不满足要求,所以需要把中值作为右边界的值,从而减小中值(每段绳子)的长度
        	r=mid;
        if(Math.abs(r-l)<0.001)
        	break;
        }
      System.out.printf("%.2f", r);
      }
} 

总结

二分法的优点在于可以将需要查找的目标范围不断减半,这样就对时间上进行了极大的优化,对于此类问题我们只需要记住几个步骤:
1.判断二分循环结束的条件。
2.判断循环提前结束的条件。
3.判断左右边界点的变化。

以上的内容为本人在做题的时候进行的总结,如果有不合理的地方还望能够指出,感谢理解和支持

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值