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

NUL演算法

發布時間: 2023-01-02 16:28:31

A. sql 中 碰到null值或0 的演算法怎麼辦

把特殊的例子拿出來唄。
我大概寫一個,不知道你的判斷條件是什麼。,比如是幾個都是0,還是有一個為空,還是怎麼樣
select case when 門幅 is null or 克重 is null or 重量 is null then 『遇到被零除數'
when 門幅=0 or 克重=0 or 重量=0 then '遇到被零除數'
else 10000/門幅/克重/0.91*重量 end as 新列名 from table;
還有一個簡單的寫法,就是
select case when 門幅<>0 and 克重<>0 and 重量<>0 then 10000/門幅/克重/0.91*重量
else 『遇到被零除數' end as 新列名 from table;

B. 設計一個在帶頭結點的單鏈表中刪除第i個結點的演算法

//刪除節點 刪除第i個節點
int Delete_Positon_LL(LinkList *phead,int i)
{
LinkList p,q;//p為值是x的節點,q是p的前一個節點
int j;

if((*phead)->next == NULL)//如果鏈表為空,做下溢處理
{
printf("單鏈表為空! ");

return 0;
}
if(i == 1) //如果是表頭,表頭後移
{

p=(*phead)->next;
(*phead)->next=(*phead)->next->next;
free(p);//釋放表頭


(2)NUL演算法擴展閱讀:

鏈接方式存儲的線性表簡稱為鏈表(Linked List)。鏈表的具體存儲表示為:

① 用一組任意的存儲單元來存放線性表的結點(這組存儲單元既可以是連續的,也可以是不連續的)

② 鏈表中結點的邏輯次序和物理次序不一定相同。為了能正確表示結點間的邏輯關系,在存儲每個結點值的同時,還必須存儲指示其後繼結點的地址(或位置)信息(稱為指針(pointer)或鏈(link))

鏈式存儲是最常用的存儲方式之一,它不僅可用來表示線性表,而且可用來表示各種非線性的數據結構。

C. 用演算法實現:單鏈表和順序表刪除。刪除順序表中值相同的多餘結點

第8章排序(演算法設計)習題練習答案
作者: 來源: http://www.csai.cn 2006年9月4日

13. 將哨兵放在R[n]中,被排序的記錄放在R[0..n-1]中,重寫直接插入排序演算法。
解:
重寫的演算法如下:
void InsertSort(SeqList R)
{//對順序表中記錄R[0..n-1]按遞增序進行插入排序
int i,j;
for(i=n-2;i>=0;i--) //在有序區中依次插入R[n-2]..R[0]
if(R[i].key>R[i+1].key) //若不是這樣則R[i]原位不動
{
R[n]=R[i];j=i+1; //R[n]是哨兵
do{ //從左向右在有序區中查找插入位置
R[j-1]=R[j]; //將關鍵字小於R[i].key的記錄向右移
j++;
}while(R[j].key<R[n].key]);
R[j-1]=R[n]; //將R[i]插入到正確位置上
}//endif
}//InsertSort.

14.以單鏈表作為存儲結構實現直接插入排序演算法。
解:
#define int KeyType //定義KeyType 為int型
typedef struct node{
KeyType key; //關鍵字域
OtherInfoType info; //其它信息域,
struct node * next; //鏈表中指針域
}RecNode; //記錄結點類型
typedef RecNode * LinkList ; //單鏈表用LinkList表示

void InsertSort(LinkList head)
{//鏈式存儲結構的直接插入排序演算法,head是帶頭結點的單鏈表
RecNode *p,*q,*s;
if ((head->next)&&(head->next->next))//當表中含有結點數大於1
{
p=head->next->next;//p指向第二個節點
head->next=NULL;
q=head;//指向插入位置的前驅節點
while(p)&&(q->next)&&(p->key<q->next->key)
q=q->next;
if (p)
{s=p;p=p->next;// 將要插入結點摘下
s->next=q->next;//插入合適位置:q結點後
q->next=s;
}
}
}

15.設計一演算法,使得在盡可能少的時間內重排數組,將所有取負值的關鍵字放在所有取非負值的關鍵字之前。請分析演算法的時間復雜度。
解:
因為只需將負數關鍵字排在前面而無需進行精確排列順序,因此本演算法採用兩端掃描的方法,就象快速排序採用的方法一樣,左邊掃描到正數時停止,開始掃描右邊,遇到負數時與左邊的當前記錄交換,如此交替進行,一趟下來就可以完成排序。

void ReSort(SeqList R)
{//重排數組,使負值關鍵字在前
int i=1,j=n; //數組存放在R[1..n]中
while (i<j) //i<j表示尚未掃描完畢
{ while(i<j&&R[i].key<0) //遇到負數則繼續掃描
i++;
R[0]=R[i]; //R[0]為輔助空間
while(i<j&&R[j].key>=0)// 遇到正數則繼續向左掃描
j--;
R[i++]=R[j];R[j--]=R[0];//交換當前兩個元素並移動指針
}//endwhile
}//ReSort

本演算法在任何情況下的比較次數均為n(每個元素和0)相比,交換次數少於n/2,總的來說,時間復雜度為O(n).

*16.寫一個雙向冒泡排序的演算法,即在排序過程中交替改變掃描方向。
解:
演算法如下:
void BubbleSort(SeqList R)
{//R[1..n]是待排序文件,雙向掃描冒泡排序
int i,j,k;
Boolean exchange; //交換標記
i=n;j=1;
exchange=TRUE;
while (i>j)&&(exchange)
{k=i-1;exchange=FALSE;
while (k>=j)//由下往上掃描
{if (r[k]>r[k+1])
{r[0]=r[k];r[k]=r[k+1];r[k+1]=r[k];exchange=TRUE;//交換
}//endif
k--;
}//endwhile
if (exchange)
{exchange=FALSE;
j++;k=j+1;
while(k<=i)//由上往下掃描
{if (r[k]<r[k-1])
{r[0]=r[k];r[k]=r[k-1];r[k-1]=r[k];exchange=TRUE;//交換
}//endif
k++;
}endwhile
i--;
}//endif
}endwhile
}//endsort

17.下面是一個自上往下掃描的冒泡排序的偽代碼演算法,它採用lastExchange 來記錄每趟掃描中進行交換的最後一個元素的位置,並以它作為下一趟排序循環終止的控制值。請仿照它寫一個自下往上掃描的冒泡排序演算法。
void BubbleSort(int A[],int n)
//不妨設A[0..n-1]是整型向量
int lastExchange,j,i=n-1;
while (i>0)
lastExchange=0;
for(j=0;j<i;j++)//從上往下掃描A[0..i]
if(A[j+1]<A[j]){
交換A[j]和A[j+1];
lastExchange=j;
}
i=lastExchange;//將i置為最後交換的位置
}//endwhile
}//BubbleSort

