Recently in 算法 Category

使用RSA算法进行加密和解密

| 1 Comment | No TrackBacks

一、  生成公钥和私钥

公钥可以对外公开,供其他人加密使用,而把私钥秘密保存用于解密。下面程序产生公钥和私钥,并将他们分别保存在文件中。
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class Skey_RSA{
public static void main(String args[]) throws Exception{
//创建密钥对生成器,指定加密和解密算法为RSA
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
//指定密钥的长度,初始化密钥对生成器
kpg.initialize(1024);
//生成密钥对
KeyPair kp=kpg.genKeyPair();
//获取公钥
PublicKey pbkey=kp.getPublic();
//获取私钥
PrivateKey prkey=kp.getPrivate();

//保存公钥到文件
FileOutputStream f1=new FileOutputStream("Skey_RSA_pub.dat");
ObjectOutputStream b1=new ObjectOutputStream(f1);
     b1.writeObject(pbkey);

//保存私钥到文件
FileOutputStream f2=new FileOutputStream("Skey_RSA_priv.dat");
ObjectOutputStream b2=new ObjectOutputStream(f2);
    b2.writeObject(prkey);
}
}

深度优先搜索和广度优先搜索

| No Comments | No TrackBacks

一、深度优先搜索 
    深度优先搜索就是在搜索树的每一层始终先只扩展一个子节点,不断地向纵深前进直到不能再前进(到达叶子节点或受到深度限制)时,才从当前节点返回到上一级节点,沿另一方向又继续前进。这种方法的搜索树是从树根开始一枝一枝逐渐形成的。

      深度优先搜索亦称为纵向搜索。由于一个有解的问题树可能含有无穷分枝,深度优先搜索如果误入无穷分枝(即深度无限),则不可能找到目标节点。所以,深度优先搜索策略是不完备的。另外,应用此策略得到的解不一定是最佳解(最短路径)。

二、 重排九宫问题游戏
在一个3乘3的九宫中有1-8的8个数及一个空格随机摆放在其中的格子里。如下面左图所示。现在要求实现这样的问题:将该九宫调整为如下图右图所示的形式。调整规则是:每次只能将与空格(上,下或左,右)相临的一个数字平移到空格中。试编程实现。 

PageRank 彻底解说.中文版

| No Comments | No TrackBacks

Base64算法详解和实现

| No Comments | No TrackBacks

我打赌当你见到Base64这个词的时候你会觉得在哪里见过,因为在你能够上网看到这篇文章的时候你已经在后台使用它了。如果您对二进制数有所了解,你就可以开始读它了。

打开一封Email,查看其原始信息(您可以通过收取、导出该邮件用文本编辑器查看)。你会看到类似这样的一个效果:

Date: Thu, 25 Dec 2003 06:33:07 +0800
From: "eSX?!" <snaix@yeah.net'>snaix@yeah.net'>snaix@yeah.net'>snaix@yeah.net>
Reply-To:
snaix@yeah.net'>snaix@yeah.net'>snaix@yeah.net'>snaix@yeah.net
To: "snaix" <snaix@126.com'>snaix@126.com>
Subject:
X-mailer: Foxmail 5.0 beta2 [cn]
Mime-Version: 1.0
Content-Type: text/plain;
charset="gb2312"
Content-Transfer-Encoding: base64

xOO6w6OsU25haVgNCg0KoaGhodXiysfSu7j2QmFzZTY0tcSy4srU08q8/qOhDQoNCkJlc3QgV2lz
aGVzIQ0KIAkJCQkNCqGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaEgICAgICAgICAgICAgICBl
U1g/IQ0KoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoSAgICAgICAgICAgICAgIHNuYWl4QHll
YWgubmV0DQqhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhICAgICAgICAgMjAwMy0x
Mi0yNQ0K

是否看到了“base64”标记?是否看到了标记下面的一行乱码?也许你会恍然大悟,对!这就是Base64编码。

什么是Base64?

按 照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)

为什么要使用Base64?

在设计这个编码的时候,我想设计人员最主要考虑了3个问题:
1.是否加密?
2.加密算法复杂程度和效率
3.如何处理传输?

    加密是肯定的,但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。
基 于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似,MIME协议等用于发送Email的协议解决的是如何收发Email,而并不 是如何安全的收发Email。因此算法的复杂程度要小,效率要高,否则因为发送Email而大量占用资源,路就有点走歪了。

     但是,如果是基于以上两点,那么我们使用最简单的恺撒法即可,为什么Base64看起来要比恺撒法复杂呢?这是因为在Email的传送过程中,由于历史原 因,Email只被允许传送ASCII字符,即一个8位字节的低7位。因此,如果您发送了一封带有非ASCII字符(即字节的最高位是1)的Email通 过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0!很明显,问题就这样产生了!因此,为了能够正常的传送Email,这个问题就必须 考虑!所以,单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。
