P隨動演算法
『壹』 rsa演算法中p,q,n,e,d一般大小都為多少啊
RSA遭受攻擊的很多情況是因為演算法實現的一些細節上的漏洞所導致的,所以在使用RSA演算法構造密碼系統時,為保證安全,在生成大素數的基礎上,還必須認真仔細選擇參數,防止漏洞的形成。根據RSA加解密過程,其主要參數有三個:模數N,加密密鑰e,解密密鑰d。
3.4.1 模數N的確定
雖然迄今人們無法證明,破解RSA系統等於對N因子分解,但一般相信RSA系統的安全性等同於因子分解,即:若能分解因子N,即能攻破RSA系統,若能攻破RSA系統,即能分解因子Ⅳ。因此,在使用RSA系統時,對於模數N的選擇非常重要。在RSA演算法中,通過產生的兩個大素數p和q相乘得到模數N,而後分別通過對它們的數學運算得到密鑰對。由此,分解模數N得到p和q是最顯然的攻擊方法,當然也是最困難的方法,如果模數N被分解,攻擊者利用得到的P和q便可計算出,進而通過公開密鑰e由解密密鑰d,則RSA體制立刻被攻破。相當一部分的對RSA的攻擊就是試圖分解模數N,選擇合適的N是實現RSA演算法並防止漏洞的重要環節。一般地,模數N的確定可以遵循以下幾個原則:
①p和q之差要大。
當p和q相差很小時,在已知n的情況下,可假定二者的平均值為,然後利用,若等式右邊可開方,則得到及,即N被分解。
②p-1和q-1的最大公因子應很小。
③p和q必須為強素數。
一素數p如果滿足:
條件一:存在兩個大素數,,使得|p-1且|p+1;
條件二:存在四個大素數,,,使得。則此素數為強素數。其中,,,稱為3級的素數,,稱為2級的素數,p則稱為1級的素數,很明顯地,任何素數均為3級的素數。只有兩個強素數的積所構成的N,其因子分解才是較難的數學問題。
④p和q應大到使得因子分解N為計算上不可能。
RSA的安全性依賴於大數的因子分解,若能因子分解模數N,則RSA即被攻破,因此模數N必須足夠大直至因子分解N在計算上不可行。因子分解問題為密碼學最基本的難題之一,如今,因子分解的演算法已有長足的進步,但仍不足以說明RSA可破解。為保證安全性,實際應用中所選擇的素數P和拿至少應該為300位以上的二進制數,相應的模數N將是600位以上的二進制數。
目前,SET(Secure Electronic Transaction)協議中要求CA採用2048比特長的密鑰,其他實體使用1024比特的密鑰。隨著計算能力的提高和分布式運算的發展,安全密鑰的長度將是動態增長的。
Jadith Moore給出了使用RSA時有關模數的一些限制:
①若給定模數的一個加/解密密鑰指數對已知,攻擊者就能分解這個模數。
②若給定模數的一個加/解密密鑰指數對已知,攻擊者無需分解模數Ⅳ就可以計算出別的加/解密密鑰指數對。
③在通信網路中,利用RSA的協議不應該使用公共模數。
④消息應該用隨機數填充以避免對加密指數的攻擊。
3.4.2 e的選取原則
在RSA演算法中,e和互質的條件容易滿足,如果選擇較小的e,則加、解密的速度加快,也便於存儲,但會導致安全問題。
一般地,e的選取有如下原則:
①e不能夠太小。在RSA系統中,每人的公開密鑰P只要滿足即可,也即e可以任意選擇,為了減少加密運算時間,很多人採用盡可能小的e值,如3。但是已經證明低指數將會導致安全問題,故此,一般選擇e為16位的素數,可以有效防止攻擊,又有較快速度。
②e應選擇使其在的階為最大。即存在i,使得,
可以有效抗擊攻擊。
3.4.3 d的選取原則
一般地,私密密鑰d要大於。在許多應用場合,常希望使用位數較短的密鑰以降低解密或簽名的時間。例如IC卡應用中,IC卡CPU的計算能力遠低於計算機主機。長度較短的d可以減少IC卡的解密或簽名時間,而讓較復雜的加密或驗證預算(e長度較長)由快速的計算機主機運行。一個直接的問題就是:解密密鑰d的長度減少是否會造成安全性的降低?很明顯地,若d的長度太
小,則可以利用已知明文M加密後得,再直接猜測d,求出是否等於M。若是,則猜測J下確,否則繼續猜測。若d的長度過小,則猜測的空間變小,猜中的可能性加大,已有證明當時,可以由連分式演算法在多項式時間內求出d值。因此其長度不能過小。
『貳』 什麼是PID調節器,並舉例說明P、I、D的調節作用。
PID 調節器是一個在工業控制應用中常見的反饋迴路部件,PID是以它的三種糾正演算法而命名的。這三種演算法都是用加法調整被控制的數值。而實際上這些加法運算大部分變成了減法運算因為被加數總是負值。以下是PID的調節作用舉例:
1.比例- 來控制當前,誤差值和一個負常數P(表示比例)相乘,然後和預定的值相加。P只是在控制器的輸出和系統的誤差成比例的時候成立。這種控制器輸出的變化與輸入控制器的偏差成比例關系。比如說,一個電熱器的控制器的比例尺范圍是10°C,它的預定值是20°C。那麼它在10°C的時候會輸出100%,在15°C的時候會輸出50%,在19°C的時候輸出10%,注意在誤差是0的時候,控制器的輸出也是0。
2.積分 - 來控制過去,誤差值是過去一段時間的誤差和,然後乘以一個負常數I,然後和預定值相加。I從過去的平均誤差值來找到系統的輸出結果和預定值的平均誤差。一個簡單的比例系統會振盪,會在預定值的附近來回變化,因為系統無法消除多餘的糾正。通過加上一個負的平均誤差比例值,平均的系統誤差值就會總是減少。所以,最終這個PID迴路系統會在預定值定下來。
3.微分- 來控制將來,計算誤差的一階導,並和一個負常數D相乘,最後和預定值相加。這個導數的控制會對系統的改變作出反應。導數的結果越大,那麼控制系統就對輸出結果作出更快速的反應。這個D參數也是PID被稱為可預測的控制器的原因。D參數對減少控制器短期的改變很有幫助。一些實際中的速度緩慢的系統可以不需要D參數。
(2)P隨動演算法擴展閱讀:
用更專業的話來講,一個PID控制器可以被稱作一個在頻域系統的濾波器。這一點在計算它是否會最終達到穩定結果時很有用。如果數值挑選不當,控制系統的輸入值會反復振盪,這導致系統可能永遠無法達到預設值。
『叄』 如何計算統計學中的P值(200分)
P值即為拒絕域的面積或概率。
P值的計算公式是
=2[1-Φ(z0)] 當被測假設H1為 p不等於p0時;
=1-Φ(z0) 當被測假設H1為 p大於p0時;
=Φ(z0) 當被測假設H1為 p小於p0時;
總之,P值越小,表明結果越顯著。但是檢驗的結果究竟是「顯著的」、「中度顯著的」還是「高度顯著的」需要我們自己根據P值的大小和實際問題來解決。
p值是指在一個概率模型中,統計摘要(如兩組樣本均值差)與實際觀測數據相同,或甚至更大這一事件發生的概率。換言之,是檢驗假設零假設成立或表現更嚴重的可能性。
p值若與選定顯著性水平(0.05或0.01)相比更小,則零假設會被否定而不可接受。然而這並不直接表明原假設正確。p值是一個服從正態分布的隨機變數,在實際使用中因樣本等各種因素存在不確定性。產生的結果可能會帶來爭議。
『肆』 雙向鏈表p點後插入節點的演算法
雙向鏈表節點:\r\ntypedef struct LinkNode\r\n{\r\n struct LinkNode * pre;\r\n\r\n struct LinkNode *next;\r\n\r\n int data;\r\n\r\n}node;\r\nsrand()\r\nrand()\r\n產生隨機數插入到鏈表\r\n然後你可以網路一下直接插入排序演算法,修改指針指向完成排序\r\n\r\n單鏈表:\r\ntypedef struct LinkNode\r\n{\r\n struct LinkNode *next;\r\n\r\n int data;\r\n\r\n}\r\n方法同上,\r\n自己動手做一下,有助於提高。其實動起手來很簡單的,不要想的太復雜
『伍』 簡述演算法的各種表示形式
一、什麼是演算法
演算法是一系列解決問題的清晰指令,也就是說,能夠對一定規范的輸入,在有限時間內獲得所要求的輸出。演算法常常含有重復的步驟和一些比較或邏輯判斷。如果一個演算法有缺陷,或不適合於某個問題,執行這個演算法將不會解決這個問題。不同的演算法可能用不同的時間、空間或效率來完成同樣的任務。一個演算法的優劣可以用空間復雜度與時間復雜度來衡量。
演算法的時間復雜度是指演算法需要消耗的時間資源。一般來說,計算機演算法是問題規模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個變數的情況,程序的循環重數就要相應改變。
『陸』 noip要用到哪些演算法
前言
離NOIP還有一個星期,匆忙的把寒假整理的演算法補充完善,看著當時的整理覺得那時還年少。第二頁貼了幾張從貼吧里找來的圖片,看著就很熱血
的。旁邊的同學都勸我不要再放PASCAL啊什麼的了,畢竟我們的下一級直接學C++。即便我本人對C++也是贊賞有加,不過PASCAL作為夢的開始終
究不能忘記。不像機房中其餘的OIERS,我以後並不想學計算機類的專業。當年來學這個競賽就是為了興趣,感受計算機之美的。經過時遷,計劃趕不上變化,
現在尚處於迷茫之中,也很難說當時做的決定是對是錯。然而我一直堅信迷茫的時候選擇難走的路會看見更好的風景。
這篇文章簡單的說了一下NOIP考試中會常用的演算法,可能難度掌握的不是太好,有一部分內容不是NOIP考查范圍,然而隨著難度的增加,看一些更高級的演算法也沒有壞處。還有一些非常非常基礎的比如鏈表啊什麼的就直接沒有寫上(別問我為什麼整理了那麼多的排序演算法)。
最後祝大家在NOIP中取得理想的成績!
搜索
DFS
框架
procere dfs(x);
var
begin
if 達到目標狀態 then 輸出結果並退出過程;
if 滿足剪枝條件 then exit;
for i:=1 to 搜索寬度 do
begin
備份現場;(注意如果現場使用了全局變數,則需要使用局部變數備份)
dfs(參數+增量);
恢復現場;
end;
優化
(1) 最優化剪枝:求最優值時,當前的狀態無論如何不可能比最優值更優,則退出,可與展望結合剪枝
(2) 可行性剪枝:提前判斷該狀態是否能得到可行解,如不能則退出
(3) 記憶化搜索:對於已經搜索過的狀態直接退出
(4) 改變搜索順序:對於看起來希望更大的決策先進行搜索
(5) 優化搜索策略
(6) 預處理找到大體搜索翻譯
(7) 改寫成IDA*演算法
(8) 卡時(注意現在聯賽中禁止使用meml掐時)
BFS
框架
初始化;把初始布局存入
設首指針head=0; 尾指針tail:=1;
repeat
inc(head),取出隊列首記錄為當前被擴展結點;
for i:=1 to 規則數 do {r是規則編號}
begin
if 新空格位置合法 then
begin
if 新布局與隊列中原有記錄不重復
tail增1,並把新布局存入隊尾;
if 達到目標 then 輸出並退出;
end;
end;
until head>=tail; {隊列空}
優化
判重的優化:hash,二叉排序樹
雙向廣搜或啟發式搜索
改寫成A*演算法
二分優化
排序
冒泡排序
var a:array[1..100] of longint;t,n,i,j:longint;
procere sort;
begin
for i:=1 to n-1 do{與每個數都進行比較}
for j:=1 to n-i do
if a[j]>a[j+1] then
begin
t:=a[j];
a[j]:=a[j+1];
a[j+1]:=t;
end;
end;
選擇排序
var a:array[1..100] of longint;t,n,i,j:longint;
procere sort;
begin
for i:=1 to n-1 do
for j:=1+i to n do{大數沉小數浮}
if a[j]>a[i] then
begin
t:=a[j];
a[j]:=a[i];
a[i]:=t;
end;
end;
插入排序
var a:array[0..100] of longint;n,i,j,t:longint;
procere sort;
begin
for i:=2 to n do
for j:=1 to (i-1) do
begin
if (a[i]<a[j]) then
begin
t:=a[j];
a[j]:=a[i];
a[i]:=t;
end;
end;
end;
桶排序
var a,b:array[0..100] of longint;r,i,j,t,k,n:longint;
procere sort;
begin
for i:=0 to 100 do b[i]:=0;{為B數組清零,小桶內容清零}
for i:=1 to n do b[a[i]]:=b[a[i]]+1;
{桶的序號就是那個要排序的東西;出現一次,桶里得旗數加一}
for i:=0 to 100 do{掃描所有的桶}
begin
if b[i]<>0 then{桶里有旗}
for j:=1 to b[i] do write(i,' ');{桶的序號就是那個數}
end;
end;
快速排序
var a:array[1..100] of longint;
n,i,h,g:longint;
procere kp(l,r:longint);{變數不能與全局變數相同,否則會被抹去}
var b,m,i,j,t:longint;
begin
i:=l;
j:=r;
m:=a[(l+r) div 2];{基準數最好從中間取}
repeat
while a[j]>m do dec(j);
while a[i]<m do inc(i);{兩側的哨兵移動}
if i<=j then
{哨兵未碰面}{「=」利用repeat循環的性質,使repeat循環得以結束}
begin
t:=a[j];
a[j]:=a[i
a[i]:=t;{交換兩個哨兵的值}
inc(j);
dec(j);{哨兵繼續運動}
end;
until i>j;
if j>l then kp(l,j);
if i<r then kp(i,r);{都是循環不結束後進行的動作}
end;
begin
read(n);
for i:=1 to n do read(a[i]);
kp(1,n); {「一」位置與「N」位置}
for i:=1 to n-1 do write(a[i],' ');
write(a[n]);{防止多輸出空格使程序結果出錯}
end.
堆排序
var a:array[1..100] of longint;
n,i,b:longint;
procere jianshu(i:longint);
begin
while ((a[i]>a[i*2])or(a[i]>a[i*2+1]))and(i<=n div 2) do
{當父親數大於子女數時並且他有孩子時進行}
begin
if a[i*2]<=a[i*2+1]{左兒子小於右兒子}
then
begin
b:=a[i*2]; a[i*2]:=a[i];a[i]:=b;{左右兒子的值互換}
jianshu(i*2);{繼續為左兒子建樹}
end
else
begin
b:=a[i*2+1];a[i*2+1]:=a[i];a[i]:=b;
jianshu(i*2+1);{上同,不過是為右兒子建樹}
end;
end;
end;
procere tiao;
begin
while n<>0 do
begin
write(a[1]);
a[1]:=a[n];
n:=n-1;
for i:=(n div 2) downto 1 do
jianshu(i);
end;
end;
begin
read(n);
for i:=1 to n do
read(a[i]);
for i:=(n div 2) downto 1 do
jianshu(i);
tiao;
end.
數學定理
中國剩餘定理
若有一些兩兩互質的整數m1, m2,… mn,則對任意的整數: a
1, a
2,... an
,以下聯立同餘方程組對模數m1, m2,… mn 有公解:
康托展開
a[i]為當前未出現的元素中是排在第幾個(從0開始)
把一個整數X展開成如下形式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!
其中a[i]為當前未出現的元素中是排在第幾個(從0開始),並且0<=a[i]<i(1<=i<=n)
錯排通項
考慮一個有n個元素的排列,若一個排列中所有的元素都不在自己原來的位置上,那麼這樣的排列就稱為原排列的一個錯排。
f[1]=0;f[2]=1;
f[n] =(n-1)(f[n-2) + f[n-1])
f[n]:=n![1-1/1!+1/2!-1/3!……+(-1)^n*1/n!]
f[n] = (n!/e+0.5),其中e是自然對數的底,[x]為x的整數部分。
費馬大定理
費馬大定理,又被稱為「費馬最後的定理」,由法國數學家費馬提出。它斷言當整數n >2時,關於x, y, z的方程 xn + yn = zn 沒有正整數解。
被提出後,經歷多人猜想辯證,歷經三百多年的歷史,最終在1995年被英國數學家安德魯·懷爾斯證明。
費馬小定理
假如a是一個整數,p是一個素數,那麼 ap ≡a (mod p)。
如果a不是p的倍數,這個定理也可以寫成ap-1 ≡1 (mod p)。----這個更加常用
逆元
由費馬小定理:假如p是質數,且gcd(a,p)=1,那麼ap-1≡1(mod p)
逆元:如果ab≡1(mod p),那麼在模p意義下,a、b互為逆元。
所以,假如p是質數,且gcd(a,p)=1,那麼a的逆元是ap-2
逆元的作用:在模意義下進行除法。乘a的逆元等同於除以a。
歐拉函數
在數論中,對正整數n,歐拉函數是小於或等於n的正整數中與n互質的數的數目。此函數以其首名研究者歐拉命名,它又稱為φ函數、歐拉商數等。
若m,a為正整數,且m,a互素,(gcd(a,m) = 1),則aφ(m)≡1,其中為φ(m)歐拉函數,mod m為同餘關系。
歐拉定理實際上是費馬小定理的推廣。
Stirling數
第一類s(p,k)的一個的組合學解釋是:將p個物體排成k個非空循環排列的方法數。
s(p,k)的遞推公式: s(p,k)=(p-1)*s(p-1,k)+s(p-1,k-1) ,1<=k<=p-1
邊界條件:s(p,0)=0 ,p>=1 s(p,p)=1 ,p>=0
遞推關系的說明:
考慮第p個物品,p可以單獨構成一個非空循環排列,這樣前p-1種物品構成k-1個非空循環排列,方法數為s(p-1,k-1);
也可以前p-1種物品構成k個非空循環排列,而第p個物品插入第i個物品的左邊,這有(p-1)*s(p-1,k)種方法。
第二類S(p,k)的一個組合學解釋是:將p個物體劃分成k個非空的不可辨別的(可以理解為盒子沒有編號)集合的方法數。
k!S(p,k)是把p個人分進k間有差別(如:被標有房號)的房間(無空房)的方法數。
S(p,k)的遞推公式是:S(p,k)=k*S(p-1,k)+S(p-1,k-1) ,1<= k<=p-1
邊界條件:S(p,p)=1 ,p>=0 S(p,0)=0 ,p>=1
遞推關系的說明:
考慮第p個物品,p可以單獨構成一個非空集合,此時前p-1個物品構成k-1個非空的不可辨別的集合,方法數為S(p-1,k-1);
也可以前p-1種物品構成k個非空的不可辨別的集合,第p個物品放入任意一個中,這樣有k*S(p-1,k)種方法。
PS:第一類斯特林數和第二類斯特林數有相同的初始條件,但遞推關系不同。
Stirling's approximation
快速求階乘,不推薦用於競賽。
數論
GCD&LCM
//GCD
function gcd(a,b:longint):longint;
begin
if b=0 then gcd:=a
else gcd:=gcd (b,a mod b);
end ;
//LCM
function lcm(a,b:longint):longint;
begin
if a<b then swap(a,b);
lcm:=a;
while lcm mod b>0 do inc(lcm,a);
end;
素數
//單個判斷
function prime (n: longint): boolean;
var i longint;
begin
for i:=2 to trunc(sqrt(n)) do
if n mod i=0 then exit(false)
exit(true);
end;
//篩法打表
procere main;
var i,j:longint;
begin
fillchar(f,sizeof(f),true);
f[0]:=false;f[1]:=false;
for i:=2 to trunc(sqrt(maxn)) do
if f[i] then
begin
j:=2*i;
while j<= maxn do
begin
f[j]:=false;
inc(j,i);
end;
end;
end;
快速冪
{a^b mod n}
function f(a,b,n:int64):int64;
var t,y:int64;
begin
t:=1;
y:=a;
while b<>0 do
begin
if(b and 1)=1 then t:=t*y mod n;
y:=y*y mod n;
{這里用了一個很強大的技巧,y*y即求出了a^(2^(i-1))不知道這是什麼的看原理}
b:=b shr 1;{去掉已經處理過的一位}
end;
exit(t);
end;
模運演算法則
(A+B) mod C = (A mod C + B mod C) mod C
(A-B) mod C = (A mod C - B mod C) mod C
(A * B) mod C = (A mod C) * (B mod C) mod C
(A / B) mod C = ???
『柒』 PID演算法中的動態P是什麼意思
1,PID增量式演算法:是PID控制演算法的一種,有濾波的選擇,系統的動態過程加速的功能。(1)濾波的選擇:可以對輸入加一個前置濾波器,使得進入控制演算法的給定值不突變,而是有一定慣性延遲的緩變數。(2)系統的動態過程加速:如果被控量繼續偏離給定值,則這兩項符號相同,而當被控量向給定值方向變化時,則這兩項的符號相反。由於這一性質,當被控量接近給定值的時候,反號的比例作用阻礙了積分作用,因而避免了積分超調以及隨之帶來的振盪,這顯然是有利於控制的。但如果被控量遠未接近給定值,僅剛開始向給定值變化時,由於比例和積分反向,將會減慢控制過程。2,PID增量演算法的飽和作用及其抑制:在PID增量演算法中,由於執行元件本身是機械或物理的積分儲存單元,如果給定值發生突變時,由演算法的比例部分和微分部分計算出的控制增量可能比較大,如果該值超過了執行元件所允許的最大限度,那麼實際上執行的控制增量將時受到限制時的值,多餘的部分將丟失,將使系統的動態過程變長,因此,需要採取一定的措施改善這種情況。
『捌』 怎麼理解 P 問題和 NP 問題
P的含義是polynomial(多項式的)。
要了解什麼是P問題NP問題,首先要引入演算法和時間復雜度的概念。
演算法一般指的是一套用於解決問題的流程。演算法要保障結果的正確性和運行效率。
對於時間復雜度,一般指的是運算步驟次數和數據量之間的關系。比如說對於一個演算法,如果數據量為n,那麼如果它的運算步驟次數為2*N^2+2次,我們取最高影響的那一項為N^2。這項是整個演算法增長速度最快一項。於是我們把演算法復雜度計成O(N^2)。
一般的演算法關系如此O(logn)<O(n)<O(n*logn)<O(n^2)等等
然後若K和C是任意常數,如果C<1,則O(C^n)<O(1)<O(n^k)。反之,若C>1,O(C^n)>O(n^k)。就好比2的n次方比n的任意階多項式都增長得快。
P問題指的是一些能夠被多項式時間復雜度的演算法准確解出的問題。而NP問題是目前為止,尚未發現多項式時間復雜度內演算法能夠解決的問題。NP問題目前最快也是指數級別的演算法時間復雜度。
在當前,我們有時會用近似演算法快速解決NP問題。對於近似演算法(有時用貪心法,有時用放寬條件的線性規劃),我們會得到接近最優解的可行解。比如求一個最大值,我如果用的是0.9倍近似演算法,意味著我的演算法產生的結果最小也是最優解的0.9倍。這樣可以保障得到的結果足夠好。
如果P=NP被證明了,也就是說NP問題都能被多項式時間復雜度解決了,那這將是人類歷史的巨大變革。
在目前准確解決NP問題的技巧中,你可以使用決策樹演算法,演算法核心以及動態規劃結合圖的樹分解等技巧。然而,這些演算法技巧還不足以把NP問題放在多項式時間復雜度下面解決。
『玖』 物理中,物質波的動量P怎樣計算
物質波的有關計算
在光具有波粒二象性的啟發下,法國物理學家德布羅意(1892~)在1924
年提出一個假說,指出波粒二象性不只是光子才有,一切微觀粒子,包括電子和
質子、中子,都有波粒二象性.他把光子的動量與波長的關系式p=h/λ
推廣到一切微觀粒子上,指出:具有質量m 和速度v 的運動粒子也具有波動性,這種波
的波長等於普朗克恆量h 跟粒子動量mv 的比.即λ= h/(mv).這個關系式後來就叫做德布羅意公式.
『拾』 一道數據結構問題,請問,這道11題,這里演算法中為什麼引入p
首先,題主所說可以一直使用參數t而不引入p,在具體實現(c語言實現)中是完全正確的。甚至可以說不引入p可以節約一次賦值操作,效率更優。
如果回答地簡單點,那就是「沒有什麼特殊含義,就是習慣如此而已」。
但是我們「過分」解讀一下,這種習慣還是有其獨到之處的。
這就不得不提到在思考問題中的遞增的思考方式:
怎樣健壯地實現功能
在以上基礎上,怎樣高效率地實現功能
在以上基礎上,怎樣優雅地實現功能
在以上基礎上,怎樣使得這種實現更具有一般性(劃重點)
在以上基礎上,怎樣使得這種實現具有可擴充性、可復用性。
這也是在工程開發中要考慮的一些點。
所以:
1.這樣寫具有更高的通用性和一般性:我們知道,不是每個語言對參數的傳遞都是「傳值」,我們可以把這段代碼看成c語言,也可以看成偽代碼。有些語言在函數內修改參數是可以影響到調用方的。根據普遍性來講,這里復制一份比較具有通用性和一般性,因為這樣寫不管你用的是什麼類型的語言都不會因為參數的傳遞方式不同而出現錯誤。
2.高擴擴性:在函數內部,在某很多些場合,我們盡量不去將參數直接拿來當變數,因為我們不知道什麼時候會有改動需求,可能之後函數下面還需要用到原來參數的值,如果上面將參數改了,後面再進行擴充時就需要改變原有的、已經穩定了的代碼——這無疑會帶來風險和成本。