希尔算法简介
① 希尔排序
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n^2)的第一批算法之一。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
第一轮排序:设置step步长为4,根据步长将数组分为四组,{1,3}, {4,2},{6,5},{0,9} 进行两两比较。
将i=step,开始往后遍历。如果a[i]>a[i-step]交换数据。得到第一轮排序结果:1 2 5 0 3 4 6 9
第二轮排序:设置step步长为2,根据步长继续两两分组,比较数据大小。得到第二轮排序结果:1 0 3 2 5 4 6 9
第三轮排序:设置step步长为1,根据步长继续两两分组,比较数据大小。得到第三轮排序结果:0 1 2 3 4 5 6 9
输出结果:
Shell sort:
0, 1, 2, 3, 4, 5, 6, 9,
希尔排序的关键并不是随便分组后各自排序,而是将相隔某个step 的记录组成一个子序列, 实现跳跃式的移动,使得排序的效率提高。
希尔排序的时间复杂度是O(nlogn),效率上比冒泡排序,直接插入排序,简单选择排序的O(n^2)要高。
② 希尔密码原理
希尔密码(Hill Cipher)是运用基本矩阵论原理的替换密码,由Lester S. Hill在1929年发明。每个字母当作26进制数字:A=0, B=1, C=2... 一串字母当成n维向量,跟一个n×n的矩阵相乘,再将得出的结果MOD26。
中文名
希尔密码
外文名
Hill Cipher
原理
基本矩阵论
类别
替换密码
提出者
Lester S. Hill
快速
导航
产生原因
原理
安全性分析
例子
简介
希尔密码是运用基本矩阵论原理的替换密码,由Lester S. Hill在1929年发明。
每个字母当作26进制数字:A=0, B=1, C=2... 一串字母当成n维向量,跟一个n×n的矩阵相乘,再将得出的结果模26。
注意用作加密的矩阵(即密匙)在必须是可逆的,否则就不可能解码。只有矩阵的行列式和26互质,才是可逆的。
产生原因
随着科技的日新月异和人们对信用卡、计算机的依赖性的加强,密码学显得愈来愈重要。密码学是一门关于加密和解密、密文和明文的学科。若将原本的符号代换成另一种符号,即可称之为广义的密码。狭义的密码主要是为了保密,是一种防止窃文者得知内容而设的另一种符号文字,也是一般人所熟知的密码。
使用信用卡、网络账号及密码、电子信箱、电子签名等都需要密码。为了方便记忆,许多人用生日、电话号码、门牌号码记做密码,但是这样安全性较差。
为了使密码更加复杂,更难解密,产生了许多不同形式的密码。密码的函数特性是明文对密码为一对一或一对多的关系,即明文是密码的函数。传统密码中有一种叫移位法,移位法基本型态是加法加密系统C=P+s(mod m)。一般来说,我们以1表示A,2表示B,……,25表示Y,26表示Z,以此类推。由于s=0时相当于未加密,而0≤s≤m-1(s≥m都可用0≤s≤m-1取代),因此,整个系统只有m-1种变化。换言之,只要试过m-1次,机密的信息就会泄漏出去。
由此看来,日常生活中的密码和传统的密码的可靠性较差,我们有必要寻求一种容易将字母的自然频度隐蔽或均匀化,从而有利于统计分析的安全可靠的加密方法。希尔密码能基本满足这一要求。
原理
希尔加密算法的基本思想是,将d个明文字母通过线性变换将它们转换为d个密文字母。解密只要作一次逆变换就可以了,密钥就是变换矩阵本身。[1]
希尔密码是多字母代换密码的一种。多字母代换密码可以利用矩阵变换方便地描述,有时又称为矩阵变换密码。令明文字母表为Z,若采用L个字母为单位进行代换,则多码代换是映射f:Z→Z。若映射是线性的,则f是线性变换,可以用Z上的L×L矩阵K表示。若是满秩的,则变换为一一映射,且存在有逆变换K。将L个字母的数字表示为Z上的L维矢量m,相应的密文矢量c,且mK=c,以K作为解密矩阵,可由c恢复出相应的明文c·K=m。
在军事通讯中,常将字符(信息)与数字对应(为方便起见,我们将字符和数字按原有的顺序对应,事实上这种对应规则是极易被破解的):
abcde…x y z
12345…242526
如信息“NOSLEEPPING”对应着一组编码14,15,19,12,5,5,16,16,9,14,7。但如果按这种方式直接传输出去,则很容易被敌方破译。于是必须采取加密措施,即用一个约定的加密矩阵K乘以原信号B,传输信号为C=KB(加密),收到信号的一方再将信号还原(破译)为B=KC。
③ 希尔排序法特点
希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因希尔于1959年提出而得名。该方法的基本思想是:先将整个待排元素序列分割成若干个子序列,由相隔某个“增量”的元素组成的,分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序,增量足够小时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下,接近最好情况,效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。希尔排序法属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序的方法。希尔排序的特点
希尔排序算法与步长有直接关系。步长依次递减,数组的局部越来越有序了。希尔排序算法思想
因为希尔排序是通过比较相距一定间隔的元素来工作的。所以先要自定义步长的范围。然后选择一个当前元素,依次按照步长来寻找指定步长的元素进行比较,比较选择出最小的元素,如果比当前元素小于指定步长的元素,就进行交换,相反就退出当前的循环查找比较。
④ 希尔排序法原理
希尔排序法(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序的方法。
算法思想简单描述
在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点,
并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为
增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除
多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现
了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中
记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量
对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成
一组,排序完成。
下面的函数是一个希尔排序算法的一个实现,初次取序列的一半为增量,
以后每次减半,直到增量为1。
希尔排序是不稳定的。
⑤ 希尔排序算法
修改如下:
添加了shellsort()函数.
对main()函数,paixu()函数,rangerand()函数作了修改
修改后的程序如下:
//---------------------------------------------------------------------------
#include<stdio.h> //printf,scanf
#include<stdlib.h> //system,srand,rand
#include<time.h>
void main(){
//================================子函数声明及说明=================================//
void head(); //操作提示
void print(long R[],long n);//输出数组中的元素
void insertsort(long R[],long n,long *bj1,long *yd1); //直接插入排序函数
void shellsort(long a[],long n,long *bj5,long *yd5); //Shell排序函数
void Bubblesort(long R[],long n,long *bj2,long *yd2); //冒泡排序函数
void quicksort(long R[], long low, long high,long *bj3,long *yd3); //快速排序函数
void selectsort(long R[],long n,long *bj4,long *yd4); //简单选择排序函数
void paixu(long R1[],long R2[],long R3[],long R4[],long N); //进行排序并计时
void rangerand(long *n); //产生随机数,并排序
int ensure(); //退出确认函数
void knowledge(); //算法相关知识
void help(); //程序使用说明
//==================================声明结束======================================//
head();
long N;
char xz;
int flag=1,c;
while(flag)
{
scanf("%c%*c",&xz);
switch(xz)
{
case '0':
system("cls");
head();
break;
case '1':
rangerand(&N);
head();
break;
case '2':
knowledge();
head();
break;
case '3':
help();
head();
break;
case '4':
c=ensure();
if(c) head();
else flag=c;
break;
default:
printf("\n !提示:请输入一位0~4的数字!\n 请重新输入选择的操作:");
break;
}
}//while循环结束
}
void head(){
//软件使用交互部分
printf(" \n =========================================================\n");
printf(" 排序算法的比较\n ——直接插入、冒泡、快速、简单选择排序\n");
printf(" =========================================================\n");
printf(" 可执行的操作:\n");
printf(" 0.清屏 \n");
printf(" 1.生成随机数并排序 \n");
printf(" 2.相关算法知识 \n");
printf(" 3.使用说明 \n");
printf(" 4.退出\n");
printf(" 请选择(0~4):");
}
void print(long R[],long n){
//输出数组中的元素
for(long i=0;i<n;i++)
{
printf("%7ld",R[i]);
if(i%10==9) printf("\n");
}
printf("\n");
}
//===============四个排序算法核心,排序并记录比较次数============================//
void insertsort(long R[],long n,long *bj1,long *yd1){
//以下为直接插入排序.待排序元素用一个数组R表示,数组有n个元素
for(long i=1;i<n;i++) //i表示插入次数,共进行n-1次插入
{
int temp=R[i]; //把待排序元素赋给temp
long j=i-1;
while((j>=0)&&(temp<R[j]))
{
(*bj1)++;
(*yd1)++;
R[j+1]=R[j];
j--; //顺序比较和移动
}
R[j+1]=temp;
(*bj1)++;
}
}
void Bubblesort(long R[],long n,long *bj2,long *yd2){
//以下为冒泡排序
int flag=1; //当flag为0,则停止排序
for(long i=1;i<n;i++) //i表示趟数,最多n-1趟
{
flag=0; //开始时元素未交换
for(long j=n-1;j>=i;j--)
{
if(R[j]<R[j-1]) //发生逆序
{
int t=R[j];
R[j]=R[j-1];
R[j-1]=t;flag=1; //交换,并标记发生了交换
(*yd2)++;
}
(*bj2)++;
}
if(flag==0) break;
}
}
void quicksort(long R[], long low, long high,long *bj3,long *yd3) {
//以下为快速排序
long i, j, pivot;
if (low < high)
{
pivot=R[low];
i=low;
j=high;
while(i<j)
{
while (i<j && R[j]>=pivot)
{
j--;
(*bj3)++;
}
if(i<j)
{
R[i++]=R[j];
(*yd3)++;
} //将比枢轴记录小的记录移到低端
while (i<j && R[i]<=pivot)
{
i++;
(*bj3)++;
}
if(i<j)
{
R[j--]=R[i]; //将比枢轴记录大的记录移到高端
(*yd3)++;
}
}
R[i]=pivot; //枢轴记录移到最终位置
quicksort(R,low,i-1,bj3,yd3);
quicksort(R,i+1,high,bj3,yd3);
}
}
void selectsort(long R[],long n,long *bj4,long *yd4){
//以下为简单选择排序
long i,j,m;int t;
for(i=0;i<n-1;i++)
{
m=i;
for(j=i+1;j<n;j++)
{
if(R[j]<R[m]) m=j; //用m定位最小值
(*bj4)++;
}
if(m!=i) //逆序,移动
{
t=R[i];
R[i]=R[m];
R[m]=t;
(*yd4)++;
}
}
}
void shellsort(long a[],long n,long *bj5,long *yd5)
{
long i, j, gap, temp;
while (gap * 3 + 1<=n)
{
gap=gap * 3 + 1;
}
while (gap > 0)
{
for ( i = gap; i < n; i++ )
{
j = i - gap;
temp = a[i];
while (( j >= 0 ) && ( a[j] > temp ))
{
a[j + gap] = a[j];
j = j - gap;
(*bj5)++;
(*yd5)++;
}
a[j + gap] = temp;
(*yd5)++;
}
gap = ( gap - 1 ) /3;
}
}
//==================================================================//
void paixu(long R1[],long R2[],long R3[],long R4[],long R5[],long N){
//排序并计时
char tj;
clock_t start, finish;
long t1,t2,t3,t4,t5;
long bj1=0,bj2=0,bj3=0,bj4=0,bj5=0;
long yd1=0,yd2=0,yd3=0,yd4=0,yd5=0;
start = clock(); //记录初始时间
insertsort(R1,N,&bj1,&yd1); //执行算法
finish = clock(); //记录终止时间
t1 = (finish - start) ; //计算算法执行时间
printf("排序结果:\n");
printf(" 直接插入排序:\n");
print(R1,N);
system("pause"); //屏幕显示"请按任意键继续…"
start = clock();
shellsort(R5,N,&bj5,&yd5);
finish = clock();
t5 = (finish - start) ;
printf(" Shell排序:\n");
print(R5,N);
system("pause"); //屏幕显示"请按任意键继续…"
start = clock();
Bubblesort(R2,N,&bj2,&yd2);
finish = clock();
t2 = (finish - start) ;
printf(" 冒泡排序:\n");
print(R2,N);
system("pause");
start = clock();
quicksort(R3,0,N-1,&bj3,&yd3);
finish = clock();
t3 = (finish - start) ;
printf(" 快速排序:\n");
print(R3,N);
system("pause");
start = clock();
selectsort(R4,N,&bj4,&yd4);
finish = clock();
t4 = (finish - start) ;
printf(" 简单选择排序:\n");
print(R4,N);
printf(" 按回车键查看统计信息...\n");
scanf("%*c",&tj);
printf(" 统计:此次共对%ld个随机数进行了排序\n",N);
printf(" 排序用时 比较次数 移动次数 \n");
printf( " 直接插入 排序 |%5d 毫秒|%10ld次|%10ld次|\n", t1,bj1,yd1);
printf( " Shell 排序 |%5d 毫秒|%10ld次|%10ld次|\n", t5,bj5,yd5);
printf( " 冒 泡 排序 |%5d 毫秒|%10ld次|%10ld次|\n", t2,bj2,yd2);
printf( " 快 速 排序 |%5d 毫秒|%10ld次|%10ld次|\n", t3,bj3,yd3);
printf( " 简单选择 排序 |%5d 毫秒|%10ld次|%10ld次|\n\n", t4,bj4,yd4);
system("pause");
}
void rangerand(long *n){
//对随机数进行排序
long m;int pd=1;char px;
long Origine[32767],R1[32767],R2[32767],R3[32767],R4[32767],R5[32768];
printf(" 请输入产生随机数的个数(1~32767): ");
while(pd)
{
scanf("%ld%*c",n);
printf("\n");
if((*n)>0&&(*n)<32768) pd=0;
else printf("提示:请输入1~32767之间的数字!\n请重新输入产生随机数的个数(1~32767): ");
}
srand((unsigned)time(NULL));
for(m=0;m<*n;m++)
{
Origine[m]=rand();//产生随机数
R1[m]=Origine[m];
R2[m]=Origine[m];
R3[m]=Origine[m];
R4[m]=Origine[m];
R5[m]=Origine[m];
}
printf(" 产生的随机数为:\n");
print(Origine,*n); //输出随机数
printf(" 按回车键开始排序...");
scanf("%*c",&px);
paixu(R1,R2,R3,R4,R5,*n);
}
int ensure(){
//确认退出
char qr;
printf(" 确定要退出本程序?(y/n)");
scanf("%c%*c",&qr);
if(qr=='y') return 0;
else return 1;
}
void knowledge(){
//算法的简要说明
printf("\n 直接插入、冒泡、快速、简单选择排序相关知识:\n");
printf(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" 直接插入、冒泡、简单选择排序的平均时间、最坏情况均为O(n^2),\n\
快速排序的平均时间为O(nlogn),最坏时间为O(n^2);\n\
一般待排序数较少时用前三种排序,而随机数较多时,快速排序的优势明显;\n\
这四种排序中只有快速排序是不稳定的。\n\n\n");
system("pause");
}
void help(){
//使用说明
printf("\n 程序说明:\n\
~~~~~~~~~\n\
设计目的:通过对相关参数的比较加深对排序算法的理解;\n\
输入:操作号;待排序随机数的个数;确认操作等\n\
输出:各算法的排序时间、比较次数、移动次数。\n\n\n");
system("pause");
}
//---------------------------------------------------------------------------