二分法

二分法

二分法所屬現代詞,指的是數學領域的概念,經常用於計算機中的查找過程中。數學方面牛頓二分法 一般地,對於函式f(x),如果存在實數c,當x=c時,若f(c)=0,那么把x=c叫做函式f(x)的零點。解方程即要求f(x)的所有零點。假定f(x)在區間(x,y)上連續先找到a、b屬於區間(x,y),使f(a),f(b)異號,說明在區間(a,b)內一定有零點,然後求f[(a+b)/2], 現在假設f(a)<0,f(b)>0,a。

基本信息

簡介

一般地,對於函式f(x),如果存在實數c,當x=c是f(c)=0,那么把x=c叫做函式f(x)的零點
方程即要求f(x)的所有零點。
先找到a、b,使f(a),f(b)異號,說明在區間(a,b)內一定有零點,然後求f【(a+b)/2】,
現在假設f(a)<0,f(b)>0,a<b
如果f【(a+b)/2】=0,該點就是零點,
如果f【(a+b)/2】<0,則在區間((a+b)/2,b)內有零點,按上述方法在求該區間中點的函式值,這樣就可以不斷接近零點
如果f【(a+b)/2】>0,同上
通過每次把f(x)的零點所在小區間收縮一半的方法,使區間的兩個端點逐步迫近函式的零點,以求得零點的近似值,這種方法叫做二分法。
由於計算過程的具體運算複雜,但每一步的方式相同,所以可通過編寫程式來運算。
例:(C語言)
方程式為:f(x) = 0,示例中f(x) = 1+x-x^3

使用示例:

input a b e: 1 2 1e-5
solution: 1.32472
源碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
double f(double x)
{
return 1+x-x*x*x;
}
int main()
{
double a = 0, b = 0, e = 1e-5;
printf("input a b e: ");
scanf("%lf%lf%lf", &a, &b, &e);
e = fabs(e);
if (fabs(f(a)) <= e)
{
printf("solution: %lg\n", a);
}
else if (fabs(f(b)) <= e)
{
printf("solution: %lg\n", b);
}
else if (f(a)*f(b) > 0)
{
printf("f(%lg)*f(%lg) > 0 ! need <= 0 !\n", a, b);
}
else
{
while (fabs(b-a) > e)
{
double c = (a+b)/2.0;
if (f(a)* f ( c ) < 0)
b = c;
else
a = c;
}
printf("solution: %lg\n", (a+b)/2.0);
}
return 0;
}

證明方法

二分法(dichotomie)即一分為二的方法.設[a,b]為R的緊區間.逐次二分法就是造出如下的區間序列([an,bn]):a0=a,b0=b,且對任一自然數n,[an+1,bn+1]或者等於[an,cn],或者等於[cn,bn],其中cn表示[an,bn]的中點

求法

給定精確度ξ,用二分法求函式f(x)零點近似值的步驟如下:
1確定區間[a,b],驗證f(a)·f(b)<0,給定精確度ξ.
2求區間(a,b)的中點c.
3計算f(c).
(1)若f(c)=0,則c就是函式的零點;
(2)若f(a)·f(c)<0,則令b=c;
(3)若f(c)·f(b)<0,則令a=c.
(4)判斷是否達到精確度ξ:即若|a-b|<ξ,則得到零點近似值a(或b),否則重複2-4.

計算機套用

由於計算過程的具體運算複雜,但每一步的方式相同,所以可通過編寫程式來運算。

Java語言