基于以上的一些主要原因产生了Base64编码。

算法详解

    Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。
具体转化形式间下图:
字符串“张3”
11010101 11000101 00110011

00110101 00011100 00010100 00110011
表1

可以这么考虑:把8位的字节连成一串110101011100010100110011
然后每次顺序选6个出来之后再把这6二进制数前面再添加两个0,就成了一个新的字节。之后再选出6个来,再添加0,依此类推,直到24个二进制数全部被选完。
让我们来看看实际结果:

字符串“张3”
11010101 HEX:D5 11000101 HEX:C5 00110011 HEX:33

00110101 00011100 00010100 00110011
字符’5’ 字符’^\’ 字符’^T’ 字符’3’
十进制53 十进制34 十进制20 十进制51
表2

这样“张3 ”这个字符串就被Base64表示为”5^\^T3”了么?。错!
Base64编码方式并不是单纯利用转化完的内容进行编码。像’^\’字符是控制字符,并不能通过计算机显示出来,在某些场合就不能使用了。Base64有其自身的编码表:

Table 1: The Base64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v (pad) =
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y
表3

这 也是Base64名称的由来,而Base64编码的结果不是根据算法把编码变为高两位是0而低6为代表数据,而是变为了上表的形式,如”A”就有7位, 而”a”就只有6位。表中,编码的编号对应的是得出的新字节的十进制值。因此,从表2可以得到对应的Base64编码:

字符串“张3”
11010101 HEX:D5 11000101 HEX:C5 00110011 HEX:33

00110101 00011100 00010100 00110011
字符’5’ 字符’^\’ 字符’^T’ 字符’3’
十进制53 十进制34 十进制20 十进制51
字符’1’ 字符’i’ 字符’U’ 字符’z’
表4

这样,字符串“张3”经过编码后就成了字符串“1iUz”了。
Base64将3个字节转变为4个字节,因此,编码后的代码量(以字节为单位,下同)约比编码前的代码量多了1/3。之所以说是“约”,是因为如果代码量正好是3的整数倍,那么自然是多了1/3。但如果不是呢?
细心的人可能已经注意到了,在The Base64 Alphabet中的最后一个有一个(pad) =字符。这个字符的目的就是用来处理这个问题的。
当代码量不是3的整数倍时,代码量/3的余数自然就是2或者1。转换的时候,结果不够6位的用0来补上相应的位置,之后再在6位的前面补两个0。转换完空出的结果就用就用“=”来补位。譬如结果若最后余下的为2个字节的“张”:

字符串“张”
11010101 HEX:D5 11000101 HEX:C5

00110101 00011100 00010100
十进制53 十进制34 十进制20 pad
字符’1’ 字符’i’ 字符’U’ 字符’=’
表6

这样,最后的2个字节被整理成了“1iU=”。
同理,若原代码只剩下一个字节,那么将会添加两个“=”。只有这两种情况,所以,Base64的编码最多会在编码结尾有两个“=”
至于将Base64的解码,只是一个简单的编码的逆过程,读者可以自己探讨。我将在文章的最后给出解码算法。

算法实现
其实在算法详解的时候基本上已经说的很清楚了。用于程序上,除去约束判断,大概可以分为如下几步几步:
读取数据3字节用AND取前6位,放入新的变量中右移两位,高两位清0AND取第一个字节的后2位和第二个字节的前4位移位放入新变量中右移两位,清0……依此类推。
解码的类C语言实现的算法:
BYTE LMoveBit(int base, int MoveNum)
{
BYTE result=base;
if(MoveNum==0)return 1;
if(MoveNum==1)return MoveNum;
result=base<<(MoveNum-1);
return result;
}