解:演算法如下:
void BubbleSort(int A[],int n)
//不妨設A[0..n-1]是整型向量
int lastExchange,j,i=0;
while (i<n) //這一條很重要,如不改為這樣,演算法將無限循環下去
lastExchange=n;
for(j=n-1;j>i;j--)//從下往上掃描A[0..i]
if(A[j-1]<A[j]){
交換A[j]和A[j-1];
lastExchange=j;
}
i=lastExchange;//將i置為最後交換的位置
}//endwhile
}//BubbleSort

18.改寫快速排序演算法,要求採用三者取中的方式選擇劃分的基準記錄;若當前被排序的區間長度小於等於3時,無須劃分而是直接採用直接插入方式對其排序。
解:
改寫後的演算法如下:
void QuickSort(SeqList R,int low ,int high)
{//對R[low..high]快速排序
int pivotpos;
if(high-low<=2)//若當前區內元素少於3個
{//則進行直接插入排序
InsertSort(R,low,high);
}
else
{
pivotpos=midPartion(R,low,high);
QuickSort(R,low,pivotpos-1);
QuickSort(R,pivotpos+1,high);
}
}//QuickSort

int midPartion(SeqList R,int i, int j)
{//三者取中規則定基準
if(R[(i+j)/2].key>R[i].key)
{ 交換R[(i+j)/2]和R[i];}
if(R[i].key>R[j].key)
{ 交換R[i]和R[j];}
if(R[i].key)<R[(i+j)/2].key)
{ 交換R[i]和R[(i+j)/2];}
//以上三個if語句就使區間的第一個記錄的key值為三個key的中間值
return Partion(R,i,j);//這樣我們就可以仍使用原來的劃分演算法了
}

