You are on page 1of 19

壹、 前言

一、研究動機
1968 年 5 月 22 日,美國海軍核子動力潛艇「天蝎號(SSN-589)」在葡萄牙和西班牙
西部的大西洋深水區失去聯繫,冷戰期間的美國決定不計代價找回載滿機密的沉船,因此美
國海軍特別成立專案辦公室,聘請科學家來協助,並由約翰‧克雷文(John P. Craven)博士
擔任首席科學家,全力搜尋它的遺骸。為了在廣大的海域完成任務,克雷文博士打算利用十
八世紀就已發表的「貝氏定理」然後逐漸改良而成的「貝葉斯搜索理論(Bayesian search
theory)」,企圖找到潛艦的殘骸,並在失聯五個月後成功尋獲天蠍號。而我們所要做的,
便是研究貝葉斯搜索法,並希望能研究出更加完善且真實的演算法。
二、研究目的
將當時克雷文博士所使用的貝葉斯搜索理論加入變因並進行修正及優化,使其中模型
更加完整,最終希望得出最有效率的搜索方式。我們預期所加入的變因及研究的最終目的如
下:
(一)加入時間變因:為了更貼近真實情況,我們在模型中增加了搜尋所需時間及轉
換區域的通勤時間,並讓在區域內成功尋獲的機率會隨著時間指數型遞減,探討加入時間因
素後整個模型的理論情況。
(二)加入水流影響:令模型中沉船在每隔一段時間後便會依水流等因素影響按不同
機率漂浮到相鄰區域,值得注意的是,為方便計算,這裡所指的一段時間皆相同,示意圖如
下:
(三)實際推演:利用程式(C++)設計出符合探討內容的模型進行實際推演,藉
由對於不同條件的模型推演結果的觀察和與理論情況的比較,而我們主要要比較的演算法為
貝葉斯搜索理論第 4 步中所提到的①②兩種取法進而探討出何者為一套最真實且完美的搜索
法用於「海底撈針」。
貳、 研究設備及器材
紙、筆、電腦(Visual Studio Code、C++、MathType)
參、研究過程或方法
一、名詞解釋
(一)貝氏定理
P(A)表示 A 事件發生機率,P(B)表示 B 事件發生機率,
由條件機率:

1
可知:

此即為貝氏定理。此定理用於將結果轉換為條件之條件機率。而考慮 B 事件的分割:

貝氏定理又可改寫為:

(二)先驗機率、後驗機率
在經過一次試驗中,試驗前機率稱之為「先驗機率」(Prior Probability),而經過試
驗後,機率將被修正為「後驗機率」(Posterior Probability)。
在貝氏定理中,經過 B 事件後機率為𝑃(𝐴|𝐵),即為後驗機率,而先驗機率為
𝑃(𝐴),因此後驗機率為先驗機率乘上所求事件為條件試驗發生之機率再除上試驗發生
機率。
(三)積分平均值
由算數平均數之概念,可定義一連續函數 f(x) 於一區間[a,b]之平均值:

𝑏−𝑎
此處 Δx 等於 ,而𝑥𝑖∗ 為將[a,b]平分為 n 等分之第 i 分割內之隨機取樣點,當對 n
𝑛

取極限,即為積分平均值:

舉例如 Wallis 乘積:

2
(四)符號定義
1、𝑃(𝑖, 𝑗)≡在第 j 次試驗後,於地區 i 失事之機率。
2、𝑞(𝑖, 𝑡)≡在 t 時刻,於地區 i 失事但未能找尋之機率。
3、𝑄(𝑖, 𝑡)≡從 t 時刻開始於第 i 區找尋沉船,過程中𝑞(𝑖, 𝑡)之平均值。
4、𝑇(𝑖)≡搜索第 i 區所需時間
5、𝜏(𝑖, 𝑗)≡由第 i 區到第 j 區所需航行時間
6、𝑝(𝑖, 𝑗, 𝑎)≡沉船由 ( i , j ) 與 a 方向相鄰區域變換的機率,而𝑎 ∈ {0,1,2,3,4},依序
代表留在原處、右方、上方、左方、下方。
二、研究方法
最初先以迭代關係在原有的基礎上進行推演,其後增加前述所設計之變因修正模型使
搜索法更加完善,過程中主要有運用到條件機率以及微積分等數學工具幫助計算,在得出理
論結果後利用程式語言設計模型讓預想情況實際推演,並記錄其推演成果實現理論方程。
三、研究內容
(一)步驟
1、對船隻失聯事件分析假設
2、根據假設,構造船隻可能出現的位置的機率
3、針對各個區域,若使沉船位於此區域,計算找沉船失敗的機率,這通常和水
深及水流等息息相關
4、從高概率區開始搜索
5、在搜索過程中不斷更新資料,若在某區搜尋無果,則該區機率下降,其他區
機率上升,而這會用到貝氏定理

