1.概述
本門課的核心是網絡的概念——網絡是事物之間相互關聯的一種模式。比如我們身在其中的社會網絡,體現朋友之間的社交聯繫。等等。
1.1網絡的基本問題
先放幾個社會中的網絡結構
金融機構之間的借貸網絡,其展現與標註方式揭示了不同部分及其作用。
2004年美國總統大選前政治性博客的網絡結構
行爲及其動力學
當人們談及複雜系統的連通性時,實際上是在討論兩個相關問題——結構層面的連通性(誰和誰相連);另一個是在行爲層面的連通性(每個個體的行動對於系統中每個其他個體都有隱含的後果。)
在Google上對Youtube的查詢量隨時間的變化情況
這種增長的可見性是整個人羣行爲的聚集效果。要理解這些過程是如何工作的,他們是怎麼通過許多人互相關聯的行爲實現的,需要研究聚合行爲的動力學。
多學科思想的交織
會用到數學建模的思想,建模是一個很重要的思想,是將實際問題轉換爲數學問題的過程。“不能用數學語言描述的知識不能夠稱爲科學,不能被建模的實際問題不能算是實際解決了。”(來自某某某的吹牛),反正很重要就對了。
本書的中心目標之一,就是要將各領域分別的研究實踐結合起來,以幫助形成這樣一個綜合。
1.2本書的核心內容
兩個主要理論圖論和博弈論。
圖論不用說了,沒學過圖論的人都不能稱作學過計算機。
博弈論是本書的特色——博弈論提供了關於個體行爲的一種模型,要點在於個體行爲的結果取決於其他個體的行爲。
2.實驗
2.1計算聚集係數和鄰里重疊度
要求
輸入:任意圖的鄰接矩陣
輸出:
1)每個節點的聚集係數
2)每個節點對的鄰里重疊度
相關定義
1)三元閉包:在一個社交圈內,若兩個人有一個共同的朋友,則這兩個人在未來成爲朋友的可能性就會提高。
2)聚集係數:節點A的聚集係數定義爲A的任意兩個朋友彼此也是朋友的概率——也就是A相鄰的節點之間邊的實際數/與A相鄰節點對的個數之比
下圖中A的聚集係數a)是1/6 b)是1/2
3)橋(Bridge)與捷徑(Local bridge):橋是指兩個端點之間唯一的路徑如下圖A與B之間的邊就是橋。
捷徑:如果邊AB的端點A和B沒有共同的朋友,則稱邊AB爲捷徑。如下圖
關係:從圖中可以明顯的看出,橋一定是捷徑但是捷徑不一定是橋.
4)強三元閉包性質:將社交網絡中的所有關係分爲兩類-強聯繫(朋友)和弱聯繫(熟人),若節點A與節點B與C之間的關係均爲強聯繫,且B和C之間無任何連接(強或弱),則稱節點A違反了強三元閉包性質.否則,稱節點A滿足強三元閉包性質.
下圖中所有的點都滿足強三元閉包性質.
5)捷徑與弱聯繫:在社交網絡中,若節點A滿足強三元閉包性質,並且至少有兩個強聯繫邊與之相連,則與其相連的任何捷徑均爲弱聯繫.
證明如下:
6)鄰里重疊度:定義一條邊的鄰里重疊度爲:與A,B均爲鄰居的節點數/與A,B中至少一個爲鄰居的節點數(不包含A,B)
可以看出當分子爲零,即鄰里重疊度爲零時,該邊是捷徑.
算法及代碼
算法:1)求聚集係數:先計算所有與當前節點連接的節點中間可能構成的連接(求法:(度(度-1)/2)(利用等差數列) 有多少個,這個數會作爲分母,然後計算實際上有多少個節點被連接上了,這個數會作爲分子。2)求鄰里重疊度:定義(A, B)邊的“與A、B均爲鄰居的節點數”與“與A、B中至少一個爲鄰居的節點數”的比值
輸入如下圖:
圖結構(使用鄰接矩陣儲存圖結構,主要的三個類的UML類圖如下):
主要代碼:
graph
template <class T>
class graph{
public:
virtual ~graph(){};
virtual int numberOfVertices() const = 0;
virtual int numberOfEdges() const = 0;
virtual bool existsEdge(int ,int ) const = 0;
virtual void insertEdge(edge<T>* ) = 0;//edge是一個模板類
virtual void easeEdge(int ,int ) = 0;
virtual int degree(int) const = 0;
virtual int inDegree(int ) const = 0;
virtual int outDegree(int ) const = 0;
virtual bool directed() const = 0;
virtual bool weighted() const = 0;
//訪問指定頂點的相鄰頂點s
virtual vertexIterator<T>* iterators(int ) = 0;
};
adjacencyWDigraph
template <class T>
class adjacencyWDigraph : public graph<T>{
protected:
int n;
int e;
T noEdge;//表示不存在的邊
public:
T **a;//鄰接矩陣
//1.帶有默認參數的構造器
adjacencyWDigraph(int nV = 0,T theNoEdge =0){
if(nV < 0){
throw "頂點個數必須大於0!";
}
n = nV;
e = 0;
noEdge = theNoEdge;
//make2dArray(a,n+1,n+1);
//創建鄰接矩陣
a = new T*[n+1];//數組的數組是二維數組
for (int j = 1; j <=n ; ++j) {
a[j] = new T[n+1];
}
for (int i = 1; i <= n ; ++i) {
//初始化鄰接矩陣
for (int j = 1; j <=n ; ++j) {
a[i][j] = noEdge;
}
}
}
int numberOfVertices() const { return n;}
int numberOfEdges() const { return e;}
bool directed() const { return true;}
bool weighted() const { return true;}
//2.判斷(i,j)邊是否存在
bool existsEdge(int i,int j) const{
if (i<1||j<1||i>n||j>n||a[i][j] == noEdge){
return false;
} else{
return true;
}
}
//3.插入邊
void insertEdge(edge<T>* theEdge){
int v1 = theEdge->vertex1();
int v2 = theEdge->vertex2();
if(v1<1||v2<1||v1>n||v2>n||v1==v2){
throw "如此插入非法!";
} else{
if (a[v1][v2] == noEdge){
e++;
}
a[v1][v2] = theEdge->weight();
}
}
//4.刪除邊
void easeEdge(int i,int j){
if(existsEdge(i,j)){
a[i][j] = noEdge;
e--;
}
}
//5.頂點的度
int degree(int theV) const{
throw "有向圖有入度和出度之分!";
}
//6.入度和出度
int outDegree(int theV) const {
int sum = 0;
for (int i = 1; i <= n; ++i) {
if (a[theV][i] != noEdge){
sum++;
}
}
return sum ;
}
int inDegree(int theV) const{
int sum = 0;
for (int i = 1; i <= n ; ++i) {
if(a[i][theV] != noEdge){
sum++;
}
}
return sum;
}
//7.遍歷器class
class myIterator : public vertexIterator<T>{
public:
myIterator(T* theRow,T theNoEdge,int numberOfV){
row = theRow;
noEdge = theNoEdge;
n = numberOfV;
currentVertex = 1;
}
~myIterator(){}
int next(T & theWeight){
for (int i = currentVertex; i <= n ; ++i) {
if (row[i] != noEdge){
currentVertex = i+1;
theWeight = row[i];
return i;
}
}
//不存在下一個鄰接的頂點
currentVertex = n+1;
return 0;
}
int next(){
for (int i = currentVertex; i <= n ; ++i) {
if (row[i] != noEdge){
currentVertex = i+1;
return i;
}
}
//不存在下一個鄰接的頂點
currentVertex = n+1;
return 0;
}
protected:
T* row;
T noEdge;
int n;
int currentVertex;
};
myIterator* iterators(int theV){
return new myIterator(a[theV],noEdge,n);
}
};
adjacencyWGraph
template<class T>
class adjacencyWGraph : public adjacencyWDigraph<T> {
public:
adjacencyWGraph(int numberOfVertices = 0, T theNoEdge = 0)
: adjacencyWDigraph<T>(numberOfVertices, theNoEdge) {}
void insertEdge(int v1, int v2, int weight) {
if (v1 < 1 || v2 < 1 || v1 > this->n || v2 > this->n || v1 == v2) {
throw "插入非法!";
}
//如果邊不存在則插入
if (this->a[v1][v2] == this->noEdge)
this->e++;
this->a[v1][v2] = weight;
this->a[v2][v1] = weight;
}
bool directed() const { return false; }
void eraseEdge(int i, int j) {
if (i >= 1 && j >= 1 && i <= this->n && j <= this->n && this->a[i][j] != this->noEdge) {
this->a[i][j] = this->noEdge;
this->a[j][i] = this->noEdge;
this->e--;
}
}
int degree(int theVertex) const {
int sum = 0;
for (int j = 1; j <= this->n; j++)
if (this->a[theVertex][j] != this->noEdge)
sum++;
return sum;
}
int outDegree(int theVertex) const {
return degree(theVertex);
}
int inDegree(int theVertex) const {
return degree(theVertex);
}
//返回A的任意兩個朋友之間也是朋友數目
int getFriendsIsFriends(int iE) {
int *friends = new int[degree(iE)];
int start = 0;
for (int j = 1; j <= this->n; ++j) {
if (this->existsEdge(iE, j)) {
friends[start++] = j;
//cout<<"鄰接的頂點是:"<<j<<endl;
}
}
int friendsIsFriends = 0;
//檢查朋友之間是否爲朋友
for (int i = 0; i < degree(iE); i++) {
//檢查這一行的值
for (int k = 0; k < degree(iE) - 1 - i; k++) {
int a = friends[i];
int b = friends[i + 1 + k];
if (this->existsEdge(a, b)) {
friendsIsFriends++;
}
}
}
return friendsIsFriends;
}
//返回與頂點i鄰接的頂點
int *getEdges(int i) {
int *my_result = new int[this->degree(i)];
//cout<<i<<"度爲:"<<this->degree(i)<<endl;
int start = 0;
for (int j = 1; j <= this->n ; ++j) {
if (this->existsEdge(i,j)){
my_result[start++] = j;
// cout<<i<<"的臨界點有"<<j<<endl;
}
}
//cout<<i<<"的相鄰的定點有幾個:"<< sizeof(my_result) <<endl;//返回的是指針的大小
return my_result;
}
};
求聚集係數
string aggCoeff[n+1];//記錄每個頂點的聚集係數
for (int k = 1; k <= n; ++k) {
int my_degree = 0;
my_degree = graph.degree(k);
//cout<<"度爲"<<my_degree<<endl;cout<<endl;
int denominator = my_degree*(my_degree-1)/2;
if (denominator==0){
aggCoeff[k]="0";
} else{
int molecule = graph.getFriendsIsFriends(k);
//cout<<"分子是:"<<molecule<<endl;
if(molecule==0){
aggCoeff[k]="0";
} else{
int temp=molecule/denominator;
if(temp==1){
aggCoeff[k]="1";
} else{
aggCoeff[k]=to_string(molecule)+"/"+to_string(denominator);
}
}
}
}
for (int l = 1; l <= n; ++l) {
cout<<"頂點"<<l<<"的聚集係數爲:"<<aggCoeff[l]<<endl;
}
求鄰里間的聚集係數
//記錄每對相鄰節點的鄰里重疊度
string *degreeEdges = new string[e];
int start2 = 0;
for (int m = 1; m < n ; ++m) {
for (int i = m+1; i <=n ; ++i) {
if (graph.existsEdge(m,i)){
//獲得與A,B鄰接的頂點
int *getAEdges = graph.getEdges(m);
int *getBEdges = graph.getEdges(i);
int legA = graph.degree(m);
int legB = graph.degree(i);
if(legA==1&&legB==1){
degreeEdges[start2++]="0";
} else{
int denominator = getDeno(getAEdges,getBEdges,legA,legB);
int molecule = getCoin(getAEdges,getBEdges,legA,legB);
if(denominator==molecule){
degreeEdges[start2++]="1";
} else if(molecule==0){
degreeEdges[start2++]="0";
} else{
degreeEdges[start2++]=to_string(molecule)+"/"+to_string(denominator);
}
}
cout<<"邊("<<m<<","<<i<<")之間的鄰里重疊度爲:"<<degreeEdges[start2-1]<<endl;
}
}
}
2.2謝林模型模擬
要求
輸入:n*n的矩陣,隨機佈局的兩種節點
輸出:
1)調節參數後輸出相應的結果
2)需要有界面顯示
相關定義
1)同質現象:我們和自己的朋友往往會有相同的特點,即諺語中所說的“物以類聚,人以羣分。”
2)選擇與社會影響:選擇——人們更具相似的特徵選擇朋友,是一種主觀的行爲。社會影響——人們會因爲需要和朋友們保持一致而改變自己的行爲,這是一種客觀的影響。
3)社團:社會交往的焦點。
4)歸屬網絡(Affiliation Networks):表明個體對社團的歸屬關係。是一個二分圖。
5)社會歸屬網絡(social-affiliation network):既包含一個個個體之間的社會網絡又包含個體和社團之間的歸屬網絡。
三元閉包原則在社會歸屬網絡中有了新的拓展:
6)社團閉包(focal closure):兩個個體之間因爲參加同一個社團而有建立連接的傾向。
7)會員閉包(membership closure):個體B會趨於參加朋友A參加的社團。
我們可以看出社團閉包是一種主觀的選擇而會員閉包是一種客觀的社會影響。
8)謝林模型:描述的是同質性對於空間隔離的影響與作用。(像美國的唐人街和黑人聚集區都是謝林模型的表現。)
算法及代碼
1)思想:
該模型的基本制約因素是,每一個代理要和一定量的同類代理成爲鄰居。 我們假設門檻值t適用於所有的代理:如果一個代理髮現自己擁有比t少的鄰居,他就有興趣挪到其他的單元,我們稱這樣的代理不滿意現狀。 例如,圖 (a)是對圖(b)的一種標註,不滿意現狀的代理用“ * ”號指出,對應門檻值等於3中,我們在每個代理後也添加了一個號碼,相當於給每個代理一個名字,不過這裏的關鍵是區分代理是屬千X類型還是O類型。
圖(b)
圖(a)
2)算法:逐步檢測各個像素點是否低於門檻值,如果低於門檻值則搬家。
3)關鍵代碼:
%根據百分比,計算像素點數量
empty_tmp = round(m * empty / 100);
ratio_tmp = round((m - empty_tmp) * ratio / 100);
sim_tmp = sim / 100;
str_tmp = get(handles.round_text,'String'); %獲取ROUND NUMBER的字符串
not_satis_red=[]; %初始化不滿意紅色點序列
not_satis_blue=[];%初始化不滿意藍色點序列
empty_now=[];%初始化空白像素點序列
%R(i,j)=1代表空置,2代表紅點,3代表藍點
for i=1:size
for j=1:size
switch R(i,j)
case 1
empty_now = [empty_now (j-1)*size+i];
case 2
satis_tmp = 0;
amount_tmp = 0;
%檢測不滿意情況
if i ~= 1 && R(i-1,j) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= 1 && j ~= 1 && R(i-1,j-1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= 1 && j ~= size && R(i-1,j+1) == R(i,j) satis_tmp = satis_tmp + 1; end
if j ~= 1 && R(i,j-1) == R(i,j) satis_tmp = satis_tmp + 1; end
if j ~= size && R(i,j+1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= size && j ~= 1 && R(i+1,j-1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= size && R(i+1,j) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= size && j ~= size && R(i+1,j+1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= 1 && j ~= 1 && R(i-1,j-1) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= 1 && R(i-1,j) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= 1 && j ~= size && R(i-1,j+1) ~= 1 amount_tmp = amount_tmp + 1; end
if j ~= 1 && R(i,j-1) ~= 1 amount_tmp = amount_tmp + 1; end
if j ~= size && R(i,j+1) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= size && j ~= 1 && R(i+1,j-1) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= size && R(i+1,j) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= size && j ~= size && R(i+1,j+1) ~= 1 amount_tmp = amount_tmp + 1; end
if satis_tmp<round(sim_tmp*amount_tmp)
not_satis_red =[not_satis_red (j-1)*size+i];
end
case 3
satis_tmp = 0;
amount_tmp = 0;
if i ~= 1 && R(i-1,j) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= 1 && j ~= 1 && R(i-1,j-1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= 1 && j ~= size && R(i-1,j+1) == R(i,j) satis_tmp = satis_tmp + 1; end
if j ~= 1 && R(i,j-1) == R(i,j) satis_tmp = satis_tmp + 1; end
if j ~= size && R(i,j+1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= size && j ~= 1 && R(i+1,j-1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= size && R(i+1,j) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= size && j ~= size && R(i+1,j+1) == R(i,j) satis_tmp = satis_tmp + 1; end
if i ~= 1 && j ~= 1 && R(i-1,j-1) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= 1 && R(i-1,j) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= 1 && j ~= size && R(i-1,j+1) ~= 1 amount_tmp = amount_tmp + 1; end
if j ~= 1 && R(i,j-1) ~= 1 amount_tmp = amount_tmp + 1; end
if j ~= size && R(i,j+1) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= size && j ~= 1 && R(i+1,j-1) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= size && R(i+1,j) ~= 1 amount_tmp = amount_tmp + 1; end
if i ~= size && j ~= size && R(i+1,j+1) ~= 1 amount_tmp = amount_tmp + 1; end
if satis_tmp<round(sim_tmp*amount_tmp)
not_satis_blue =[not_satis_blue (j-1)*size+i];
end
end
end
end