char base64_alphabet[]=
{'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','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','0','1','2','3','4','5','6','7','8','9','+','/','='};
BYTE Base64Decode(char *base64code, DWORD base64length)
{
char buf[4];
int i,j;
int k;
int l=0;
BYTE temp1[4],temp2;
BYTE *Buffer=new BYTE[base64length*3/4];
DWORD base64a=(base64length/4)-1;
DWORD base64b=0;
for(;base64b<base64a+1;base64b++)
{
for(i=0;i<4;i++)
{
buf[i]=*(base64code+(base64b*4)+i);
for(j=0;j<65;j++)
{
if(buf[i]==base64_alphabet[j])
{
temp1[i]=j;
break;
}
}
}
i--;
for(k=1;k<4;k++)
{
if(temp1[i-(k-1)]==64){m_padnum++; continue;}
temp1[i-(k-1)]=temp1[i-(k-1)]/LMoveBit(2,(k-1)*2);
temp2=temp1[i-k];
temp2=temp2&(LMoveBit(2,k*2)-1);
temp2*=LMoveBit(2,8-(2*k));//move 4
temp1[i-(k-1)]=temp1[i-(k-1)]+temp2;
Buffer[base64b*3+(3-k)]=temp1[i-(k-1)];
}
}
return Buffer;
}

根据这段算法,文章最开始给出的Email内容,可以解码为:
你好,SnaiX

  这是一个Base64的测试邮件!

Best Wishes!
               eSX?!
              
snaix@yeah.net'>snaix@yeah.net'>snaix@yeah.net'>snaix@yeah.net
                  2003-12-25 

动态规划求解最长公共子串问题

| No Comments | No TrackBacks

算法思想

求字符串str1,str2的最长公共子串的长度。

定义二元函数函数f(m,n):分别以str1[m],str2[n]结尾的连续公共子串的长度

而对于f(m+1,n+1) 有以下两种情况

1.str1[m+1] != str2[n+1],则有f(m+1,n+1) =0

2.str1[m+1] == str2[n+1],则有f(m+1,n+1) = f(m,n) + 1

另外f(0,j) = 0(j>=0)

       f(j,0) = 0 (j>=0)

按照上面这个公式,我们用容易写出这个算法的实现

算法实现

     1    int commstr(char *str1, char *str2)

     2    /* 返回str1,str2的最长公共之串长度*/

     3    {

     4           int len1=strlen(str1),len2=strlen(str2),row,col,max=0;

     5           int **pf = new int*[len1+1];//动态分配一个二维数组作为辅助空间

     6           for (row=0; row<len1+1; row++)

     7                  pf[row] = new int[len2+1];

     8   

     9           //数组赋初值

    10           for (row=0; row<len1+1; row++)

    11                  pf[row][0] = 0;

    12           for (col=0; col<len2+1; col++)

    13                  pf[0][col] = 0;

    14   

    15         for (row=1; row<=len1; row++)

    16                  for (col=1;col<=len2; col++)

    17                  {

    18                         if (str1[row-1] == str2[col-1])

    19                         {

    20                                pf[row][col] = pf[row-1][col-1] + 1;

    21                                max = pf[row][col] > max ? pf[row][col] : max;

    22                         }

    23                         else

    24                                pf[row][col] = 0;

    25                  }

    26           //空间回收    

    27           for (row=0; row<len1+1; row++)

    28                  delete[] pf[row];

    29           delete[] pf;

    30   

    31           return max;                  

    32    }

 



程序的输出

  

字符串"blog.csdn.net"和"csdn.blog"求公共子串时的输出结果   

String:

    1. blog.csdn.net

    2. csdn.blog

        c   s   d   n   .   b   l   o   g  

    0   0   0   0   0   0   0   0   0   0  

b   0   0   0   0   0   0   1   0   0   0  

l   0   0   0   0   0   0   0   2   0   0  

o   0   0   0   0   0   0   0   0   3   0  

g   0   0   0   0   0   0   0   0   0   4  

.   0   0   0   0   0   1   0   0   0   0  

c   0   1   0   0   0   0   0   0   0   0  

s   0   0   2   0   0   0   0   0   0   0  

d   0   0   0   3   0   0   0   0   0   0  

n   0   0   0   0   4   0   0   0   0   0  

.   0   0   0   0   0   5   0   0   0   0  

n   0   0   0   0   1   0   0   0   0   0  

e   0   0   0   0   0   0   0   0   0   0  

t   0   0   0   0   0   0   0   0   0   0  


max substr length:5

这是程序的输出结果,请注意红色字体


时间空间复杂度分析

如果用n,m表示两个字符串的长度的话,那么算法的 

时间复杂度为O(n*m),空间复杂度也为O(n*m)

 

 

附:完整的源程序g++编译通过

#include <stdio.h>

#include <string.h>

 

void print_table(char *str1,char *str2,int **pf)

{

       int i,j,row,col;

       row = strlen(str1);

       col = strlen(str2);

       printf("\t\t");

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

              printf("%c\t",str2[i]);

             

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

       {

              for (j=0; j<=col; j++)

              {

                     if (j == 0)

                     {

                            printf("\n");

                            if (i)

                                    printf("%c\t",str1[i-1]);

                            else

                                   printf("\t");

                     }

                     printf("%d\t",pf[i][j]);

              }

       }

}    

                    