※在第四步中,原方法中只挑先驗機率 P 最大的區域先進行搜索,我們認為考慮實際找到船
之機率做為挑選的機率或許更加合理,故除最大的𝑃(𝑖, 𝑗)之外,亦取用了另一種條件即:

3
(二)原情況
定理 1.當𝑞(𝑖, 𝑡)不變時,搜索沉船的機率方程組為

令𝑞(𝑖, 𝑡)=𝑞(𝑖, 0)為一定值不隨時間 t 變化,若未成功尋獲沉船,首先先計算找尋成功之


機率:

而若沉船沉於𝑖𝑗 區,未尋獲之機率為:

因此後驗機率為:

執行 100000 次搜尋次數期望值程式如下:

#include <bits/stdc++.h>
using namespace std;

#define x first
#define y second
typedef pair<int, int> pos;

mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); // random

int n, m;
pos boat, cur;
vector<vector<double>> P, q, P0, q0;

void read()
{

4
cin >> n >> m;

P.assign(n, vector<double>(m));
q.assign(n, vector<double>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> P[i][j];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> q[i][j];
P0 = P, q0 = q;
}

pos boat0()
{
int tmp = rnd() % 1000000;
double sum = 0;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
sum += P[i][j] * 1000000;
if (sum >= tmp)
return {i, j};
}
return {n - 1, m - 1}; // unnecessary
}

void init()
{
P = P0, q = q0, boat = boat0();
}