19.對給定的j(1≤j≤n ),要求在無序的記錄區R[1..n]中找到按關鍵字自小到大排在第j個位置上的記錄(即在無序集合中找到第j個最小元),試利用快速排序的劃分思想編寫演算法實現上述的查找操作。
答:
int QuickSort(SeqList R,int j,int low,int high)
{ //對R[low..high]快速排序
int pivotpos; //劃分後的基準記錄的位置
if(low<high){//僅當區間長度大於1時才須排序
pivotpos=Partition(R,low,high); //對R[low..high]做劃分
if (pivotpos==j) return r[j];
else if (pivotpos>j) return(R,j,low,pivotpos-1);
else return quicksort(R,j,pivotpos+1,high);
}
} //QuickSort

20.以單鏈表為存儲結構,寫一個直接選擇排序演算法。
答:
#define int KeyType //定義KeyType 為int型
typedef struct node{
KeyType key; //關鍵字域
OtherInfoType info; //其它信息域,
struct node * next; //鏈表中指針域
}RecNode; //記錄結點類型

typedef RecNode * LinkList ; //單鏈表用LinkList表示

void selectsort(linklist head)
{RecNode *p,*q,*s;
if(head->next)&&(head->next->next)
{p=head->next;//p指向當前已排好序最大元素的前趨
while (p->next)
{q=p->next;s=p;
while(q)
{if (q->key<s->key) s=q;
q=q->next;
}//endwhile
交換s結點和p結點的數據;
p=p->next;
}//endwhile
}//endif
}//endsort

21.寫一個heapInsert(R,key)演算法,將關鍵字插入到堆R中去,並保證插入R後仍是堆。提示:應為堆R增加一個長度屬性描述(即改寫本章定義的SeqList類型描述,使其含有長度域);將key先插入R中已有元素的尾部(即原堆的長度加1的位置,插入後堆的長度加1),然後從下往上調整,使插入的關鍵字滿足性質。請分析演算法的時間。
答:
#define n 100//假設文件的最長可能長度
typedef int KeyType; //定義KeyType 為int型
typedef struct node{
KeyType key; //關鍵字域
OtherInfoType info; //其它信息域,
}Rectype; //記錄結點類型

typedef struct{
Rectype data[n] ; //存放記錄的空間
int length;//文件長度
}seqlist;

void heapInsert(seqlist *R,KeyType key)
{//原有堆元素在R->data[1]~R->data[R->length],
//將新的關鍵字key插入到R->data[R->length+1]位置後,
//以R->data[0]為輔助空間,調整為堆(此處設為大根堆)
int large;//large指向調整結點的左右孩子中關鍵字較大者
int low,high;//low和high分別指向待調整堆的第一個和最後一個記錄
int i;
R->length++;R->data[R->length].key=key;//插入新的記錄
for(i=R->length/2;i>0;i--)//建堆
{
low=i;high=R->length;
R->data[0].key=R->data[low].key;//R->data[low]是當前調整的結點
for(large=2*low;large<=high;large*=2){
//若large>high,則表示R->data[low]是葉子,調整結束;
//否則令large指向R->data[low]的左孩子
if(large<high&&R->data[large].key<R->data[large+1].key)
large++;//若R->data[low]的右孩子存在
//且關鍵字大於左兄弟,則令large指向它
if (R->data[0].key<R->data[large].key)
{ R->data[low].key= R->data[large].key;
low=large;//令low指向新的調整結點
}
else break;//當前調整結點不小於其孩子結點的關鍵字,結束調整
}//endfor
R->data[low].key=R->data[0].key;//將被調整結點放入最終的位置上
}//end of for
}end of heapinsert

演算法分析:
設文件長度為n,則該演算法需進行n/2趟調整,總的時間復雜度與初建堆類似,最壞時間復雜度為O(nlgn),輔助空間為O(1).

22.寫一個建堆演算法:從空堆開始,依次讀入元素調用上題中堆插入演算法將其插入堆中。
答:
void BuildHeap(seqlist *R)
{
KeyType key;
R->length=0;//建一個空堆
scanf("%d",&key);//設MAXINT為不可能的關鍵字
while(key!=MAXINT)
{
heapInsert(R,key);
scanf("%d",&key);
}
}

