當前位置:首頁 » 操作系統 » ik演算法

ik演算法

發布時間: 2022-08-19 02:20:43

① 簡述演算法的各種表示形式

一、什麼是演算法

演算法是一系列解決問題的清晰指令,也就是說,能夠對一定規范的輸入,在有限時間內獲得所要求的輸出。演算法常常含有重復的步驟和一些比較或邏輯判斷。如果一個演算法有缺陷,或不適合於某個問題,執行這個演算法將不會解決這個問題。不同的演算法可能用不同的時間、空間或效率來完成同樣的任務。一個演算法的優劣可以用空間復雜度與時間復雜度來衡量。

演算法的時間復雜度是指演算法需要消耗的時間資源。一般來說,計算機演算法是問題規模n 的函數f(n),演算法執行的時間的增長率與f(n) 的增長率正相關,稱作漸進時間復雜度(Asymptotic Time Complexity)。時間復雜度用「O(數量級)」來表示,稱為「階」。常見的時間復雜度有: O(1)常數階;O(log2n)對數階;O(n)線性階;O(n2)平方階。

演算法的空間復雜度是指演算法需要消耗的空間資源。其計算和表示方法與時間復雜度類似,一般都用復雜度的漸近性來表示。同時間復雜度相比,空間復雜度的分析要簡單得多。

二、演算法設計的方法

1.遞推法

遞推法是利用問題本身所具有的一種遞推關系求問題解的一種方法。設要求問題規模為N的解,當N=1時,解或為已知,或能非常方便地得到解。能採用遞推法構造演算法的問題有重要的遞推性質,即當得到問題規模為i-1的解後,由問題的遞推性質,能從已求得的規模為1,2,…,i-1的一系列解,構造出問題規模為I的解。這樣,程序可從i=0或i=1出發,重復地,由已知至i-1規模的解,通過遞推,獲得規模為i的解,直至得到規模為N的解。

【問題】 階乘計算

問題描述:編寫程序,對給定的n(n≤100),計算並輸出k的階乘k!(k=1,2,…,n)的全部有效數字。

由於要求的整數可能大大超出一般整數的位數,程序用一維數組存儲長整數,存儲長整數數組的每個元素只存儲長整數的一位數字。如有m位成整數N用數組a[ ]存儲:

N=a[m]×10m-1+a[m-1]×10m-2+ … +a[2]×101+a[1]×100

並用a[0]存儲長整數N的位數m,即a[0]=m。按上述約定,數組的每個元素存儲k的階乘k!的一位數字,並從低位到高位依次存於數組的第二個元素、第三個元素……。例如,5!=120,在數組中的存儲形式為:

3 0 2 1 ……

首元素3表示長整數是一個3位數,接著是低位到高位依次是0、2、1,表示成整數120。

計算階乘k!可採用對已求得的階乘(k-1)!連續累加k-1次後求得。例如,已知4!=24,計算5!,可對原來的24累加4次24後得到120。細節見以下程序。

# include <stdio.h>

# include <malloc.h>

# define MAXN 1000

void pnext(int a[ ],int k)

{ int *b,m=a[0],i,j,r,carry;

b=(int * ) malloc(sizeof(int)* (m+1));

for ( i=1;i<=m;i++) b[i]=a[i];

for ( j=1;j<=k;j++)

{ for ( carry=0,i=1;i<=m;i++)

{ r=(i<a[0]?a[i]+b[i]:a[i])+carry;

a[i]=r%10;

carry=r/10;

}

if (carry) a[++m]=carry;

}

free(b);

a[0]=m;

}

void write(int *a,int k)

{ int i;

printf(「%4d!=」,k);

for (i=a[0];i>0;i--)

printf(「%d」,a[i]);

printf(「\n\n」);

}

void main()

{ int a[MAXN],n,k;

printf(「Enter the number n: 「);

scanf(「%d」,&n);

a[0]=1;

a[1]=1;

write(a,1);

for (k=2;k<=n;k++)

{ pnext(a,k);

write(a,k);

getchar();

}

}

2.遞歸

遞歸是設計和描述演算法的一種有力的工具,由於它在復雜演算法的描述中被經常採用,為此在進一步介紹其他演算法設計方法之前先討論它。

能採用遞歸描述的演算法通常有這樣的特徵:為求解規模為N的問題,設法將它分解成規模較小的問題,然後從這些小問題的解方便地構造出大問題的解,並且這些規模較小的問題也能採用同樣的分解和綜合方法,分解成規模更小的問題,並從這些更小問題的解構造出規模較大問題的解。特別地,當規模N=1時,能直接得解。

【問題】 編寫計算斐波那契(Fibonacci)數列的第n項函數fib(n)。

斐波那契數列為:0、1、1、2、3、……,即:

fib(0)=0;

fib(1)=1;

fib(n)=fib(n-1)+fib(n-2) (當n>1時)。

寫成遞歸函數有:

int fib(int n)

{ if (n==0) return 0;

if (n==1) return 1;

if (n>1) return fib(n-1)+fib(n-2);

}