int commstr(char *str1, char *str2)

/* 返回str1,str2的最长公共之串长度*/

{

       int len1=strlen(str1),len2=strlen(str2),row,col,max=0;

       int **pf = new int*[len1+1];//动态分配一个二维数组作为辅助空间

       for (row=0; row<len1+1; row++)

              pf[row] = new int[len2+1];

 

       //数组赋初值

       for (row=0; row<len1+1; row++)

              pf[row][0] = 0;

       for (col=0; col<len2+1; col++)

              pf[0][col] = 0;

 

      for (row=1; row<=len1; row++)

              for (col=1;col<=len2; col++)

              {

                     if (str1[row-1] == str2[col-1])

                     {

                            pf[row][col] = pf[row-1][col-1] + 1;

                            max = pf[row][col] > max ? pf[row][col] : max;

                     }

                     else

                            pf[row][col] = 0;

              }

       print_table(str1,str2,pf);

       //空间回收    

       for (row=0; row<len1+1; row++)

              delete[] pf[row];

       delete[] pf;

 

       return max;                  

}

 

int main(int argc,char **argv)

{

       if (argc >= 3)

       {

              printf("String:\n\t1. %s\n\t2. %s\n",argv[1],argv[2]);

           printf("\nmax substr length:%d\n",commstr(argv[1],argv[2]));

       }

       return 0;

      

}

质数填表问题的回溯算法

| No Comments | No TrackBacks

//质数填表问题处理头文件
//质数填表问题的回溯算法
===================================================
 问题描述:
  在9(3*3)个方格的方阵中填入数字1-N(N>=10)内的某9个数字,
 每个方格填一个整数,使所有相邻两个方格内的两个整数之和为质
 数.
 编程思想:
 {
  int m=8,ok=1;
  int n=8;
  do
  {
   if(ok)
   {
    if(m==n)
    {
     输出解;
     调整; 
    }
    else
     扩展; //向前试探
   }
   else
    调整;  //向后回溯
   ok = 检查前m个整数填写的合理性

  }while(m!=0)
 }
*/
#include "stdlib.h"
#define  MAXN 100
#define  N 12
int pnumber[MAXN];
int flag[N+1];
int checkMatrix[][3] = {{-1},{0,-1},{1,-1},{0,-1},{1,3,-1},{2,4,-1},{3,-1},{4,6,-1},{5,7,-1}};
//显示输入填写的数字
void PrintSetPrimeNumber(int array[])
{
 int i,j;
 for(i=0;i<3;i++)
 {
  for(j=0;j<3;j++)
   printf("%4d",array[3*i+j]);
  printf("\n");
 }
 scanf("%*c");
}
//判断自然数是否是质数
int IsPrimeNumber(int m)
{
 int i;
 int primes[] = {2,3,5,7,11,13,17,19,23,29,-1};
 if((m == 1) || (m % 2 == 0)) return(0); /*非质数*/
 for(i=0;primes[i]>0;i++)
  if(m == primes[i])   return(1); /*质数*/
 for(i=3;i*i <=m;)
 {
  if(m % i == 0) return(0); /*非质数*/
  i+=2;
 }
 return(1);  /*质数*/
}
//选择下一个要试探填写的自然数
int SelectNumber(int start)
{
 int i;
 for(i=start;i<=N;i++)
  if(flag[i])  return(i);
 return(0);
}
//查测当前位置的插入数据是否合理
int Check(int pos)
{
 int i,j;
 if(pos < 0)  return(0);
 for(i=0;((j=checkMatrix[pos][i]) >=0);i++)
  if(!IsPrimeNumber(pnumber[pos] + pnumber[j])) return(0);
 return(1);
}

int Extend(int pos)
{
 pnumber[++pos] = SelectNumber(1);
 flag[pnumber[pos]] = 0;
 return(pos);
}

int Change(int pos)
{
 int j;
 while((pos >= 0) && (j = SelectNumber(pnumber[pos]+1)) == 0)
  flag[pnumber[pos--]] = 1;
 if(pos<0) return(-1);
 flag[pnumber[pos]] = 1;
 pnumber[pos] = j;
 flag[j] = 0;
 return(pos);
}