23.寫一個堆刪除演算法:HeapDelete(R,i),將R[i]從堆中刪去,並分析演算法時間,提示:先將R[i]和堆中最後一個元素交換,並將堆長度減1,然後從位置i開始向下調整,使其滿足堆性質。
答:
void HeapDelete(seqlist *R,int i)
{//原有堆元素在R->data[1]~R->data[R->length],
//將R->data[i]刪除,即將R->data[R->length]放入R->data[i]中後,
//將R->length減1,再進行堆的調整,
//以R->data[0]為輔助空間,調整為堆(此處設為大根堆)
int large;//large指向調整結點的左右孩子中關鍵字較大者
int low,high;//low和high分別指向待調整堆的第一個和最後一個記錄
int j;
if (i>R->length)
Error("have no such node");
R->data[i].key=R->data[R->length].key;
R->length--;R->data[R->length].key=key;//插入新的記錄
for(j=i/2;j>0;j--)//建堆
{
low=j;high=R->length;
R->data[0].key=R->data[low].key;//R->data[low]是當前調整的結點
for(large=2*low;large<=high;large*=2){
//若large>high,則表示R->data[low]是葉子,調整結束;
//否則令large指向R->data[low]的左孩子
if(large<high&&R->data[large].key<R->data[large+1].key)
large++;//若R->data[low]的右孩子存在
//且關鍵字大於左兄弟,則令large指向它
if (R->data[0].key<R->data[large].key)
{ R->data[low].key= R->data[large].key;
low=large;//令low指向新的調整結點
}
else break;//當前調整結點不小於其孩子結點的關鍵字,結束調整
}//endfor
R->data[low].key=R->data[0].key;//將被調整結點放入最終的位置上
}//end of for
}end of HeapDelete

24.已知兩個單鏈表中的元素遞增有序,試寫一演算法將這兩個有序表歸並成一個遞增有序的單鏈表。演算法應利用原有的鏈表結點空間。
答:
typedef struct node{
KeyType key; //關鍵字域
OtherInfoType info; //其它信息域,
struct node * next; //鏈表中指針域
}RecNode; //記錄結點類型

typedef RecNode * LinkList ; //單鏈表用LinkList表示

void mergesort(LinkList la,LinkList lb,LinkList lc)
{RecNode *p,*q,*s,*r;
lc=la;
p=la;//p是la表掃描指針,指向待比較結點的前一位置
q=lb->next;//q是lb表掃描指針,指向比較的結點
while(p->next)&&(q)
if (p->next->key<=q->key)
p=p->next;
else {s=q;q=q->next;
s->next=p->next;p->next=s;//將s結點插入到p結點後
p=s;}
if (!p->next) p->next=q;
free(lb);
}

25.設向量A[0..n-1]中存有n個互不相同的整數,且每個元素的值均在0到n-1之間。試寫一時間為O(n)的演算法將向量A排序,結果可輸出到另一個向量B[0..n-1]中。
答:
sort(int *A,int *B)
{//將向量A排序後送入B向量中
int i;
for(i=0;i<=n-1;i++)
B[A[i]]=A[i];
}

*26.寫一組英文單詞按字典序排列的基數排序演算法。設單詞均由大寫字母構成,最長的單詞有d個字母。提示:所有長度不足d個字母的單詞都在尾處補足空格,排序時設置27個箱子,分別與空格,A,B...Z對應。
答:
#define KeySize 10 //設關鍵字位數d=10
#define Radix 27 //基數rd為27
typedef RecType DataType;//將隊列中結點數據類型改為RecType類型
typedef struct node{
char key[KeySize]; //關鍵字域
OtherInfoType info; //其它信息域,
}RecType; //記錄結點類型
typedef RecType seqlist[n+1];

void RadixSort(seqlist R)
{
LinkQueue B[Radix];
int i;
for(i=0;i<Radix;i++)//箱子置空
InitQueue(&B[i]);
for(i=KeySize-1;i>=0;i--){//從低位到高位做d趟箱排序
Distribute(R,B,i);//第KeySize-i趟分配
Collect(R,B);//第KeySize-i趟收集
}
}

void Distribute(seqlist R,LinkQueue B[], int j)
{//按關鍵字的第j個分量進行分配,初始時箱子為空
int i;
j=KeySize-j; // 確定關鍵字從低位起的位置
for(i=0;i<n;i++) //依次掃描R[i],將其裝箱
if (R[i].key[j]-'A'>26)
EnQueue(&B[0],R[i]);//將第j位關鍵字位空格的記錄入第0個隊列
else EnQueue(&B[0],R[R[i].key[j]-'A'+1]);
}

void Collect(seqlist R,LinkQueue B[])
{
//依次將各非空箱子中的記錄收集起來,本過程結束,各箱子都變空
int i,j;
for (j=0;j<Radix;j++)
while(!QueueEmpty(&B[j]))
R[i++]=DeQueue(&B[j]);//將出隊記錄依次輸出到R[i]中
}

