一、蒙特卡洛算法
1、定義
蒙特卡洛算法是以概率和統計的理論、方法爲基礎的一種數值計算方法,將所求解的問題同一定的概率模型相聯繫,用計算機實現統計模擬或抽樣,以獲得問題的近似解,故又稱隨機抽樣法或統計實驗法。
2、適用範圍
可以較好的解決多重積分計算、微分方程求解、積分方程求解、特徵值計算和非線性方程組求解等高難度和複雜的數學計算問題。
3、特點
蒙特卡洛算法可以應用在很多場合,但求的是近似解,在模擬樣本越大的情況下,越接近於真實值,單樣本數增加會帶來計算量的大幅上升。對於一些簡單問題來說,蒙特卡洛是個笨辦法,但對於許多問題來說,它往往是個有效,有時甚至是唯一可行的方法。
4、舉例
y = x^2 ,y = 12 - x 與 X 軸在第一象限與 X 軸圍成一個曲邊三角形。設計一個隨機試驗,求該圖形的近似值。
(1)作圖
Code:
%作圖
x = 0:0.25:12;
y1 = x.^2;
y2 = 12 - x;
plot(x, y1, x, y2)
xlabel('x');ylabel('y');
%產生圖例
legend('y1=x^2', 'y2=12-x');
title('蒙特卡洛算法');
%圖中x軸和y軸的範圍,中括號前面是y軸範圍,中括號後面是x軸範圍
axis([0 15 0 15]);
text(3, 9, '交點');
%加上網格線
grid on
(2)設計的隨機試驗的思想:在矩形區域[0,12]*[0.9]上產生服從均與分佈的10^7個隨機點,統計隨機點落在曲邊三角形內的個數,則曲邊三角形的面積近似於上述矩形的面積乘以頻率。
Code:
%蒙特卡洛算法的具體實現
%產生一個1行10000000列的矩陣,矩陣中每個數是從0到12之間隨機取
x = unifrnd(0, 12, [1, 10000000]);
y = unifrnd(0, 9, [1, 10000000]);
frequency = sum(y<x.^2&x<=3)+ sum(y<12-x&x>=3);
area = 12*9*frequency/10^7;
disp(area);
所求近似值:
參考博客:https://blog.csdn.net/u013414501/article/details/50478898
二、數據擬合
1、定義
已知有限個數據點,求近似函數,可不過已知數據點,只要求在某種意義下它在這些點上的總偏差最小,從而能較好的反應數據的整體變化趨勢。
2、常用方法
- 一般採用最小二乘法。
- 擬合的實現分爲 MATLAB 和 excel 實現。MATLAB 的實現就是 polyfit 函數,主要是多項式擬合。
3、舉例
(1) 數據如下:
序號 x y z
1 426.6279 0.066 2.897867
2 465.325 0.123 1.621569
3 504.0792 0.102 2.429227
4 419.1864 0.057 3.50554
5 464.2019 0.103 1.153921
6 383.0993 0.057 2.297169
7 416.3144 0.049 3.058917
8 464.2762 0.088 1.369858
9 453.0949 0.09 3.028741
10 376.9057 0.049 4.047241
11 409.0494 0.045 4.838143
12 449.4363 0.079 4.120973
13 372.1432 0.041 3.604795
14 389.0911 0.085 2.048922
15 446.7059 0.057 3.372603
16 347.5848 0.03 4.643016
17 379.3764 0.041 4.74171
18 453.6719 0.082 1.841441
19 388.1694 0.051 2.293532
20 444.9446 0.076 3.541803
21 437.4085 0.056 3.984765
22 408.9602 0.078 2.291967
23 393.7606 0.059 2.910391
24 443.1192 0.063 3.080523
25 514.1963 0.153 1.314749
26 377.8119 0.041 3.967584
27 421.5248 0.063 3.005718
28 421.5248 0.063 3.005718
29 421.5248 0.063 3.005718
30 421.5248 0.063 3.005718
31 421.5248 0.063 3.005718
32 421.5248 0.063 3.005718
33 421.5248 0.063 3.005718
34 421.5248 0.063 3.005718
35 421.5248 0.063 3.005718
36 421.5248 0.063 3.005718
37 416.1229 0.111 1.281646
38 369.019 0.04 2.861201
39 362.2008 0.036 3.060995
40 417.1425 0.038 3.69532
(2) 方法一:使用MATLAB編寫代碼
%讀取表格
A = xlsread('E:\表格\1.xls', 'Sheet1', 'A1:AN2');
B = A;
[I, J] = size(B);
%數據擬合
%x爲矩陣的第一行,y爲矩陣的第二行
x = A(1,:);
y = A(2,:);
%polyfit爲matlab中的擬合函數,第一個參數是數據的橫座標
%第二個參數是數據的縱座標,第三個參數是多項式的最高階數
%返回值p中包含n+1個多項式係數
p = polyfit(x, y, 2);
disp(p);
%下面是作圖的代碼
x1 = 300:10:600;
%polyval是matlab中的求值函數,求x1對應的函數值y1
y1 = polyval(p,x1);
plot(x,y,'*r',x1,y1,'-b');
%plot(x,'DisplayName','x','YDataSource','x');
%figure(gcf);
(3) 方法三:使用matlab的圖形化擬合包(推薦)
-
將數據導入工作區並通過cftool命令打開matlab的圖形化擬合包
-
選擇x、y變量
-
選擇擬合方式和最高項次數
-
得到擬合結果
使用圖形化擬合工具不僅簡單快捷,還可以使用多種擬合方式,尋找到最好的擬合曲線。
三、數據插值
1、定義
-
在離散數據的基礎上補插連續函數,使得這條連續曲線通過給定的全部離散數據點。即求過已知有限個數據點的近似函數。
-
從定義上看,插值和擬合有一定的相似度,但插值要求近似函數通過給定的所有離散數據,而擬合併不要求這樣,只要近似函數能較好的反映數據變化的趨勢即可(近似含義不同),當測量值是準確的,沒有誤差時,一般用插值;當測量值與真實值有誤差時,一般用數據擬合。
2、作用
插值是離散函數逼近的重要方法,利用它可通過函數在有限個點處的取值情況,估算出函數在其他點處的近似值。
3、舉例
%years、service和wage是原始數據
years = 1950:10:1990;
service = 10:10:30;
wage = [ 150.697 199.592 187.625 179.323 195.072; 250.287 203.212 179.092 322.767 226.505;153.706 426.730 249.633 120.281 598.243];
[X, Y] = meshgrid(years, service);
% % 三維曲線
% plot3(X, Y, wage)
% 三維曲面
figure
surf(X, Y, wage)
%interp2是matlab中的二維插值函數,前兩個參數是已知位置,後兩個是未知位置,w是未知位置的插值結果
w = interp2(service,years,wage,15,1975);
四、圖論
1、最短路問題
最短路問題就是選擇一條距離最短的路線。
例如:一名貨櫃車司機奉命在最短的時間內將一車貨物從甲地運往乙地。從甲地到乙地的公路網縱橫交錯,因此有多種行車路線,這名司機應選擇哪條線路呢?假設貨櫃車的運行速度是恆定的,那麼這一問題相當於需要找到一條從甲地到乙地的最短路。(Dijkstra算法)
具體介紹見這裏:最短路徑—Dijkstra算法和Floyd算法
(1)Dijkstra算法
先給出一個無向圖
用Dijkstra算法找出以A爲起點的單源最短路徑步驟如下
代碼模板:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<fstream>
using namespace std;
const int maxnum = 100;
const int maxint = 2147483647;
int dist[maxnum]; // 表示當前點到源點的最短路徑長度
int prev[maxnum]; // 記錄當前點的前一個結點
int c[maxnum][maxnum]; // 記錄圖的兩點間路徑長度
int n, line; // n表示圖的結點數,line表示路徑個數
void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
{
bool s[maxnum]; // 判斷是否已存入該點到S集合中
for(int i=1; i<=n; ++i)
{
dist[i] = c[v][i];
s[i] = 0; // 初始都未用過該點
if(dist[i] == maxint)
prev[i] = 0;
else
prev[i] = v;
}
dist[v] = 0;
s[v] = 1;
// 依次將未放入S集合的結點中,取dist[]最小值的結點,放入結合S中
// 一旦S包含了所有V中頂點,dist就記錄了從源點到所有其他頂點之間的最短路徑長度
for(int i=2; i<=n; ++i)
{
int tmp = maxint;
int u = v;
// 找出當前未使用的點j的dist[j]最小值
for(int j=1; j<=n; ++j)
if((!s[j]) && dist[j]<tmp)
{
u = j; // u保存當前鄰接點中距離最小的點的號碼
tmp = dist[j];
}
s[u] = 1; // 表示u點已存入S集合中
// 更新dist
for(int j=1; j<=n; ++j)
if((!s[j]) && c[u][j]<maxint)
{
int newdist = dist[u] + c[u][j];
if(newdist < dist[j])
{
dist[j] = newdist;
prev[j] = u;
}
}
}
}
void searchPath(int *prev,int v, int u)
{
int que[maxnum];
int tot = 1;
que[tot] = u;
tot++;
int tmp = prev[u];
while(tmp != v)
{
que[tot] = tmp;
tot++;
tmp = prev[tmp];
}
que[tot] = v;
for(int i=tot; i>=1; --i)
if(i != 1)
cout << que[i] << " -> ";
else
cout << que[i] << endl;
}
int main()
{
//freopen("input.txt", "r", stdin);
// 各數組都從下標1開始
// 輸入結點數
cin >> n;
// 輸入路徑數
cin >> line;
int p, q, len; // 輸入p, q兩點及其路徑長度
// 初始化c[][]爲maxint
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
c[i][j] = maxint;
for(int i=1; i<=line; ++i)
{
cin >> p >> q >> len;
if(len < c[p][q]) // 有重邊
{
c[p][q] = len; // p指向q
c[q][p] = len; // q指向p,這樣表示無向圖
}
}
for(int i=1; i<=n; ++i)
dist[i] = maxint;
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
printf("%-16d", c[i][j]);
printf("\n");
}
Dijkstra(n, 1, dist, prev, c); //僅調用函數求出了源點到其他點的距離 改法:Dijkstra(n, x, dist, prev, c); 其中x=1,2,3,4,...,n
// for(int i=1; i<=n; ++i) //dist存儲了源點到其他點的距離情況
// {
// printf("%-16d", dist[i]);
// }
printf("\n");
// 最短路徑長度
cout << "源點到最後一個頂點的最短路徑長度: " << dist[n] << endl;
// 路徑
cout << "源點到最後一個頂點的路徑爲: ";
searchPath(prev, 1, n);
return 0;
}
/*
輸入數據:
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
輸出數據:
999999 10 999999 30 100
10 999999 50 999999 999999
999999 50 999999 20 10
30 999999 20 999999 60
100 999999 10 60 999999
源點到最後一個頂點的最短路徑長度: 60
源點到最後一個頂點的路徑爲: 1 -> 4 -> 3 -> 5
*/
(2)Floyd算法
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<fstream>
using namespace std;
//設點與點之間的距離均爲double型
double INFTY=2147483647;
const int MAX=1000;
double dis[MAX][MAX];
double a[MAX][MAX];
int path[MAX][MAX];
int n,m; //結點個數
void Floyd()
{
int i,j,k;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
dis[i][j]=a[i][j];
if(i!=j&&a[i][j]<INFTY)
{
path[i][j]=i;
}
else
path[i][j]=-1;
}
}
for(k=1;k<=n;k++)
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(dis[i][k]+dis[k][j]<dis[i][j])
{
dis[i][j]=dis[i][k]+dis[k][j];
path[i][j]=path[k][j];
}
}
}
}
}
int main()
{
//freopen("datain.txt","r",stdin);
int beg,enda;
double dist;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)
a[i][j]=0;
else
a[i][j]=INFTY;
}
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%lf",&beg,&enda,&dist);
a[beg][enda]=a[enda][beg]=dist;
}
Floyd();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
printf("%-12lf",dis[i][j]);
}
printf("\n");
}
return 0;
}