バイナリ検索
例:Aは頭の中で1から1000までの数字を考え、Bは推測し、質問をすることができ、Aは「はい」または「いいえ」にしか答えることができません。質問の最小数を推測する方法は?
- 順次検索:1ですか?2ですか?…999ですか?平均500回質問してください
- バイナリ検索:500より大きいですか?750より大きいですか?625より大きいですか?…推測範囲を前回の半分に減らすのに10回しかかかりません
- バイナリ検索の内容は順番になっている必要があります。
例1:関数BinarySeachを記述して、サイズ要素を含むint配列内の要素pを検索し、見つかった場合は小さいものから大きいものへと並べ替え、見つかった場合は要素インデックスを返し、見つからなかった場合は-1を返します。複雑さO(log(n))が必要です。
コード:
int BinarySeach(int a[],int size,int p){
int L = 0; //查找区间的左端点
int R = size - 1; //查找区间的右端点
while(L <= R){
//如果查找区间不为空就继续查找
int mid = L+(R-L)/2; //取查找区间正中元素的下标
if(p == a[mid])
return mid;
else if(p > a[mid]) //说明p在a[]的左半部
L = mid + 1; //重置查找区间的左端点
else
R = mid - 1; // 重置查找区间的右端点
}
return -1;
}
例2:関数LowerBoundを記述して、サイズ要素を含み、小さいものから大きいものへとソートされたint配列a内の指定された整数pよりも小さい最大の添え字を持つ要素を検索します。見つかった場合はその添え字を返し、見つからなかった場合は-1を返します。
コード:
int LowerBound(int a[],int size,int p){
int L = 0; //查找区间的左端点
int R = size - 1; //查找区间的右端点
int lastPos = -1; //目前为止找到的最优解
while(L <= R){
//如果查找区间正中元素的下标
int mid = L+(R-L)/2; //去查找区间正中元素的下标
if (a[mid] >= p)
R = mid - 1;
else{
lastPos = mid;
L = mid+1;
}
}
return lastPos;
}
注:
int mid = (L+R)/2;
//取查找区间正中元素的下标
(L + R)がオーバーフローしすぎないようにするには:
int mid = L+(R-L) /2;
例3:二分法による方程式の根の検索
次の方程式の根を検索します。f(x)= x 3 -5x 2 + 10x-80 = 0;検出される根がaの場合、| f(a)| < = 10-6。
解決策:f(x)の導関数を取り、f '(x)= 3x 2 -10x +10を取得します。1つの変数で2次方程式の根を見つけるための式は、方程式f '(x)= 0には解がないことを知っているため、f'(x)は常に0より大きくなります。したがって、f(x)は単調に増加しています。f(0)<0およびf(100)> 0であることは簡単にわかります。したがって、間隔[0、100]にはルートが1つだけ存在する必要があります。f(x)は[0、100]で単調であるため、二分法を使用して[0、100]の間隔でルートを見つけることができます。
コード
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
double EPS = 1e-6;
double f(double x){
return x*x*x - 5*x*x + 10*x - 80;
}
int main(){
double root, x1 = 0,x2 = 100,y;
root = x1+(x2-x1)/2;
int triedTimes = 1;//记录一共尝试多少次
y = f(root);
while( fabs(y) > EPS){
if(y > 0)
x2 = root;
else
x1 = root;
root = x1+(x2-x1)/2;
y = f(root);
triedTimes++;
}
printf("%.08f\n",root);
printf("%d",triedTimes);
return 0;
}