遞歸演算法的執行過程分遞推和回歸兩個階段。在遞推階段,把較復雜的問題(規模為n)的求解推到比原問題簡單一些的問題(規模小於n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是說,為計算fib(n),必須先計算fib(n-1)和fib(n-2),而計算fib(n-1)和fib(n-2),又必須先計算fib(n-3)和fib(n-4)。依次類推,直至計算fib(1)和fib(0),分別能立即得到結果1和0。在遞推階段,必須要有終止遞歸的情況。例如在函數fib中,當n為1和0的情況。

在回歸階段,當獲得最簡單情況的解後,逐級返回,依次得到稍復雜問題的解,例如得到fib(1)和fib(0)後,返回得到fib(2)的結果,……,在得到了fib(n-1)和fib(n-2)的結果後,返回得到fib(n)的結果。

在編寫遞歸函數時要注意,函數中的局部變數和參數知識局限於當前調用層,當遞推進入「簡單問題」層時,原來層次上的參數和局部變數便被隱蔽起來。在一系列「簡單問題」層,它們各有自己的參數和局部變數。

由於遞歸引起一系列的函數調用,並且可能會有一系列的重復計算,遞歸演算法的執行效率相對較低。當某個遞歸演算法能較方便地轉換成遞推演算法時,通常按遞推演算法編寫程序。例如上例計算斐波那契數列的第n項的函數fib(n)應採用遞推演算法,即從斐波那契數列的前兩項出發,逐次由前兩項計算出下一項,直至計算出要求的第n項。

【問題】 組合問題

問題描述:找出從自然數1、2、……、n中任取r個數的所有組合。例如n=5,r=3的所有組合為: (1)5、4、3 (2)5、4、2 (3)5、4、1

(4)5、3、2 (5)5、3、1 (6)5、2、1

(7)4、3、2 (8)4、3、1 (9)4、2、1

(10)3、2、1

分析所列的10個組合,可以採用這樣的遞歸思想來考慮求組合函數的演算法。設函數為void comb(int m,int k)為找出從自然數1、2、……、m中任取k個數的所有組合。當組合的第一個數字選定時,其後的數字是從餘下的m-1個數中取k-1數的組合。這就將求m個數中取k個數的組合問題轉化成求m-1個數中取k-1個數的組合問題。設函數引入工作數組a[ ]存放求出的組合的數字,約定函數將確定的k個數字組合的第一個數字放在a[k]中,當一個組合求出後,才將a[ ]中的一個組合輸出。第一個數可以是m、m-1、……、k,函數將確定組合的第一個數字放入數組後,有兩種可能的選擇,因還未去頂組合的其餘元素,繼續遞歸去確定;或因已確定了組合的全部元素,輸出這個組合。細節見以下程序中的函數comb。

【程序】

# include <stdio.h>

# define MAXN 100

int a[MAXN];

void comb(int m,int k)

{ int i,j;

for (i=m;i>=k;i--)

{ a[k]=i;

if (k>1)

comb(i-1,k-1);

else

{ for (j=a[0];j>0;j--)

printf(「%4d」,a[j]);

printf(「\n」);

}

}

}

void main()

{ a[0]=3;

comb(5,3);

}

3.回溯法

回溯法也稱為試探法,該方法首先暫時放棄關於問題規模大小的限制,並將問題的候選解按某種順序逐一枚舉和檢驗。當發現當前候選解不可能是解時,就選擇下一個候選解;倘若當前候選解除了還不滿足問題規模要求外,滿足所有其他要求時,繼續擴大當前候選解的規模,並繼續試探。如果當前候選解滿足包括問題規模在內的所有要求時,該候選解就是問題的一個解。在回溯法中,放棄當前候選解,尋找下一個候選解的過程稱為回溯。擴大當前候選解的規模,以繼續試探的過程稱為向前試探。

【問題】 組合問題

問題描述:找出從自然數1,2,…,n中任取r個數的所有組合。

採用回溯法找問題的解,將找到的組合以從小到大順序存於a[0],a[1],…,a[r-1]中,組合的元素滿足以下性質:

(1) a[i+1]>a[i],後一個數字比前一個大;

(2) a[i]-i<=n-r+1。

按回溯法的思想,找解過程可以敘述如下:

首先放棄組合數個數為r的條件,候選組合從只有一個數字1開始。因該候選解滿足除問題規模之外的全部條件,擴大其規模,並使其滿足上述條件(1),候選組合改為1,2。繼續這一過程,得到候選組合1,2,3。該候選解滿足包括問題規模在內的全部條件,因而是一個解。在該解的基礎上,選下一個候選解,因a[2]上的3調整為4,以及以後調整為5都滿足問題的全部要求,得到解1,2,4和1,2,5。由於對5不能再作調整,就要從a[2]回溯到a[1],這時,a[1]=2,可以調整為3,並向前試探,得到解1,3,4。重復上述向前試探和向後回溯,直至要從a[0]再回溯時,說明已經找完問題的全部解。按上述思想寫成程序如下:

【程序】

# define MAXN 100

int a[MAXN];

void comb(int m,int r)

{ int i,j;

i=0;

a[i]=1;

do {

if (a[i]-i<=m-r+1

{ if (i==r-1)

{ for (j=0;j<r;j++)

printf(「%4d」,a[j]);

printf(「\n」);

}

a[i]++;

continue;

}

else

{ if (i==0)

return;

a[--i]++;

}

} while (1)

}

main()

{ comb(5,3);

}

4.貪婪法

貪婪法是一種不追求最優解,只希望得到較為滿意解的方法。貪婪法一般可以快速得到滿意的解,因為它省去了為找最優解要窮盡所有可能而必須耗費的大量時間。貪婪法常以當前情況為基礎作最優選擇,而不考慮各種可能的整體情況,所以貪婪法不要回溯。

例如平時購物找錢時,為使找回的零錢的硬幣數最少,不考慮找零錢的所有各種發表方案,而是從最大面值的幣種開始,按遞減的順序考慮各幣種,先盡量用大面值的幣種,當不足大面值幣種的金額時才去考慮下一種較小面值的幣種。這就是在使用貪婪法。這種方法在這里總是最優,是因為銀行對其發行的硬幣種類和硬幣面值的巧妙安排。如只有面值分別為1、5和11單位的硬幣,而希望找回總額為15單位的硬幣。按貪婪演算法,應找1個11單位面值的硬幣和4個1單位面值的硬幣,共找回5個硬幣。但最優的解應是3個5單位面值的硬幣。

【問題】 裝箱問題

問題描述:裝箱問題可簡述如下:設有編號為0、1、…、n-1的n種物品,體積分別為v0、v1、…、vn-1。將這n種物品裝到容量都為V的若干箱子里。約定這n種物品的體積均不超過V,即對於0≤i<n,有0<vi≤V。不同的裝箱方案所需要的箱子數目可能不同。裝箱問題要求使裝盡這n種物品的箱子數要少。

若考察將n種物品的集合分劃成n個或小於n個物品的所有子集,最優解就可以找到。但所有可能劃分的總數太大。對適當大的n,找出所有可能的劃分要花費的時間是無法承受的。為此,對裝箱問題採用非常簡單的近似演算法,即貪婪法。該演算法依次將物品放到它第一個能放進去的箱子中,該演算法雖不能保證找到最優解,但還是能找到非常好的解。不失一般性,設n件物品的體積是按從大到小排好序的,即有v0≥v1≥…≥vn-1。如不滿足上述要求,只要先對這n件物品按它們的體積從大到小排序,然後按排序結果對物品重新編號即可。裝箱演算法簡單描述如下:

{ 輸入箱子的容積;

輸入物品種數n;

按體積從大到小順序,輸入各物品的體積;

預置已用箱子鏈為空;

預置已用箱子計數器box_count為0;

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

{ 從已用的第一隻箱子開始順序尋找能放入物品i 的箱子j;

if (已用箱子都不能再放物品i)

{ 另用一個箱子,並將物品i放入該箱子;

box_count++;

}

else

將物品i放入箱子j;

}

}

上述演算法能求出需要的箱子數box_count,並能求出各箱子所裝物品。下面的例子說明該演算法不一定能找到最優解,設有6種物品,它們的體積分別為:60、45、35、20、20和20單位體積,箱子的容積為100個單位體積。按上述演算法計算,需三隻箱子,各箱子所裝物品分別為:第一隻箱子裝物品1、3;第二隻箱子裝物品2、4、5;第三隻箱子裝物品6。而最優解為兩只箱子,分別裝物品1、4、5和2、3、6。

若每隻箱子所裝物品用鏈表來表示,鏈表首結點指針存於一個結構中,結構記錄尚剩餘的空間量和該箱子所裝物品鏈表的首指針。另將全部箱子的信息也構成鏈表。以下是按以上演算法編寫的程序。

【程序】

# include <stdio.h>

# include <stdlib.h>

typedef struct ele

{ int vno;

struct ele *link;

} ELE;

typedef struct hnode

{ int remainder;

ELE *head;

Struct hnode *next;

} HNODE;

void main()

{ int n, i, box_count, box_volume, *a;

HNODE *box_h, *box_t, *j;

ELE *p, *q;

Printf(「輸入箱子容積\n」);

Scanf(「%d」,&box_volume);

Printf(「輸入物品種數\n」);

Scanf(「%d」,&n);

A=(int *)malloc(sizeof(int)*n);

Printf(「請按體積從大到小順序輸入各物品的體積:」);

For (i=0;i<n;i++) scanf(「%d」,a+i);

Box_h=box_t=NULL;

Box_count=0;

For (i=0;i<n;i++)

{ p=(ELE *)malloc(sizeof(ELE));

p->vno=i;

for (j=box_h;j!=NULL;j=j->next)

if (j->remainder>=a[i]) break;

if (j==NULL)

{ j=(HNODE *)malloc(sizeof(HNODE));

j->remainder=box_volume-a[i];

j->head=NULL;

if (box_h==NULL) box_h=box_t=j;

else box_t=boix_t->next=j;

j->next=NULL;

box_count++;

}

else j->remainder-=a[i];

for (q=j->next;q!=NULL&&q->link!=NULL;q=q->link);

if (q==NULL)

{ p->link=j->head;

j->head=p;

}

else

{ p->link=NULL;

q->link=p;

}

}

printf(「共使用了%d只箱子」,box_count);

printf(「各箱子裝物品情況如下:」);

for (j=box_h,i=1;j!=NULL;j=j->next,i++)

{ printf(「第%2d只箱子,還剩餘容積%4d,所裝物品有;\n」,I,j->remainder);

for (p=j->head;p!=NULL;p=p->link)

printf(「%4d」,p->vno+1);

printf(「\n」);

}

}

5.分治法

任何一個可以用計算機求解的問題所需的計算時間都與其規模N有關。問題的規模越小,越容易直接求解,解題所需的計算時間也越少。例如,對於n個元素的排序問題,當n=1時,不需任何計算;n=2時,只要作一次比較即可排好序;n=3時只要作3次比較即可,…。而當n較大時,問題就不那麼容易處理了。要想直接解決一個規模較大的問題,有時是相當困難的。

分治法的設計思想是,將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。

如果原問題可分割成k個子問題(1<k≤n),且這些子問題都可解,並可利用這些子問題的解求出原問題的解,那麼這種分治法就是可行的。由分治法產生的子問題往往是原問題的較小模式,這就為使用遞歸技術提供了方便。在這種情況下,反復應用分治手段,可以使子問題與原問題類型一致而其規模卻不斷縮小,最終使子問題縮小到很容易直接求出其解。這自然導致遞歸過程的產生。分治與遞歸像一對孿生兄弟,經常同時應用在演算法設計之中,並由此產生許多高效演算法。

分治法所能解決的問題一般具有以下幾個特徵:

(1)該問題的規模縮小到一定的程度就可以容易地解決;

(2)該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質;

(3)利用該問題分解出的子問題的解可以合並為該問題的解;

(4)該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。

上述的第一條特徵是絕大多數問題都可以滿足的,因為問題的計算復雜性一般是隨著問題規模的增加而增加;第二條特徵是應用分治法的前提,它也是大多數問題可以滿足的,此特徵反映了遞歸思想的應用;第三條特徵是關鍵,能否利用分治法完全取決於問題是否具有第三條特徵,如果具備了第一條和第二條特徵,而不具備第三條特徵,則可以考慮貪心法或動態規劃法。第四條特徵涉及到分治法的效率,如果各子問題是不獨立的,則分治法要做許多不必要的工作,重復地解公共的子問題,此時雖然可用分治法,但一般用動態規劃法較好。

分治法在每一層遞歸上都有三個步驟:

(1)分解:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;

(2)解決:若子問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題;

(3)合並:將各個子問題的解合並為原問題的解。

6.動態規劃法

經常會遇到復雜問題不能簡單地分解成幾個子問題,而會分解出一系列的子問題。簡單地採用把大問題分解成子問題,並綜合子問題的解導出大問題的解的方法,問題求解耗時會按問題規模呈冪級數增加。

為了節約重復求相同子問題的時間,引入一個數組,不管它們是否對最終解有用,把所有子問題的解存於該數組中,這就是動態規劃法所採用的基本方法。以下先用實例說明動態規劃方法的使用。

【問題】 求兩字元序列的最長公共字元子序列

問題描述:字元序列的子序列是指從給定字元序列中隨意地(不一定連續)去掉若干個字元(可能一個也不去掉)後所形成的字元序列。令給定的字元序列X=「x0,x1,…,xm-1」,序列Y=「y0,y1,…,yk-1」是X的子序列,存在X的一個嚴格遞增下標序列<i0,i1,…,ik-1>,使得對所有的j=0,1,…,k-1,有xij=yj。例如,X=「ABCBDAB」,Y=「BCDB」是X的一個子序列。

考慮最長公共子序列問題如何分解成子問題,設A=「a0,a1,…,am-1」,B=「b0,b1,…,bm-1」,並Z=「z0,z1,…,zk-1」為它們的最長公共子序列。不難證明有以下性質:

(1) 如果am-1=bn-1,則zk-1=am-1=bn-1,且「z0,z1,…,zk-2」是「a0,a1,…,am-2」和「b0,b1,…,bn-2」的一個最長公共子序列;

(2) 如果am-1!=bn-1,則若zk-1!=am-1,蘊涵「z0,z1,…,zk-1」是「a0,a1,…,am-2」和「b0,b1,…,bn-1」的一個最長公共子序列;

(3) 如果am-1!=bn-1,則若zk-1!=bn-1,蘊涵「z0,z1,…,zk-1」是「a0,a1,…,am-1」和「b0,b1,…,bn-2」的一個最長公共子序列。

這樣,在找A和B的公共子序列時,如有am-1=bn-1,則進一步解決一個子問題,找「a0,a1,…,am-2」和「b0,b1,…,bm-2」的一個最長公共子序列;如果am-1!=bn-1,則要解決兩個子問題,找出「a0,a1,…,am-2」和「b0,b1,…,bn-1」的一個最長公共子序列和找出「a0,a1,…,am-1」和「b0,b1,…,bn-2」的一個最長公共子序列,再取兩者中較長者作為A和B的最長公共子序列。

代碼如下:

# include <stdio.h>

# include <string.h>

# define N 100

char a[N],b[N],str[N];

int lcs_len(char *a, char *b, int c[ ][ N])

{ int m=strlen(a), n=strlen(b), i,j;

for (i=0;i<=m;i++) c[i][0]=0;

for (i=0;i<=n;i++) c[0][i]=0;

for (i=1;i<=m;i++)

for (j=1;j<=m;j++)

if (a[i-1]==b[j-1])

c[i][j]=c[i-1][j-1]+1;

else if (c[i-1][j]>=c[i][j-1])

c[i][j]=c[i-1][j];

else

c[i][j]=c[i][j-1];

return c[m][n];

}

char *buile_lcs(char s[ ],char *a, char *b)

{ int k, i=strlen(a), j=strlen(b);

k=lcs_len(a,b,c);

s[k]=』\0』;

while (k>0)

if (c[i][j]==c[i-1][j]) i--;

else if (c[i][j]==c[i][j-1]) j--;

else { s[--k]=a[i-1];

i--; j--;

}

return s;

}

void main()

{ printf (「Enter two string(<%d)!\n」,N);

scanf(「%s%s」,a,b);

printf(「LCS=%s\n」,build_lcs(str,a,b));

}

7.迭代法

迭代法是用於求方程或方程組近似根的一種常用的演算法設計方法。設方程為f(x)=0,用某種數學方法導出等價的形式x=g(x),然後按以下步驟執行:

(1) 選一個方程的近似根,賦給變數x0;

(2) 將x0的值保存於變數x1,然後計算g(x1),並將結果存於變數x0;

(3) 當x0與x1的差的絕對值還小於指定的精度要求時,重復步驟(2)的計算。

若方程有根,並且用上述方法計算出來的近似根序列收斂,則按上述方法求得的x0就認為是方程的根。上述演算法用C程序的形式表示為:

程序如下:

【演算法】迭代法求方程組的根

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

x[i]=初始近似根;

do {

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

y[i] = x[i];

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

x[i] = gi(X);

for (delta=0.0,i=0;i<n;i++)

if (fabs(y[i]-x[i])>delta) delta=fabs(y[i]-x[i]); } while (delta>Epsilon);

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

printf(「變數x[%d]的近似根是 %f」,I,x[i]);

printf(「\n」);

} 具體使用迭代法求根時應注意以下兩種可能發生的情況:

(1)如果方程無解,演算法求出的近似根序列就不會收斂,迭代過程會變成死循環,因此在使用迭代演算法前應先考察方程是否有解,並在程序中對迭代的次數給予限制;

(2)方程雖然有解,但迭代公式選擇不當,或迭代的初始近似根選擇不合理,也會導致迭代失敗。

8.窮舉搜索法

窮舉搜索法是對可能是解的眾多候選解按某種順序進行逐一枚舉和檢驗,並從眾找出那些符合要求的候選解作為問題的解。

【問題】 將A、B、C、D、E、F這六個變數排成如圖所示的三角形,這六個變數分別取[1,6]上的整數,且均不相同。求使三角形三條邊上的變數之和相等的全部解。如圖就是一個解。

程序引入變數a、b、c、d、e、f,並讓它們分別順序取1至6的整數,在它們互不相同的條件下,測試由它們排成的如圖所示的三角形三條邊上的變數之和是否相等,如相等即為一種滿足要求的排列,把它們輸出。當這些變數取盡所有的組合後,程序就可得到全部可能的解。程序如下:

# include <stdio.h>

void main()

{ int a,b,c,d,e,f;

for (a=1;a<=6;a++) {

for (b=1;b<=6;b++) {

if (b==a) continue;

for (c=1;c<=6;c++) {

if (c==a)||(c==b) continue;

for (d=1;d<=6;d++) {

if (d==a)||(d==b)||(d==c) continue;

for (e=1;e<=6;e++) {

if (e==a)||(e==b)||(e==c)||(e==d) continue;

f = 21-(a+b+c+d+e);

if ((a+b+c==c+d+e))&&(a+b+c==e+f+a)) {

printf(「%6d,a);

printf(「%4d%4d」,b,f);

printf(「%2d%4d%4d」,c,d,e);

scanf(「%*c」);

}

}

}

}

}

}}