pos select_p()
{
pos ret = {0, 0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (P[ret.x][ret.y] < P[i][j])
ret = {i, j};
return ret;
}

pos select_q()
{
pos ret = {0, 0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (P[ret.x][ret.y] * (1 - q[ret.x][ret.y]) < P[i][j] * (1 -

5
q[i][j]))
ret = {i, j};
return ret;
}

bool find()
{
if (cur != boat)
return false;
// cur == boat
int tmp = rnd() % 1000000;
return tmp >= q[boat.x][boat.y] * 1000000;
}

void change_p()
{
double pcur = P[cur.x][cur.y];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
P[i][j] = P[i][j] / (1.0 - pcur * (1.0 - q[cur.x][cur.y]));
P[cur.x][cur.y] *= q[cur.x][cur.y];
}

int runp()
{
init();
int cnt = 0;
while (true)
{
++cnt;
if (cnt > 100000)
return -1;
cur = select_p();
if (find())
return cnt;
change_p();
}
}
int runq()
{
init();
int cnt = 0;
while (true)
{
++cnt;
if (cnt > 10000)
return -1;
cur = select_q();

6
if (find())
return cnt;
change_p();
}
}
int main()
{
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read();
int sum = 0, cnt = 100000, fail = 0;
for (int i = 0; i < cnt; ++i)
{
int result = runp();
if (result == -1)
++fail;
else
sum += result;
}
cout << fixed << setprecision(8);
cout << "Fail rate p: " << 1.0 * fail / cnt << '\n';
cout << "Succeed avg p: " << 1.0 * sum / (cnt - fail) << '\n';
sum = 0;
fail = 0;
for (int i = 0; i < cnt; ++i)
{
int result = runq();
if (result == -1)
++fail;
else
sum += result;
}
cout << fixed << setprecision(8);
cout << "Fail rate q: " << 1.0 * fail / cnt << '\n';
cout << "Succeed avg q: " << 1.0 * sum / (cnt - fail) << '\n';
return 0;
}

7
(三)考慮時間變化

定理 2.當𝑞(𝑖, 𝑡)加入時間因素影響時,搜索沉船的機率方程組為

在此情況,即為將q值修正為Q值,值得注意的是,在方案②選取時,q值亦要修正
為Q值。
−𝑎𝑖 𝑡
設立一函數𝑓𝑖 (𝑡) = 𝑒 模擬時間造成的找尋難易度增加,設立𝑎𝑖 表示區域不同所造
成的難度增加快慢,由此𝑞(𝑖, 𝑡)則修正為下式:

而在搜索過程中,𝑞(𝑖, 𝑡)也一直變化,計算平均搜索失敗機率:

此處需要計算指數函數之積分,推導如下:

上式即為𝑄(𝑖, 𝑡),而將定理一之𝑞(𝑖, 𝑡)代換為𝑄(𝑖, 𝑡),即為定理二。


值得注意的是,t 值計算是將先前的路程時間與搜索時間加總,即為下式:

8
因此定理二可寫為:

執行 100000 次搜尋次數期望值程式如下:

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int, int> pos;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); // random
int n, m;
pos boat, cur;
vector<vector<double>> P, q, P0, q0, a, t;
vector<vector<vector<vector<double>>>> dis;
double time_stamp;
const double eps = 1e-9;
double Qt(pos i)
{
return 1.0 - (1.0 - q[i.x][i.y]) / t[i.x][i.y] / a[i.x][i.y] * (exp(-
a[i.x][i.y] * time_stamp) - exp(-a[i.x][i.y] * (time_stamp + t[i.x][i.y])));
}
void read()
{
cin >> n >> m;
P.assign(n, vector<double>(m));
q.assign(n, vector<double>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> P[i][j];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> q[i][j];
P0 = P, q0 = q;
a.assign(n, vector<double>(m));

9
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> a[i][j], a[i][j] = max(a[i][j], eps);
t.assign(n, vector<double>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> t[i][j];
dis.assign(n, vector<vector<vector<double>>>(m, vector<vector<double>>(n,
vector<double>(m))));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
for (int k = 0; k < n; ++k)
for (int l = 0; l < m; ++l)
cin >> dis[i][j][k][l];
}
pos boat0()
{
int tmp = rnd() % 1000000;
double sum = 0;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
sum += P[i][j] * 1000000;
if (sum >= tmp)
return {i, j};
}
return {n - 1, m - 1}; // unnecessary
}
void init()
{
P = P0, q = q0, boat = boat0();
time_stamp = 0;
}
pos select_p()
{
pos ret = {0, 0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (P[ret.x][ret.y] < P[i][j])
ret = {i, j};
return ret;
}
pos select_q()
{
pos ret = {0, 0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (P[ret.x][ret.y] * (1 - Qt(ret)) < P[i][j] * (1 - Qt({i, j})))

10
ret = {i, j};
return ret;
}
bool find()
{
if (cur != boat)
return false;
// cur == boat
int tmp = rnd() % 1000000;
return tmp >= Qt(cur) * 1000000;
}

void change_p()
{
double pcur = P[cur.x][cur.y];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
P[i][j] = P[i][j] / (1.0 - pcur * (1.0 - Qt(cur)));
P[cur.x][cur.y] *= Qt(cur);
}
int runp()
{
init();
int cnt = 0;
while (true)
{
++cnt;
if (cnt > 10000)
return -1;
pos pre = cur;
cur = select_p();
if (cnt > 1)
time_stamp += dis[pre.x][pre.y][cur.x][cur.y];
if (find())
return cnt;
change_p();
time_stamp += t[cur.x][cur.y];
}
}
int runq()
{
init();
int cnt = 0;
while (true)
{
++cnt;
if (cnt > 10000)
return -1;

11
pos pre = cur;
cur = select_q();
if (cnt > 1)
time_stamp += dis[pre.x][pre.y][cur.x][cur.y];
if (find())
return cnt;
change_p();
time_stamp += t[cur.x][cur.y];
}
}
int main()
{
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read();
int sum = 0, cnt = 100000, fail = 0;
for (int i = 0; i < cnt; ++i)
{
int result = runp();
if (result == -1)
++fail;
else
sum += result;
}
cout << fixed << setprecision(8);
cout << "Fail rate p: " << 1.0 * fail / cnt << '\n';
cout << "Succeed avg p: " << 1.0 * sum / (cnt - fail) << '\n';
sum = 0;
fail = 0;
for (int i = 0; i < cnt; ++i)
{
int result = runq();
if (result == -1)
++fail;
else
sum += result;
}
cout << fixed << setprecision(8);
cout << "Fail rate q: " << 1.0 * fail / cnt << '\n';
cout << "Succeed avg q: " << 1.0 * sum / (cnt - fail) << '\n';
return 0;
}

12
(四)考慮水流等因素造成的沉船位置改變

定理 3.𝑞(𝑖, 𝑡)不變,水流會有機率的將沉船移至周圍區域,則搜索沉船的機率方程為:

與原情況不同的是,還須計算周圍沉船的流動的機率,𝑃(𝑠𝑢𝑟𝑟𝑜𝑢𝑛𝑑𝑖𝑛𝑔𝑠)計算如下:
1、照原情況算出後驗機率
2、按照水流判斷船在該方向之流入流出
3、將流入的格子後驗機率乘上該方向流動機率加總即為𝑃(𝑠𝑢𝑟𝑟𝑜𝑢𝑛𝑑𝑖𝑛𝑔𝑠)
舉例如下:

圖一、水流示意圖
在圖例中,水流流入(𝑥, 𝑦)的格子為(𝑥, 𝑦 − 1)和(𝑥 − 1, 𝑦),因此𝑃(𝑠𝑢𝑟𝑟𝑜𝑢𝑛𝑑𝑖𝑛𝑔𝑠) =
𝑟(𝑥 − 1, 𝑦, 𝑗)𝑝(𝑥, 𝑦, 3) + 𝑟(𝑥, 𝑦 − 1, 𝑗)𝑝(𝑥, 𝑦, 4),𝑟(𝑥, 𝑦, 𝑗)代表原先算出搜索第 j 次後之後驗機
率;而為方便起見,我們將水流方向儲存於 p 值,當水流為向上或向右時,p 值定為正,反
之定為負。

執行 100000 次搜尋次數期望值程式如下:

#include <bits/stdc++.h>
using namespace std;

#define x first

13
#define y second
typedef pair<int, int> pos;

mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); // random

int n, m;
pos boat, cur;
vector<vector<double>> P, q, P0, q0, p[5], Ppre;

const pos dpos[] = {{0, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 0}};
// 0, right, up, left, down

double Psurrounding(pos i)
{
double ret = 0;
for (int k = 1; k <= 2; ++k)
if (p[k][i.x][i.y] < 0)
ret -= p[k][i.x][i.y] * Ppre[i.x + dpos[k].x][i.y + dpos[k].y];
for (int k = 3; k <= 4; ++k)
if (p[k][i.x][i.y] > 0)
ret += p[k][i.x][i.y] * Ppre[i.x + dpos[k].x][i.y + dpos[k].y];
return ret;
}

void read()
{
cin >> n >> m;

P.assign(n, vector<double>(m));
q.assign(n, vector<double>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> P[i][j];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> q[i][j];
P0 = P, q0 = q;

for (int k = 0; k < 5; ++k)


p[k].assign(n, vector<double>(m, 0));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> p[0][i][j];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
cin >> p[1][i][j];
if (j + 1 < m)

14
p[3][i][j + 1] = p[1][i][j];
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
cin >> p[2][i][j];
if (i - 1 >= 0)
p[4][i - 1][j] = p[2][i][j];
}
}

pos boat0()
{
int tmp = rnd() % 1000000;
double sum = 0;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
sum += P[i][j] * 1000000;
if (sum >= tmp)
return {i, j};
}
return {n - 1, m - 1}; // unnececessary
}

void init()
{
P = P0, q = q0, boat = boat0();
}

pos select_p()
{
pos ret = {0, 0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (P[ret.x][ret.y] < P[i][j])
ret = {i, j};
return ret;
}

pos select_q()
{
pos ret = {0, 0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (P[ret.x][ret.y] * (1 - q[ret.x][ret.y]) < P[i][j] * (1 -
q[i][j]))
ret = {i, j};

15
return ret;
}

bool find()
{
if (cur != boat)
return false;
// cur == boat
int tmp = rnd() % 1000000;
return tmp >= q[boat.x][boat.y] * 1000000;
}

void change_p()
{
Ppre = P;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
P[i][j] = Ppre[i][j] * p[0][i][j] / (1.0 - Ppre[cur.x][cur.y] *
(1.0 - q[cur.x][cur.y]));
P[cur.x][cur.y] *= q[cur.x][cur.y];

for (int i = 0; i < n; ++i)


for (int j = 0; j < m; ++j)
P[i][j] += Psurrounding({i, j});
}

void change_boat()
{
int tmp = rnd() % 1000000;
double sum = 0;
sum += p[0][boat.x][boat.y] * 1000000;
if (sum >= tmp)
return;

for (int i = 1; i <= 2; ++i)


{
if (p[i][boat.x][boat.y] <= 0)
continue;
sum += p[i][boat.x][boat.y] * 1000000;
if (sum >= tmp)
{
boat.x += dpos[i].x, boat.y += dpos[i].y;
return;
}
}

for (int i = 3; i <= 4; ++i)


{

16
if (p[i][boat.x][boat.y] >= 0)
continue;
sum -= p[i][boat.x][boat.y] * 1000000;
if (sum >= tmp)
{
boat.x += dpos[i].x, boat.y += dpos[i].y;
return;
}
}
}
int runp()
{
init();
int cnt = 0;
while (true)
{
++cnt;
if (cnt > 100000)
return -1;
cur = select_p();
if (find())
return cnt;
change_p();
change_boat();
}
}
int runq()
{
init();
int cnt = 0;
while (true)
{
++cnt;
if (cnt > 10000)
return -1;
cur = select_q();
if (find())
return cnt;
change_p();
change_boat();
}
}

int main()
{
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read();

17
int sum = 0, cnt = 100000, fail = 0;
for (int i = 0; i < cnt; ++i)
{
int result = runp();
if (result == -1)
++fail;
else
sum += result;
}
cout << fixed << setprecision(8);
cout << "Fail rate p: " << 1.0 * fail / cnt << '\n';
cout << "Succeed avg p: " << 1.0 * sum / (cnt - fail) << '\n';
sum = 0;
fail = 0;
for (int i = 0; i < cnt; ++i)
{
int result = runq();
if (result == -1)
++fail;
else
sum += result;
}
cout << fixed << setprecision(8);
cout << "Fail rate q: " << 1.0 * fail / cnt << '\n';
cout << "Succeed avg q: " << 1.0 * sum / (cnt - fail) << '\n';

return 0;
}

肆、討論
我們藉由程式對各個情況都分別使用以下兩種方案進行十萬次的搜索:


比較兩方案搜尋沉船的平均搜索次數及失敗率(在這邊我們定義搜索次數大於一萬即判斷為
失敗),而我們發現,無論加入哪種變因,①方案所需的平均搜索次數大多都多於②方案
我們推測是因為選取②方案可以使後驗機率變化較大,修正機率效率較高,因此可以較快找
到。而我們還可以將所有變因統合,將𝑞(𝑖, 𝑡)改為𝑄(𝑖, 𝑡)且考慮𝑃(𝑠𝑢𝑟𝑟𝑜𝑢𝑛𝑑𝑖𝑛𝑔𝑠),即把時間
和空間統合。

18
伍、結論
一、我們藉由程式模擬出搜索法的進行,並算出期望值,以下為定理一到三的測資舉例:

圖二、三、四、測資舉例
圖四中,其" input "部分第一行規定海域的範圍;二、三行為 p 值;四、五行則為 q 值
的設定,而其他圖的各種資料即為該變因的數值設定。

二、未來展望:雖然我們可經由電腦求得期望值,但我們尚未推導理論期望值,我們希望未
來能夠可以找出測資與期望值關係式;而本研究考慮到機率隨時間遞減和船的位置改變之變
因,希望未來能夠再將變因修正,以更貼近真實情況。

陸、參考文獻資料

[1] Math Support Centre. Statistics: Bayes’ Theorem. Retrieved from

http://www.hep.upenn.edu/~johnda/Papers/Bayes.pdf

19

You might also like