D. OPT頁面置換演算法最優性證明。

1常見的置換演算法

1.最佳置換演算法(OPT)(理想置換演算法):所選擇的被淘汰頁面將是以後永不使用的,或者是在最長時間內不再被訪問的頁面,這樣可以保證獲得最低的缺頁率。2.先進先出置換演算法(FIFO):優先淘汰最早進入的頁面,亦即在內存中駐留時間最久的頁面。3.最近最久未使用(LRU)演算法:選擇最近最長時間未訪問過的頁面予以淘汰。4.Clock置換演算法(LRU演算法的近似實現):給每一幀關聯一個附加位,稱為使用位。5.最少使用(LFU)置換演算法6.工作集演算法7 . 工作集時鍾演算法8. 老化演算法(非常類似LRU的有效演算法)9. NRU(最近未使用)演算法10. 第二次機會演算法2操作系統頁面置換演算法代碼#include <stdio.h>[1]#include <stdlib.h>#include <unistd.h> #define TRUE 1#define FALSE 0#define INVALID -1#define NUL 0#define total_instruction 320 /*指令流長*/#define total_vp 32 /*虛頁長*/#define clear_period 50 /*清零周期*/typedef struct{ /*頁面結構*/int pn,pfn,counter,time;}pl_type;pl_type pl[total_vp]; /*頁面結構數組*/struct pfc_struct{ /*頁面控制結構*/int pn,pfn;struct pfc_struct *next;};typedef struct pfc_struct pfc_type;pfc_type pfc[total_vp],*freepf_head,*busypf_head,*busypf_tail;int diseffect,a[total_instruction];int page[total_instruction], offset[total_instruction];void initialize(int);void FIFO(int);void LRU(int);void NUR(int);int main(){int S,i;srand((int)getpid());S=(int)rand()%390;for(i=0;i<total_instruction;i+=1) /*產生指令隊列*/{a[i]=S; /*任選一指令訪問點*/a[i+1]=a[i]+1; /*順序執行一條指令*/a[i+2]=(int)rand()%390; /*執行前地址指令m』*/a[i+3]=a[i+2]+1; /*執行後地址指令*/S=(int)rand()%390;}for(i=0;i<total_instruction;i++) /*將指令序列變換成頁地址流*/{page[i]=a[i]/10;offset[i]=a[i]%10;}for(i=4;i<=32;i++) /*用戶內存工作區從4個頁面到32個頁面*/{printf("%2d page frames",i);FIFO(i);LRU(i);NUR(i);printf(" ");}return 0;}void FIFO(int total_pf) /*FIFO(First in First out)ALGORITHM*//*用戶進程的內存頁面數*/{int i;pfc_type *p, *t;initialize(total_pf); /*初始化相關頁面控制用數據結構*/busypf_head=busypf_tail=NUL; /*忙頁面隊列頭,對列尾鏈接*/for(i=0;i<total_instruction;i++){if(pl[page[i]].pfn==INVALID) /*頁面失效*/{diseffect+=1; /*失效次數*/if(freepf_head==NUL) /*無空閑頁面*/{p=busypf_head->next;pl[busypf_head->pn].pfn=INVALID; /*釋放忙頁面隊列中的第一個頁面*/freepf_head=busypf_head;freepf_head->next=NUL;busypf_head=p;}p=freepf_head->next; /*按方式調新頁面入內存頁面*/freepf_head->next=NUL;freepf_head->pn=page[i];pl[page[i]].pfn=freepf_head->pfn;if(busypf_tail==NUL)busypf_head=busypf_tail=freepf_head;else{busypf_tail->next=freepf_head;busypf_tail=freepf_head;}freepf_head=p;}}printf("FIFO:%6.4F",1-(float)diseffect/320);}void LRU(int total_pf){int min,minj,i,j,present_time;initialize(total_pf);present_time=0;for(i=0;i<total_instruction;i++){if(pl[page[i]].pfn==INVALID) /*頁面失效*/{diseffect++;if(freepf_head==NUL) /*無空閑頁面*/{min=32767;for(j=0;j<total_vp;j++)if(min>pl[j].time&&pl[j].pfn!=INVALID){min=pl[j].time;minj=j;}freepf_head=&pfc[pl[minj].pfn];pl[minj].pfn=INVALID;pl[minj].time=-1;freepf_head->next=NUL;}pl[page[i]].pfn=freepf_head->pfn;pl[page[i]].time=present_time;freepf_head=freepf_head->next;}elsepl[page[i]].time=present_time;present_time++;}printf("LRU:%6.4f",1-(float)diseffect/320);}void NUR(int total_pf){int i,j,dp,cont_flag,old_dp;pfc_type *t;initialize(total_pf);dp=0;for(i=0;i<total_instruction;i++){if(pl[page[i]].pfn==INVALID) /*頁面失效*/{diseffect++;if(freepf_head==NUL) /*無空閑頁面*/{cont_flag=TRUE;old_dp=dp;while(cont_flag)if(pl[dp].counter==0&&pl[dp].pfn!=INVALID)cont_flag=FALSE;else{dp++;if(dp==total_vp)dp=0;if(dp==old_dp)for(j=0;j<total_vp;j++)pl[j].counter=0;}freepf_head=&pfc[pl[dp].pfn];pl[dp].pfn=INVALID;freepf_head->next=NUL;}pl[page[i]].pfn=freepf_head->pfn;freepf_head=freepf_head->next;}elsepl[page[i]].counter=1;if(i%clear_period==0)for(j=0;j<total_vp;j++)pl[j].counter=0;}printf("NUR:%6.4f",1-(float)diseffect/320);}void initialize(int total_pf) /*初始化相關數據結構*//*用戶進程的內存頁面數*/{int i;diseffect=0;for(i=0;i<total_vp;i++){pl[i].pn=i;pl[i].pfn=INVALID; /*置頁面控制結構中的頁號,頁面為空*/pl[i].counter=0;pl[i].time=-1; /*頁面控制結構中的訪問次數為0,時間為-1*/}for(i=1;i<total_pf;i++){pfc[i-1].next=&pfc[i];pfc[i-1].pfn=i-1;/*建立pfc[i-1]和pfc[i]之間的連接*/}pfc[total_pf-1].next=NUL;pfc[total_pf-1].pfn=total_pf-1;freepf_head=&pfc[0]; /*頁面隊列的頭指針為pfc[0]*/}/*說明:本程序在Linux的gcc下和c-free下編譯運行通過*/【http://wenku..com/link?url=o_】
不知道能不能打開-是復制的 但也辛苦半天 忘採納~