按窮舉法編寫的程序通常不能適應變化的情況。如問題改成有9個變數排成三角形,每條邊有4個變數的情況,程序的循環重數就要相應改變。

② 演算法:求最快的演算法,我的演算法超時了,2s,64MB

申請一個長度為n的數組A,用於存儲某個列的元素。(這步沒必要,只是為了說明清晰)
把第一列裝進A,掃描第二列,並與第一列相應元素比較。統計第1列時為1且第二列時也為1的次數m,以及第1列時為0且第二列時也為0的次數n,則出現在第一列和第二列的滿足題意矩陣個數為:
m(m-1)/2 + n(n-1)/2
繼續掃描第三列,第四列...第n列(這里復雜度為n^2,因為要掃描整個矩陣),並將每次掃描後計算出的數值累計求和。
上面只包括了那些矩陣,它的左上和左下元素在第一列。上述過程不斷迭代即可(這里復雜度為n,因為到迭代n列)
演算法復雜度O(n^3),對於這么小的矩陣,不可能超時。

③ 一個演算法的『計算量』該如何量化

這問題提的好。衡量演算法開銷通常使用O()運算符由於同一個演算法運行於不同的機器上所耗費的實際時間是不同的,所以不能使用實際時間單位衡量演算法運行效率,而應使用邏輯單位。描述演算法復雜度的參數為演算法的輸入數據規模,通常用n來表示,那麼演算法的復雜度可表示為一個關於n的函數。通常最常用的描述演算法復雜度的符號為O符號,即將復雜度表示為O(f(n))。其中f(n)用函數形式描述演算法執行命令條數與輸入規模n的關系,而O()起到估算化簡的作用。比如某個演算法經過邏輯分析後,其指令數可表示為f(n)=8n^2+10n+500,那麼可以使用O(f(n))來簡化其表達,O()符號運算性質有多條,總體來說就是保留增長率最高的項且忽略常數系數,上面的表達式化簡結果為O(n^2)。當然O()符號不能完美描述演算法開銷,因為它忽略了常數的影響,當某些項前的常數系數非常非常大時,會對演算法復雜度的判斷造成誤差,這就要具體問題具體分析了。下面簡單說一下具體如何分析。for (i = 0;ik *= i;}這段代碼每次循環中執行一次乘法兩次賦值(假定乘法使用單周期乘法器實現),循環開始執行一次賦值,那麼共計執行指令數3n+1,即復雜度為O(n)。for (i = 0;ifor (j = 0;jk += i * j;}循環嵌套時,內層循環執行3n+1條指令,外層循環n次,共n*(3n+1)+1=3n^2 + n +1條指令,即O(n^2)。

④ 行列式按列展開的方法是跟按行展開的一樣嗎

是一樣的,展開都是正確的。第一張圖里的錯誤步驟在第二行。

一、錯誤指導:

(1)+(3) x 7/3,應該是

| 0 4 -10/3 |

|0 -5 5 |

|3 9 2 |

第一行第二列的10,算錯了,應該是4= -17-(-7/3)*9。

用4代入,最後算出的結果會是10,而不是100。

二、行列式演算法:

1、為了計算更高階行列式,我們需要引入兩個概念:全排列和逆序數。
全排列比較簡單,在高中就學過:n個不同元素的不同排列法一共有

2、全排列:在這些排列中,如果規定從小到大是標准次序,則每有兩個元素不是標准次序就稱為一個「逆序」。比如32514中,3在2前面,3在1前面,5在1前面,5在4前面,2在1前面。逆序數就是排列中逆序的數目,用t表示。

3、逆序數:逆序數沒有計算方法,就是靠數出來的!每次看一個數,看前面有比它大的有幾個。如果逆序數是奇數,這個排列叫奇排列,否則叫偶排列。標准次序逆序是0,所以是偶排列。

4、n階行列式,n階行列式的值,n階行列式一共有n!項(因為是a的第二個下標的全排列),每一項都是不同行不同列的n個元素的積,當第二下標的排列是奇排列符號為負,否則為正。

(4)ik演算法擴展閱讀:


一、行列式的性質:

1、行列式A中某行(或列)用同一數k乘,其結果等於kA。

2、行列式A等於其轉置行列式AT(AT的第i行為A的第i列)。

3、若n階行列式|αij|中某行(或列);行列式則|αij|是兩個行列式的和,這兩個行列式的第i行(或列),一個是b1,b2,…,bn;另一個是с1,с2,…,сn;其餘各行(或列)上的元與|αij|的完全一樣。

4、行列式A中兩行(或列)互換,其結果等於-A。 ⑤把行列式A的某行(或列)中各元同乘一數後加到另一行(或列)中各對應元上,結果仍然是A。

二、行列式數學定義:

1、若n階方陣A=(aij),則A相應的行列式D記作D=|A|=detA=det(aij)

2、若矩陣A相應的行列式D=0,稱A為奇異矩陣,否則稱為非奇異矩陣.

3、標號集:序列1,2,...,n中任取k個元素i1,i2,...,ik滿足1≤i1<i2<...<ik≤n(1)

4、i1,i2,...,ik構成{1,2,...,n}的一個具有k個元素的子列,{1,2,...,n}的具有k個元素的滿足(1)的子列的全體記作C(n,k),顯然C(n,k)共有個子列。

5、因此C(n,k)是一個具有個元素的標號集(參見第二十一章,1,二),C(n,k)的元素記作σ,τ,...,σ∈C(n,k)。

6、表示σ={i1,i2,...,ik}是{1,2,...,n}的滿足(1)的一個子列.若令τ={j1,j2,...,jk}∈C(n,k),則σ=τ表示i1=j1,i2=j2,...,ik=jk。


參考資料來源:網路-行列式

⑤ 古典密碼安全演算法有哪些

世界上最早的一種密碼產生於公元前兩世紀。是由一位希臘人提出的,人們稱之為
棋盤密碼,原因為該密碼將26個字母放在5×5的方格里,i,j放在一個格子里,具體情
況如下表所示

1 2 3 4 5
1 a b c d e
2 f g h i,j k
3 l m n o p
4 q r s t u
5 v w x y z

這樣,每個字母就對應了由兩個數構成的字元αβ,α是該字母所在行的標號,β是列
標號。如c對應13,s對應43等。如果接收到密文為

43 15 13 45 42 15 32 15 43 43 11 22 15

則對應的明文即為secure message。

另一種具有代表性的密碼是凱撒密碼。它是將英文字母向前推移k位。如k=5,則密
文字母與明文與如下對應關系

a b c d e f g h i j k l m n o p q r s t u v w x y z
F G H I J K L M N O P Q R S T U V W X Y Z A B C D E

於是對應於明文secure message,可得密文為XJHZWJRJXXFLJ。此時,k就是密鑰。為了
傳送方便,可以將26個字母一一對應於從0到25的26個整數。如a對1,b對2,……,y對
25,z對0。這樣凱撒加密變換實際就是一個同餘式

c≡m+k mod 26

其中m是明文字母對應的數,c是與明文對應的密文的數。

隨後,為了提高凱撒密碼的安全性,人們對凱撒密碼進行了改進。選取k,b作為兩
個參數,其中要求k與26互素,明文與密文的對應規則為

c≡km+b mod 26

可以看出,k=1就是前面提到的凱撒密碼。於是這種加密變換是凱撒野加密變換的
推廣,並且其保密程度也比凱撒密碼高。

以上介紹的密碼體制都屬於單表置換。意思是一個明文字母對應的密文字母是確定
的。根據這個特點,利用頻率分析可以對這樣的密碼體制進行有效的攻擊。方法是在大
量的書籍、報刊和文章中,統計各個字母出現的頻率。例如,e出現的次數最多,其次
是t,a,o,I等等。破譯者通過對密文中各字母出現頻率的分析,結合自然語言的字母頻
率特徵,就可以將該密碼體制破譯。

鑒於單表置換密碼體制具有這樣的攻擊弱點,人們自然就會想辦法對其進行改進,
來彌補這個弱點,增加抗攻擊能力。法國密碼學家維吉尼亞於1586年提出一個種多表式
密碼,即一個明文字母可以表示成多個密文字母。其原理是這樣的:給出密鑰
K=k[1]k[2]…k[n],若明文為M=m[1]m[2]…m[n],則對應的密文為C=c[1]c[2]…c[n]。
其中C[i]=(m[i]+k[i]) mod 26。例如,若明文M為data security,密鑰k=best,將明
文分解為長為4的序列data security,對每4個字母,用k=best加密後得密文為

C=EELT TIUN SMLR

從中可以看出,當K為一個字母時,就是凱撒密碼。而且容易看出,K越長,保密程
度就越高。顯然這樣的密碼體制比單表置換密碼體制具有更強的抗攻擊能力,而且其加
密、解密均可用所謂的維吉尼亞方陣來進行,從而在操作上簡單易行。該密碼可用所謂
的維吉尼亞方陣來進行,從而在操作上簡單易行。該密碼曾被認為是三百年內破譯不了
的密碼,因而這種密碼在今天仍被使用著。

古典密碼的發展已有悠久的歷史了。盡管這些密碼大都比較簡單,但它在今天仍有
其參考價值。

⑥ 矩陣演算法是什麼

矩陣演算法指矩陣與演算法。

矩陣乘法是一種高效的演算法可以把一些一維遞推優化到log( n ),還可以求路徑方案等,所以更是是一種應用性極強的演算法。矩陣,是線性代數中的基本概念之一。

一個m×n的矩陣就是m×n個數排成m行n列的一個數陣。由於它把許多數據緊湊的集中到了一起,所以有時候可以簡便地表示一些復雜的模型。矩陣乘法看起來很奇怪,但實際上非常有用,應用也十分廣泛。

矩陣乘法的兩個重要性質:

一,矩陣乘法不滿足交換律。

二,矩陣乘法滿足結合律。矩陣乘法不滿足交換律,因為交換後兩個矩陣有可能不能相乘。它又滿足結合律,假設你有三個矩陣A、B、C,那麼(AB)C和A(BC)的結果的第i行第j列上的數都等於所有A(ik)*B(kl)*C(lj)的和(枚舉所有的k和l)。

⑦ 這是什麼加密演算法

電報。。。

⑧ 條件模擬

設已知RFZ(u)在n個點處的觀測值為z(uα),α=1,…,n隨機模擬的目的是建立一系列的與Z(u)等概率的高清晰度的實現

。實際上是針對A中的N個特定的點(通常是網格的節點),給出各個實現在這些點上的值

地質勘探三維可視化技術及系統開發

這樣的實現可以取若干個,ι代表其中的每個實現。

如果在原給數據點uα處的模擬結果與原始數據相同,即

地質勘探三維可視化技術及系統開發

就說這模擬是條件模擬。

這段程序的目的是針對Z(u)是連續或離散的情形,給出計算條件模擬(7.3.1)的各種演算法。

一、序列高斯模擬

這種方法適用於連續型的RF Z(u)。共分如下幾個步驟:

(1)建立單變數cdfFz(z)(histsmth),它代表Z(u)在整個研究區的變化規律。設Z(u)的n個樣本值按遞增順序排列成序列

地質勘探三維可視化技術及系統開發

則對應於第k個大值的累計頻率為

地質勘探三維可視化技術及系統開發

其中的wj是人為給定的一組權值。

(2)用Fz(z)對z作正態得分變換(nscore及backtr)設RF Y(u)是正態分布的,它的單變數cdf是正態的,即

地質勘探三維可視化技術及系統開發

是標准正態分布函數,均值為0,方差為1。我們要建立一個變換Y=φ(Z),原則是Y與Z的對應於p分位數的概率相等

地質勘探三維可視化技術及系統開發

地質勘探三維可視化技術及系統開發

其中是

的逆函數,或Y的分位數函數。(7.3.3)式給出的y是z的正態得分變換。

(3)序列模擬

(ⅰ)定義一個隨機通道,它通過N個待模擬的節點u1,…,u N中的每一個。設這些點的隨機順序為

(ⅱ)對於每個待模擬的點

當j=1時,它的條件數據是原給的n個數據,記為(n)={ Z(ui)=z(ui),i=1,…,n)。如果j>1,它的條件數據還包含已被模擬的數據

j}。但在實際計算時,僅取