publicintbinarySearch(int[]data,intaim){//以int數組為例,aim為需要查找的數
intstart=0;
intend=data.length-1;
intmid=(start+end)/2;//a
while(data[mid]!=aim&&end>start){//如果data[mid]等於aim則死循環,所以排除
if(data[mid]>aim){
end=mid-1;
}elseif(data[mid]<aim){
start=mid+1;
}
mid=(start+end)/2;//b,注意a,b
}
return(data[mid]!=aim)?-1:mid;//返回結果
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//針對已經排序好的數組進行查找(對上面代碼進行的改進)
publicstaticbooleanbinarySearch(int[]array,inttarget){
intleft=0;
intright=array.length-1;
intmid=(left+right)/2;
while(array[mid]!=target&&right>left){
if(array[mid]>target){
right=mid+1;
}
elseif(array[mid]<target){
left=mid+1;
}
mid=(left+right)/2;
//判斷在縮小範圍後,新的left或者right是否會將target排除
if(array[right]<target){
break;//若縮小後right比target小,即target不在數組中
}
elseif(array[left]>target){
break;//若縮小後left比target大,即target不在數組中
}
}
return(array[mid]==target);
}

C語言

方程式為:f(x)=0,示例中f(x)=1+x-x^3
使用示例:
inputabe:121e-5
solution:1.32472
源碼如下:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<assert.h>
doublef(doublex)
{
return1+x-x*x*x;
}
intmain()
{
doublea=0,b=0,e=1e-5;
printf("inputabe:");
scanf("%lf%lf%lf",&a,&b,&e);
e=fabs(e);
if(fabs(f(a))<=e)
{
printf("solution:%lg\n",a);
}
elseif(fabs(f(b))<=e)
{
printf("solution:%lg\n",b);
}
elseif(f(a)*f(b)>0)
{
printf("f(%lg)*f(%lg)>0!need<=0!\n",a,b);
}
else
{
while(fabs(b-a)>e)
{
doublec=(a+b)/2.0;
if(f(a)*f(c)<0)
b=c;
else
a=c;
}
printf("solution:%lg\n",(a+b)/2.0);
}
return0;
}

C++語言

[類C編寫].
|f(x)|<10^-5f(x)=2x^3-4x^2+3x-6
#include"iostream"
#include"stdio.h"
#include"math.h"
#definenull0
doublefx(double);//f(x)函式
voidmain()
{
doublexa(null),xb(null),xc(null);
do
{
printf("請輸入一個範圍x0x1:");
std::cin>>xa>>xb;//輸入xaxb的值
printf("%f%f",xa,xb);
}
while(fx(xa)*fx(xb)>=0);//判斷輸入範圍內是否包含函式值0
do
{
if(fx((xc=(xa+xb)/2))*fx(xb)<0)//二分法判斷函式值包含0的X取值區間
{
xa=xc;
}
else
{
xb=xc;
}
}
while(fx(xc)>pow(10.0,-5)||fx(xc)<-1*pow(10.0,-5));//判斷x根是否在接近函式值0的精確範圍內
printf("\n得數為:%f",xc);
}
doublefx(doublex)
{
return(2.0*pow(x,3)-4.0*pow(x,2)+3*x-6.0);
}

C++語言中的二分查找法

算法:當數據量很大適宜採用該方法。採用二分法查找時,數據需是排好序的。
基本思想:假設數據是按升序排序的,對於給定值x,從序列的中間位置開始比較,如果當前位置值等於x,則查找成功;若x小於當前位置值,則在數列的前半段中查找;若x大於當前位置值則在數列的後半段中繼續查找
,直到找到為止。
假如有一組數為3,12,24,36,55,68,75,88要查給定的值24.可設三個變數front,mid,end分別指向數據的上界,中間和下界,mid=(front+end)/2.
1.開始令front=0(指向3),end=7(指向88),則mid=3(指向36)。因為mid>x,故應在前半段中查找。
2.令新的end=mid-1=2,而front=0不變,則新的mid=1。此時x>mid,故確定應在後半段中查找。
3.令新的front=mid+1=2,而end=2不變,則新的mid=2,此時a[mid]=x,查找成功。
如果要查找的數不是數列中的數,例如x=25,當第三次判斷時,x>a[mid],按以上規律,令front=mid+1,即front=3,出現front>end的情況,表示查找不成功。
例:在有序的有N個元素的數組中查找用戶輸進去的數據x。
算法如下:
1.確定查找範圍front=0,end=N-1,計算中項mid(front+end)/2。
2.若a[mid]=x或front>=end,則結束查找;否則,向下繼續。
3.若a[mid]<x,說明待查找的元素值只可能在比中項元素大的範圍內,則把mid+1的值賦給front,並重新計算mid,轉去執行步驟2;若a[mid]>x,說明待查找的元素值只可能在比中項元素小的範圍內,則把mid-1的值賦給end,並重新計算mid,轉去執行步驟2。
代碼:
#include<iostream>
#defineN10
usingnamespacestd;
intmain()
{
inta[N],front,end,mid,x,i;
cout<<"請輸入已排好序的a數組元素:"<<endl;
for(i=0;i<N;i++)
cin>>a[i];
cout<<"請輸入待查找的數x:"<<endl;
cin>>x;
front=0;
end=N-1;
mid=(front+end)/2;
while(front<end&&a[mid]!=x)
{
if(a[mid]<x)front=mid+1;
if(a[mid]>x)end=mid-1;
mid=front+(end-front)/2;
}
if(a[mid]!=x)
cout<<"沒找到!"<<endl;
else
cout<<"找到了!在第"<<mid+1<<"項里。"<<endl;
return0;
}

MATLAB語言

functiony=f(x)
y=f(x);%函式f(t)的表達式
i=0;%二分次數記數
a=a;%求根區間左端
b=b;%求根區間右端
fa=f(a);%計算f(a)的值
fb=f(b);%計算f(b)的值
c=(a+b)/2;%計算區間中點
fc=f(c);%計算區間中點f(c)
whileabs(fc)>=ε;%判斷f(c)是否為零點
iffa*fc>=0;%判斷左側區間是否有根
fa=fc;
a=c;
elsefb=fc;
b=c;
end
c=(a+b)/2;
fc=f(c);
i=i+1;
end
fprintf('\n%s%.6f\t%s%d','c,'疊代次數i=",i)%計算結果輸出

快速排序偽代碼(非隨機)

下面的過程實現快速排序:
QUICKSORT(A,p,r)
1 ifp<r
2 thenq←PARTITION(A,p,r)
3 QUICKSORT(A,p,q-1)
4 QUICKSORT(A,q+1,r)
為排序一個完整的數組A,最初的調用是QUICKSORT(A,1,length[A])。
快速排序算法的關鍵是PARTITION過程,它對子數組A[p..r]進行就地重排:
PARTITION(A,p,r)
1 x←A[r]
2 i←p-1
3 forj←ptor-1
4 doifA[j]≤x
5 theni←i+1
6 exchangeA[i]←→A[j]
7 exchangeA[i+1]←→A[r]
8 returni+1

快速排序偽代碼(隨機)

對PARTITION和QUICKSORT所作的改動比較小。在新的劃分過程中,我們在真正進行劃分之前實現交換:
(其中PARTITION過程同快速排序偽代碼(非隨機))
RANDOMIZED-PARTITION(A,p,r)
1 i←RANDOM(p,r)
2 exchangeA[r]←→A[i]
3 returnPARTITION(A,p,r)
新的快速排序過程不再調用PARTITION,而是調用RANDOMIZED-PARTITION。
RANDOMIZED-QUICKSORT(A,p,r)
1 ifp<r
2 thenq←RANDOMIZED-PARTITION(A,p,r)
3 RANDOMIZED-QUICKSORT(A,p,q-1)
4 RANDOMIZED-QUICKSORT(A,q+1,r)
Pascal,

遞歸快排1

procedurework(l,r:longint);
vari,j,tmp:longint;
begin
ifl<rthenbegin
i:=l;j:=r;tmp:=stone[i];
whilei<jdo
begin
while(i<j)and(tmp<stone[j])dodec(j);
if(i<j)then
begin
stone[i]:=stone[j];
inc(i);
end;
while(i<j)and(tmp>stone[i])doinc(i);
ifi<jthen
begin
stone[j]:=stone[i];
dec(j);
end;
end;
stone[i]:=tmp;
work(l,i-1);
work(i+1,r);
end;
end;//本段程式中stone是要排序的數組,從小到大排序,stone數組為longint(長整型)類型。在主程式中的調用命令為“work(1,n);”不含引號。表示將stone數組中的1到n號元素進行排序。
Pascal,

遞歸快排2

Programquiksort;
//快速排序法
constmax=100;
varn:integer;
a:array[1..max]oflongint;
proceduresort(l,r:longint);
vari,j,x,y:longint;
begin
i:=l;j:=r;x:=a[(l+r)div2];
repeat
whilea[i]<xdoinc(i);
whilex<a[j]dodec(j);
ifi<=jthen
begin
y:=a[i];a[i]:=a[j];a[j]:=y;
inc(i);dec(j);
end;
untili>j;
ifl<jthensort(l,j);
ifi<rthensort(i,r);
end;
begin
//生成數組;
randomize;
forn:=1tomaxdo
begin
a[n]:=random(1000);
write(a[n]:5);
end;
writeln;
//排序
sort(1,max);
//輸出
forn:=1tomaxdowrite(a[n]:5);writeln;
end.
Delphi

遞歸快排3

TNTA=arrayofinteger;
var
A:TNTA;
procedureQuicSort(varArr:TNTA;AStart,AEnd:Integer);
var
I,J,Sign:integer;
procedureSwitch(A,B:Integer);
var
Tmp:Integer;
begin
Tmp:=Arr[A];
Arr[A]:=Arr[B];
Arr[B]:=Tmp;
end;
begin
ifAEnd<=AStartthen
Exit;
Sign:=(AStart+AEnd)div2;
{Switchvalue}
Switch(Sign,AEnd);
{Starttosort}
J:=AStart;
forI:=AStarttoAEnd-1do
begin
if(Arr[I]<Arr[AEnd]){and(I<>J)}then
begin
Switch(J,I);
Inc(J);
end;
end;
Switch(J,AENd);
QuicSort(Arr,AStart,J);
QuicSort(Arr,J+1,AEnd);
end;
procedureTForm1.btn1Click(Sender:TObject);
const
LEN=10000;
var
I:Integer;
Start:Cardinal;
begin
SetLength(A,LEN);
Randomize;
forI:=Low(A)toHigh(A)do
A[I]:=Random(LEN*10);
Start:=GetTickCount;
QuicSort(A,Low(A),High(A));
ShowMessageFmt("%dlargequicksorttaketime:%d',[LEN,GetTickCount-Start]);
end;
Pascal,

非遞歸快排1

var
s:packedarray[0..100,1..7]oflongint;
t:boolean;
i,j,k,p,l,m,n,r,x,ii,jj,o:longint;
a:packedarray[1..200000]oflongint;
functioncheck:boolean;
begin
ifi>2thenexit(false);
caseiof
1:if(s[k,3]<s[k,2])thenexit(true);
2:ifs[k,1]<s[k,4]thenexit(true);
end;
exit(false);
end;
procedureqs;/非遞歸快速排序
begin
k:=1;
t:=true;
s[k,1]:=1;
s[k,2]:=n;
s[k,3]:=1;
whilek>0do
begin
r:=s[k,2];
l:=s[k,1];
ii:=s[k,3];
jj:=s[k,4];
iftthen
if(r-l>30)then
begin
x:=a[(r-l+1)shr1+l];
ii:=s[k,1];jj:=s[k,2];
repeat
whilea[ii]<xdoinc(ii);
whilea[jj]>xdodec(jj);
ifii<=jjthen
begin
m:=a[ii];
a[ii]:=a[jj];
a[jj]:=m;
inc(ii);dec(jj);
end;
untilii>jj;
s[k,3]:=ii;
s[k,4]:=jj;
end
elsebegin
forii:=ltordo
begin
m:=a[ii];jj:=ii-1;
while(m<a[jj])and(jj>0)do
begin
a[jj+1]:=a[jj];
dec(jj);
end;
a[jj+1]:=m;
end;
t:=false;dec(k);
end;
iftthen
fori:=1to3do
ifcheckthenbreak;
ifnottthen
begin
i:=s[k,5];
repeat
inc(i);
until(i>2)orcheck;
end;
ifi>2thenbegint:=false;dec(k);end
elset:=true;
iftthen
begin
s[k,5]:=i;
inc(k);
caseiof
1:begins[k,1]:=s[k-1,3];s[k,2]:=s[k-1,2];end;
2:begins[k,1]:=s[k-1,1];s[k,2]:=s[k-1,4];end;
end;
end;
end;
end;
begin
readln(n);
fori:=1tondoread(a[i]);
k:=1;
qs;
fori:=1tondo//輸出
write(a[i],'');
writeln;
end.
經測試,非遞歸快排比遞歸快排快。
Pascal,

非遞歸快排2

//此段快排使用l佇列儲存待處理範圍
var
a:Array[1..100000]oflongint;
l:Array[1..100000,1..2]oflongint;
n,i:longint;
procedurefs;//非遞歸快排
var
s,e,k,j,ms,m:longint;
begin
s:=1;e:=1;l[1,1]:=1;l[1,2]:=n;
whiles<=edo
begin
k:=l[s,1];j:=l[s,2];m:=random(j-k+1)+k;
ms:=a[m];a[m]:=a[k];
whilek<jdo
begin
while(k<j)and(a[j]>ms)dodec(j);
ifk<jthenbegina[k]:=a[j];inc(k);end;
while(k<j)and(a[k]<ms)doinc(k);
ifk<jthenbegina[j]:=a[k];dec(j);end;
end;
a[k]:=ms;
ifl[s,1]<k-1thenbegininc(e);l[e,1]:=l[s,1];l[e,2]:=k-1;end;
ifj+1<l[s,2]thenbegininc(e);l[e,1]:=j+1;l[e,2]:=l[s,2];end;
inc(s);
end;
end;
begin
randomize;
read(n);
fori:=1tondoread(a[i]);
fs;
fori:=1tondowrite(a[i],'');
end.

實戰

Problem:大整數開方NOIP2011普及組初賽完善程式第二題
題目描述
輸入一個正整數n(1<n<10^100),試用二分法計算它的平方根的整數部分。
代碼
Const
SIZE=200;
Type
hugeint=Record
len:Integer;
num:Array[1..SIZE]OfInteger;
End;
//len表示大整數的位數;num[1]表示個位、num[2]表示十位,以此類推
Var
s:String;
i:Integer;
target,left,middle,right:hugeint;
Functiontimes(a,b:hugeint):hugeint;
//計算大整數a和b的乘積
Var
i,j:Integer;
ans:hugeint;
Begin
FillChar(ans,SizeOf(ans),0);
Fori:=1Toa.lenDo
Forj:=1Tob.lenDo
ans.num[i+j-1]:=ans.num[i+j-1]+a.num[i]*b.num[j];
Fori:=1Toa.len+b.lenDo
Begin
ans.num[i+1]:=ans.num[i+1]+ans.num[i]DIV10;
ans.num[i]:=ans.num[i]mod10;
Ifans.num[a.len+b.len]>0
Thenans.len:=a.len+b.len
Elseans.len:=a.len+b.len-1;
End;
times:=ans;
End;
Functionadd(a,b:hugeint):hugeint;
//計算大整數a和b的和
Var
i:Integer;
ans:hugeint;
Begin
FillChar(ans.num,SizeOf(ans.num),0);
Ifa.len>b.len
Thenans.len:=a.len
Elseans.len:=b.len;
Fori:=1Toans.lenDo
Begin
ans.num[i]:=ans.num[i]+a.num[i]+b.num[i];
ans.num[i+1]:=ans.num[i+1]+ans.num[i]DIV10;
ans.num[i]:=ans.num[i]MOD10;
End;
Ifans.num[ans.len+1]>0
ThenInc(ans.len);
add:=ans;
End;
Functionaverage(a,b:hugeint):hugeint;
//計算大整數a和b的平均數的整數部分
Var
i:Integer;
ans:hugeint;
Begin
ans:=add(a,b);
Fori:=ans.lenDownTo2Do
Begin
ans.num[i-1]:=ans.num[i-1]+(ans.num[i]mod2)*10;
ans.num[i]:=ans.num[i]DIV2;
End;
ans.num[1]:=ans.num[1]DIV2;
Ifans.num[ans.len]=0
ThenDec(ans.len);
average:=ans;
End;
Functionplustwo(a:hugeint):hugeint;
//計算大整數a加2後的結果
Var
i:Integer;
ans:hugeint;
Begin
ans:=a;
ans.num[1]:=ans.num[1]+2;
i:=1;
While(i<=ans.len)AND(ans.num[i]>=10)Do
Begin
ans.num[i+1]:=ans.num[i+1]+ans.num[i]DIV10;
ans.num[i]:=ans.num[i]MOD10;
Inc(i);
End;
Ifans.num[ans.len+1]>0
Theninc(ans.len);
plustwo:=ans;
End;
Functionover(a,b:hugeint):Boolean;
//若大整數a>b則返回1,否則返回0
Var
i:Integer;
Begin
If(a.len<b.len)Then
Begin
over:=FALSE;
Exit;
End;
Ifa.len>b.lenThen
Begin
over:=TRUE;
Exit;
End;
Fori:=a.lenDownTo1Do
Begin
Ifa.num[i]<b.num[i]Then
Begin
over:=FALSE;
Exit;
End;
Ifa.num[i]>b.num[i]Then
Begin
over:=TRUE;
Exit;
End;
End;
over:=FALSE;
End;
Begin
Readln(s);
FillChar(target.num,SizeOf(target.num),0);
target.len:=Length(s);
Fori:=1Totarget.lenDo
target.num[i]:=Ord(s[target.len-i+1])-ord('0');
FillChar(left.num,SizeOf(left.num),0);
left.len:=1;
left.num[1]:=1;
right:=target;
Repeat
middle:=average(left,right);
Ifover(times(middle,middle),target)
Thenright:=middle
Elseleft:=middle;
Untilover(plustwo(left),right);
Fori:=left.lenDownTo1Do
Write(left.num[i]);
Writeln;
End.

經濟學方面

傳統的經濟學家把經濟分為實物經濟和貨幣經濟兩部分,其中,經濟理論分析實際變數的決定,而貨幣理論分析價格的決定,兩者之間並沒有多大的關係,這就是所謂的二分法。

哲學方面

又稱二分說,愛利亞學派芝諾四大著名悖論之一 證明運動是不可能的。 其主要思路是:假設一個存在物經過空間而運動,為了穿越某個空間,就必須穿越這個空間的一半。為了穿越這一半,就必須穿越這一半的一半;以此類推,直至無窮。所以,運動是不可能的

一般使用方面

即將所有的事物根據其屬性分成兩種。錯誤的分類可能導致邏輯謬論,如:非黑即白,不是忠的就是奸的。這很明顯忽略了中間狀態的存在。正確的分類法應如:白-非白。

相關詞條

相關搜尋

熱門詞條

聯絡我們