E. PHP對稱加密-AES

對稱加解密演算法中,當前最為安全的是 AES 加密演算法(以前應該是是 DES 加密演算法),PHP 提供了兩個可以用於 AES 加密演算法的函數簇: Mcrypt OpenSSL

其中 Mcrypt 在 PHP 7.1.0 中被棄用(The Function Mycrypt is Deprecated),在 PHP 7.2.0 中被移除,所以即可起你應該使用 OpenSSL 來實現 AES 的數據加解密。

在一些場景下,我們不能保證兩套通信系統都使用了相函數簇去實現加密演算法,可能 siteA 使用了最新的 OpenSSL 來實現了 AES 加密,但作為第三方服務的 siteB 可能仍在使用 Mcrypt 演算法,這就要求我們必須清楚 Mcrypt 同 OpenSSL 之間的差異,以便保證數據加解密的一致性。

下文中我們將分別使用 Mcrypt 和 OpenSSL 來實現 AES-128/192/256-CBC 加解密,二者同步加解密的要點為:

協同好以上兩點,就可以讓 Mcrypt 和 OpenSSL 之間一致性的對數據進行加解密。

AES 是當前最為常用的安全對稱加密演算法,關於對稱加密這里就不在闡述了。

AES 有三種演算法,主要是對數據塊的大小存在區別:

AES-128:需要提供 16 位的密鑰 key
AES-192:需要提供 24 位的密鑰 key
AES-256:需要提供 32 位的密鑰 key

AES 是按數據塊大小(128/192/256)對待加密內容進行分塊處理的,會經常出現最後一段數據長度不足的場景,這時就需要填充數據長度到加密演算法對應的數據塊大小。

主要的填充演算法有填充 NUL("0") 和 PKCS7,Mcrypt 默認使用的 NUL("0") 填充演算法,當前已不被推薦,OpenSSL 則默認模式使用 PKCS7 對數據進行填充並對加密後的數據進行了 base64encode 編碼,所以建議開發中使用 PKCS7 對待加密數據進行填充,已保證通用性(alipay sdk 中雖然使用了 Mcrypt 加密簇,但使用 PKCS7 演算法對數據進行了填充,這樣在一定程度上親和了 OpenSSL 加密演算法)。

