Typescript的interface、class和abstract class

interface,class,和abstract class這3個概念,既有聯繫,又有區別,本文嘗試着結合官方文檔來闡述這三者之間的關係。

1. Declaration Merging

Declaration Type Namespace Type Value
Namespace X X
Class X X
Enum X X
Interface X
Type Alias X
Function X
Variable X

首先我們來講一下上面這張表格,當我們第一列的關鍵字進行聲明時,我們在做什麼。

namespace job {
   haircut(): void;
}

class Man{
	name: string;
}
let imgss = new Man();

enum Color {red, blue, yellow}

interface dogfood {

  brand: string;
  price: number
}
type event = 'mouse' | 'keyboard';

function foo(){}

let a = 2;
var b = {};
const c = null;
	

namespace用來聲明一個命名空間,比較著名的命名空間有lodash,裏面有一堆工具函數,統統放在一個叫_的namespace裏面,同時你也可以let $ = _;所以namespace也聲明了一個值。

class聲明了一個值,也聲明了一種類型,你可以把Man賦值給一個變量,所以class是一種值,也可以說imgss是一個Man(類型),此時Man承擔了一種類型的角色。

enum聲明了一個值,也聲明了一種類型。我們說red是一種Color,Color在這裏承擔類型的角色,也可以把Color賦值給一個變量