某鄰域內的部分數據。

(ⅲ)根據正態得分數據y(ui)計算變差函數,再用克立格方法計算RF Y

的條件分布函數(ccdf)的均值和方差,從而完全確定ccdf。

(ⅳ)根據這分布函數提取模擬值

(ⅴ)將此模擬值y(1)

轉化為原始變數的模擬值

地質勘探三維可視化技術及系統開發

並作為下一個模擬點的條件數據,返回(ⅱ),再抽取出模擬植(yι

,直到j+1=N,進行下一步。

對模擬值進行一些內插或外推常常是必要的。

如果必要的話,可以進行多次模擬,得{Z(1)(uj),J=1,…,N},1=1,2,…,L。每次模擬的隨機通道可以是相同的,但最好還是不同的。

二、序列指示模擬

為了在每個待模擬點

處抽取模擬值,關鍵是對RF Z

在這一點處的不確定性進行分析,即建立這一點處的ccdf 前一個方法是將原來的RF轉化為正態RF來實現的。但我們可以用指示克立格直接求出ccdf。

(1)離散變數的模擬

設Z(u)是離散RF,對於

取K個不同狀態s1,…,sk中的一個,而且僅取一個。由此,可將Z(u)轉化為指示RF

地質勘探三維可視化技術及系統開發

這時,具體的觀測值i(u,sk)應滿足如下條件

地質勘探三維可視化技術及系統開發

前已指出,對指示變數的Krige估計給出Z(u)取各種狀態的條件概率的估計值

地質勘探三維可視化技術及系統開發

圖7-15 序列高斯模擬計算流程(L—模擬次)

其中Pk=E{I(u,sk)=1}=P{Z(u)=sk}可取為z(uα)中的取sk的點所佔的比例。

對於每個待模擬的點

用指示克立格可以求出

取各種值的一列系條件概率的估計值。

地質勘探三維可視化技術及系統開發

其中的條件(*)包括原始數據和已有的模擬結果,這些條件概率的和不一定等於1,將它們作歸一化處理,並將K個類任意排序,仍記為1,2,…,K,令

地質勘探三維可視化技術及系統開發

在區間[0,1]內建立cdf型尺度,即將它割成K個子區間,各區間長度分別為P1,P2,…,Pk

地質勘探三維可視化技術及系統開發

在[0,1]內取均勻分布的隨機數p,p所落入的區間決定了在點

處所模擬的類。將模擬結果加入到原始數據中,修改指示數據集合,並進行下一下點

(2)連續變數的模擬

將連續變數Z(u)離散化成K個互不相交的區間sk:(zk-1,zk],k=1,…,K,這可以將Z(u)看成是含有K類的離散變數,並按前述方法進行模擬。這樣做造成的類內差異的損失可用某種先驗的類內分布來部分地補賞。

將連續變數分成K段的優點是可以在每類內用不同的變差函數模型來刻劃空間分布,從而可以處理多母體混合的問題。另一個優點是這種估計和模擬方法可以處理軟數據。

對於純的離散變數來說,它的各類沒有任何順序,而這里的各類都有一定的順序。因此,最好用相應的「累計類指示值」來刻劃各類。令

地質勘探三維可視化技術及系統開發

類(zk-1,zk]用乘積I(u,zk)[1-I(u,zk-1)]=1來定義。除了第一類和最後一類,累計指示變差函數的推斷要比「類」指示變差函數的推斷容易,尤其當某些類有小的邊緣概率時。累計指示值與所研究的連續變數的ccdf直接有關。

對於隨機通道上的每個待模擬的點

用IK方法可得K個條件概率估計值

地質勘探三維可視化技術及系統開發

圖7-16 序列指示模擬計算流程

用類內插值的方法,可以對所有可能的閾值z∈[zmin,zmax]得到估計

用蒙特—卡羅法可得模擬值z()ι

:取[0,1]內的均勻分布的隨機數p(ι)∈[0,1],反求上述ccdf的p(ι)分位數

地質勘探三維可視化技術及系統開發

它使

地質勘探三維可視化技術及系統開發

將已模擬的結果作為已知數據,修改指示數據集合,再模擬下一個點。

三、概率場模擬

在序列模擬中,將每個模擬值都看成是下一次模擬的條件數據,因此,在每個模擬點處必須重新計算ccdf。如果每次模擬的條件數據都只是原始數據,就可以保持ccdf不變,從而提高計算速度。這里,用於從ccdf中抽取模擬值的概率本身是相關的,而不象序列模擬那樣是相互獨立的。

設F(u,z|(n))和

是位於u和u的ccdf,這條件僅是原始的n個點的數據。這些ccdf可以通過對連續變數Z(u)的正態得分變換實施多次高斯克立格得到,也可以通過對指示數據實施IK得到。於是Z(u)的模擬值就可以用具有空間相關性的概率p(ι)(u)和

(從)這些ccdf中抽取得到:

地質勘探三維可視化技術及系統開發

它使

地質勘探三維可視化技術及系統開發

概率值p(1)(u)和p(ι)的空間相關性是因為它們來自一個概率場或RF P(u)的同一個實現(ι),這P(u)服從[0,1]內的平穩的均勻分布,而且它的協方差函數來自數據的均勻變換的樣本協方差。

注意,概率場的實現{p(1)(u),u∈A}不要求條件,因為ccdf本身就是以原始數據為條件的。在數據點uα處,ccdf F(uα,z|(n))具有零方差,從而集中於數據值z(uα),因此不管概率值p(1)(uα)是什麼,那個ccdf總是返回到數據值z(1)(uα)=z(uα)。

與序列方法相反,概率場方法將滿足原始數據條件化的任務和協方差再現的任務分開,前者通過ccdf F(u,z|(n))來完成,後者通過概率值P(ι)(u)來完成。

該法的主要優點是計算速度快:

a.F(u,z|(n))的條件只是n個原始數據,只須計算一次並存儲起來。

b.由於概率場的實現{p(ι)(u),u∈A,ι=1,…,L}是無條件的,各種快速的模擬演算法都可以用,例如譜方法,無條件fratals,或簡單的隨機滑動平均等。

c.每個P實現就可用於從已算出的ccdf中抽取Z(u)的實現。

四、高斯場的多重截斷

該法可用高斯實現(例如用序列高斯模擬)的多重截斷對離散型RF進行模擬。

具體計算分成如下幾個步驟:

(1)這里所研究的對象為離散型的RF Z(u),u∈ G,它可取k個不同的狀態s1,…,sk。原始數據為z(uα)=z(uα1,uα2,uα3),α=1,2,…,n,它的取值為上述k個不同狀態中的一個。

(2)根據問題的實際背景調整各狀態的順序,使之能夠反映實際狀態的接觸關系,假定現有的狀態順序即為調整後的結果。根據原始數據,計算各狀態所佔有的比例p1,p2,…,pk,作為Z(u)取各狀態的概率的近似。將這些比例轉化為累積概率。

地質勘探三維可視化技術及系統開發

由此形成正態分布的截斷值。

0=t0≤t1≤…≤tk=1

有時要考慮與空間位置有關的截斷值ti(u), i=1,…,K,u∈ G。

圖7-17 概率場模擬計算流程

(3)造一個平穩的高斯RF Y(u)~N(0,1),u∈G。它的相關系數函數ρ(h)的選擇應保證在該高斯函數經過截斷處理後,得到的理論交叉指示變異函數模型與原始數據的實驗交叉指示變異函數相容。

以此為基礎,求Y(u)的非條件模擬yi(u)(可用轉向帶法),要同時算出網路節點在原始數據點處的模擬結果。

(4)運用克立格方法,將原始數據轉化為Y(u)的條件值,以保證將Y(u)的條件模擬結果截斷處理後,條件化到已知數據。

(5)在前兩步的基礎上,求Y(u)的條件模擬(例如用誤差模擬方法)。

(6)將上述條件模擬結果ycs(u)按下述方法作多重截斷處理:

地質勘探三維可視化技術及系統開發

由此得到原來離散RFZ(u)的模擬結果:

地質勘探三維可視化技術及系統開發

圖7-18 高斯場的多重截斷計算流程

⑨ ikanalyzer 可以做詞性標注嗎

  • 可以的

熱點內容
安卓9的小葯丸怎麼弄出來 發布:2025-04-03 10:31:04 瀏覽:15
newphp 發布:2025-04-03 10:30:23 瀏覽:749
matlab用什麼語言編程 發布:2025-04-03 10:29:34 瀏覽:34
源碼計算溢出 發布:2025-04-03 10:17:36 瀏覽:715
逍客用什麼配置最好 發布:2025-04-03 10:17:25 瀏覽:602
安卓怎麼鎖屏攝像 發布:2025-04-03 09:39:03 瀏覽:165
java編譯生成什麼文件 發布:2025-04-03 09:38:39 瀏覽:543
testdisklinux 發布:2025-04-03 09:35:29 瀏覽:878
unity3d塔防游戲源碼 發布:2025-04-03 09:27:37 瀏覽:834
源代碼程序經過編譯 發布:2025-04-03 09:23:35 瀏覽:689