標籤: 南投搬家費用

  • 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(最大期望)算法推導、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\) 是決策變量的取值範圍

    更多種類的選址問題

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

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

    總結

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

    參考文獻

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

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

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

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

    目錄

    • 簡介
    • 比特幣的網絡
      • 網絡發現與同步
    • 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的博客

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

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 捨電動車 韓國現代氫動力貨卡將上市 目標零碳排征服瑞士高山

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

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 盜獵猖獗 科學家想妙招 用假犀牛角打亂黑市

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

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 車型電氣化成趨勢 通用擬在華推多款電動車

    車型電氣化成趨勢 通用擬在華推多款電動車

    車型電氣化已成為目前各大車企在新能源方面的發展方向,如大眾、BMW、奔馳等品牌皆是如此。作為美國車企巨頭的通用汽車,也開始沿襲電動化產品的發展趨勢。

    通用汽車CEO丹‧艾克森表示,將會加強在電動汽車領域的推廣,其中中國市場是通用全球市場中的重要一環。此外,通用汽車還將通過推出豪華電動車跑車,抗衡目前風頭正勁的豪華電動車品牌特斯拉。

    據通用汽車高管透露,未來通用汽車將在華推出凱迪拉克ELR和賽歐EV等多款電動車和插電混動車,加之已經引入的沃藍達,通用汽車在華將形成豐富電動車產品陣容。

    通用汽車曾官方表示:“至2017年,通用汽車在全球范圍內每年將生產50萬輛採用電氣化技術的車輛。”對于目前通用汽車採用插電式混動車型僅有雪佛蘭沃藍達一款車型。今後還將陸續投放凱迪拉克ELR插電混動跑車和一款價格相比沃藍達更低的入門級插電混動車。此外,通用汽車還有多款搭載eAssist微混動力係統的車型,如別克君越、別克君威、雪佛蘭邁銳寶以及雪佛蘭Impala。

    除通用汽車在華全面發展電氣化車型外,如BMW品牌和大眾集團也計劃在華開展電動車項目。BMW在發布全新子品牌BMW i,該品牌將致力于發展新能源車型,並于2014年將旗下首款電動車i3及超跑電動車i8發布上市。大眾集團也計劃先期在華投放e-up!,同時在2014年陸續將有多款車型匹配插電混合動力,從而使大眾汽車實現全面電氣化。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • Jmeter(十二) – 從入門到精通 – JMeter邏輯控制器 – 終篇(詳解教程)

    Jmeter(十二) – 從入門到精通 – JMeter邏輯控制器 – 終篇(詳解教程)

    1.簡介

    Jmeter官網對邏輯控制器的解釋是:“Logic Controllers determine the order in which Samplers are processed.”。

    意思是說,邏輯控制器可以控制採樣器(samplers)的執行順序。由此可知,控制器需要和採樣器一起使用,否則控制器就沒有什麼意義了。放在控制器下面的所有的採樣器都會當做一個整體,執行時也會一起被執行。

    JMeter邏輯控制器可以對元件的執行邏輯進行控制,除僅一次控制器外,其他可以嵌套別的種類的邏輯控制器。

    2.邏輯控制器分類

    JMeter中的Logic Controller分為兩類:
    (1)控制測試計劃執行過程中節點的邏輯執行順序,如:Loop Controller、If Controller等;
    (2)對測試計劃中的腳本進行分組、方便JMeter統計執行結果以及進行腳本的運行時控制等,如:Throughput Controller、Transaction Controller。

    3.預覽邏輯控制器 

    首先我們來看一下JMeter的邏輯控制器,路徑:線程組(用戶)->添加->邏輯控制器(Logic Controller);我們可以清楚地看到JMeter5中共有17個邏輯控制器,如下圖所示:

    如果上圖您看得不是很清楚的話,宏哥總結了一個思維導圖,關於JMeter5的邏輯控制器類型,如下圖所示: 

     通過以上的了解,我們對邏輯控制器有了一個大致的了解和認識。下面宏哥就給小夥伴或則童鞋們分享講解一些通常在工作中會用到的邏輯控制器。 

    4.常用邏輯控制器詳解

      這一小節,宏哥就由上而下地詳細地講解一下常用的邏輯控制器。

    4.1Runtime Controller

    運行控制器用來控制其子元件的執行時長。市場單位是秒。

     1、我們先來看看這個Runtime Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 運行控制器,如下圖所示:

    2、關鍵參數說明如下:

    Name:名稱,可以隨意設置,甚至為空;

    Comments:註釋,可隨意設置,可以為空;

    Runtime:默認為1,去掉1則默認為0,此時不執行其節點下的元件。 與線程組中的調度器的持續時間 效果一致。不填 或 0,不會執行樣例

    4.1.1Runtime控制器控制其下取樣器執行2s

    1、創建測試計劃,設置 Runtime 控制器的運行時間 為 2,線程組設置默認不變,如下圖所示:

    Runtime 控制器設置

    線程組設置

    2、配置好以後,運行JMeter,然後查看結果樹,如下圖所示:

    4.1.2使用線程組中的調度器控制樣例運行3s

    1、創建測試計劃,設置 Runtime 控制器的運行時間 為 2,線程組設置運行時間3,如下圖所示:

    線程組設置

    Runtime 控制器設置

    2、配置好以後,運行JMeter,然後查看結果樹,如下圖所示:

    線程組設置3,Runtime控制器設置2,但是運行時間是2s。所以從上邊的運行時間得出結論:如果線程組中設置了持續時間,Runtime 控制器也設置了 運行時間,那麼會優先於線程組中的設置。

    4.2Simple Controller

    Simple Controller用來指定了一個執行單元,它不改變元件的執行順序。在它下邊還可以嵌套其他控制器。簡單控制器可以編輯只有名稱和註釋。就像他的名字一樣,簡單,可以理解為一個文件夾,就是分組用的,沒有其他特殊功能,但相比不添加簡單控制器,區別在於簡單控制器可以被模塊控制器所引用。其作用就是分組,比如QQ好友列表,可分為家人、同學、等。一般是請求較多,需要分組時採用。

     1、我們先來看看這個Simple Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 簡單控制器,如下圖所示:

    2、關鍵參數說明如下:

    Name:名稱,可以隨意設置,甚至為空;

    Comments:註釋,可隨意設置,可以為空。

    4.2.1簡單實例

    1、創建測試計劃,線程組設置循環10,如下圖所示:

    2、配置好以後,運行JMeter,然後查看結果樹,如下圖所示:

    4.3Throughput Controller

    用來控制其下元件的執行次數,並無控制吞吐量的功能,想要控制吞吐量可以使用Constant Throughput Timer,後邊會講解到。吞吐量控制器有兩種模式:Total Executions:設置運行次數與Percent Executions:設置運行比例(1~100之間)。

    1、我們先來看看這個Throughput Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 吞吐量控制器,如下圖所示: 

    2、關鍵參數說明如下:

    Name:名稱,可以隨意設置,甚至為空;

    Comments:註釋,可隨意設置,可以為空;

    Total Executions:執行百分比(1-100);

    percent Executions:執行數量;

    Throughput:根據上邊選擇的方式填寫,百分比為0~100;

    Per User:線程數,當選Total Executions時,是線程數;當選percent Executions時,是線程數*循環次數。

    4.3.1不勾選Per User

    1、線程組中設置 線程數量 2,循環次數 10,吞吐量控制器 設置 Total Executions,吞吐量設置為 2,其下添加一個取樣器,如下圖所示:

    2、配置好以後,運行JMeter,然後查看結果樹(執行了2次),如下圖所示:

    3、現在將 吞吐量控制器 設置為百分比的控制方式,吞吐量設置為:50%,如下圖所示:

    4、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了10次,計算方式:10=吞吐量50% * 循環次數10 * 線程數 2),如下圖所示:

    4.3.2勾選Per User

    1、線程組中設置 線程數量 2,循環次數 10,吞吐量控制器 設置 Total Executions,吞吐量設置為 2,其下添加一個取樣器,勾選Per User,如下圖所示:

    線程組設置

    吞吐量控制器

    2、配置好以後,點擊“保存”,運行JMeter,然後查看結果樹(總共執行了4次,其中吞吐量設置為2,執行2次,線程設置為2,執行2次,總共4次),函數 __threadNum 只是簡單地返回當前線程的編號,如下圖所示:

    3、現在將 吞吐量控制器 設置為百分比的控制方式,吞吐量設置為:50,如下圖所示:

    4、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了10次,計算方式:10=吞吐量50% * 循環次數10 * 線程數 2),如下圖所示:

    綜上所述:

    勾選Per User:

    1.線程數*循環次數>=線程數*吞吐量時,Total Executions模式的執行次數=線程數*吞吐量。

    2.線程數*循環次數<線程數*吞吐量時,Total Executions模式的執行次數=當線程數*循環次數。

    不勾選Per User:

    1.線程數*循環次數<=吞吐量時,Total Executions模式的執行次數=線程數*循環次數。

    2.線程數*循環次數>吞吐量時,Total Executions模式的執行次數=吞吐量。

    l Percent Executions:設置運行比例(1~100之間),單位為%

    不管Per User是否勾選,按Percent Executions模式的執行次數都不受Per User影響,Percent Executions模式的執行次數=線程數*循環次數*吞吐量%。(循環次數=線程組循環次數*循環控制器循環次數)

    l Per User:勾選該項的話則按虛擬用戶數(線程數)來計算執行次數,不勾選則按所有虛擬用戶數來計算執行次數

    測試計劃

    序號 線程數 循環次數 模式 Throughput Per User 執行次數
    1 2 10 Percent 50 Y 10
    2 2 10 Percent 50 N 10
    3 2 10 Total 7 Y 14
    4 2 10 Total 7 N 7
    5 2 2 Total 7 Y 4
    6 2 2 Total 7 N 4

    下面說明一下這6個場景:
    (1)序號1和2場景,Per User 對總執行次數沒有影響。
    (2)序號3場景,Per User勾選,每個虛擬用戶(線程)執行7次,共執行14次。
    (3)序號4場景,Per User不勾選,則所有虛擬用戶執行7次。
    (4)序號5場景,Per User勾選,每個虛擬用戶(線程)執行7次,共執行14次,由於Thread Group計劃循環次數是4(2線程*2循環)次,所以最多只能執行4次。
    (5)序號6場景,Per User不勾選,所有虛擬用戶執行7次,由於Thread Group計劃循環次數是4(2線程*2循環)次,所以最多只能執行4次。

    4.4Module Controller

    模塊控制器可以快速的切換腳本,不用來回的新建,方便腳本調試。 

    可以理解為引用、調用的意思,執行內容為Module To Run種所選的內容,引用範圍為當前測試計劃內的測試片段、邏輯控制器<模塊控制器除外>
    被引用的邏輯控制器、測試片段可以為禁用狀態,被引用后仍然會被執行。
    可以將模塊控制器與包括控制器一起學習比較,模塊控制器是從內部文件中引用,引用上相對比較靈活,可以只引用部分測試片段或模塊內容,包括控制器是從外部文件引用,只能引用整個測試片段的內容。
    注意:被應用的模塊位置不可隨意變更,變更後會執行時出現提示引用失敗
    找到目標元素:快速查找與跳轉的作用,點擊後會立即跳轉到所選的邏輯控制器的內容詳情

    1、我們先來看看這個Module Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 >  模塊控制器,如下圖所示: 

    2、關鍵參數說明如下:

    Name:名稱,可以隨意設置,甚至為空;

    Comments:註釋,可隨意設置,可以為空;

    Forever:勾選上這一項表示一直循環下去。

    4.4.1實例

    1、創建測試計劃,添加兩個測試片段,並且在每個測試片段下添加一個取樣器,然後,添加線程組,再添加模塊控制器,最後添加查看結果樹,如下圖所示:

    2、配置模塊控制器,選擇第一個測試片段,如下圖所示:

    3、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第1個測試片段的取樣器),如下圖所示:

    4、配置模塊控制器,選擇第二個測試片段,如下圖所示: 

    5、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第2個測試片段的取樣器),如下圖所示:

    4.5Switch Controller

    Switch Controller:開關控制器,通過其下樣例順序數值或名稱 控制執行某一個樣例。

     1、我們先來看看這個if Controller長得是啥樣子,路徑:線程組 > 添加 > 邏輯控制器 > 如果 (if) 控制器,如下圖所示:

    2、關鍵參數說明如下:

    Name:名稱,可以隨意設置,甚至為空;

    Comments:註釋,可隨意設置,可以為空;

    Switch Value:指定請求的索引或者名稱,索引從0開始,如果沒有賦值,或者索引超過請求個數的話就執行第0個請求。可以是数字,也可以是字符,為字符時匹配取樣器名稱,如果匹配不上就會默認並找取樣器名稱為default的取樣器,如果沒有則不運行。

    4.5.1數值

    數值:表示將執行其下第 數值+1個取樣器,例如:填1,將執行第2個取樣器;填0或者不填,將執行第1個取樣器;數值超出其下取樣器數目時,執行第1個取樣器。

    1、創建一個測試計劃,設置線程組和Switch控制器,如下圖所示:

    線程組

    Switch控制器

    2、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第3<數值+1>個取樣器),如下圖所示:

    3、修改Switch控制器的數值為0或者不填,如下圖所示:

    4、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了第1<數值為0或者不填,執行第1個取樣器>個取樣器),如下圖所示:

    4.5.2字符

    1、創建一個測試計劃,設置線程組和Switch控制器(直接使用取樣器名字),如下圖所示:

    線程組

    Switch控制器

    2、配置好以後,點擊“保存”運行JMeter,然後查看結果樹(執行了使用名字的取樣器),如下圖所示:

    5.小結

    好了,今天關於邏輯控制器的上篇就講解到這裏,這一篇主要介紹了 Runtime Controller 、 Simple Controller 、Throughput ControllerModule Controller 和  Switch Controller

     

    您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波  推薦  不要忘記哦!!!

    別忘了點 推薦 留下您來過的痕迹

     

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • Java 多線程基礎(十)interrupt()和線程終止方式

    Java 多線程基礎(十)interrupt()和線程終止方式

    一、interrupt() 介紹

    interrupt() 定義在 Thread 類中,作用是中斷本線程

    本線程中斷自己是被允許的;其它線程調用本線程的 interrupt() 方法時,會通過 checkAccess() 檢查權限。這有可能拋出 SecurityException 異常。
    如果本線程是處於阻塞狀態:調用線程的 wait() , wait(long) 或 wait(long, int) 會讓它進入等待(阻塞)狀態,或者調用線程的 join(),join(long),join(long, int),sleep(long),sleep(long, int) 也會讓它進入阻塞狀態。若線程在阻塞狀態時,調用了它的 interrupt() 方法,那麼它的“中斷狀態”會被清除並且會收到一個 InterruptedException 異常。例如,線程通過 wait() 進入阻塞狀態,此時通過 interrupt() 中斷該線程;調用 interrupt() 會立即將線程的中斷標記設為 true,但是由於線程處於阻塞狀態,所以該“中斷標記”會立即被清除為 “false”,同時,會產生一個 InterruptedException 的異常
    如果線程被阻塞在一個 Selector 選擇器中,那麼通過 interrupt() 中斷它時;線程的中斷標記會被設置為 true,並且它會立即從選擇操作中返回。
    如果不屬於前面所說的情況,那麼通過 interrupt() 中斷線程時,它的中斷標記會被設置為 true。
    中斷一個“已終止的線程”不會產生任何操作。

    二、線程終止方式

    Thread中的 stop() 和 suspend() 方法,由於固有的不安全性,已經建議不再使用!
    下面,我先分別討論線程在“阻塞狀態”和“運行狀態”的終止方式,然後再總結出一個通用的方式。

    (一)、終止處於“阻塞狀態”的線程.

    通常,我們通過“中斷”方式終止處於“阻塞狀態”的線程
    當線程由於被調用了 sleep(),,wait(),join() 等方法而進入阻塞狀態;若此時調用線程的 interrupt() 將線程的中斷標記設為 true。由於處於阻塞狀態,中斷標記會被清除,同時產生一個InterruptedException 異常。將 InterruptedException 放在適當的位置就能終止線程,形式如下:

    public void run() {
        try {
            while (true) {
                // 執行業務
            }
        } catch (InterruptedException ie) {  
            // 由於產生InterruptedException異常,退出while(true)循環,線程終止!
        }
    }

    說明:

    在while(true)中不斷的執行業務代碼,當線程處於阻塞狀態時,調用線程的 interrupt() 產生 InterruptedException 中斷。中斷的捕獲在 while(true) 之外,這樣就退出了 while(true) 循環!

    注意:

    對 InterruptedException 的捕獲務一般放在 while(true) 循環體的外面,這樣,在產生異常時就退出了 while(true) 循環。否則,InterruptedException 在 while(true) 循環體之內,就需要額外的添加退出處理。形式如下: 

    public void run() {
        while (true) {
            try {
                // 執行任務...
            } catch (InterruptedException ie) {  
                // InterruptedException在while(true)循環體內。
                // 當線程產生了InterruptedException異常時,while(true)仍能繼續運行!需要手動退出
                break;
            }
        }
    }

    說明:

    上面的 InterruptedException 異常的捕獲在 whle(true) 之內。當產生 InterruptedException 異常時,被 catch 處理之外,仍然在 while(true) 循環體內;要退出 while(true) 循環體,需要額外的執行退出while(true) 的操作。

    (二)、終止處於“運行狀態”的線程

    通常,我們通過“標記”方式終止處於“運行狀態”的線程。其中,包括“中斷標記”和“額外添加標記”。

    1、通過“中斷標記”終止線程

    public void run() {
        while (!isInterrupted()) {
            // 執行任務...
        }
    }

    說明:

    isInterrupted() 是判斷線程的中斷標記是不是為 true。當線程處於運行狀態,並且我們需要終止它時;可以調用線程的 interrupt() 方法,使用線程的中斷標記為 true,即 isInterrupted() 會返回true。此時,就會退出while循環。
    注意:interrupt() 並不會終止處於“運行狀態”的線程!它會將線程的中斷標記設為 true。

    2、通過“額外添加標記”終止線程

    private volatile boolean flag= true;
    protected void stopTask() {
        flag = false;
    }
    public void run() {
        while (flag) {
            // 執行任務...
        }
    }

    說明:

    線程中有一個 flag 標記,它的默認值是 true;並且我們提供 stopTask() 來設置 flag 標記。當我們需要終止該線程時,調用該線程的 stopTask() 方法就可以讓線程退出 while 循環。
    注意:將 flag 定義為 volatile 類型,是為了保證 flag 的可見性。即其它線程通過 stopTask() 修改了 flag 之後,本線程能看到修改后的 flag 的值。

    (三)、通過方式

    綜合線程處於“阻塞狀態”和“運行狀態”的終止方式,比較通用的終止線程的形式如下:

    public void run() {
        try {
            // 1. isInterrupted()保證,只要中斷標記為true就終止線程。
            while (!isInterrupted()) {
                // 執行任務...
            }
        } catch (InterruptedException ie) {  
            // 2. InterruptedException異常保證,當InterruptedException異常產生時,線程被終止。
        }
    }
    1、isInterrupted()保證,只要中斷標記為 true 就終止線程。
    2、InterruptedException 異常保證,當 InterruptedException 異常產生時,線程被終止。

    三、示例

    public class InterruptTest {
        public static void main(String[] args) {
            try {
                Thread t1 = new MyThread("t1"); // 新建線程t1
                System.out.println(t1.getName() + "[" + t1.getState() + "] is new.");
                
                t1.start();// 啟動線程t1
                System.out.println(t1.getName() + "[" + t1.getState() + "] is started.");
                
                Thread.sleep(300);// 休眠300毫秒,然後主線程給t1發“中斷”指令,查看t1狀態
                t1.interrupt();
                System.out.println(t1.getName() + "[" + t1.getState() + "] is interrupted.");
                
                Thread.sleep(300);// 休眠300毫秒,然後查看t1狀態
                System.out.println(t1.getName() + "[" + t1.getState() + "] is interrupted now.");
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    class MyThread extends Thread{
        public MyThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            try {
                int i = 0;
                while(!isInterrupted()) {
                    Thread.sleep(100);// 休眠100毫秒
                    ++i;
                    System.out.println(Thread.currentThread().getName() + "[" + this.getState() + "] loop " + i);
                }
            }catch(InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "[" + this.getState() + "] catch InterruptedException");
            }
        }
    }
    // 運行結果
    t1 [ NEW ] is new.
    t1 [ RUNNABLE ] is started.
    t1 [ RUNNABLE ] loop 1
    t1 [ RUNNABLE ] loop 2
    t1 [ RUNNABLE ] loop 3
    t1 [ RUNNABLE ] catch InterruptedException
    t1 [ TERMINATED ] is interrupted.
    t1 [ TERMINATED ] is interrupted now.

    說明:

    ①、主線程 main 中通過 new MyThread(“t1”) 創建線程 t1,之後通過 t1.start() 啟動線程 t1。
    ②、t1 啟動之後,會不斷的檢查它的中斷標記,如果中斷標記為“false”;則休眠 100ms。
    ③、t1 休眠之後,會切換到主線程main;主線程再次運行時,會執行t1.interrupt()中斷線程t1。t1收到中斷指令之後,會將t1的中斷標記設置“false”,而且會拋出 InterruptedException 異常。在 t1 的 run() 方法中,是在循環體 while 之外捕獲的異常;因此循環被終止。

    我們對上面的結果進行小小的修改,將run()方法中捕獲InterruptedException異常的代碼塊移到while循環體內。

    public class InterruptTest {
        public static void main(String[] args) {
            try {
                Thread t1 = new MyThread("t1"); // 新建線程t1
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is new.");
                
                t1.start();// 啟動線程t1
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is started.");
                
                Thread.sleep(300);// 休眠300毫秒,然後主線程給t1發“中斷”指令,查看t1狀態
                t1.interrupt();
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted.");
                
                Thread.sleep(300);// 休眠300毫秒,然後查看t1狀態
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted now.");
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            
        }
    }
    class MyThread extends Thread{
        public MyThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            int i = 0;
            while(!isInterrupted()) {
                try {
                    Thread.sleep(100); // 休眠100ms
                } catch (InterruptedException ie) {  
                    System.out.println(Thread.currentThread().getName() +" [ "+this.getState()+" ] catch InterruptedException.");  
                }
                i++;
                System.out.println(Thread.currentThread().getName()+" [ "+this.getState()+" ] loop " + i);  
            }
        }
    }
    // 運行結果
    t1 [ NEW ] is new.
    t1 [ RUNNABLE ] is started.
    t1 [ RUNNABLE ] loop 1
    t1 [ RUNNABLE ] loop 2
    t1 [ TIMED_WAITING ] is interrupted.
    t1 [ RUNNABLE ] catch InterruptedException.
    t1 [ RUNNABLE ] loop 3
    t1 [ RUNNABLE ] loop 4
    t1 [ RUNNABLE ] loop 5
    t1 [ RUNNABLE ] loop 6
    t1 [ RUNNABLE ] is interrupted now.
    t1 [ RUNNABLE ] loop 7
    ...... // 無限循環

    說明:

    程序進入了死循環了。

    這是因為,t1在“等待(阻塞)狀態”時,被 interrupt() 中斷;此時,會清除中斷標記(即 isInterrupted() 會返回 false),而且會拋出 InterruptedException 異常(該異常在while循環體內被捕獲)。因此,t1理所當然的會進入死循環了。
    解決該問題,需要我們在捕獲異常時,額外的進行退出 while 循環的處理。例如,在 MyThread 的 catch(InterruptedException) 中添加 break 或 return 就能解決該問題。

    下面是通過“額外添加標記”的方式終止“狀態狀態”的線程的示例:

    public class InterruptTest {
        public static void main(String[] args) {
            try {
                MyThread t1 = new MyThread("t1"); // 新建線程t1
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is new.");
                
                t1.start();// 啟動線程t1
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is started.");
                
                Thread.sleep(300);// 休眠300毫秒,然後主線程給t1發“中斷”指令,查看t1狀態
                t1.stopTask();
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted.");
                
                Thread.sleep(300);// 休眠300毫秒,然後查看t1狀態
                System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted now.");
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            
        }
    }
    class MyThread extends Thread{
        private volatile boolean flag = true;
        public void stopTask() {
            flag = false;
        }
        public MyThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            synchronized (this) {
                int i = 0;
                while(flag) {
                    try {
                        Thread.sleep(100); // 休眠100ms
                    } catch (InterruptedException ie) {  
                        System.out.println(Thread.currentThread().getName() +" [ "+this.getState()+" ] catch InterruptedException.");  
                        break;
                    }
                    i++;
                    System.out.println(Thread.currentThread().getName()+" [ "+this.getState()+" ] loop " + i);  
                }
            }
            
        }
    }
    // 運行結果
    t1 [ NEW ] is new.
    t1 [ RUNNABLE ] is started.
    t1 [ RUNNABLE ] loop 1
    t1 [ RUNNABLE ] loop 2
    t1 [ RUNNABLE ] loop 3
    t1 [ RUNNABLE ] is interrupted.
    t1 [ TERMINATED ] is interrupted now.

    四、interrupted() 和 isInterrupted()的區別

    interrupted() 和 isInterrupted()都能夠用於檢測對象的“中斷標記”。
    區別是,interrupted() 除了返回中斷標記之外,它還會清除中斷標記(即將中斷標記設為 false);而 isInterrupted() 僅僅返回中斷標記

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 核外洩「鈾污染」超標1300倍 美國西屋工廠地板破8公分

    摘錄自2018年7月26日東森新聞報導

    美國南卡羅萊納州西屋公司(Westinghouse)驚傳核外洩,放射性鈾已經污染廠區下方的土壤,程度是一般土壤環境中鈾含量的約1300倍。據《美聯社》報導,核燃料生產廠的鋼筋水泥地板破洞約8公分,導致放射性鈾外洩。美國疾病控管及防治中心的資料顯示,若水中的鈾含量超標,可能造成飲用者腎臟受損。

    美國聯邦核能規範委員會(Nuclear Regulatory Commission,NRC)已經證實此事。該廠位於南卡首府、最大城哥倫比亞市的南邊,放射性的鈾是用來製造核燃料棒。

    西屋公司二年前曾因工廠防治空污裝置中的鈾累積過多,而關閉部分廠區。

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案