interface聲明了一種類型,但是你不能把dogfood賦值給某個變量,否則你會得到一個報錯“dogfood’ only refers to a type, but is being used as a value here`

其他function,let,var,const都在聲明一個值,你 不能說xxx是一個a,或者xxx是一個foo,不能把值當成類型使用。

2. interface和class

我們知道,不算symbol,js中有6種基本類型,number,string,boolean,null, undefined, object。但是只依靠這幾種類型,來描述某個函數需要傳什麼樣的參數,是遠遠不夠的,這也是interface的使命–描述一個值(value)的形狀(type)。

現在我們來看class,class首先也具有interface的能力,描述一個形狀,或者說代表一種類型。此外class還提供了實現,也就是說可以被實例化;

所以class可以implements interface:

interface ManLike {
  speak(): void;
  leg: number;
  hand: number;
}
class Human implements ManLike {
  leg: number = 2;
  hand: number = 2;
  speak() {
    console.log('i can speak');
  }
}

而interface可以extends class,此時的class承擔類型的角色

interface Chinese extends Human {
  country: string;
}

那麼interface能不能extends enum或者type alias呢,這兩個兄弟也聲明了type啊,答案是不行的,官方報錯的信息:

An interface can only extend an object type or intersection of object types with statically known members.

3. class和abstract class

class和abstract class的區別主要是abstract class不能被實例化:

abstract Human {
	name: string;
    abstract lang(): void;
	toString() {
    	return `<human:${this.name}>`
    }
}
new Human // Cannot create an instance of an abstract class.

4. interface和abstract class

兩者都不能被實例化,但是abstract class 也可以被賦值給變量。
interface 裏面不能有方法的實現,abstract class 可以提供部分的方法實現,這些方法可以被子類調用。

參考: https://www.typescriptlang.org/docs/handbook/declaration-merging.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

EM(最大期望)算法推導、GMM的應用與代碼實現

  EM算法是一種迭代算法,用於含有隱變量的概率模型參數的極大似然估計。

使用EM算法的原因

  首先舉李航老師《統計學習方法》中的例子來說明為什麼要用EM算法估計含有隱變量的概率模型參數。

  假設有三枚硬幣,分別記作A, B, C。這些硬幣正面出現的概率分別是$\pi,p,q$。進行如下擲硬幣試驗:先擲硬幣A,根據其結果選出硬幣B或C,正面選硬幣B,反面邊硬幣C;然後擲選出的硬幣,擲硬幣的結果出現正面記作1,反面記作0;獨立地重複$n$次試驗,觀測結果為$\{y_1,y_2,…,y_n\}$。問三硬幣出現正面的概率。

  三硬幣模型(也就是第二枚硬幣正反面的概率)可以寫作

$ \begin{aligned} &P(y|\pi,p,q) \\ =&\sum\limits_z P(y,z|\pi,p,q)\\ =&\sum\limits_z P(y|z,\pi,p,q)P(z|\pi,p,q)\\ =&\pi p^y(1-p)^{1-y}+(1-\pi)q^y(1-q)^{1-y} \end{aligned} $

  其中$z$表示硬幣A的結果,也就是前面說的隱變量。通常我們直接使用極大似然估計,即最大化似然函數

$ \begin{aligned} &\max\limits_{\pi,p,q}\prod\limits_{i=1}^n P(y_i|\pi,p,q) \\ =&\max\limits_{\pi,p,q}\prod\limits_{i=1}^n[\pi p^{y_i}(1-p)^{1-y_i}+(1-\pi)q^{y_i}(1-q)^{1-y_i}]\\ =&\max\limits_{\pi,p,q}\sum\limits_{i=1}^n\log[\pi p^{y_i}(1-p)^{1-y_i}+(1-\pi)q^{y_i}(1-q)^{1-y_i}]\\ =&\max\limits_{\pi,p,q}L(\pi,p,q) \end{aligned} $

  分別對$\pi,p,q$求偏導並等於0,求解線性方程組來估計這三個參數。但是,由於它是帶有隱變量的,在獲取最終的隨機變量之前有一個分支選擇的過程,導致這個$\log$的內部是加和的形式,計算導數十分困難,而待求解的方程組不是線性方程組。當複雜度一高,解這種方程組幾乎成為不可能的事。以下推導EM算法,它以迭代的方式來求解這些參數,應該也算一種貪心吧。

算法導出與理解

  對於參數為$\theta$且含有隱變量$Z$的概率模型,進行$n$次抽樣。假設隨機變量$Y$的觀察值為$\mathcal{Y} = \{y_1,y_2,…,y_n\}$,隱變量$Z$的$m$個可能的取值為$\mathcal{Z}=\{z_1,z_2,…,z_m\}$。

  寫出似然函數:

$ \begin{aligned} L(\theta) &= \sum\limits_{Y\in\mathcal{Y}}\log P(Y|\theta)\\ &=\sum\limits_{Y\in\mathcal{Y}}\log \sum\limits_{Z\in \mathcal{Z}} P(Y,Z|\theta)\\ \end{aligned} $

  EM算法首先初始化參數$\theta = \theta^0$,然後每一步迭代都會使似然函數增大,即$L(\theta^{k+1})\ge L(\theta^k)$。如何做到不斷變大呢?考慮迭代前的似然函數(為了方便不用$\theta^{k+1}$):

$ \begin{gather} \begin{aligned} L(\theta)=&\sum\limits_{Y\in \mathcal{Y}} \log\sum\limits_{Z\in \mathcal{Z}} P(Y,Z|\theta)\\ =&\sum\limits_{Y\in \mathcal{Y}} \log\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\frac{P(Y,Z|\theta)}{P(Z|Y,\theta^k)}\\ \end{aligned} \label{} \end{gather} $

  至於上式的第二個等式為什麼取出$P(Z|Y,\theta^k)$而不是別的,正向的原因我想不出來,馬後炮原因在後面記錄。

  考慮其中的求和

$ \sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)=1$

  且由於$\log$函數是凹函數,因此由Jenson不等式得

$ \begin{gather} \begin{aligned} L(\theta) \ge&\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\log\frac{P(Y,Z|\theta)}{P(Z|Y,\theta^k)}\\ =&B(\theta,\theta^k) \end{aligned}\label{} \end{gather} $

  當$\theta = \theta^k$時,有

$ \begin{gather} \begin{aligned} L(\theta^k) \ge& B(\theta^k,\theta^k)\\ =&\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\log\frac{P(Y,Z|\theta^k)}{P(Z|Y,\theta^k)}\\ =&\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\log P(Y|\theta^k)\\ =&\sum\limits_{Y\in \mathcal{Y}}\log P(Y|\theta^k)\\ =&L(\theta^k)\\ \end{aligned} \label{} \end{gather} $

  也就是在這時,$(2)$式取等,即$L(\theta^k) = B(\theta^k,\theta^k)$。取

$ \begin{gather} \theta^*=\text{arg}\max\limits_{\theta}B(\theta,\theta^k)\label{} \end{gather} $

  可得不等式

$L(\theta^*)\ge B(\theta^*,\theta^k)\ge B(\theta^k,\theta^k) = L(\theta^k)$

  所以,我們只要優化$(4)$式,讓$\theta^{k+1} = \theta^*$,即可保證每次迭代的非遞減勢頭,有$L(\theta^{k+1})\ge L(\theta^k)$。而由於似然函數是概率乘積的對數,一定有$L(\theta) < 0$,所以迭代有上界並且會收斂。以下是《統計學習方法》中EM算法一次迭代的示意圖:

  進一步簡化$(4)$式,去掉優化無關項:

$ \begin{aligned} \theta^*=&\text{arg}\max\limits_{\theta}B(\theta,\theta^k) \\ =&\text{arg}\max\limits_{\theta}\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\log\frac{P(Y,Z|\theta)}{P(Z|Y,\theta^k)} \\ =&\text{arg}\max\limits_{\theta}\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\log P(Y,Z|\theta) \\ =&\text{arg}\max\limits_{\theta}Q(\theta,\theta^k) \\ \end{aligned} $

  $Q$函數使用導數求極值的方程與沒有隱變量的方程類似,容易求解。

  綜上,EM算法的流程為:

  1. 設置$\theta^0$的初值。EM算法對初值是敏感的,不同初值迭代出來的結果可能不同。

  2. 更新$\theta^k = \text{arg}\max\limits_{\theta}Q(\theta,\theta^{k-1})$。理解上來說,通常將這一步分為計算$Q$與極大化$Q$兩步,即求期望E與求極大M,但在代碼中並不會將它們分出來,因此這裏濃縮為一步。另外,如果這個優化很難計算的話,因為有不等式的保證,直接取$\theta^k$為某個$\hat{\theta}$,只要有$Q(\hat{\theta},\theta^{k-1})\ge Q(\theta^{k-1},\theta^{k-1})$即可。

  3. 比較$\theta^k$與$\theta^{k-1}$的差異,比如求它們的差的二范數,若小於一定閾值就結束迭代,否則重複步驟2。

  下面記錄一下我對$(1)$式取出$P(Z|Y,\theta^k)$而不取別的$P$的理解:

  經過以上的推導,我認為這是為了給不等式取等創造條件。如果不能確定$L(\theta^k)$與$Q(\theta^k,\theta^k)$能否取等,那麼取$Q$的最大值$Q(\theta^*,\theta^k)$時,儘管有$Q(\theta^*,\theta^k)\ge Q(\theta^k,\theta^k)$,但並不能保證$L(\theta^*)\ge L(\theta^k)$,迭代的不減性質就就沒了。

  我這裏暫且把它看做一種巧合,是研究EM算法的大佬,碰巧想用Jenson不等式來迭代而構造出來的一種做法。本人段位還太弱,無法正向理解其中的緣故,只能以這種方式來揣度大佬的思路了。知乎大佬發的EM算法九層理解(點擊鏈接),我當前只能到第3層,有時間一定要拜讀一下深度學習之父的著作。

高斯混合模型的應用

迭代式推導

  假設高斯混合模型混合了$m$個高斯分佈,參數為$\theta = (\alpha_1,\theta_1,\alpha_2,\theta_2,…,\alpha_m,\theta_m),\theta_i=(\mu_i,\sigma_i)$則整個概率分佈為:

$\displaystyle P(y|\theta) = \sum\limits_{i=1}^m\alpha_i \phi(y|\theta_i) =  \sum\limits_{i=1}^m\frac{\alpha_i }{\sqrt{2\pi}\sigma_i}\exp\left(-\frac{(y-\mu_i)^2}{2\sigma_i^2}\right),\;\text{where}\;\sum\limits_{j=1}^m\alpha_j = 1$

  對混合分佈抽樣$n$次得到$\{y_1,…,y_n\}$,則在第$k+1$次迭代,待優化式為:

$\begin{gather}\begin{aligned} &\max\limits_{\theta}Q(\theta,\theta^k) \\ =&\max\limits_{\theta}\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta^k)\log P(Y,Z|\theta) \\ =&\max\limits_{\theta}\sum\limits_{Y\in \mathcal{Y}}\sum\limits_{Z\in \mathcal{Z}} \frac{P(Z,Y|\theta^k)}{P(Y|\theta^k)}\log P(Y,Z|\theta) \\ =&\max\limits_{\theta}\sum\limits_{i=1}^n\sum\limits_{j=1}^m \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)} \log \left[\alpha_j\phi(y_i|\theta_j)\right] \\ =&\max\limits_{\theta}\sum\limits_{i=1}^n\sum\limits_{j=1}^m \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)} \log \left[ \frac{\alpha_j}{\sqrt{2\pi}\sigma_j}\exp\left(-\frac{(y_i-\mu_j)^2}{2\sigma_j^2}\right) \right]\\ =&\max\limits_{\theta}\sum\limits_{j=1}^m \sum\limits_{i=1}^n \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)} \left[ \log \alpha_j – \log \sigma_j-\frac{(y_i-\mu_j)^2}{2\sigma_j^2} \right]\\  \end{aligned} \label{}\end{gather}$

計算α

  定義

$\displaystyle n_j = \sum\limits_{i=1}^n \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)}$

  則對於$\alpha$,優化式為

$\begin{gather} \begin{aligned} \max\limits_{\alpha}\sum\limits_{j=1}^m n_j \log \alpha_j \end{aligned} \label{}\end{gather}$

  又因為$\sum\limits_{j=1}^m \alpha_j=1$,所以只需優化$m-1$個參數,上式變為:

$ \max\limits_\alpha \left[ \begin{matrix} n_1&n_2&\cdots &n_{m-1}&n_{m}\\ \end{matrix} \right] \cdot \left[ \begin{matrix} \log\alpha_1\\ \log\alpha_2\\ \vdots\\ \log\alpha_{m-1}\\ \log(1-\alpha_1-\cdots-\alpha_{m-1})\\ \end{matrix} \right] $

  對每個$\alpha_j$求導並等於0,得到線性方程組:

$\left[\begin{matrix}n_1+n_m&n_1&n_1&\cdots&n_1\\n_2&n_2+n_m&n_2&\cdots&n_2\\n_3&n_3&n_3+n_m&\cdots&n_3\\&&&\vdots&\\n_{m-1}&n_{m-1}&n_{m-1}&\cdots&n_{m-1}+n_m\\\end{matrix}\right]\cdot\left[\begin{matrix}\alpha_1\\\alpha_2\\\alpha_3\\\vdots\\\alpha_{m-1}\\\end{matrix}\right]=\left[\begin{matrix}n_1\\n_2\\n_3\\\vdots\\n_{m-1}\\\end{matrix}\right]$

  求解這個爪形線性方程組,得到

$\left[\begin{matrix}\sum_{j=1}^mn_j/n_1&0&0&\cdots&0\\-n_2/n_1&1&0&\cdots&0\\-n_3/n_1&0&1&\cdots&0\\&&&\vdots&\\-n_{m-1}/n_1&0&0&\cdots&1\\\end{matrix}\right]\cdot\left[\begin{matrix}\alpha_1\\\alpha_2\\\alpha_3\\\vdots\\\alpha_{m-1}\\\end{matrix}\right]=\left[\begin{matrix}1\\0\\0\\\vdots\\0\\\end{matrix}\right]$

  因為

$\displaystyle \sum\limits_{j=1}^m n_j =   \sum\limits_{j=1}^m\sum\limits_{i=1}^n \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)}=\sum\limits_{i=1}^n \sum\limits_{j=1}^m \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)} =\sum\limits_{i=1}^n 1 =  n$

  解得

$\displaystyle\alpha_j = \frac{n_j}{n} = \frac{1}{n}\sum\limits_{i=1}^n \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)}$

計算σ與μ

  與$\alpha$不同,它的方程組是所有$\alpha_j$之間聯立的;而$\sigma,\mu$的方程組則是$\sigma_j$與$\mu_j$之間聯立的。定義

$\displaystyle p_{ji} = \frac{\alpha_j^k\phi(y_i|\theta_j^k)} {\sum\limits_{l=1}^m \alpha_l^k\phi(y_i|\theta_l^k)}$

  則對於$\sigma_j,\mu_j$,優化式為(比較$(6),(7)$式的區別)

$\begin{gather}\displaystyle\min\limits_{\sigma_j,\mu_j}\sum\limits_{i=1}^n p_{ji} \left(\log \sigma_j+\frac{(y_i-\mu_j)^2}{2\sigma_j^2} \right)\label{}\end{gather}$

  對上式求導等於0,解得

$ \begin{aligned} &\mu_j = \frac{\sum\limits_{i=1}^np_{ji}y_i}{\sum\limits_{i=1}^np_{ji}} = \frac{\sum\limits_{i=1}^np_{ji}y_i}{n_j} = \frac{\sum\limits_{i=1}^np_{ji}y_i}{n\alpha_j}\\ &\sigma^2_j = \frac{\sum\limits_{i=1}^np_{ji}(y_i-\mu_j)^2}{\sum\limits_{i=1}^np_{ji}} = \frac{\sum\limits_{i=1}^np_{ji}(y_i-\mu_j)^2}{n_j} = \frac{\sum\limits_{i=1}^np_{ji}(y_i-\mu_j)^2}{n\alpha_j} \end{aligned} $

代碼實現

  對於概率密度為$P(x) = −2x+2,x\in (0,1)$的隨機變量,以下代碼實現GMM對這一概率密度的的擬合。共10000個抽樣,GMM混合了100個高斯分佈。

#%%定義參數、函數、抽樣
import numpy as np
import matplotlib.pyplot as plt

dis_num = 100 #用於擬合的分佈數量
sample_num = 10000 #用於擬合的分佈數量
alphas = np.random.rand(dis_num) 
alphas /= np.sum(alphas)  
mus = np.random.rand(dis_num)
sigmas = np.random.rand(dis_num)**2#方差,不是標準差
samples = 1-(1-np.random.rand(sample_num))**0.5 #樣本
C_pi = (2*np.pi)**0.5

dis_val = np.zeros([sample_num,dis_num])    #每個樣本在每個分佈成員上都有值,形成一個sample_num*dis_num的矩陣
pij = np.zeros([sample_num,dis_num])        #pij矩陣
def calc_dis_val(sample,alpha,mu,sigma,c_pi):
    return alpha*np.exp(-(sample[:,np.newaxis]-mu)**2/(2*sigma))/(c_pi*sigma**0.5) 
def calc_pij(dis_v):  
    return dis_v / dis_v.sum(axis = 1)[:,np.newaxis]      
#%%優化 
for i in range(1000):
    print(i)
    dis_val = calc_dis_val(samples,alphas,mus,sigmas,C_pi)
    pij = calc_pij(dis_val)  
    nj = pij.sum(axis = 0)
    alphas_before = alphas
    alphas = nj / sample_num
    mus = (pij*samples[:,np.newaxis]).sum(axis=0)/nj
    sigmas = (pij*(samples[:,np.newaxis] - mus)**2 ).sum(axis=0)/nj
    a = np.linalg.norm(alphas_before - alphas)
    print(a)
    if  a< 0.001:
        break

#%%繪圖 
plt.rcParams['font.sans-serif']=['SimHei'] #用來正常显示中文標籤
plt.rcParams['axes.unicode_minus']=False #用來正常显示負號
def get_dis_val(x,alpha,sigma,mu,c_pi):
    y = np.zeros([len(x)]) 
    for a,s,m in zip(alpha,sigma,mu):   
        y += a*np.exp(-(x-m)**2/(2*s))/(c_pi*s**0.5)   
    return y
def paint(alpha,sigma,mu,c_pi,samples):
    x = np.linspace(-1,2,500)
    y = get_dis_val(x,alpha,sigma,mu,c_pi) 
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.hist(samples,density = True,label = '抽樣分佈') 
    ax.plot(x,y,label = "擬合的概率密度")
    ax.legend(loc = 'best')
    plt.show()
paint(alphas,sigmas,mus,C_pi,samples)

  以下是擬合結果圖,有點像是核函數估計,但是完全不同:

EM算法的推廣

  EM算法的推廣是對EM算法的另一種解釋,最終的結論是一樣的,它可以使我們對EM算法的理解更加深入。它也解釋了我在$(1)$式下方提出的疑問:為什麼取出$P(Z|Y,\theta^k)$而不是別的。

  定義$F$函數,即所謂Free energy自由能(自由能具體是啥先不研究了):

$ \begin{aligned} F(\tilde{P},\theta) &= E_{\tilde{P}}(\log P(Y,Z|\theta)) + H(\tilde{P})\\ &= \sum\limits_{Z\in \mathcal{Z}} \tilde{P}(Z)\log P(Y,Z|\theta) – \sum\limits_{Z\in \mathcal{Z}} \tilde{P}(Z)\log \tilde{P}(Z)\\ \end{aligned} $

  其中$\tilde{P}$是$Z$的某個概率分佈(不一定是單獨的分佈,可能是在某個條件下的分佈),$E_{\tilde{P}}$表示分佈$\tilde{P}$下的期望,$H$表示信息熵。

  我們計算一下,對於固定的$\theta$,什麼樣的$\tilde{P}$會使$F(\tilde{P},\theta) $最大。也就是找到一個函數$\tilde{P}_{\theta}$,使$F$極大,寫成優化的形式就是(這裡是找函數而不是找參數哦,理解上可能要用到泛函分析的內容):

$ \begin{aligned} &\max\limits_{\tilde{P}} \sum\limits_{Z\in \mathcal{Z}} \tilde{P}(Z)\log P(Y,Z|\theta) – \sum\limits_{Z\in \mathcal{Z}} \tilde{P}(Z)\log \tilde{P}(Z)\\ &\;\text{s.t.}\; \sum\limits_{Z\in \mathcal{Z}}\tilde{P}(Z) = 1 \end{aligned} $

  拉格朗日函數(拉格朗日對偶性,點擊鏈接)為:

$ \begin{aligned} L =  \sum\limits_{Z\in \mathcal{Z}} \tilde{P}(Z)\log P(Y,Z|\theta) – \sum\limits_{Z\in \mathcal{Z}} \tilde{P}(Z)\log \tilde{P}(Z)+ \lambda\left(1-\sum\limits_{Z\in \mathcal{Z}}\tilde{P}(Z)\right) \end{aligned} $

  因為每個$\tilde{P}(Z)$之間都是求和,沒有其它其它諸如乘積的操作,所以可以直接令$L$對某個$\tilde{P}(Z)$求導等於$0$來計算極值:

$ \begin{aligned} \frac{\partial L}{\partial \tilde{P}(Z)} = \log P(Y,Z|\theta) – \log \tilde{P}(Z) -1 -\lambda = 0 \end{aligned} $

  於是可以推出:

$ \begin{aligned} P(Y,Z|\theta) = e^{1+\lambda}\tilde{P}(Z) \end{aligned} $

  又由約束$\sum\limits_{Z\in \mathcal{Z}}\tilde{P}(Z) = 1$:

$P(Y|\theta) = e^{1+\lambda}$

  於是得到

$\begin{gather}\tilde{P}_{\theta}(Z) = P(Z|Y,\theta)\label{}\end{gather}$

  代回$F(\tilde{P},\theta)$,得到

$ \begin{aligned} F(\tilde{P}_\theta,\theta) &= \sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta)\log P(Y,Z|\theta) – \sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta)\log P(Z|Y,\theta)\\ &= \sum\limits_{Z\in \mathcal{Z}} P(Z|Y,\theta)\log \frac{P(Y,Z|\theta)}{P(Z|Y,\theta)}\\ &= \log P(Y|\theta)\\ \end{aligned} $

  也就是說,對$F$關於$\tilde{P}$進行最大化后,$F$就是待求分佈的對數似然;然後再關於$\theta$最大化,也就算得了最終要估計的參數$\hat{\theta}$。所以,EM算法也可以解釋為$F$的極大-極大算法。優化結果$(8)$式也解釋了我之前在$(1)$式下方的提問。

  那麼,怎麼使用$F$函數進行估計呢?還是要用迭代來算,迭代方式是和前面介紹的一樣的(懶得記錄了,統計學習方法上直接看吧)。實際上,$F$函數的方法只是提供了EM算法的另一種解釋,具體方法上並沒有提升之處。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

【離散優化】覆蓋問題

覆蓋問題

我們知道設施選址問題有兩類基礎問題,分別是中值問題和覆蓋問題,下面要介紹的就是覆蓋問題。

什麼是覆蓋問題?

覆蓋問題是以所期望的服務範圍滿足大多數或者所有用戶需求為前提,確定設施的位置。覆蓋模型的思想是離服務設施較近的用戶越多,則服務越好。

覆蓋問題的分類

覆蓋問題主要分為兩類:

  • 集合覆蓋問題(Location Set Covering Problem,LSCP)
  • 最大覆蓋問題(Maximum Covering Location Problem,MCLP)

覆蓋模型常用於哪些場景?

由於 P-中值模型常以總距離或者總時間作為測度指標,使得其並不適用於一些特殊的場景,比如消防中心和救護車等應急設施的區位選址問題,而覆蓋模型則比較適用於這些場景。

如何定義覆蓋?

如果需求點 \(i\) 到備選設施點 \(j\) 的距離或者時間小於臨界值 \(D_c\),那麼稱需求點 \(i\) 被候選設施點 \(j\) 覆蓋。、

下面介紹兩類覆蓋問題的數學模型表達

集合覆蓋問題 (Location Set Covering Problem,LSCP)

目標函數:

\[\min \sum_{j \in J}x_j \]

約束:

\[\sum_{j \in N_i} x_j \geqslant 1 \quad \forall i \in I \tag{c-1} \]

\[x_j \in \{0, 1\} \quad \forall j \in J \tag{c-2} \]

其中,

  • \(N_i = \{j:a_{ij}=1\}\) 是覆蓋需求點 \(i\) 的候選設施點的集合,變量 \(a_{ij}\) 用來判斷需求點 \(i\) 是否被候選設施點 \(j\) 覆蓋,若是,則 \(a_{ij}=1\),否則 \(a_{ij}=0\)
  • 目標函數旨在尋求設施總量最小
  • 約束 \(c-1\) 保證每個需求點至少被一個設施服務範圍所覆蓋
  • 約束 \(c-2\) 是決策變量的取值範圍

在某些場景中,集合覆蓋問題有以下兩個缺點:

  • 為了保證所有需求點均被覆蓋而引入過多的設施,以至於超出預算
  • 模型無法區分需求點的需求強度

現實生活中,常常由於預算或者資源的約束,有限的設施不能保證空間中所有需求點都被覆蓋,此時,優先考慮需求強度大的需求點是十分必要的,下面要介紹的最大覆蓋模型就是為了解決這個問題而被提出。

最大覆蓋問題(Maximum Covering Location Problem,MCLP)

目標函數

\[\max \sum_{i \in N_i} \omega_iz_i \]

約束

\[z_i \leqslant \sum_{j \in N_i}x_j \quad \forall i \in I \tag{c-1} \]

\[\sum_{j\in J}x_j = p \tag{c-2} \]

\[x_j \in \{0,1\} \quad \forall j \in J \tag{c-3} \]

\[z_i = \{0, 1\} \quad \forall i \in I \tag{c-4} \]

其中,

  • \(\omega_i\) 為需求點 \(i\) 的需求強度

  • \(z_i\) 用來判斷需求點 \(i\) 是否被覆蓋,若覆蓋,則為 1,否則為 0

  • 目標函數旨在尋求有限設施(\(p\) 個)覆蓋的需求最多

  • 約束 \(c-1\) 要求除非在備選設施點中已定位一個設施可以覆蓋需求點 \(i\),否則需求點 \(i\) 將不被記作被覆蓋

  • 約束 \(c-2\) 限制設施的總數為 \(p\)

  • 約束 \(c-3, c-4\) 是決策變量的取值範圍

更多種類的選址問題

以上介紹的覆蓋問題的基礎模型框架,然而具體問題一般是較為複雜的設施選址問題,這就需要我們對基礎模型設置不同的條件從而進行擴展,比如:

  • 用於環境污染防治的鄰避型設施選址問題
  • 用於不同服務等級的層次型設置選址問題
  • 用於商業競爭的競爭型設施選址問題
  • 選址問題也開始考慮動態、不確定性等因素

總結

總結以上兩類問題,我們可以發現最大覆蓋模型和集合覆蓋模型的主要區別在於對設施數量和需求強度的關注不同,前者一般適用於建設經費充足或者設施成本相同的情況,後者則適用於有設施成本約束的選址決策。

參考文獻

本文內容主要從論文《設施選址問題中的基礎模型與求解方法比較》總結而來。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

【Mongodb】 可複製集搭建

可複製集 replica set

概念圖

可複製集需要至少3個以上的mongodb節點,其中有一個主節點promary,其餘的為副本節點secondary

可複製集有三個角色:

  • 主要成員(Primary):主要接收所有寫操作。就是主節點。
  • 副本成員(Secondary):從主節點通過複製操作以維護相同的數據集,即備份數據,不可寫操作,但可以讀操作(但需要配置)。是默認的一種從節點類型。
  • 仲裁者(Arbiter):不保留任何數據的副本,只具有投票選舉作用。當然也可以將仲裁服務器維護為副本集的一部分,即副本成員同時也可以是仲裁者。也是一種從節點類型。

關於仲裁者:
如果主節點+副本節點是偶數推薦添加仲裁者,如果主節點+ 副本節點是奇數可以不添加仲裁者。仲裁者將永遠是仲裁者,而主要人員可能會退出並成為次要人員,而次要人員可能成為選舉期間的主要人員。

為什麼要用可複製集?它有什麼重要性?

  1. 避免數據丟失,保障數據安全,提高系統安全性;
    (最少3節點,最大50節點)
  2. 自動化災備機制,主節點宕機后通過選舉產生新主機;提高系統健壯性;
    (7個選舉節點上限)
  3. 讀寫分離,負載均衡,提高系統性能;

搭建

準備三個mongodb節點

正準備三個mongodb節點,我們先搭建一個主節點,2個副本節點的模式
修改配置mongo.conf

  • 第一個
systemLog:
  #MongoDB發送所有日誌輸出的目標指定為文件 
  destination: file
  #mongod或mongos應向其發送所有診斷日誌記錄信息的日誌文件的路徑 
  path: "/home/amber/mongodb/mongodb-001/log/mongod.log" 
  #當mongos或mongod實例重新啟動時,mongos或mongod會將新條目附加到現有日誌文件的末尾。 
  logAppend: true
storage: 
  #mongod實例存儲其數據的目錄。storage.dbPath設置僅適用於mongod。 
  dbPath: "/home/amber/mongodb/mongodb-001/data/db" 
  journal:
    #啟用或禁用持久性日誌以確保數據文件保持有效和可恢復。 
    enabled: true
processManagement:
  #啟用在後台運行mongos或mongod進程的守護進程模式。 
  fork: true 
  #指定用於保存mongos或mongod進程的進程ID的文件位置,其中mongos或mongod將寫入其PID 
  pidFilePath: "/home/amber/mongodb/mongodb-001/log/mongod.pid" 
net:
  #服務實例綁定所有IP,有副作用,副本集初始化的時候,節點名字會自動設置為本地域名,而不是ip 
  #bindIpAll: true 
  #服務實例綁定的IP 
  bindIp: 0.0.0.0
  #bindIp 
  #綁定的端口 
  port: 27017
replication: 
  #副本集的名稱 
  replSetName: myrs
  • 第二個第三個配置
    把上述文件中的mongodb-001換成mongodb-002``mongodb-003
    端口分別換成27018 27019

然後分別在mongodb-00X的根目錄下執行啟動命令

./bin/mongod -f ./conf/mongod.conf

檢查進程

ps -ef|grep mongod

設置主節點

進入27017的那個mongod的客戶端,並且執行

rs.initiate({
      _id: "myrs", //  需要和replSetName的名稱一致
      version: 1,
      members: [{ _id: 0, host : "192.168.xx.xx:27017" }]});

或者

rs.initiate({}) 

執行結果

提示:
1)“ok”的值為1,說明創建成功。
2)命令行提示符發生變化,變成了一個從節點角色,此時默認不能讀寫。稍等片刻,回車,變成主節  點。

配置副本節點

再27017的mongod客戶端,也就是主節點上執行192.168.xx.xx:27018是副本節點的ip和端口

rs.add("192.168.xx.xx:27018")
rs.add("192.168.xx.xx:27019")

使用

rs.status()

就可以看到members會有三個節點了

測試

再主節點插入一條數據

use article;
db.comment.insert({name: "amber"})

再從節點查看,結果

這是因為需要再從節點再次進行

rs.slaveok() // 確認當前節點是副本節點
db.comment.find(); // 查看當前數據

可以看到已經有數據了,這樣一主二從的可複製成功了

如果關閉主節點,在從節點執行rs.status();
可以看到原來的主節點的health變成了0

27018變成了新的主節點

主節點的選舉原則

MongoDB在副本集中,主節點選舉的觸發條件:
1) 主節點故障
2) 主節點網絡不可達(默認心跳信息為10秒)
3) 人工干預(rs.stepDown(600))

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

【Spring註解驅動開發】使用InitializingBean和DisposableBean來管理bean的生命周期,你真的了解嗎?

寫在前面

在《【Spring註解驅動開發】如何使用@Bean註解指定初始化和銷毀的方法?看這一篇就夠了!!》一文中,我們講述了如何使用@Bean註解來指定bean初始化和銷毀的方法。具體的用法就是在@Bean註解中使用init-method屬性和destroy-method屬性來指定初始化方法和銷毀方法。除此之外,Spring中是否還提供了其他的方式來對bean實例進行初始化和銷毀呢?

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

InitializingBean接口

1.InitializingBean接口概述

Spring中提供了一個InitializingBean接口,InitializingBean接口為bean提供了屬性初始化后的處理方法,它只包括afterPropertiesSet方法,凡是繼承該接口的類,在bean的屬性初始化后都會執行該方法。InitializingBean接口的源碼如下所示。

package org.springframework.beans.factory;
public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}

根據InitializingBean接口中提供的afterPropertiesSet()方法的名字可以推斷出:afterPropertiesSet()方法是在屬性賦好值之後調用的。那到底是不是這樣呢?我們來分析下afterPropertiesSet()方法的調用時機。

2.何時調用InitializingBean接口?

我們定位到Spring中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory類下的invokeInitMethods()方法中,來查看Spring加載bean的方法。

題外話:不要問我為什麼會是這個invokeInitMethods()方法,如果你和我一樣對Spring的源碼非常熟悉的話,你也會知道是這個invokeInitMethods()方法,哈哈哈哈!所以,小夥伴們不要只顧着使用Spring,還是要多看看Spring的源碼啊!Spring框架中使用了大量優秀的設計模型,其代碼的編寫規範和嚴謹程度也是業界開源框架中數一數二的,非常值得閱讀。

我們來到AbstractAutowireCapableBeanFactory類下的invokeInitMethods()方法,如下所示。

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
    throws Throwable {
	//判斷該bean是否實現了實現了InitializingBean接口,如果實現了InitializingBean接口,則調用bean的afterPropertiesSet方法
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    //調用afterPropertiesSet()方法
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            //調用afterPropertiesSet()方法
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
            //通過反射的方式調用init-method
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

分析上述代碼后,我們可以初步得出如下信息:

  • Spring為bean提供了兩種初始化bean的方式,實現InitializingBean接口,實現afterPropertiesSet方法,或者在配置文件和@Bean註解中通過init-method指定,兩種方式可以同時使用。
  • 實現InitializingBean接口是直接調用afterPropertiesSet()方法,比通過反射調用init-method指定的方法效率相對來說要高點。但是init-method方式消除了對Spring的依賴。
  • 如果調用afterPropertiesSet方法時出錯,則不調用init-method指定的方法。

也就是說Spring為bean提供了兩種初始化的方式,第一種實現InitializingBean接口,實現afterPropertiesSet方法,第二種配置文件或@Bean註解中通過init-method指定,兩種方式可以同時使用,同時使用先調用afterPropertiesSet方法,后執行init-method指定的方法。

DisposableBean接口

1.DisposableBean接口概述

實現org.springframework.beans.factory.DisposableBean接口的bean在銷毀前,Spring將會調用DisposableBean接口的destroy()方法。我們先來看下DisposableBean接口的源碼,如下所示。

package org.springframework.beans.factory;
public interface DisposableBean {
	void destroy() throws Exception;
}

可以看到,在DisposableBean接口中只定義了一個destroy()方法。

在Bean生命周期結束前調用destory()方法做一些收尾工作,亦可以使用destory-method。前者與Spring耦合高,使用類型強轉.方法名(),效率高。後者耦合低,使用反射,效率相對低

2.DisposableBean接口注意事項

多例bean的生命周期不歸Spring容器來管理,這裏的DisposableBean中的方法是由Spring容器來調用的,所以如果一個多例實現了DisposableBean是沒有啥意義的,因為相應的方法根本不會被調用,當然在XML配置文件中指定了destroy方法,也是沒有意義的。所以,在多實例bean情況下,Spring不會自動調用bean的銷毀方法。

單實例bean案例

創建一個Animal的類實現InitializingBean和DisposableBean接口,代碼如下:

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試InitializingBean接口和DisposableBean接口
 */
public class Animal implements InitializingBean, DisposableBean {
    public Animal(){
        System.out.println("執行了Animal類的無參數構造方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("執行了Animal類的初始化方法。。。。。");

    }
    @Override
    public void destroy() throws Exception {
        System.out.println("執行了Animal類的銷毀方法。。。。。");

    }
}

接下來,我們新建一個AnimalConfig類,並將Animal通過@Bean註解的方式註冊到Spring容器中,如下所示。

package io.mykit.spring.plugins.register.config;

import io.mykit.spring.plugins.register.bean.Animal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
 * @author binghe
 * @version 1.0.0
 * @description AnimalConfig
 */
@Configuration
@ComponentScan("io.mykit.spring.plugins.register.bean")
public class AnimalConfig {
    @Bean
    public Animal animal(){
        return new Animal();
    }
}

接下來,我們在BeanLifeCircleTest類中新增testBeanLifeCircle02()方法來進行測試,如下所示。

@Test
public void testBeanLifeCircle02(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class);
    System.out.println("IOC容器創建完成...");
    //關閉IOC容器
    context.close();
}

運行BeanLifeCircleTest類中的testBeanLifeCircle02()方法,輸出的結果信息如下所示。

執行了Animal類的無參數構造方法
執行了Animal類的初始化方法。。。。。
IOC容器創建完成...
執行了Animal類的銷毀方法。。。。。

從輸出的結果信息可以看出:單實例bean下,IOC容器創建完成后,會自動調用bean的初始化方法;而在容器銷毀前,會自動調用bean的銷毀方法。

多實例bean案例

多實例bean的案例代碼基本與單實例bean的案例代碼相同,只不過在AnimalConfig類中,我們在animal()方法上添加了@Scope(“prototype”)註解,如下所示。

package io.mykit.spring.plugins.register.config;
import io.mykit.spring.plugins.register.bean.Animal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
 * @author binghe
 * @version 1.0.0
 * @description AnimalConfig
 */
@Configuration
@ComponentScan("io.mykit.spring.plugins.register.bean")
public class AnimalConfig {
    @Bean
    @Scope("prototype")
    public Animal animal(){
        return new Animal();
    }
}

接下來,我們在BeanLifeCircleTest類中新增testBeanLifeCircle03()方法來進行測試,如下所示。

@Test
public void testBeanLifeCircle03(){
    //創建IOC容器
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class);
    System.out.println("IOC容器創建完成...");
    System.out.println("-------");
    //調用時創建對象
    Object bean = ctx.getBean("animal");
    System.out.println("-------");
    //調用時創建對象
    Object bean1 = ctx.getBean("animal");
    System.out.println("-------");
    //關閉IOC容器
    ctx.close();
}

運行BeanLifeCircleTest類中的testBeanLifeCircle03()方法,輸出的結果信息如下所示。

IOC容器創建完成...
-------
執行了Animal類的無參數構造方法
執行了Animal類的初始化方法。。。。。
-------
執行了Animal類的無參數構造方法
執行了Animal類的初始化方法。。。。。
-------

從輸出的結果信息中可以看出:在多實例bean情況下,Spring不會自動調用bean的銷毀方法。

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

區塊鏈系列教程之:比特幣中的網絡和區塊鏈

目錄

  • 簡介
  • 比特幣的網絡
    • 網絡發現與同步
  • SPV節點
    • 區塊鏈頭
    • Merkle Tree
  • 比特幣中的區塊鏈
    • 區塊標識符
  • 創世區塊
  • 總結

簡介

比特幣的底層就是區塊鏈技術,區塊鏈也是因為比特幣而廣為人知的。和其他的區塊鏈技術相比,比特幣的區塊鏈有什麼特徵呢?作為去區塊鏈的鼻祖,又有什麼與眾不同的特性呢?快來跟我們一起看看吧。

比特幣的網絡

比特幣使用的是P2P(peer-to-peer)網絡,此P2P非彼P2P,這裡是點對點的網絡架構,而不是人對人的借錢模式。

P2P是指位於同一網絡中的每台計算機都彼此對等,各個節點共同提供網絡服務,不存在任何“特殊”節點。每個網絡節點以“扁平(flat)”的拓撲結構相互連通。在P2P網絡中不存在任何服務端(server)、中央化的服務、以及層級結構。

傳統的網絡結構是client-server的模式,所有的client都是和server交互獲取信息, 只要server掛掉了,client也就沒有用了。

而在P2P網絡中,沒有server的概念,每個節點可以作為一個server。對比起來P2P網絡在穩定性方面要比C-S架構的系統要穩定得多。

網絡發現與同步

既然是P2P網絡,那麼問題來了,這個P2P網絡是怎麼建立起來的呢?節點之間是怎麼發現的呢?

有做過P2P下載的同學應該都聽說過種子的概念,這個種子裏面保存了其他活躍的節點的地址。通過下載種子就可以連接對應的節點。

而每個節點又保存了最近連接或者活躍的節點,這樣就形成了龐大的P2P網絡。

同樣的,比特幣的P2P網絡也是這樣的。

新節點是如何發現網絡中的對等節點的呢?雖然比特幣網絡中沒有特殊節點,但是客戶端會維持一個列表,那裡列出了那些長期穩定運行的節點。這樣的節點被稱為“種子節點(seed nodes)”

節點必須持續進行兩項工作:在失去已有連接時發現新節點,並在其他節點啟動時為其提供幫助。

SPV節點

我們之前介紹了,在比特幣的世界里既沒有賬戶,也沒有餘額,只有分散到區塊鏈里的UTXO(Unspent Transaction Outputs)。

那麼如果想要驗證交易的話,需要從歷史的交易中查找所有的和該交易有關的交易,從而進行完整,全面的驗證。

這樣做的問題就是,如果下載所有的歷史記錄,那麼需要上百G的硬盤空間,這對於手機或者其他輕量級的客戶端是無法想象的。

於是SPV出現了。SPV的全稱是Simplified payment verification,叫做簡單認證支付。

SPV保存的不是整個區塊鏈,而是區塊鏈的頭部,因為每個區塊鏈頭只有80字節,所以即使把所有的區塊頭都下載保存起來也不會很大。

區塊鏈頭

區塊頭由三組區塊元數據組成。首先是一組引用父區塊哈希值的數據,這組元數據用於將該區塊與區塊鏈中前一區塊相連接。

第二組元數據,即難度、時間戳和nonce,與挖礦競爭相關。

第三組元數據是merkle樹根(一種用來有效地總結區塊中所有交易的數據結構)。

Nonce、難度目標和時間戳會用於挖礦過程,Merkle根用來索引和組織該區塊所有的交易信息。

上圖是一個區塊鏈頭組成的鏈。

Merkle Tree

Merkle Tree,是一種樹(數據結構中所說的樹),網上大都稱為Merkle Hash Tree,這是因為 它所構造的Merkle Tree的所有節點都是Hash值。Merkle Tree具有以下特點:

  1. 它是一種樹,可以是二叉樹,也可以多叉樹,無論是幾叉樹,它都具有樹結構的所有特點;

  2. Merkle樹的恭弘=叶 恭弘子節點上的value,是由你指定的,這主要看你的設計了,如Merkle Hash Tree會將數據的Hash值作為恭弘=叶 恭弘子節點的值;

  3. 非恭弘=叶 恭弘子節點的value是根據它下面所有的恭弘=叶 恭弘子節點值,然後按照一定的算法計算而得出的。如Merkle Hash Tree的非恭弘=叶 恭弘子節點value的計算方法是將該節點的所有子節點進行組合,然後對組合結果進行hash計算所得出的hash value。

有了Merkle Tree,我們只需要知道和要驗證的交易相關的其他Merkle Tree中的信息,就可以計算出整個Merkle Tree的值,這樣就可以直接使用頭部信息進行驗證了。這就是SPV的原理。

比特幣中的區塊鏈

區塊鏈是由包含交易信息的區塊從後向前有序鏈接起來的數據結構。它可以被存儲為flat file(一種包含沒有相對關係記錄的文件),或是存儲在一個簡單數據庫中。

比特幣核心客戶端使用Google的LevelDB數據庫存儲區塊鏈元數據。

它由一個包含元數據的區塊頭和緊跟其後的構成區塊主體的一長串交易組成。區塊頭是80字節,而平均每個交易至少是250字節,而且平均每個區塊至少包含超過500個交易。

區塊標識符

那怎麼表示一個區塊呢?我們使用區塊標誌符。

區塊主標識符是它的加密哈希值,一個通過SHA256算法對區塊頭進行二次哈希計算而得到的数字指紋。產生的32字節哈希值被稱為區塊哈希值,但是更準確的名稱是:區塊頭哈希值,因為只有區塊頭被用於計算。

第二種識別區塊的方式是通過該區塊在區塊鏈中的位置,即“區塊高度(block height)”。第一個區塊,其區塊高度為0
和區塊哈希值不同的是,區塊高度並不是唯一的標識符。雖然一個單一的區塊總是會有一個明確的、固定的區塊高度,但反過來卻並不成立,一個區塊高度並不總是識別一個單一的區塊。兩個或兩個以上的區塊可能有相同的區塊高度,在區塊鏈里爭奪同一位置。

創世區塊

區塊鏈里的第一個區塊創建於2009年,被稱為創世區塊。它是區塊鏈裏面所有區塊的共同祖先,這意味着你從任一區塊,循鏈向後回溯,最終都將到達創世區塊。

因為創世區塊被編入到比特幣客戶端軟件里,所以每一個節點都始於至少包含一個區塊的區塊鏈,這能確保創世區塊不會被改變。每一個節點都“知道”創世區塊的哈希值、結構、被創建的時間和裏面的一個交易。因此,每個節點都把該區塊作為區塊鏈的首區塊,從而構建了一個安全的、可信的區塊鏈的根。

創世區塊的哈希值為:
0000000000 19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f

創世區塊包含一個隱藏的信息。在其Coinbase交易的輸入中包含這樣一句話“The Times 03/Jan/2009 Chancellor on brink of second bailout forbanks.”這句話是泰晤士報當天的頭版文章標題,引用這句話,既是對該區塊產生時間的說明,也可視為半開玩笑地提醒人們一個獨立的貨幣制度的重要性,同時告訴人們隨着比特幣的發展,一場前所未有的世界性貨幣革命將要發生。該消息是由比特幣的創立者中本聰嵌入創世區塊中。

coinbase的值是:04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73

解碼方法如下:

在python shell下:

“04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73”.decode(‘hex’)

輸出:

‘\x04\xff\xff\x00\x1d\x01\x04EThe Times 03/Jan/2009 Chancellor on brink of second bailout for banks’

總結

本文介紹了比特幣的網絡和比特幣中的區塊鏈的相關概念,希望大家能夠喜歡。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/bitcoin-blockchain-network/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

「半雞半鴨」:考古發現最古老禽類化石

摘錄自2020年3月28日大紀元報導

考古學家發現一隻禽類生物的頭骨和腿骨化石,經檢驗發現它生活在距今約6,680萬~6,670萬年前,是至今全球發現的最古老的鳥類化石。

這份近期發表在《自然》(Nature)期刊上的研究公布了這一發現。研究者之一劍橋大學的古生物學家菲爾德(Daniel Field)說:「這是我們至今發現的最早存在的鳥類的證據。」

恐龍大約在距今6,600萬年前滅絕,因此這隻生物生活在僅比那個時間點早一點的時期。在此之前,科學家找到的最早的鳥類化石大約生活在距今6,650萬年前。新發現的化石比這個更早一些。

研究人員估計這隻鳥的體重為400克左右,只有現在的水鳥——鳧的一半大。「我們認為它的臉看起來有點像現代的雞,但是頭骨的後面看起來又更像現在的鴨子。」菲爾德說。一起發現的還有它的腿骨化石,看起來它有著兩條細長的腿,說明它是生活在岸邊的一種禽類。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

幫居民生計找出路 坦尚尼亞社區林業 改善盜伐有成

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

地球暖化冰河融解 北極新發現5座島嶼

摘錄自2019年10月23日民視新聞報導

俄羅斯研究人員23日表示,他們在北極偏遠地區發現了5座新島,目前還沒有命名研究人員是在法蘭士約瑟夫地群島,發現這5座新島嶼。島嶼原本為冰河覆蓋,因為冰河融化的關係,才被人發現。從無人機空拍畫面可以看到島嶼上有北極熊和海象等動物居住。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

一種失去飛翔能力的鳥 使科學家重拾對環境的希望

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