void Find()
{
 int ok = 1,pos = 0;
 pnumber[pos] = 1;
 flag[pnumber[pos]] = 0;
 do
 {
  if(ok)
  {
   if(pos == 8)
   {
    PrintSetPrimeNumber(pnumber); /*输入填写结果*/
    pos = Change(pos); /*调整下一个将填入的自然数*/
   }
   else
    pos = Extend(pos); /*扩展填写范围<试探>*/
  }
  else
   pos = Change(pos); /*调整下一个将填入的自然数<回溯>*/
  ok = Check(pos);

 }while(pos>=0);
}

拓朴排序算法实现

| No Comments | No TrackBacks

关键词 拓朴排序

                   

[0引言]

   有时,可以用图来表示一类事物或集合间的先后依赖关系。比如:集合A 包含于B,就画一条由B指向A的箭头;如果C事件较D事件先发生,则画一条由C指向D的 箭头。建立完这样的依赖关系图,就可以对其依赖的先后关系进行排序。比如:将不依赖于其它的集合先挑选出来,再挑仅依赖于已挑选出集合的集合,就可将所有 集合的内部组成有个完整的结果。再如:把需要先做的事件(即不需要依赖于其它事件即可完成的事件先挑出来)放在序列的前面,由这样决定好的序列,将会使得 全局的完成时间最短,而且工序间的衔接紧密,提高生产效益。

 

[1实现]

    有鉴于以上对拓朴排序的认识,可以得到对依赖关系统图进行拓朴排序的算法思路:

       Clear ansArr to null

       Index=0

       While TopGraph still has node to be sorted

For each node in the TopGraph

If current node is a independent node

                                   Ans[index]=current node

                                   Index<--index+1

                                   Delete current node from the TopGraph

End if

              End for

              If no independent node founded

                            Output “Error”

Break

              End if

       End While

 

   并不是很复杂,是一个每次寻找独立结点的贪心算法。

 

算法的实现采用了基于关系矩阵的图的形式:

void    topologySort(int** inMatrix,int* topSeqArr,int row,int col)

{

       int i,j;

       int* labeledRow;

       int labeledNum;

       unsigned noFirstNode;

      

       if(inMatrix==NULL||topSeqArr==NULL)

       {

              printf("in topologySort ,pass in matrix or topSeqArr is null\n");

              return;

       }

      

       labeledRow=(int*)malloc(sizeof(int)*row);

       if(labeledRow==NULL)

       {

              printf("in topologySort, mem apply failure.\n");

              return;

       }

      

 

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

              labeledRow[i]=0;

 

       labeledNum=0;

       while(labeledNum!=row)

       {

              noFirstNode=1;

             

              for(j=0;j<col;j++)

              {

                     if(!labeledRow[j])   //forget this line.

                     {

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

                            {

                                   if(!labeledRow[i])

                                   {      //j is not a first node

                                          if(i!=j&&inMatrix[i][j]==1)

                                          {

                                                 break; 

                                          }

                                   }

                            }

                    

                            if(i==row)//j is a first node,note it ,del from the graph

                            {

                                   topSeqArr[labeledNum]=j;

                                   labeledRow[j]=1;                

                                   labeledNum++;

                                   noFirstNode=0;

                            }

                     }

              }

 

              if(noFirstNode)

              {

                     printf("can't arrange a toplogy sort.\n");

                     return;

              }

       }

      

       if(DEBUG)

       {

              printf("topsort result:\n");

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

                     printf("%d ",topSeqArr[i]);

              printf("\n");

       }

       free(labeledRow);

}

 

//测试主程序

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

#define SIZE 8

#define DEBUG 1

int staticM[SIZE][SIZE]={{1,1,1,0,0,0,1,0},

                                          {0,1,0,0,0,0,1,0},

                                          {0,0,1,0,0,1,0,0},

                                          {0,1,0,1,1,0,1,0},

                                          {0,0,0,0,1,1,0,1},

                                          {0,0,0,0,0,1,0,0},

                                          {0,0,0,0,0,0,1,0},

                                          {0,0,0,0,0,0,0,1},

                                          };

 

void    topologySort(int** inMatrix,int* topSeqArr,int row,int col);

 

void main()

{

       int** testMatrix;

       int* topSeq;

 

       int len=SIZE;

       int i,j;

 

       testMatrix=(int**)malloc(sizeof(int*)*len);

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

              testMatrix[i]=(int*)malloc(sizeof(int)*len);

 

       topSeq=(int*)malloc(sizeof(int)*len);

 

       if(testMatrix==NULL||topSeq==NULL)

       {

              printf("testMatrix==NULL\n");

              return;

       }

 

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

              for(j=0;j<len;j++)

                     testMatrix[i][j]=staticM[i][j];

      

       topologySort(testMatrix,topSeq,len,l