第一篇:RC4算法總結(jié)
一、RC4算法原理
RC4算法的原理很簡單,包括初始化算法(KSA)和偽隨機(jī)子密碼生成算法(PRGA)兩大部分。
RC4產(chǎn)生一個(gè)偽隨機(jī)比特流(a keystream),加密的時(shí)候,把它跟明文進(jìn)行比特級別的異或處理,解密時(shí)進(jìn)行一樣的步驟(因?yàn)楫惢虿僮魇菍ΨQ的)。(這個(gè)類似于Vernam cipher,只不過后者不使用偽隨機(jī)比特流而直接使用隨機(jī)比特流)。為了產(chǎn)生keystream,本密碼算法使用時(shí)需要兩個(gè)數(shù)據(jù)的私有空間來保存內(nèi)部狀態(tài):
1.總共256個(gè)字節(jié)的序列(下面用“S“代替)
2.兩個(gè)8比特的索引指針(下面用“i”和“j”代替)
比特流序列的初始化是根據(jù)key的長度(key的長度通常在40到256比特之間),使用key-scheduling 算法來進(jìn)行的(KSA),一旦完成了初始化,比特流就可以根據(jù)偽隨機(jī)生成算法(PRGA)來產(chǎn)生。
(1)The key-scheduling algorithm(KSA)key-scheduling算法用來初始化數(shù)組“S”中的字節(jié)序列,“keylength”定義了key的字節(jié)長度,可能的范圍是[1, 256],典型的值是5到16之間,相應(yīng)的key長度就是40-128比特。首先,數(shù)組“S”被初始化成identity permutation(身份鑒別的序列),隨后在PRGA的算法中進(jìn)行256為周期的循環(huán)列舉出來,每次處理的方式都是一樣的,是聯(lián)合key的字節(jié)進(jìn)行的。
for i from 0 to 255 S[i] := i endfor
j := 0
for i from 0 to 255
j:=(j + S[i] + key[i mod keylength])mod 256 swap(&S[i],&S[j])endfor
(2)偽隨機(jī)生成算法(PRGA)
對于盡可能多的每個(gè)列舉過程,PRGA算法修改 內(nèi)部的狀態(tài)并輸出keystream的一個(gè)字節(jié)。在每次循環(huán)中,PRGA把i加一,并把i所指向的S值加到j(luò)上去,然后交換S[i]和S[j]的值,最后 輸出S[i]和S[j]的和(取256的模)對應(yīng)的S值。至多經(jīng)過256次,S每個(gè)位置上的值都被交換一次。
i := 0 j := 0
while GeneratingOutput: i :=(i + 1)mod 256 j :=(j + S[i])mod 256 swap(&S[i],&S[j])output S[(S[i] + S[j])mod 256] endwhile
二、RC4算法實(shí)現(xiàn)
實(shí)現(xiàn)一
假設(shè)S-box的長度為256,密鑰長度為Len。
KSA部分:
先來看看算法的初始化部分(用C代碼表示):
其中,參數(shù)1是一個(gè)256長度的char型數(shù)組,定義為: unsigned char sBox[256];
參數(shù)2是密鑰,其內(nèi)容可以隨便定義:char key[256];
參數(shù)3是密鑰的長度,Len = strlen(key);
void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len)
{
int i =0, j = 0, k[256] = {0};
for(i=0;i<256;i++)
{
s[i]=i;
k[i]=key[i%Len];
}
for(i=0;i<256;i++)
{
j=(j+s[i]+k[i])%256;
swap(s,x,y);
} } 在初始化的過程中,密鑰的主要功能是將S-box攪亂,i確保S-box的每個(gè)元素都得到處理,j保證S-box的攪亂是隨機(jī)的。而不同的S-box在經(jīng)過偽隨機(jī)子密碼生成算法的處理后可以得到不同的子密鑰序列,將S-box和明文進(jìn)行xor運(yùn)算,得到密文,解密過程也完全相同。
PRGA部分:
再來看看算法的加密部分(用C代碼表示):
其中,參數(shù)1是上邊rc4_init函數(shù)中,被攪亂的S-box;
參數(shù)2是需要加密的數(shù)據(jù)data;
參數(shù)3是data的長度.void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len)
{
int x = 0, y = 0, t = 0, i = 0;
for(i=0;i { x=(x+1)%256; y=(y+s[x])%256; t=(s[x]+s[y])%256;swap(s,x,y); Data[i] ^= s[t]; } } 最后,在main函數(shù)中,調(diào)用順序如下: void main() { unsigned char s[256] = {0};//S-box unsigned char key[256] = {”just for test“}; unsigned char pData[512] = ”這是一個(gè)用來加密的數(shù)據(jù)Data“;ULONG len = strlen(pData);printf(”pData = %sn“,pData);printf(”key = %s, length = %dn“,key,strlen(key)); rc4_init(s,(unsigned char *)key,strlen(key));//初始化 rc4_crypt(s,(unsigned char *)pData,len);//加密 printf(”pData = %snn“,pData); rc4_crypt(s,(unsigned char *)pData,len);//解密 printf(”pData = %snn",pData);} 實(shí)現(xiàn)二(對實(shí)現(xiàn)一的改進(jìn)) 許多流加密算法都是基于Linear feedback shift register(LFSRs, 寄存器線性反饋移位), 雖然在硬件上有效率但是在軟件實(shí)現(xiàn)上卻可能比較慢。RC4的設(shè)計(jì)避免了LFSRs的使用,對于軟件實(shí)現(xiàn)是相當(dāng)理想的,它只需字節(jié)操作,使用了256字節(jié)的 狀態(tài)數(shù)組(從S[0]到S[255]), k字節(jié)的key內(nèi)存(從key[0]到key[k-1]),整數(shù)i,j和k。進(jìn)行256的取模操作可以用255的字節(jié)AND來進(jìn)行(在有些平臺上,只需簡單地進(jìn)行字節(jié)相加,忽略掉溢出即可)。 /*交換數(shù)組s中的兩個(gè)元素*/ void swap(unsigned char *s, unsigned int i, unsigned int j){ unsigned char temp = s[i];s[i] = s[j];s[j] = temp;} /*KSA算法初始化s-box*/ void rc4_init(unsigned char *s, unsigned char *key, unsigned long length){ unsigned int i = 0, j = 0;unsigned char temp = 0;for(i = 0;i < 256;i++){ s[i] = i;} for(i = 0;i < 256;i++){ } } /*和255進(jìn)行邏輯與運(yùn)算相當(dāng)于對256求模*/ j =(j+s[i]+key[i%length])&255;swap(s, i, j);/*PRGA算法生成偽隨機(jī)序列*/ void rc4_crypt(unsigned char *s, unsigned char *data, unsigned long length){ int x = 0, y = 0, t = 0, i = 0;for(i=0;i x =(x+1)&255; y =(y+s[x])&255; swap(s, x, y); t =(s[x]+s[y])&255; data[i] ^= s[t];} } RC電路在模擬電路、脈沖數(shù)字電路中得到廣泛的應(yīng)用,由于電 路的形式以及信號源和R,C元件參數(shù)的不同,因而組成了RC電路的各種應(yīng)用形式:微分電路、積分電路、耦合電路、濾波電路及脈沖分壓器。關(guān)鍵詞:RC電路。微分、積分電路。耦合電路。在模擬及脈沖數(shù)字電路中,常常用到由電阻R和電容C組成的RC電路,在些電路中,電阻R和電容C的取值不同、輸入和輸出關(guān)系以及處理的波形之間的關(guān)系,產(chǎn)生了RC電路的 不同應(yīng)用,下面分別談?wù)勎⒎蛛娐?、積分電路、耦合電路、脈沖分壓器以及濾波電路。 1.RC微分電路 如圖1所示,電阻R和電容C串聯(lián)后接入輸入信號VI,由電阻R輸出信號VO,當(dāng)RC 數(shù)值與輸入方波寬度tW之間滿足:RC< 在t=t1時(shí),VI由0→Vm,因電容上電壓不能突變(來不及充電,相當(dāng)于短 路,VC=0),輸入電壓VI全降在電阻R上,即VO=VR=VI=V m。隨后(t>t1),電容C的電壓按指數(shù)規(guī)律快速充電上升,輸出電壓隨之按指數(shù)規(guī) 律下降(因VO=VI-VC=Vm-VC),經(jīng)過大約3τ(τ=R × C)時(shí),VCVm,VO0,τ(RC)的值愈小,此過程愈快,輸出正 脈沖愈窄。 t=t2時(shí),VI由Vm→0,相當(dāng)于輸入端被短路,電容原先充有左正右負(fù)的電壓V m開始按指數(shù)規(guī)律經(jīng)電阻R放電,剛開始,電容C來不及放電,他的左端(正電)接地,所以VO=-Vm,之后VO隨電容的放電也按指數(shù)規(guī)律減小,同樣經(jīng)過大 約3τ后,放電完畢,輸出一個(gè)負(fù)脈沖。 只要脈沖寬度tW>(5~10)τ,在tW時(shí)間內(nèi),電容C已完成充電或放電(約需3 τ),輸出端就能輸出正負(fù)尖脈沖,才能成為微分電路,因而電路的充放電時(shí)間常數(shù)τ必須 滿足:τ<(1/5~1/10)tW,這是微分電路的必要條件。 由于輸出波形VO與輸入波形VI之間恰好符合微分運(yùn)算的結(jié)果[VO=RC(dVI/dt)],即輸出波形是取輸入波形的變化部分。如果將VI按傅里葉級展開,進(jìn)行微分運(yùn)算的結(jié)果,也將是VO的表達(dá)式。他主要用于對復(fù)雜波形的分離和分頻器,如從電視信號的復(fù)合同步脈沖分離出行同步脈沖和時(shí)鐘的倍頻應(yīng)用。 2.RC耦合電路 圖1中,如果電路時(shí)間常數(shù)τ(RC)>>tW,他將變成一個(gè)RC耦合電路。輸 出波形與輸入波形一樣。如圖3所示。 (1)在t=t1時(shí),第一個(gè)方波到來,VI由0→Vm,因電容電壓不能突變(VC=0),VO=VR=VI=Vm。 (2)t1 (3)t=t2時(shí),VO由Vm→0,相當(dāng)于輸入端被短路,此時(shí),VC已充有左 正右負(fù)電壓Δ[Δ=(VI/τ)×tW],經(jīng)電阻R非常緩慢地放電。 (4)t=t3時(shí),因電容還來不及放完電,積累了一定電荷,第二個(gè)方波到來,電阻上的電 壓就不是Vm,而是VR=Vm-VC(VC≠0),這樣第二個(gè)輸出 方波比第一個(gè)輸出方 波略微往下平移,第三個(gè)輸出方波比第二個(gè)輸出方波又略微往下平移,…,最后,當(dāng)輸出波 形的正半周“面積”與負(fù)半周“面積”相等時(shí),就達(dá)到了穩(wěn)定狀態(tài)。也就是電容在一個(gè)周期 內(nèi)充得的電荷與放掉的電荷相等時(shí),輸出波形就穩(wěn)定不再平移,電容上的平均電壓等于輸入 信號中電壓的直流分量(利用C的隔直作用),把輸入信號往下平移這個(gè)直流分量,便得到 輸出波形,起到傳送輸入信號的交流成分,因此是一個(gè)耦合電路。 以上的微分電路與耦合電路,在電路形式上是一樣的,關(guān)鍵是tW與τ的關(guān)系,下面比 較一下τ與方波周期T(T>tW)不同時(shí)的結(jié)果,如圖4所示。在這三種情形中,由于電 容C的隔直作用,輸出波形都是一個(gè)周期內(nèi)正、負(fù)“面積”相等,即其平均值為0,不再含有 直流成份。 ①當(dāng)τ>>T時(shí),電容C的充放電非常緩慢,其輸出波形近似理想方波,是理想耦合電路。 ②當(dāng)τ=T時(shí),電容C有一定的充放電,其輸出波形的平頂部分有一定的下降或上升,不是 理想方波。 ③當(dāng)τ< 3.RC積分電路 如圖5所示,電阻R和電容C串聯(lián)接入輸入信號VI,由電容C輸出信號V0,當(dāng)RC(τ)數(shù)值與輸入方波寬度tW之間滿足:τ>>tW,這種電路稱為積分電路。在 電容C兩端(輸出端)得到鋸齒波電壓,如圖6所示。 (3)t=t2時(shí),VI由Vm→0,相當(dāng)于輸入端被短路,電容原先充有左正右負(fù)電 壓VI(VI 這樣,輸出信號就是鋸齒波,近似為三角形波,τ>>tW是本電路必要條件,因?yàn)樗?在方波到來期間,電容只是緩慢充電,VC還未上升到Vm時(shí),方波就消失,電容 開始放電,以免電容電壓出現(xiàn)一個(gè)穩(wěn)定電壓值,而且τ越大,鋸齒波越接近三角波。輸出波 形是對輸入波形積分運(yùn)算的結(jié)果號的變化量。,他是突出輸入信號的直流及緩變分量,降低輸入信4.RC濾波電路(無源) 在模擬電路,由RC組成的無源濾波電路中,根據(jù)電容的接法及大小主要可分為低通濾波 電路(如圖7)和高通濾波電路(如圖8)。 (1)在圖7的低通濾波電路中,他跟積分電路有些相似(電容C都是并在輸出端),但 他們是應(yīng) 用在不同的電路功能上,積分電路主要是利用電容C充電時(shí)的積分作用,在輸入方波情形下,來產(chǎn)生周期性的鋸齒波(三角波),因此電容C及電阻R是根據(jù)方波的tW來選取,而 低通濾波電路,是將較高頻率的信號旁路掉(因XC=1/(2πfC),f較大時(shí),XC較 小,相當(dāng)于短路),因而電容C的值是參照低頻點(diǎn)的數(shù)值來確定,對于電源的濾波電路,理 論上C值愈大愈好。 (2)圖8的高通濾波電路與微分電路或耦合電路形式相同。在脈沖數(shù)字電路中,因RC與脈 寬tW的關(guān)系不同而區(qū)分為微分電路和耦合電路;在模擬電路,選擇恰當(dāng)?shù)碾娙軨值,就可以有選擇性地讓較高頻的信號通過,而阻斷直流及低頻信號,如高音喇叭串接的電容,就是阻止中低音進(jìn)入高音喇叭,以免燒壞。另一方面,在多級交流放大電路中,他也是一種 耦合電路。 5.RC脈沖分壓器 當(dāng)需要將脈沖信號經(jīng)電阻分壓傳到下一級時(shí),由于電路中存在各種形式的電容,如寄生電容,他相當(dāng)于在負(fù)載側(cè)接有一負(fù)載電容(如圖9),當(dāng)輸入一脈沖信號時(shí),因電容CL的 充電,電壓不能突變,使輸出波形前沿變壞,失真。為此,可在R1兩端并接一加速電容 C1,這樣組成一個(gè)RC脈沖分壓器(如圖10)。 (1)t=0+時(shí),電容視為短路,電流只流經(jīng)C1,CL,VO由C1和CL分壓得到: 但是,任何信號源都有一定的內(nèi)阻,以及一些電路的需要,通常采取過補(bǔ)償?shù)霓k法,如電視 信號中,為突出傳送圖像的輪廓,采用勾邊電路,就是通過加大C1的取值。 求RC電路的放電時(shí)間為1分鍾,電壓從9V降到5v.放電電流為300mA左右,選擇最佳的的R值和C值。 RC電路的放電方程是:UC=US*e-t/RC,其中,US=9,UC=5,t=60,代入公式可求出時(shí)間常數(shù)RC的值,現(xiàn)在關(guān)鍵的就是要確定R和C的值了,它只能通過你所要求的放電電路來選擇了,由放電電流公式:I=C*dU/dt,再將此公式代入上面的公式中可得:I=-US*C/RCe-t/RC,將C看成一個(gè)未知參數(shù),然后作出I-t曲線,計(jì)算出該曲線與直線I=300所圍成的面積,這個(gè)積分上下限為t=0-60,去使面積最小的C值就可. 算法分析與設(shè)計(jì)總結(jié)報(bào)告 71110415 錢玉明 在計(jì)算機(jī)軟件專業(yè)中,算法分析與設(shè)計(jì)是一門非常重要的課程,很多人為它如癡如醉。很多問題的解決,程序的編寫都要依賴它,在軟件還是面向過程的階段,就有程序=算法+數(shù)據(jù)結(jié)構(gòu)這個(gè)公式。算法的學(xué)習(xí)對于培養(yǎng)一個(gè)人的邏輯思維能力是有極大幫助的,它可以培養(yǎng)我們養(yǎng)成思考分析問題,解決問題的能力。作為IT行業(yè)學(xué)生,學(xué)習(xí)算法無疑會(huì)增強(qiáng)自己的競爭力,修煉自己的“內(nèi)功”。 下面我將談?wù)勎覍@門課程的心得與體會(huì)。 一、數(shù)學(xué)是算法的基礎(chǔ) 經(jīng)過這門課的學(xué)習(xí),我深刻的領(lǐng)悟到數(shù)學(xué)是一切算法分析與設(shè)計(jì)的基礎(chǔ)。這門課的很多時(shí)間多花在了數(shù)學(xué)公式定理的引入和證明上。雖然很枯燥,但是有必不可少。我們可以清晰的看到好多算法思路是從這些公式定理中得出來的,尤其是算法性能的分析更是與數(shù)學(xué)息息相關(guān)。其中有幾個(gè)定理令我印象深刻。 ①主定理 本門課中它主要應(yīng)用在分治法性能分析上。例如:T(n)=a*T(n/b)+f(n),它可以看作一個(gè)大問題分解為a個(gè)子問題,其中子問題的規(guī)模為b。而f(n)可看作這些子問題的組合時(shí)的消耗。這些可以利用主定理的相關(guān)結(jié)論進(jìn)行分析處理。當(dāng)f(n)量級高于nlogba時(shí),我們可以設(shè)法降低子問題組合時(shí)的消耗來提高性能。反之我們可以降低nlogba的消耗,即可以擴(kuò)大問題的規(guī)?;蛘邷p小子問題的個(gè)數(shù)。因此主定理可以幫助我們清晰的分析出算法的性能以及如何進(jìn)行有效的改進(jìn)。 ②隨機(jī)算法中的許多定理的運(yùn)用 在這門課中,我學(xué)到了以前從未遇見過的隨機(jī)算法,它給予我很大的啟示。隨機(jī)算法不隨機(jī),它可通過多次的嘗試來降低它的錯(cuò)誤率以至于可以忽略不計(jì)。這些都不是空穴來風(fēng),它是建立在嚴(yán)格的定理的證明上。如素?cái)?shù)判定定理是個(gè)很明顯的例子。它運(yùn)用了包括費(fèi)馬小定理在內(nèi)的各種定理。將這些定理進(jìn)行有效的組合利用,才得出行之有效的素?cái)?shù)判定的定理。尤其是對尋找證據(jù)數(shù)算法的改進(jìn)的依據(jù),也是建立在3個(gè)定理上。還有檢查字符串是否匹配也是運(yùn)用了許多定理:指紋的運(yùn)用,理論出錯(cuò)率的計(jì)算,算法性能的評價(jià)也都是建立在數(shù)學(xué)定理的運(yùn)用上。 這些算法都給予了我很大啟發(fā),要想學(xué)好算法,學(xué)好數(shù)學(xué)是必不可少的。沒有深厚的數(shù)學(xué)功力作為地基,即使再漂亮的算法框架,代碼實(shí)現(xiàn)也只能是根底淺的墻上蘆葦。 二、算法的核心是思想 我們學(xué)習(xí)這門課不是僅僅掌握那幾個(gè)經(jīng)典算法例子,更重要的是為了學(xué)習(xí)蘊(yùn)含在其中的思想方法。為什么呢?舉個(gè)例子。有同學(xué)曾問我這樣一個(gè)問題:1000只瓶子裝滿水,但有一瓶有毒,且毒發(fā)期為1個(gè)星期?,F(xiàn)在用10只老鼠在一個(gè)星期內(nèi)判斷那只瓶子有毒,每只老鼠可以喝多個(gè)瓶子的水,每個(gè)瓶子可以只喝一點(diǎn)。問如何解決?其實(shí)一開始我也一頭霧水,但是他提醒我跟計(jì)算機(jī)領(lǐng)域相關(guān),我就立馬有了思路,運(yùn)用二進(jìn)制。因?yàn)橛?jì)算機(jī)的最基本思想就是二進(jìn)制。所以說,我們不僅要學(xué)習(xí)算法,更得學(xué)習(xí)思想方法。 ①算法最基本的設(shè)計(jì)方法包括分治法,動(dòng)態(tài)規(guī)劃法,貪心法,周游法,回溯法,分支定界法。我們可利用分治法做快速排序,降低找n個(gè)元素中最大元和最小元的量級,降低n位二進(jìn)制x和y相乘的量級,做Strassen矩陣乘法等等。它的思想就是規(guī)模很大的問題分解為規(guī)模較小的獨(dú)立的子問題,關(guān)鍵是子問題要與原問題同類,可以采取平衡法來提高性能。 動(dòng)態(tài)規(guī)劃法是把大問題分解為子問題,但是子問題是重復(fù)的,后面的問題可以利用前面解決過的問題的結(jié)果。如構(gòu)造最優(yōu)二叉查找樹,解決矩陣連乘時(shí)最小計(jì)算次數(shù)問題,尋找最長公共子序列等等。 貪心法就是局部最優(yōu)法,先使局部最優(yōu),再依次構(gòu)造出更大的局部直至整體。如Kruscal最小生成樹算法,求哈夫曼編碼問題。 周游法就是簡單理解就是采取一定的策略遍歷圖中所有的點(diǎn),典型的應(yīng)用就是圖中的深度優(yōu)先搜索(DFS)和廣度優(yōu)先搜索(BFS)。 回溯法就是就是在滿足一定的條件后就往前走,當(dāng)走到某步時(shí),發(fā)現(xiàn)不滿足條件就退回一步重新選擇新的路線。典型的應(yīng)用就是8皇后問題,平面點(diǎn)集的凸包問題和0-1背包問題。 分支定界法:它是解決整數(shù)規(guī)劃問題一種最常用的方法。典型應(yīng)用就是解決整數(shù)規(guī)劃問題。 ②評價(jià)算法性能的方法如平攤分析中的聚集法,會(huì)計(jì)法和勢能法。聚集法就是把指令分為幾類,計(jì)算每一類的消耗,再全部疊加起來。會(huì)計(jì)法就是計(jì)算某個(gè)指令時(shí)提前將另一個(gè)指令的消耗也算進(jìn)去,以后計(jì)算另一個(gè)指令時(shí)就不必再算了。勢能法計(jì)算每一步的勢的變化以及執(zhí)行這步指令的消耗,再將每一步消耗全部累計(jì)。 這幾種方法都是平攤分析法,平攤分析的實(shí)質(zhì)就是總體考慮指令的消耗時(shí)間,盡管某些指令的消耗時(shí)間很大也可以忽略不計(jì)。上述三種方法難易程度差不多,每種方法都有屬于它的難點(diǎn)。如聚集法中如何將指令有效分類,會(huì)計(jì)法中用什么指令提前計(jì)算什么指令的消耗,勢能法中如何選取勢能。因此掌握這些方法原理還不夠,還要學(xué)會(huì)去應(yīng)用,在具體的問題中去判斷分析。 三、算法與應(yīng)用緊密相關(guān) 我認(rèn)為學(xué)習(xí)算法不能局限于書本上的理論運(yùn)算,局限于如何提高性能以降低復(fù)雜度,我們要將它與實(shí)際生活聯(lián)系起來。其實(shí)算法問題的產(chǎn)生就來自于生活,設(shè)計(jì)出高效的算法就是為了更好的應(yīng)用。如尋找最長公共子序列算法可以應(yīng)用在生物信息學(xué)中通過檢測相似DNA片段的相似成分來檢測生物特性的相似性,也可以用來判斷兩個(gè)字符串的相近性,這可應(yīng)用在數(shù)據(jù)挖掘中??焖俑盗⑷~變換(FFT)可應(yīng)用在計(jì)算多項(xiàng)式相乘上來降低復(fù)雜度,脫線min算法就是利用了Union-Find這種結(jié)構(gòu)。還有圖中相關(guān)算法,它對于解決網(wǎng)絡(luò)流量分配問題起了很大的幫助,等等。 這些應(yīng)用給了我很大的啟發(fā):因?yàn)閱渭冎v一個(gè)Union-Find算法,即使了解了它的實(shí)現(xiàn)原理,遇到具體的實(shí)際問題也不知去如何應(yīng)用。這就要求我們要將自己學(xué)到的算法要和實(shí)際問題結(jié)合起來,不能停留在思想方法階段,要學(xué)以致用,做到具體問題具體分析。 四、對計(jì)算模型和NP問題的理解 由于對這部分內(nèi)容不是很理解,所以就粗淺的談一下我的看法。 首先談到計(jì)算模型,就不得不提到圖靈計(jì)算,他將基本的計(jì)算抽象化,造出一個(gè)圖靈機(jī),得出了計(jì)算的本質(zhì)。并提出圖靈機(jī)可以計(jì)算的問題都是可以計(jì)算的,否則就是不可計(jì)算的。由此引申出一個(gè)著名論題:任何合理的計(jì)算模型都是相互等價(jià)的。它說明了可計(jì)算性本身不依賴于任何具體的模型而客觀存在。 NP問題比較復(fù)雜,我認(rèn)為它是制約算法發(fā)展的瓶頸,但這也是算法分析的魅力所在。NP問題一般可分為3類,NP-C問題,NP-hard問題以及頑型問題。NP-C它有個(gè)特殊的性質(zhì),如果存在一個(gè)NP-C問題找到一個(gè)多項(xiàng)式時(shí)間的解法,則所有的NP-C問題都能找到多項(xiàng)式時(shí)間解法。如哈密頓回路問題。NP-hard主要是解決最優(yōu)化問題。它不一定是NP問題。這些問題在規(guī)模較小時(shí)可以找出精確解,但是規(guī)模大時(shí),就因時(shí)間太復(fù)雜而找不到最優(yōu)解。此時(shí)一般會(huì)采用近似算法的解法。頑型問題就是已經(jīng)證明不可能有多項(xiàng)式時(shí)間的算法,如漢諾塔問題。 最后談?wù)剬@門課程的建議 ①對于這門算法課,我認(rèn)為應(yīng)該加強(qiáng)對算法思想方法的學(xué)習(xí)。所以我建議老師可不可以先拋出問題而不給出答案,講完一章,再發(fā)課件。讓我們先思考一會(huì)兒,或者給出個(gè)獎(jiǎng)勵(lì)機(jī)制,誰能解決這個(gè)問題,平時(shí)成績加分。這在一定程度上會(huì)將強(qiáng)我們思考分析問題的能力。因?yàn)槲腋杏X到,一個(gè)問題出來,未經(jīng)過思考就已經(jīng)知曉它的答案,就沒什么意思,得不到提高,而且也不能加深對問題的思考和理解。下次遇到類似的問題也就沒有什么印象。而且上課讓我們思考,點(diǎn)名回答問題可以一定程度上有效的防止不認(rèn)真聽課的現(xiàn)象。 ②作業(yè)安排的不是很恰當(dāng)。本門課主要安排了三次作業(yè),個(gè)人感覺只有第一次作業(yè)比較有意思。后面兩次作業(yè)只是實(shí)現(xiàn)一下偽代碼,沒有太多的技術(shù)含量。而且對于培養(yǎng)我們的解決問題的能力也沒有太多的幫助,因?yàn)檫@間接成為了程序設(shè)計(jì)題,不是算法設(shè)計(jì)題。 ③本門課的時(shí)間安排的不太恰當(dāng),因?yàn)楸緦W(xué)期的課程太多,壓力太大。沒有太多的時(shí)間去學(xué)習(xí)這門課程。因?yàn)槲蚁嘈糯蠹叶紝λ信d趣,比較重視,想花功夫,但苦于沒時(shí)間。所以可不可以將課程提前一個(gè)學(xué)期,那時(shí)候離散數(shù)學(xué)也已經(jīng)學(xué)過,且課程的壓力也不是很大。錯(cuò)開時(shí)間的話,我覺得應(yīng)該能夠更好提高大家算法分析設(shè)計(jì)的能力。 算法分塊總結(jié) 為備戰(zhàn)2005年11月4日成都一戰(zhàn),特將已經(jīng)做過的題目按算法分塊做一個(gè)全面詳細(xì)的總結(jié),主要突出算法思路,盡量選取有代表性的題目,盡量做到算法的全面性,不漏任何ACM可能涉及的算法思路。算法設(shè)計(jì)中,時(shí)刻都要牢記要減少冗余,要以簡潔高效為追求目標(biāo)。另外當(dāng)遇到陌生的問題時(shí),要想方設(shè)法進(jìn)行模型簡化,轉(zhuǎn)化,轉(zhuǎn)化成我們熟悉的東西。 圖論模型的應(yīng)用 分層圖思想的應(yīng)用: 用此思想可以建立起更簡潔、嚴(yán)謹(jǐn)?shù)臄?shù)學(xué)模型,進(jìn)而很容易得到有效算法。重要的是,新建立的圖有一些很好的性質(zhì): 由于層是由復(fù)制得到的,所以所有層都非常相似,以至于我們只要在邏輯上分出層的概念即可,根本不用在程序中進(jìn)行新層的存儲(chǔ),甚至幾乎不需要花時(shí)間去處理。由于層之間的相似性,很多計(jì)算結(jié)果都是相同的。所以我們只需對這些計(jì)算進(jìn)行一次,把結(jié)果存起來,而不需要反復(fù)計(jì)算。如此看來,雖然看起來圖變大了,但實(shí)際上問題的規(guī)模并沒有變大。層之間是拓?fù)溆行虻?。這也就意味著在層之間可以很容易實(shí)現(xiàn)遞推等處理,為發(fā)現(xiàn)有效算法打下了良好的基礎(chǔ)。 這些特點(diǎn)說明這個(gè)分層圖思想還是很有潛力的,尤其是各層有很多公共計(jì)算結(jié)果這一點(diǎn),有可能大大消除冗余計(jì)算,進(jìn)而降低算法時(shí)間復(fù)雜度。二分圖最大及完備匹配的應(yīng)用: ZOJ place the robots: 二分圖最優(yōu)匹配的應(yīng)用: 最大網(wǎng)絡(luò)流算法的應(yīng)用:典型應(yīng)用就求圖的最小割。最小費(fèi)用最大流的應(yīng)用: 容量有上下界的最大流的應(yīng)用: 歐拉路以及歐拉回路的應(yīng)用:主要利用求歐拉路的套圈算法。最小生成樹: 求最小生成樹,比較常用的算法有Prim算法和Kruskal算法。前者借助Fibonacci堆可以使復(fù)雜度降為O(Vlog2V+E),后者一般應(yīng)用于稀疏圖,其時(shí)間復(fù)雜度為O(Elog2V)。最小K度限制生成樹: 抽象成數(shù)學(xué)模型就是: 設(shè)G=(V,E,ω)是連通的無向圖,v0 ∈V是特別指定的一個(gè)頂點(diǎn),k為給定的一個(gè)正整數(shù)。首先考慮邊界情況。先求出問題有解時(shí)k 的最小值:把v0點(diǎn)從圖中刪去后,圖中可能會(huì)出 現(xiàn)m 個(gè)連通分量,而這m 個(gè)連通分量必須通過v0來連接,所以,在圖G 的所有生成樹中 dT(v0)≥m。也就是說,當(dāng)k 首先,將 v0和與之關(guān)聯(lián)的邊分別從圖中刪去,此時(shí)的圖可能不再連通,對各個(gè)連通分量,分別求最小生成樹。接著,對于每個(gè)連通分量V’,求一點(diǎn)v1,v1∈V’,且ω(v0,v1)=min{ω(v0,v’)|v’∈V’},則該連通分量通過邊(v1,v0)與v0相連。于是,我們就得到了一個(gè)m度限制生成樹,不難證明,這就是最小m度限制生成樹。這一步的時(shí)間復(fù)雜度為O(Vlog2V+E)我們所求的樹是無根樹,為了解題的簡便,把該樹轉(zhuǎn)化成以v0為根的有根樹。 假設(shè)已經(jīng)得到了最小p度限制生成樹,如何求最小p+1 度限制生成樹呢?在原先的樹中加入一條與v0相關(guān)聯(lián)的邊后,必定形成一個(gè)環(huán)。若想得到一棵p+1 度限制生成樹,需刪去一條在環(huán)上的且與v0無關(guān)聯(lián)的邊。刪去的邊的權(quán)值越大,則所得到的生成樹的權(quán)值和就越小。動(dòng)態(tài)規(guī)劃就有了用武之地。設(shè)Best(v)為路徑v0—v上與v0無關(guān)聯(lián)且權(quán)值最大的邊。定義father(v)為v的父結(jié)點(diǎn),動(dòng)態(tài)轉(zhuǎn)移方程:Best(v)=max(Best(father(v)),(father(v),v)),邊界條件為Best[v0]=-∞,Best[v’]=-∞|(v0,v’)∈E(T)。 狀態(tài)共|V|個(gè),狀態(tài)轉(zhuǎn)移的時(shí)間復(fù)雜度O(1),所以總的時(shí)間復(fù)雜度為O(V)。故由最小p度限制生成樹得到最小p+1度限制生成樹的時(shí)間復(fù)雜度為O(V)。1 先求出最小m度限制生成樹; 2由最小m度限制生成樹得到最小m+1度限制生成樹;3 當(dāng)dT(v0)=k時(shí)停止。 加邊和去邊過程,利用動(dòng)態(tài)規(guī)劃優(yōu)化特別值得注意。 次小生成樹: 加邊和去邊很值得注意。 每加入一條不在樹上的邊,總能形成一個(gè)環(huán),只有刪去環(huán)上的一條邊,才能保證交換后仍然是生成樹,而刪去邊的權(quán)值越大,新得到的生成樹的權(quán)值和越小。具體做法: 首先做一步預(yù)處理,求出樹上每兩個(gè)結(jié)點(diǎn)之間的路徑上的權(quán)值最大的邊,然后,枚舉圖中不在樹上的邊,有了剛才的預(yù)處理,我們就可以用O(1)的時(shí)間得到形成的環(huán)上的權(quán)值最大的邊。如何預(yù)處理呢?因?yàn)檫@是一棵樹,所以并不需要什么高深的算法,只要簡單的BFS 即可。 最短路徑的應(yīng)用: Dijkstra 算法應(yīng)用: Folyed 算法應(yīng)用: Bellman-Ford 算法的應(yīng)用: 差分約束系統(tǒng)的應(yīng)用: 搜索算法 搜索對象和搜索順序的選取最為重要。一些麻煩題,要注意利用數(shù)據(jù)有序化,要找一個(gè)較優(yōu)的搜索出發(fā)點(diǎn),凡是能用高效算法的地方盡量爭取用高效算法。基本的遞歸回溯深搜,記憶化搜索,注意剪枝: 廣搜(BFS)的應(yīng)用: 枚舉思想的應(yīng)用: ZOJ 1252 island of logic A*算法的應(yīng)用: IDA*算法的應(yīng)用,以及跳躍式搜索探索: 限深搜索,限次: 迭代加深搜索: 部分搜索+高效算法(比如二分匹配,動(dòng)態(tài)規(guī)劃): ZOJ milk bottle data: 剪枝優(yōu)化探索: 可行性剪枝,最優(yōu)性剪枝,調(diào)整搜索順序是常用的優(yōu)化手段。 動(dòng)態(tài)規(guī)劃 動(dòng)態(tài)規(guī)劃最重要的就是狀態(tài)的選取,以及狀態(tài)轉(zhuǎn)移方程,另外還要考慮高效的預(yù)處理(以便更好更快的實(shí)現(xiàn)狀態(tài)轉(zhuǎn)移)。最常用的思想就是用枚舉最后一次操作。 狀態(tài)壓縮DP,又叫帶集合的動(dòng)態(tài)規(guī)劃:題目特點(diǎn)是有一維的維數(shù)特別小。類似TSP問題的DP: 狀態(tài)劃分比較困難的題目: 樹形DP: 四邊形不等式的應(yīng)用探索:四邊形不等式通常應(yīng)用是把O(n^3)復(fù)雜度O(n^2) 高檔數(shù)據(jù)結(jié)構(gòu)的應(yīng)用 并查集的應(yīng)用: 巧用并查集中的路徑壓縮思想: 堆的利用: 線段樹的應(yīng)用: 總結(jié)用線段樹解題的方法 根據(jù)題目要求將一個(gè)區(qū)間建成線段樹,一般的題目都需要對坐標(biāo)離散。建樹時(shí),不要拘泥于線段樹這個(gè)名字而只將線段建樹,只要是表示區(qū)間,而且區(qū)間是由單位元素(可以是一個(gè)點(diǎn)、線段、或數(shù)組中一個(gè)值)組成的,都可以建線段樹;不要拘泥于一維,根據(jù)題目要求可以建立面積樹、體積樹等等 樹的每個(gè)節(jié)點(diǎn)根據(jù)題目所需,設(shè)置變量記錄要求的值 用樹形結(jié)構(gòu)來維護(hù)這些變量:如果是求總數(shù),則是左右兒子總數(shù)之和加上本節(jié)點(diǎn)的總數(shù),如果要求最值,則是左右兒子的最大值再聯(lián)系本區(qū)間。利用每次插入、刪除時(shí),都只對O(logL)個(gè)節(jié)點(diǎn)修改這個(gè)特點(diǎn),在O(logL)的時(shí)間內(nèi)維護(hù)修改后相關(guān)節(jié)點(diǎn)的變量。 在非規(guī)則刪除操作和大規(guī)模修改數(shù)據(jù)操作中,要靈活的運(yùn)用子樹的收縮與葉子節(jié)點(diǎn)的釋放,避免重復(fù)操作。 Trie的應(yīng)用:; Trie圖的應(yīng)用探索: 后綴數(shù)組的應(yīng)用研究: 在字符串處理當(dāng)中,后綴樹和后綴數(shù)組都是非常有力的工具,其中后綴樹了解得比較多,關(guān)于后綴數(shù)組則很少見于國內(nèi)的資料。其實(shí)后綴數(shù)組是后綴樹的一個(gè)非常精巧的替代品,它比后綴樹容易編程實(shí)現(xiàn),能夠?qū)崿F(xiàn)后綴樹的很多功能而時(shí)間復(fù)雜度也不太遜色,并且,它比后綴樹所占用的空間小很多。 樹狀數(shù)組的應(yīng)用探索:; 計(jì)算幾何 掌握基本算法的實(shí)現(xiàn)。凸包的應(yīng)用:; 半平面交算法的應(yīng)用:; 幾何+模擬類題目:幾何設(shè)計(jì)好算法,模擬控制好精度。掃描法:; 轉(zhuǎn)化法:ZOJ 1606 將求所圍的格子數(shù),巧妙的轉(zhuǎn)化為求多邊形的面積。離散法思想的應(yīng)用:; 經(jīng)典算法:找平面上的最近點(diǎn)對。 貪心 矩形切割 二分思想應(yīng)用 活用經(jīng)典算法 利用歸并排序算法思想求數(shù)列的逆序?qū)?shù): 利用快速排序算法思想,查詢N個(gè)數(shù)中的第K小數(shù): 博弈問題 博弈類題目通常用三類解法:第一類推結(jié)論; 第二類遞推,找N位置,P位置; 第三類SG函數(shù)的應(yīng)用。第四類極大極小法,甚至配合上αβ剪枝。最難掌握的就是第四類極大極小法。 第一類:推結(jié)論。典型題目: 第二類:遞推。典型題目: 比如有向無環(huán)圖類型的博弈。在一個(gè)有向圖中,我們把選手I有必勝策略的初始位置稱為N位置(Next player winning),其余的位置被稱為P位置(Previous player winning)。很顯然,P位置和N位置應(yīng)該具有如下性質(zhì): 1. 所有的結(jié)束位置都是P位置。 2. 對于每一個(gè)N位置,至少存在一種移動(dòng)可以將棋子移動(dòng)到一個(gè)P位置。3. 對于每一個(gè)P位置,它的每一種移動(dòng)都會(huì)將棋子移到一個(gè)N位置。 這樣,獲勝的策略就是每次都把棋子移動(dòng)到一個(gè)P位置,因?yàn)樵谝粋€(gè)P位置,你的對手只能將棋子移動(dòng)到一個(gè)N位置,然后你總有一種方法再把棋子移動(dòng)到一個(gè)P位置。一直這樣移動(dòng),最后你一定會(huì)將棋子移動(dòng)到一個(gè)結(jié)束位置(結(jié)束位置是P位置),這時(shí)你的對手將無法在移動(dòng)棋子,你便贏得了勝利。 與此同時(shí),得到了這些性質(zhì),我們便很容易通過倒退的方法求出哪些位置是P位置,哪些位置是N位置,具體的算法為: 1. 將所有的結(jié)束位置標(biāo)為P位置。 2. 將所有能一步到達(dá)P位置的點(diǎn)標(biāo)為N位置。 3. 找出所有只能到達(dá)N位置的點(diǎn),將它們標(biāo)為P位置。 4. 如果在第三步中沒有找到新的被標(biāo)為P位置的點(diǎn),則算法結(jié)束,否則轉(zhuǎn)到步驟2。這樣我們便確定了所有位置,對于題目給出的任一初始位置,我們都能夠很快確定出是選手I獲勝還是選手II獲勝了。第三類:SG函數(shù)的應(yīng)用。 關(guān)于SG函數(shù)的基本知識:對于一個(gè)有向圖(X, F)來說,SG函數(shù)g是一個(gè)在X上的函數(shù),并且它返回一個(gè)非負(fù)整數(shù)值,具體定義為 g(x)?min{n?0,n?g(y)對于所有y?F(x)} 1. 對于所有的結(jié)束位置x,g(x)= 0。 2. 對于每一個(gè)g(x)≠ 0的位置x,在它可以一步到達(dá)的位置中至少存在一個(gè)位置y使得g(y)= 0。 3.對于每一個(gè)g(x)= 0的位置x,所有可以由它一步到達(dá)的位置y都有g(shù)(y)≠ 0。 定理 如果g(xi)是第i個(gè)有向圖的SG函數(shù)值,i = 1,…,n,那么在由這n個(gè)有向圖組成的狀態(tài)的SG函數(shù)值g(x1,…xn)= g(x1)xor g(x2)xor … xor g(xn) 第四類:極大極小法。 典型題目:ZOJ 1155:Triangle War ZOJ 1993:A Number Game 矩陣妙用 矩陣最基本的妙用就是利用快速乘法O(logn)來求解遞推關(guān)系(最基本的就是求Fibonacci數(shù)列的某項(xiàng))和各種圖形變換,以及利用高斯消元法變成階梯矩陣。典型題目: 數(shù)學(xué)模型舉例 向量思想的應(yīng)用: UVA 10089:注意降維和向量的規(guī)范化 ; 利用復(fù)數(shù)思想進(jìn)行向量旋轉(zhuǎn)。 UVA 10253: 遞推 數(shù)代集合 數(shù)代集合的思想: ACM ICPC 2002-2003, Northeastern European Region, Northern Subregion 中有一題:Intuitionistic Logic 用枚舉+數(shù)代集合思想優(yōu)化,注意到題中有一句話:“You may assume that the number H = |H| of elements of H?doesn't exceed 100”,這句話告訴我們H的元素個(gè)數(shù)不會(huì)超過100,因此可以考慮用一個(gè)數(shù)代替一個(gè)集合,首先把所有的運(yùn)算結(jié)果都用預(yù)處理算出來,到計(jì)算的時(shí)候只要用O(1)的復(fù)雜度就可以完成一次運(yùn)算。 組合數(shù)學(xué) Polya定理則是解決同構(gòu)染色計(jì)數(shù)問題的有力工具。 補(bǔ)集轉(zhuǎn)化思想 ZOJ 單色三角形: 字符串相關(guān) 擴(kuò)展的KMP算法應(yīng)用:;最長回文串; 最長公共子串; 最長公共前綴; 填充問題 高精度運(yùn)算 三維空間問題專題 無論什么問題,一旦擴(kuò)展到三難空間,就變得很有難度了。三維空間的問題,很考代碼實(shí)現(xiàn)能力。 其它問題的心得 解決一些判斷同構(gòu)問題的方法:同構(gòu)的關(guān)鍵在于一一對應(yīng),而如果枚舉一一對應(yīng)的關(guān)系,時(shí)間復(fù)雜度相當(dāng)?shù)母?,利用最小表示,就能把一個(gè)事物的本質(zhì)表示出來。求最小表示時(shí),我們一定要仔細(xì)分析,將一切能區(qū)分兩個(gè)元素的條件都在最小表示中體現(xiàn),而且又不能主觀的加上其他條件。得到最小表示后,我們往往還要尋求適當(dāng)?shù)?、高效的匹配算法(例如KMP字符匹配之類的),來比較最小表示是否相同,這里常常要將我們熟悉的高效算法進(jìn)行推廣 源程序代碼: } 一、自然數(shù)拆分(遞歸) } #include 二、快速排序(遞歸)int a[100];void spilt(int t)#include spilt(j+1);} } int partitions(int a[],int from,int to)void main(){ { int n,i; int value=a[from];printf(“please enter the number:”); while(from a[from]=a[to]; while(from ++from; a[to]=a[from]; } a[from]=value; return from; } void qsort(int a[],int from,int to){ int pivottag;if(from {pivottag=partitions(a,from,to);qsort(a,from,pivottag-1);qsort(a,pivottag+1,to); } scanf(“%d”,&n); for(i=1;i<=n/2;i++){ a[1]=i;a[2]=n-i;spilt(2); 三、刪數(shù)字(貪心) #include int a[11]={3,0,0,0,9,8,1,4,7,5,1}; int k=0,i=0,j; int m; while(i<11) { printf(“%d ”,a[i]); i++;} printf(“n please input delete number:”); 四、全排列(遞歸)#include int i;char temp;if(k==n) for(i=0;i<=3;i++) {printf(“%c ”,a[i]);} else { for(i=k;i<=n;i++) { temp=a[i]; a[i]=a[k]; a[k]=temp; A(a,k+1,n); } } } main(){ int n; char a[4]={'a','b','c','d'},temp; A(a,0,3); getch(); return 0;} 五、多段圖(動(dòng)態(tài)規(guī)劃)#include “stdio.h” #define n 12 //圖的頂點(diǎn)數(shù) { while(from scanf(“%d”,&m);for(k=0;k { for(i=0;i<=11-k;i++) { if(a[i]>a[i+1]) { for(j=i;j<10;j++) {a[j]=a[j+1];} break;//滿足條件就跳轉(zhuǎn) } } } int quicksort(int a[],int n){ qsort(a,0,n);} } printf(“the change numbers:”); for(i=0;i<11-m;i++) { if(a[i]!=0) { printf(“%d ”,a[i]);} } } #define k 4 //圖的段數(shù) #define MAX 23767 int cost[n][n];//成本值數(shù)組 int path[k];//存儲(chǔ)最短路徑的數(shù)組 void creatgraph()//創(chuàng)建圖的(成本)鄰接矩陣 { int i,j; for(i=0;i for(j=0;j scanf(“%d”,&cost[i][j]);//獲取成本矩陣數(shù)據(jù) } void printgraph()//輸出圖的成本矩陣 { int i,j; printf(“成本矩陣:n”); for(i=0;i { for(j=0;j printf(“%d ”,cost[i][j]); printf(“n”); } } //使用向前遞推算法求多段圖的最短路徑 void FrontPath(){ int i,j,length,temp,v[n],d[n]; for(i=0;i v[i]=0;for(i=n-2;i>=0;i--){ for(length=MAX,j=i+1;j<=n-1;j++) if(cost[i][j]>0 &&(cost[i][j])+v[j] {length=cost[i][j]+v[j];temp=j;} v[i]=length; d[i]=temp; } path[0]=0;//起點(diǎn) path[k-1]=n-1;//最后的目標(biāo) for(i=1;i<=k-2;i++)(path[i])=d[path[i-1]];//將最短路徑存入數(shù)組中 } //使用向后遞推算法求多段圖的最短路徑 void BackPath(){ int i,j,length,temp,v[n],d[n]; for(i=0;i for(i=1;i<=n-1;i++) { for(length=MAX,j=i-1;j>=0;j--) if(cost[j][i]>0 &&(cost[j][i])+v[j] {length=cost[j][i]+v[j];temp=j;} v[i]=length; d[i]=temp; } path[0]=0; path[k-1]=n-1; for(i=k-2;i>=1;i--)(path[i])=d[path[i+1]];} //輸出最短路徑序列 void printpath(){ int i; for(i=0;i printf(“%d ”,path[i]);} main(){ freopen(“E:1input.txt”,“r”,stdin); creatgraph(); printgraph(); FrontPath(); printf(“輸出使用向前遞推算法所得的最短路徑:n”); printpath(); printf(“n輸出使用向后遞推算法所得的最短路徑:n”); BackPath(); printpath();printf(“n”);} 六、背包問題(遞歸)int knap(int m, int n){ int x; x=m-mn; if x>0 sign=1; else if x==0 sign=0; else sign=-1; switch(sign){ case 0: knap=1;break; case 1: if(n>1) if knap(m-mn,n-1) knap=1; else knap= knap(m,n-1); else knap=0; case-1: if(n>1) knap= knap(m,n-1); else knap=0; } } 七、8皇后(回溯)#include int i; i=1; while(i if((X[i]==X[k])||(abs(X[i]-X[k])==abs(i-k))) return 0; i++; } return 1;} void Nqueens(int X[N+1]){ int k, i; X[1]=0;k=1; while(k>0){ X[k]=X[k]+1; while((X[k]<=N)&&(!place(k,X))) X[k]=X[k]+1; if(X[k]<=N) if(k==N){ for(i=1;i<=N;i++) printf(“%3d”,X[i]);printf(“n”); } else{ k=k+1; X[k]=0; } else k=k-1; } } void main(){ int n, i; int X[N+1]={0}; clrscr(); Nqueens(X); printf(“The end!”);} 八、圖著色(回溯)#include int j,t; while(1){ nextValue(k); if(X[k]==0) return 0; if(k==(N-1)){ for(t=0;t printf(“%3d”,X[t]); printf(“n”); count++; } else mcoloring(k+1); } } int nextValue(int k){ int j; while(1){ X[k]=(X[k]+1)%(M+1); if(X[k]==0) return 0; for(j=0;j if((GRAPH[k][j]==1)&&(X[k]==X[j])) break; } if(j==N){ return 0; } } } void main(){ int k; clrscr(); k=0; mcoloring(k); printf(“ncount=%dn”,count);} 矩陣鏈乘法(動(dòng)態(tài)規(guī)劃)? 符號S[i, j]的意義: 符號S(i, j)表示,使得下列公式右邊取最小值的那個(gè)k值 public static void matrixChain(int [ ] p, int [ ][ ] m, int [ ][ ] s) { int n=p.length-1; for(int i = 1;i <= n;i++)m[i][i] = 0; for(int r = 2;r <= n;r++) for(int i = 1;i <= n-r+1;i++){ int j=i+r-1; m[i][j] = m[i+1][j]+ p[i-1]*p[i]*p[j]; s[i][j] = i; for(int k = i+1;k < j;k++){ int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j]; if(t < m[i][j]){ m[i][j] = t; s[i][j] = k;} } } } O的定義: 如果存在兩個(gè)正常數(shù)c和n0,對于所有的n≥n0時(shí),有: |f(n)|≤c|g(n)|,稱函數(shù)f(n)當(dāng)n充分大時(shí)的階比g(n)低,記為 f(n)=O(g(n))。計(jì)算時(shí)間f(n)的一個(gè)上界函數(shù) Ω的定義: 如果存在正常數(shù)c和n0,對于所有n≥n0時(shí),有: |f(n)|≥c|g(n)|,則稱函數(shù)f(n)當(dāng)n充分大時(shí)下有界,且g(n)是它的一個(gè)下界,即f(n)的階不低于g(n)的階。記為: f(n)=Ω(g(n))。Θ的定義: 如果存在正常數(shù)c1,c2和n0,對于所有的n>n0,有: c1|g(n)|≤f(n)≤c2|g(n)|,則記f(n)=Θ(g(n))意味著該算法在最好和最壞的情況下計(jì)算時(shí)間就一個(gè)常因子范圍內(nèi)而言是相同的。(1)多項(xiàng)式時(shí)間算法: O(1) (2)指數(shù)時(shí)間算法: O(2n) Move(n,n+1)(2n+1,2n+2)move(2n-1,2n)(n,n+1)call chess(n-1) 貪心方法基本思想: 貪心算法總是作出在當(dāng)前看來最好的選擇。也就是說貪心算法并不從整體最優(yōu)考慮,它所作出的選擇只是在某種意義上的局部最優(yōu)選擇 所求問題的整體最優(yōu)解可以通過一系列局部最優(yōu)的選擇,即貪心選擇來達(dá)到。這是貪心算法可行的第一個(gè)基本要素,也是貪心算法與動(dòng)態(tài)規(guī)劃算法的主要區(qū)別。 多段圖: COST[j]=c(j,r)+COST[r]; 回溯法: (假定集合Si的大小是mi)不斷地用修改過的規(guī)范函數(shù)Pi(x1,…,xi)去測試正在構(gòu)造中的n-元組的部分向量(x1,…,xi),看其是否可能導(dǎo)致最優(yōu)解。如果判定(x1,…,xi)不可能導(dǎo)致最優(yōu)解,那么就將可能要測試的mi+1…mn個(gè)向量略去。約束條件: (1)顯式約束:限定每一個(gè)xi只能從給定的集合Si上取值。 (2)解 空 間:對于問題的一個(gè)實(shí)例,解向量滿足顯式 約束條件的所有多元組,構(gòu)成了該實(shí)例 的一個(gè)解空間。 (3)隱式約束:規(guī)定解空間中實(shí)際上滿足規(guī)范函數(shù)的元 組,描述了xi必須彼此相關(guān)的情況?;咀龇ǎ?/p> 在問題的解空間樹中,按深度優(yōu)先策略,從根結(jié)點(diǎn)出發(fā)搜索解空間樹。算法搜索至解空間樹的任意一點(diǎn)時(shí),先判斷該結(jié)點(diǎn)是否包含問題的解:如果肯定不包含,則跳過對該結(jié)點(diǎn)為根的子樹的搜索,逐層向其祖先結(jié)點(diǎn)回溯;否則,進(jìn)入該子樹,繼續(xù)按深度優(yōu)先策略搜索。 8皇后問題 約束條件 限界函數(shù): 子集和數(shù)問題: 約束條件 限界函數(shù): 回溯法--術(shù)語: 活結(jié)點(diǎn):已生成一個(gè)結(jié)點(diǎn)而它的所有兒子結(jié)點(diǎn)還沒有 全部生成的結(jié)點(diǎn)稱為活結(jié)點(diǎn)。 E-結(jié)點(diǎn):當(dāng)前正在生成其兒子結(jié)點(diǎn)的活結(jié)點(diǎn)叫E-結(jié)點(diǎn)。 死結(jié)點(diǎn):不再進(jìn)一步擴(kuò)展或其兒子結(jié)點(diǎn)已全部生成的結(jié)點(diǎn)稱為死結(jié)點(diǎn)。 使用限界函數(shù)的深度優(yōu)先節(jié)點(diǎn)生成的方法成為回溯法;E-結(jié)點(diǎn)一直保持到死為止的狀態(tài)生成的方法 稱之為分支限界方法 且用限界函數(shù)幫助避免生成不包含答案結(jié)點(diǎn)子樹的狀態(tài)空間的檢索方法。區(qū)別: 分支限界法本質(zhì)上就是含有剪枝的回溯法,根據(jù)遞歸的條件不同,是有不同的時(shí)間復(fù)雜度的。 回溯法深度優(yōu)先搜索堆棧或節(jié)點(diǎn)的所有子節(jié)點(diǎn)被遍歷后才被從棧中彈出找出滿足約束條件的所有解 分支限界法廣度優(yōu)先或最小消耗優(yōu)先搜索隊(duì)列,優(yōu)先隊(duì)列每個(gè)結(jié)點(diǎn)只有一次成為活結(jié)點(diǎn)的機(jī)會(huì)找出滿足約束條件下的一個(gè)解或特定意義下的最優(yōu)解 一般如果只考慮時(shí)間復(fù)雜度二者都是指數(shù)級別的 可是因?yàn)榉种藿绶ù嬖谥鞣N剪枝,用起來時(shí)間還是很快的int M, W[10],X[10];void sumofsub(int s, int k, int r){ int j; X[k]=1; if(s+W[k]==M){ for(j=1;j<=k;j++) printf(“%d ”,X[j]); printf(“n”); } else if((s+W[k]+W[k+1])<=M){ sumofsub(s+W[k],k+1,r-W[k]); } if((s+r-W[k]>=M)&&(s+W[k+1]<=M)){ X[k]=0; sumofsub(s,k+1,r-W[k]); } } void main(){ M=30; W[1]=15; W[2]=9; W[3]=8; W[4]=7; W[5]=6; W[6]=5; W[7]=4; W[8]=3; W[9]=2; W[10]=1; sumofsub(0,1,60);} P是所有可在多項(xiàng)式時(shí)間內(nèi)用確定算法求解的判定問題的集合。NP是所有可在多項(xiàng)式時(shí)間內(nèi)用不確定算法求解的判定問題的集合 如果可滿足星月化為一個(gè)問題L,則此問題L是NP-難度的。如果L是NP難度的且L NP,則此問題是NP-完全的第二篇:RC電路總結(jié)
第三篇:算法總結(jié)
第四篇:算法總結(jié)
第五篇:算法總結(jié)材料