Mcrypt 的默認填充演算法。NUL 即為 Ascii 表的編號為 0 的元素,即空元素,轉移字元是 "",PHP 的 pack 打包函數在 'a' 模式下就是以 NUL 字元對內容進行填充的,當然,使用 "" 手動拼接也是可以的。

OpenSSL的默認填充演算法。下面我們給出 PKCS7 填充演算法 PHP 的實現:

默認使用 NUL("") 自動對待加密數據進行填充以對齊加密演算法數據塊長度。

獲取 mcrypt 支持的演算法,這里我們只關注 AES 演算法。

注意:mcrypt 雖然支持 AES 三種演算法,但除 MCRYPT_RIJNDAEL_128 外, MCRYPT_RIJNDAEL_192/256 並未遵循 AES-192/256 標准進行加解密的演算法,即如果你同其他系統通信(java/.net),使用 MCRYPT_RIJNDAEL_192/256 可能無法被其他嚴格按照 AES-192/256 標準的系統正確的數據解密。官方文檔頁面中也有人在 User Contributed Notes 中提及。這里給出如何使用 mcrpyt 做標注的 AES-128/192/256 加解密

即演算法統一使用 MCRYPT_RIJNDAEL_128 ,並通過 key 的位數 來選定是以何種 AES 標准做的加密,iv 是建議添加且建議固定為16位(OpenSSL的 AES加密 iv 始終為 16 位,便於統一對齊),mode 選用的 CBC 模式。

mcrypt 在對數據進行加密處理時,如果發現數據長度與使用的加密演算法的數據塊長度未對齊,則會自動使用 "" 對待加密數據進行填充,但 "" 填充模式已不再被推薦,為了與其他系統有更好的兼容性,建議大家手動對數據進行 PKCS7 填充。

openssl 簇加密方法更為簡單明確,mcrypt 還要將加密演算法分為 cipher + mode 去指定,openssl 則只需要直接指定 method 為 AES-128-CBC,AES-192-CBC,AES-256-CBC 即可。且提供了三種數據處理模式,即 默認模式 0 / OPENSSL_RAW_DATA / OPENSSL_ZERO_PADDING 。

openssl 默認的數據填充方式是 PKCS7,為兼容 mcrpty 也提供處理 "0" 填充的數據的模式,具體為下:

options 參數即為重要,它是兼容 mcrpty 演算法的關鍵:

options = 0 : 默認模式,自動對明文進行 pkcs7 padding,且數據做 base64 編碼處理。
options = 1 : OPENSSL_RAW_DATA,自動對明文進行 pkcs7 padding, 且數據未經 base64 編碼處理。
options = 2 : OPENSSL_ZERO_PADDING,要求待加密的數據長度已按 "0" 填充與加密演算法數據塊長度對齊,即同 mcrpty 默認填充的方式一致,且對數據做 base64 編碼處理。注意,此模式下 openssl 要求待加密數據已按 "0" 填充好,其並不會自動幫你填充數據,如果未填充對齊,則會報錯。

故可以得出 mcrpty簇 與 openssl簇 的兼容條件如下:

建議將源碼復制到本地運行,根據運行結果更好理解。

1.二者使用的何種填充演算法。

2.二者對數據是否有 base64 編碼要求。

3.mcrypt 需固定使用 MCRYPT_RIJNDAEL_128,並通過調整 key 的長度 16, 24,32 來實現 ase-128/192/256 加密演算法。

熱點內容
ad濾波演算法 發布:2025-05-10 01:17:59 瀏覽:893
自製網路存儲 發布:2025-05-10 01:03:29 瀏覽:504
java防重復提交 發布:2025-05-10 01:02:32 瀏覽:736
如何在家用樂高製作解壓玩具 發布:2025-05-10 00:55:07 瀏覽:438
linux的mtu 發布:2025-05-10 00:54:52 瀏覽:154
android判斷root 發布:2025-05-10 00:44:06 瀏覽:688
if條件編譯 發布:2025-05-10 00:21:02 瀏覽:72
圓管根數演算法 發布:2025-05-10 00:18:55 瀏覽:676
中短發編程 發布:2025-05-10 00:16:07 瀏覽:39
安卓相機的解析度在哪裡 發布:2025-05-10 00:15:28 瀏覽:236