?算法在同构意义下的分类
简单算法与由简单算法组成的复杂算法,一个算法对应一个函数
算法复杂性常用术语
大O估计:常数复杂性O(1),对数复杂性O(logn),线性复杂性O(n),nlogn复杂性O(nlogn),多项式复杂性O(n^b),指数复杂性O(b^n),b>1,阶乘复杂性O(n!)
算法的最坏情况分析告诉我们算法需要多少次运算就保证给出问题的解答。
复杂性分析:最坏情况分析,平均情况分析
能用具有多项式最坏情况复杂性的算法解决的问题称为易处理的,易处理的问题属于P类
另有:不易处理的,不可解的
能以多项式时间验证解的问题属于NP类
递归程序比迭代版本占用更多的存储空间,花费更多的时间
在设计好的算法中,经常使用的算法有以下43种[很多数值算法和非数值算法都没有列举出来]:
排序1~16
eg15:冒泡排序
procedure bubblesort(a1,,an)
for i:=1 to n-1//i循环n-1次,即使数组下标从0开始,也是i循环n-1次
begin
for j:=1 to n-i//j循环n-i次
if a_j>a_j+1 then 交换a_j与a_j+1
end{a1,,an为升序}
调用mathlib72.dll中函数_pibub@8对n个整数进行冒泡升序排序,函数原型为extern "C" _declspec(dllexport)void __stdcall pibub(int *p,int n);
//5678901234
// the sorted number;0123456789
记向量A=A[i=-1]={8,6,5,4,7,9,2,3,10,1},则冒泡排序的外层i循环有n-1=9次,升序排序过程为:
A[i=0]={6,5,4,7,8,2,3,9,1,10},
A[i=1]={5,4,6,7,2,3,8,1,9,10},
A[i=2]={4,5,6,2,3,7,1,8,9,10},
A[i=3]={4,5,2,3,6,1,7,8,9,10},
……
A[i=8]={1,2,3,4,5,6,7,8,9,10}。
以上人工分析的排序过程与计算机程序调试输出的结果是一致的:
A[i=0]=65478239110
A[i=1]=54672381910
A[i=2]=45623718910
A[i=3]=45236178910
A[i=4]=42351678910
A[i=5]=23415678910
A[i=6]=23145678910
A[i=7]=21345678910
A[i=8]=12345678910
只需要在函数体里加入以下代码:
printf("\n");
printf("A[i=%d]=",i);
for(int k=0;k<n;k++)
printf("%d",p[k]);
调用mathlib72.dll中函数_pcbub@8对n个字符进行冒泡升序排序,函数原型为extern "C" _declspec(dllexport)void __stdcall pcbub(char *p,int n);
调用MATHLIB.DLL中函数_phbub@16对n个字符串进行冒泡升序排序,函数原型为extern "C" _declspec(dllexport)void __stdcall phbub(char **p,int n,int k,int m);
……
static char *p[10]={"main","gou","zhao","lin","wang","zhang","li","zhen","ma","sub"};
phbub(p,10,0,9);//10个字符串排序
……
//main , gou , zhao , lin , wang , zhang , li , zhen , ma , sub ,
//gou , li , lin , ma , main , sub , wang , zhang , zhao , zhen ,
#include<stdio.h>
//#pragma comment(lib,"F:\\实用程序与重要数据备份\\mathlib72.lib")
//extern "C" _declspec(dllexport)void __stdcall pibub(int *p,int n,int ascending);
//bubble_sort
void __stdcall pibub(int *p,int n,int ascending)//ascending=1表示升序,0表示降序
{
int temp,i,j;
if(ascending)
{
for(i=0;i<n-1;i++)
{
for(j=0;j<n-i-1;j++)
if(p[j]>p[j+1])//冒泡升序
{
temp=p[j];
p[j]=p[j+1];
p[j+1]=temp;
}
}
}
else
{
for(i=0;i<n-1;i++)//i循环n-1次
{
for(j=0;j<n-i-1;j++)//不能写成for(j=0;j<n-i;j++),即j循环n-i-1次
if(p[j]<p[j+1])//冒泡降序
{
temp=p[j];
p[j]=p[j+1];
p[j+1]=temp;
}
}
}
}
void __stdcall pisort(int *p,int n,int ascending)//ascending=1表示升序,0表示降序
{
int temp,i,j;
if(ascending)
{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
if(p[i]<p[j])//升序
或for(j=i;j<n;j++)
{
if(p[i]>p[j])//选择升序排序Ⅰ
{
temp=p[i];
p[i]=p[j];
p[j]=temp;
}
}
}
else
{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
if(p[i]>p[j])//降序
或for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
if(p[i]<p[j])//选择降序排序Ⅰ
{
temp=p[i];
p[i]=p[j];
p[j]=temp;
}
}
}
}
int main()
{
int a[10]={0},i;
for(i=0;i<5;i++)
a[i]=i+5;
for(i=5;i<10;i++)
a[i]=i-5;
for(i=0;i<10;i++)
printf("%d",a[i]);
pibub(a,10,1);//10个整数冒泡升序排序
printf("\n the sorted number;");
for(i=0;i<10;i++)
printf("%d",a[i]);
pibub(a,10,0);//10个整数冒泡降序排序
printf("\n the sorted number;");
for(i=0;i<10;i++)
printf("%d",a[i]);
return 0;
}
5678901234
the sorted number;0123456789
the sorted number;9876543210
若内外循环处的代码改为
for(i=0;i<n-1;i++)
{
for(j=i;j<n-1;j++)
{
temp=p[i];
if(p[i]>p[j+1])//选择升序,?不是p[j]>p[j+1]
{
p[i]=p[j+1];
p[j+1]=temp;
}
}就叫做选择升序排序Ⅱ,
以下是选择升序排序的过程:
86547923101
A[i=0]=18657943102[?不是18657942103]
A[i=1]=12867954103
A[i=2]=12387965104
A[i=3]=12348976105
A[i=4]=12345987106
A[i=5]=12345698107
A[i=6]=12345679108
A[i=7]=12345678109
A[i=8]=12345678910
若内外循环处的代码改为
for(i=0;i<n;i++)
{
for(j=0;j<i;j++)
{
if(p[j]>p[i])//插入升序排序
{
temp=p[i];
//插入
for(int k=i;k>=j;k--)p[k]=p[k-1];
p[j]=temp;
}
}
就叫做插入升序排序,
以下是插入升序排序的过程:
86547923101
A[i=0]=86547923101
A[i=1]=68547923101
A[i=2]=56847923101
A[i=3]=45687923101
A[i=4]=45678923101
A[i=5]=45678923101
A[i=6]=24567893101
A[i=7]=23456789101
A[i=8]=23456789101
A[i=9]=12345678910
另外有快速排序(1962年Hoare提出该算法)的几个函数:_piqck@8,_pcqck@8,_phqck@16,函数原型完全与冒泡排序相同。
Void quicksort(char *arr,int startPos,int endPos)//对字符数组进行快速升序排序
{
Char ch=arr[startPos];
Int i= startPos;
Int j=endPos;
While(i<j)
{
While(arr[j]>=ch && i<j)—j;
Arr=arr[j];
While(arr[j]<=ch && i<j)++I;
Arr[j]=arr;
}
Arr=ch;
If(i-1>startPos)quicksort(arr,startpos,i-1);
If(endpos>i+1) quicksort(arr,i+1,endpos);
}
选择17
查找18
顺序查找法适合于存储结构为顺序存储或链接存储的线性表。
int seqsearch(sqlist r,int k,int n)
{
int i=0;
while(i<n&&r[i].key!=k)
i++;
if(i>n)i=-1;
return (i);
}
递归顺序搜索算法
procedure search(i,j,x)
if x=a_i then
location:=i
else if(i=j) then
location:=0
else
search(i+1,j,x)
eg2:线性搜索算法,O(n)
procedure linear_search(x:整数,a_1,a_2,,a_n:递增整数)
i:=1{i是搜索区间的左端点}
while i<=n&&x!=a_i
i:=i+1
if i<=n then location:=i
else location:=0
{location是等于x的项的下标,或在找不到x时为0}
二分查找:又称为对分搜索/折半查找(Binary Search),它要求线性表中结点必须按值递增或递减顺序排列。
二分查找的存储结构仅限于顺序存储结构,且是有序的。
int binsearch(sqlist r,int k,int n)
{
int i,low=0,high=n-1,mid,find=0;
while(low<=high&&!find)
{
mid=(low+high)/2;
if(k<r[mid].key)high=mid-1;
else if(k>r[mid].key)high=mid+1;
else{i=mid;find=1;}
}
if(!find)i=-1;
return (i);
}
递归二叉搜索算法,取整函数记为【x】
procedure binary_search(x,i,j)
m:=【(i+j)/2】
if x=a_m then
location:=m
else if(x<a_m&&i<m) then
binary_search(x,i,m-1)
else if(x>a_m&&j>m) then
binary_search(x,m+1,j)
else location:=0
eg3:对分搜索算法,O(logn)
procedure binary_search(x:整数,a_1,a_2,,a_n:递增整数)
i:=1{i是搜索区间的左端点}
j:=n{j是搜索区间的右端点}
while i<j
begin
m:=【(i+j)/2】
if x>a_m then i:=m+1//搜索限制在序列的下半段
else j:=m
end
if x=a_i then location:=i
else location:=0
{location是等于x的项的下标,或在找不到x时为0}
串匹配19
eg2:[寻找最大重复子串]串匹配是比较两个字符串A和B,若A(模板串长度m=11)是B(主串长度n=19)的一部分,则找出A在字符串B中的起始的位置。问主串B中是否包含模板串A?若是,给出它的位置。
简单模式匹配算法/朴素的模式匹配算法
int simple_match(char *t,char *p)
{
int n=strlen(t),m=strlen(p),i,j,k;//一般n>m
for(j=0;j<n-m;j++)
{
for(i=0;i<m&&t[j+i]==p[i];i++)
if(i==m)return 1;
}
return 0;
}
克努特Knuth-莫里斯Morris-普拉特Pratt算法,KMP模式匹配函数
int KMP_match(char *t,char *p,int next[])
{
int k,j;
if(*t=='\0')return 0;
for(k=j=0;t[k]!='\0';)
while(j!=-1&&p[j]!=t[k])
{j=next[j];k++;j++;
if(p[j]='\0')return k-j;}
return -1;
}
有关数论的算法20
求最大公约数gcd
函数原型为:int __stdcall nlcb(int *a, int n);//求n个数的最大公约数
int f(int n)类型的数学函数
//MYDLL.DLL中计算1+2+……n=n(n+1)/2的Summary函数的原型
typedef int (*SUMMARY)(int);
SUMMARY Summary;
//MYDLL.DLL中计算n!的Factorial函数的原型
typedef int (*FACTORIAL)(int);
FACTORIAL Factorial;
Summary=(SUMMARY)GetProcAddress(ghMathsDLL,"Summary");
Factorial=(FACTORIAL)GetProcAddress(ghMathsDLL,"Factorial");
int nSum=Summary(10);//计算1+2+……n=n(n+1)/2,为55
int nFact=Factorial(10);//计算n!,为3628800
eg4:欧几里得算法
Procedure gcd(a,b:正整数)
x:=a
y:=b//以迭代实现的程序有两个局部变量
while y!=0
begin
r:=x mod y
x:=y
y:=r
end{gcd(a,b)是x}
令a=bq+r,其中a,b,q,r为整数,则gcd(a,b)=gcd(b,r)
计算gcd(a,b)的递归[创建一个栈]算法,没有局部变量
递归程序比迭代版本占用更多的存储空间,花费更多的时间
Procedure gcd(a,b:非负整数且a<b)
if a=0 then gcd(a,b)=b
else gcd(a,b):=gcd(b mod a,a)
模运算22
求解模线性方程23
中国余数定理24
元素的幂25
计算a^n的递归算法
Procedure power(a:非0实数,n:非负整数)
if n=0 then power(a,n)=1
else power(a,n)=a*power(a,n-1)
RSA公钥加密系统26
/*----*----*----*----*----*----*----*----*----*----*----*----*
加密与解密算法模块接口定义(可以参考压缩与解压缩的函数原型,压缩与解压缩本身就可以作为一种加密手段)
*----*----*----*----*----*----*----*----*----*----*----*----*/
///////////////////////////////////////////
// 函数名 : EDcode
// 描述 : 文本的加密与解密
// 返回类型 : char *
// 参数 : char *src 加密或解密的数据
// 参数 : int Key 加密或解密的密钥
'函数返回值:
' 1. CharString为空时返回"1"
' 2. 加密或解密失败返回"0"
' 3. 成功则返回加密或解密后的字符串
加密解密字符串的几个函数原型:
①EDcode加密解密:EDcode(EDcode(sStr,iKey),iKey)=sStr;函数原型为_bstr_t EDcode(_bstr_t CharString,short Key);
②Crypt加密DeCrypt解密:DeCrypt(Crypt(sStr,sKey),sKey)=sStr,且Crypt(sStr,sKey)!=DeCrypt(sStr,sKey),Crypt(Crypt(sStr,sKey),sKey)!=sStr,DeCrypt(DeCrypt(sStr,sKey),sKey)!=sStr;函数原型为_bstr_t crypt(_bstr_t CharStr,_bstr_t psw);_bstr_t decrypt(_bstr_t CharStr,_bstr_t psw);
在VC中测试这几个函数的代码如下:
#include <afxwin.h>
#import "C:\\WINDOWS\\system32\\vbdll0105.dll" no_namespace //自动产生类型库的头文件和实现文件
int main(int argc, char* argv[])
{
int *pret;
printf("调用这个DLL之前,需要先拷贝到系统目录并注册或在程序中动态注册。用#import导入类型库,利用VC提供的智能指针包装类\n");
CoInitialize(NULL);
_vbdllPtr spvbdll = NULL;//在类型库头文件中定义
spvbdll.CreateInstance(__uuidof(vbdll));//在类型库头文件中定义
//_bstr_t EDcode(_bstr_t CharString,short Key);
_bstr_t CharString="Text1";
short Key=52;
_bstr_t retString=spvbdll->EDcode(CharString,Key);
_bstr_t retString2=spvbdll->EDcode(retString,Key);
printf("两个输入参数为:CharString=%s,Key=%d\n",(char *)CharString,Key);
printf("retString=EDcode(CharString,Key)为:%s\n",(char *)retString);
printf("retString2=EDcode(retString,Key)为:%s\n",(char *)retString2);
//_bstr_t crypt(_bstr_t CharStr,_bstr_t psw);
//_bstr_t decrypt(_bstr_t CharStr,_bstr_t psw);
_bstr_t CharStr="Text1";
_bstr_t psw="Text3";
_bstr_t retStr=spvbdll->crypt(CharStr,psw);
_bstr_t retStr2=spvbdll->decrypt(retStr,psw);
printf("两个输入参数为:CharStr=%s,psw=%s\n",(char *)CharStr,(char *)psw);
printf("retStr=crypt(CharStr,psw)为:%s\n",(char *)retStr);
printf("retStr2=decrypt(retStr,psw)为:%s\n",(char *)retStr2);
spvbdll.Release();
CoUninitialize();
getchar();
return 0;
}
程序输出:
调用这个DLL之前,需要先拷贝到系统目录并注册或在程序中动态注册。用#import导入类型库,利用VC提供的智能指针包装类
两个输入参数为:CharString=Text1,Key=52
retString=EDcode(CharString,Key)为:n}wW[
retString2=EDcode(retString,Key)为:Text1
两个输入参数为:CharStr=Text1,psw=Text3
retStr=crypt(CharStr,psw)为:Wd?t8
retStr2=decrypt(retStr,psw)为:Text1
凯撒加密的接口与实现代码:
extern "C" _declspec(dllexport)int __stdcall fkmod26(int x,int k);
extern "C" _declspec(dllexport)void __stdcall ECode(char *str,int k);
Private Declare Function fkmod26 Lib "mathlibEx.dll" Alias "_fkmod26@8" (ByVal x As Long, ByVal k As Long) As Long
Private Declare Sub ECode Lib "mathlibEx.dll" Alias "_ECode@8" (ByVal str As String, ByVal k As Long)
//f(x)=(x+k)mod26,f^(-1)(x)=(x-k)mod26
int __stdcall fkmod26(int x,int k)
{
int ret=(x+k)%26;
while(ret<0){ret=ret+26;}//if(ret<0)ret=ret+26;
while(ret>25){ret=ret-26;}//if(ret>25)ret=ret-26;
return ret;
}
void __stdcall ECode(char *str,int k)
{
int nsize=strlen(str);
for(int i=0;i<nsize;i++)
{
if(str[i]<='z'&&str[i]>='a')
str[i]=fkmod26(str[i]-97,k)+97;
if(str[i]<='Z'&&str[i]>='A')
str[i]=fkmod26(str[i]-65,k)+65;
}
}
素数的测试27
整数的因子分解28
计算几何学29
搜索算法30
图论算法31
邻接矩阵A_1={{0,1,1,0},{0,0,0,1},{1,1,0,1},{1,0,0,0}}表示的有向图G_1=<V,E>,其中顶点集V={v1,v2,v3,v4},有向边集E=<v1,v2>,<v1,v3>,<v2,v4>,<v3,v1>,<v3,v2>,<v3,v4>,<v4,v1>}。
邻接矩阵A_2={{0,1,0,0},{0,0,0,1},{1,1,0,1},{1,0,0,0}}表示的有向图G_2=<V,E>,其中顶点集V={v1,v2,v3,v4},有向边集E=<v1,v2>,<v2,v3>,<v2,v4>,<v3,v1>,<v3,v2>,<v3,v4>,<v4,v1>}。
显然,G_1与G_2是同构的。
编写函数mattolist将一个无向图的邻接矩阵转换成邻接表?
由图的邻接矩阵A求图的可达矩阵P?
//求出无向图G的连通分量个数。
int getnum(AdjList *g)
{
int n=0;,visited[MAXVEX];
for(int i=0;i<MAXVEX;i++)
visited[i]=0;
dfs(g,0);
for(int i=0;i<g->n;i++)
if(visited[i]==0)
{
n++;
dfs(g,i);
}
return n;
}
//求出无向图G的连通分量
void comp(graph g,int visited[])
{
for(int i=0;i<vtxnum;i++)
visited[i+1]=0;
for(int i=0;i<vtxnum;i++)
{
if(!visited[i+1])
{
printf("A connected component is:");
dfs(g,i+1);
}
}
}
1959年,荷兰计算机科学家艾兹格·W·迪科斯彻/迪杰斯特拉(Edsger Wybe Dijkstra,1930.5.11-2002.8.6,1972年第七届图灵奖得主)提出了目前离散数学应用广泛的最短路径算法(SPF,Dijkstra's Shortest Path First Algorithm)。——求无向赋权图中给定两点间的最短路径的Dijkstra算法
1960年,史蒂芬·沃舍尔(Stephen Warshall,1935-2006.12.11)给出了求传递闭包的一个Warshall算法。
1962年,罗伯特·弗洛伊德(Robert W.Floyd,1936.6.8-2001.9.25,1978年第十三届图灵奖得主)与史蒂芬·沃舍尔(Stephen Warshall,1935-2006.12.11)合作发布Floyed-Warshall算法,简称Floyd算法。——求无向赋权图中给定两点间的最短路径的Floyd算法
例:G=<V,E,W>,V={v1,v2,v3,v4,v5,v6,v7},E={(v1,v2),(v1,v3),(v2,v3),(v2,v4),(v2,v5),(v3,v5),(v4,v5),(v4,v6),(v5,v6)},W={W(v1,v2)=1,W(v1,v3)=4,W(v2,v3)=2,W(v2,v4)=7,W(v2,v5)=5,W(v3,v5)=1,W(v4,v5)=3,W(v4,v6)=2,W(v5,v6)=6},简单无向赋权图G中,v1到v6的最短路径为(v1,v2,v3,v5,v4,v6),其长度为9。
二叉树的遍历
约定:二叉树的以下非正式文本记法是根据完全二叉树、满二叉树中的元素编号来的。
记二叉树BT={v1,v2,v3,v4,v5,v6,v7,v10,v11,v14,v15},则中序遍历为v4,v2,v10,v5,v11,v1,v6,v3,v14,v7,v15。
记二叉树BT={v1,v2,v3,v4,v5,v6,v9,v13},则后序遍历为v9,v4,v5,v2,v13,v6,v3,v1。
记二叉树BT={v1,v2,v3,v4,v6,v7,v9,v12,v14,v15},则先序遍历为v1,v2,v4,v9,v3,v6,v12,v7,v14,15。
记二叉树BT={v1,v2,v3,v5,v7,v10,v11,v14},则中序遍历为v2,v10,v5,v11,v1,v3,v14,v7,后序遍历为v10,v11,v5,v2,v14,v7,v3,v1。
图的遍历32
BFS33
DFS34
例1:无向图G=<V,E>中,G的顶点集V(G)={v1,v2,v3,v4,v5,v6,v7,v8},G的边集E(G)={(v1,v3),(v1,v6),(v3,v6),(v2,v4),(v2,v5),(v2,v8),(v4,v7),(v5,v7),(v7,v8)},则
深度优先搜索:v1->v3->v6,v2->v4->v7->v5->v8;
广度优先搜索:v1->v3->v6,v2->v4->v5->v8->v7;
例2:在上例1中添加三条边:(v1,v4),(v6,v7),(v5,v8),得到的无向连通图G=<V,E>中,G的顶点集V(G)={v1,v2,v3,v4,v5,v6,v7,v8},G的边集E(G)={(v1,v3),(v1,v6),(v3,v6),(v2,v4),(v2,v5),(v2,v8),(v4,v7),(v5,v7),(v7,v8),(v1,v4),(v6,v7),(v5,v8)},则
深度优先搜索:v1->v3->v6->v7->v4->v2->v5->v8;
广度优先搜索:v1->v3->v4->v6->v2->v7->v5->v8。
深度优先搜索也称为回溯,由深/宽度优先搜索构造生成树
procedure depth_first_search(G:带n个有序顶点v1,v2,,vn的简单图)
T:=以v1作为根且没有其他顶点的根树
visit(v1)
{T是所需要的树}
procedure visit(v1)
for v的每个邻居w
begin
if w不在T里 then
begin
把顶点w和边{v,w}放到T里
visit(w)
end
end
void dfs(AdjList *adj,int v0,int visited[MAXVEX])//邻接表表示的图,初始顶点编号。
{
struct ArcNode *p;//单链表,依附于顶点的边。最好用edgenode代替ArcNode。
visited[v0]=1;//辅助数组,初态为0(FALSE)
cout<<v0<<"";
p=adj[v0]->firstarc;//取v的边表头指针
while(p!=NULL)
{
if(visited[p->adjvex]==0)//从v未访问过的邻接点出发进行深度优先搜索,找v的下一个邻接点
dfs(adj,p->adjvex);
p=p->nextarc;
}
}
图的连通性35
最小生成树36
哪些连通简单图恰好有一个生成树?树
Kruskal算法37
Prim算法38
1956年,约瑟夫·伯纳德·克鲁斯卡尔(Joseph Bernard Kruskal,1928.1.29-)在研二时提出了产生最小生成树(MST,minimum cost spanning tree)的Kruskal算法。
1957年,罗伯特·克雷·普林/普里姆(Robert Clay Prim,1921-)独立发现了产生最小生成树(MST,minimum cost spanning tree)的Prim算法。
Kruskal算法,1956年
procedure Kruskal(G:带n个顶点的带权连通无向图)//void kruskal(graph g);
T:=空图
for i:=1 to n-1
begin
e:=当添加到T里时不形成简单回路的G里权最小的边
T:=添加e之后的T
end{T是G的最小生成树}
Prim算法,1957
procedure Kruskal(G:带n个顶点的连通无向图)//void prim(int gn[vtxnum][vtxnum],int n);
T:=权最小的边
for i:=1 to n-2
begin
e:=与T里顶点相关联的权最小的边,并且若添加到T里则不形成简单回路
T:=添加e之后的T
end{T是G的最小生成树}
组合数学算法39
最优化算法40
近似算法41
七种主要的算法设计策略(技术)
递归技术1,迭代法,递推法,递归和回归
分治法2,eg4:分而治之算法
[找出伪币]16个伪币中有1个是伪造的
模拟法3
贪心算法4,贪婪法greedy method
贪心算法是在每个步骤都做最优选择的算法。在算法的每个步骤都最优化,并不保证产生全局最优解。
状态空间搜索法5,回溯法back-tracing,穷举搜索法(枚举法、穷举法):确定范围,逐次验证,比较判断,输出结果
随机算法6
动态规划7,分支定界branch and bound,遗传算法,LP,整数规划,模拟退火,哈希技术
算法的设计/实际取决于数据的逻辑结构(集合结构、线性、树状、网络结构)/物理存储结构(逻辑结构的存储镜象)
基本映射模型:sequential,linked,indexed,hashing映射
数据结构DS的物理结构P对应于从DS的数据元素到存储区M(维护着逻辑结构S)的一个映射P:(D,S)->M
DS和他的所有基本操作可以看作一个整体——称之为Module
一个合适的解应是在每列、每行上确实有一个皇后,且一条斜线上也最多只有一个皇后。
8*8矩阵
{
{0,Q,0,0,0,0,0,0},
{0,0,0,Q,0,0,0,0},
{0,0,0,0,0,Q,0,0},
{0,0,0,0,0,0,0,Q},
{0,0,Q,0,0,0,0,0},
{Q,0,0,0,0,0,0,0},
{0,0,0,0,0,0,Q,0},
{0,0,0,0,Q,0,0,0}
}
{
输入棋盘大小值n;m=0;good=0;
do{if(m==n){输出解;改变之,形成下一个候选解;}}else 扩展当前候选解至下一列;else 改变之,形成下一个候选解;
good=检查当前候选解的合理性;}while(m!=0);
}
4*4矩阵僵局1
{
{Q,?,?,?},
{×,×,Q,?},
{×,×,×,×},
{0,0,0,0}
}
4*4矩阵僵局2
{
{Q,?,?,?},
{×,×,×,Q},
{×,Q,×,0},
{×,×,×,×}
}
4*4矩阵解答1
{
{×,Q,?,?},
{×,×,×,Q},
{Q,×,×,×},
{0,×,Q,×}
}
4*4矩阵解答2
{
{0,0,Q,0},
{Q,0,0,0},
{0,0,0,Q},
{0,Q,0,0}
}
找一个/所有由n个字符组成但不含有相同相邻子序列的字符串。
输入n,输出其和等于n的所有不增的正整数和式。
数值分析(Numerical Analysis)中的数值算法42
1.用mathlib52.dll中的_brank@12求给定m*n矩阵的秩。分别用控制台界面和图形界面实现。
#include "stdio.h"
#pragma comment(lib,"mathlib52.lib")
extern "C" _declspec(dllexport)int __stdcall brank(double *a,int m,int n);
//定理:m*n矩阵A(m<=n)及其转置n*m矩阵A^T的秩相等,其中用brank函数计算rank(A)时是正确的,即要求m<=n。
//例如:rank(A={0,0})=0!=1;BRANK0.C给出的示例是错的,rank(a1=A^T)=rank(b1=A)=3!=4
void main()
{
static double a[3][2]={ {2,1},
{6,3},
{4,2},
};
static double b[2][3]={ {2,6,4},
{1,3,2},
};
static double a1[5][4]={ {1.0,2.0,3.0,4.0},
{5.0,6.0,7.0,8.0},
{9.0,10.0,11.0,12.0},
{13.0,14.0,15.0,16.0},
{17.0,18.0,19.0,20.0}};
static double b1[4][5]={ {1.0,5.0,9.0,13.0,17.0},
{2.0,6.0,10.0,14.0,18.0},
{3.0,7.0,11.0,15.0,19.0},
{4.0,8.0,12.0,16.0,20.0}};
printf("\n");
printf("RANK=%d\n",brank(&a[0][0],3,2));//2,NO,应该为1
printf("RANK=%d\n",brank(&b[0][0],2,3));//1,YES
printf("RANK=%d\n",brank(&a1[0][0],5,4));//4,NO,应该为3
printf("RANK=%d\n",brank(&b1[0][0],4,5));//3,YES
printf("\n");
getchar();
}
2.利用mathlib.dll中的_agaus@12(函数原型为typedef int (*FunctionPionter)(double *a,double *b,int n);)可求出给定n元线性方程组的根x_1,x_2,…,x_n。abint是针对整系数的增广矩阵输入的情形,输出可以是实数。4元线性方程组求根的例子:对于增广矩阵{{2368,2471,2568,12671,18471},{1968,2071,12168,2271,17471},{1581,11675,1768,1871,16471},{11161,1254,1397,1490,15471}},用两个函数求出来的根都是{{1.040577e+000},{9.870508e-001},{9.350403e-001},{8.812823e-001}},问题:agaus和abint这两个函数中,哪个计算的快些?
3.利用MATHLIB72.DLL中的函数_lcoss@8可以计算余弦积分Ci(x)=γ+lnx+∫[0->x](cost-1)/tdt=ci(x)=-∫[x->+∞]cost/tdt=Ci(x)-Ci(+∞),Ci(0.00)=-80.0132626,Ci(0.50)=-0.1777841,Ci(1.00)=0.3374039,Ci(1.50)=0.4703563,Ci(1000.00)=0.0008189,Ci(+∞)=0,Ci(0)=-∞=ln(0)+γ。
4.利用MATHLIB72.DLL中的函数_lsinn@8可以计算大写的正弦积分Si(x)=∫[0->x]sint/tdt=si(x)+pi/2,Si(1.00)=0.9460831,Si(0.00)=0.0000000,Si(1000.00)=1.5701391,Si(pi)=1.85193706,Si(2pi)=1.17。
5.利用MATHLIB72.DLL中的伽马函数_lgam1@8验证Γ(0.5)=sqrt(pi),由伽马函数表示的贝塔函数B(0.5,0.5)=pi。
6利用mathlib52.dll中的_cjcbi@24 求实对称矩阵的特征值以及由特征向量作为列向量组成的矩阵V。
7.利用RK4和MATHFUNLIB.DLL中的_Solver1D@24求出一阶常微分方程y'=P(x,y)和y'=P(x)y在初值条件y(x_0)=y_0下的解y(x),程序的输入为P(x,y),x_0,y_0。
8.一元实函与多元实函的函数原型,算法复杂度与所调用的库函数有关。
float area(float a,float b,float c)//根据三角形的三边求面积,调用了MSVCRT.DLL中的fabs和sqrt。如果函数失败,返回值为零
{float t,p;
if(a+b>c&&fabs(a-b)<c)
{p=(a+b+c)/2;
t=sqrt(p*(p-a)*(p-b)*(p-c)); }
return t;
}
9.求n阶方阵A的n阶行列式的值det(A),函数原型为double MDet(short * n,SAFEARRAY * * mtxA);或double MDet2(short n,SAFEARRAY * * mtxA);SAFEARRAY * *对应vb中的二维数组。HRESULT MDet([in, out] short* n, [in, out] SAFEARRAY(double)* mtxA, [out, retval] double*);或double MDet([in, out] short* n,[in, out] SAFEARRAY(double)* mtxA);
以下两个函数的原型是完全一致的,对应vb中的Function f(ByVal n As Integer, mtxA() As Double) As Double,在vc中调用这种函数原型时,调用者要申请安全数组。
double MDet2 (short n,SAFEARRAY * * mtxA );//计算n阶方阵行列式的值
double Sum (short n,SAFEARRAY * * mtxA );//计算n阶方阵各元素的和
'''''''''''
' 模块名:MatrixModule.bas
' 函数名:MDet
' 功能: 求n阶行列式的值
' 参数: n - Integer型变量,方阵的阶数。
' mtxA - Double型二维数组,体积为n x n,存放方阵。
' 返回值:Double型,行列式的值。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//求行列式的值
Function mdet(n As Integer, mtxa() As Double) As Double或Function MDet2(ByVal n As Integer, mtxA() As Double) As Double
……
End Function
Sub main()
Dim mtxa(4, 4) As Double
Dim dbldeta As Double
mtxa(1, 1) = 1: mtxa(1, 2) = 2: mtxa(1, 3) = 3: mtxa(1, 4) = 4
mtxa(2, 1) = 5: mtxa(2, 2) = 6: mtxa(2, 3) = 7: mtxa(2, 4) = 8
mtxa(3, 1) = 9: mtxa(3, 2) = 10: mtxa(3, 3) = 11: mtxa(3, 4) = 12
mtxa(4, 1) = 13: mtxa(4, 2) = 14: mtxa(4, 3) = 15: mtxa(4, 4) = 16
dbldeta = mdet(4, mtxa)
'MsgBox "det(a)=" & dbldeta
Debug.Print "det(a)=" & dbldeta 'det(a)=-7.88957201718231E-31(为0)
End Sub
#include <afxwin.h>
#import "..\\..\\vbdll0105.dll" no_namespace//自动产生类型库的头文件和实现文件
int main(int argc, char* argv[])
{
int *pret,r,i;
printf("调用这个DLL之前,需要先拷贝到系统目录并注册或在程序中动态注册。用#import导入类型库,利用VC提供的智能指针包装类\n");
CoInitialize(NULL);
_vbdllPtr spvbdll = NULL;
spvbdll.CreateInstance(__uuidof(vbdll));
//使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建二维数组
SAFEARRAY* pArray=NULL;
HRESULT hr=SafeArrayAllocDescriptor(2,&pArray);
pArray->rgsabound[0].lLbound=1;//由0改为1,否则调用VB写的COM DLL时运行出错
pArray->rgsabound[0].cElements=4;
pArray->rgsabound[1].lLbound=1;//由0改为1,否则调用VB写的COM DLL时运行出错
pArray->rgsabound[1].cElements=4;
/*
pArray->rgsabound[0].lLbound=0;
pArray->rgsabound[0].cElements=4;
pArray->rgsabound[1].lLbound=0;
pArray->rgsabound[1].cElements=4;
*/
pArray->cbElements=sizeof(double);
//为一个安全数组分配内存
hr=SafeArrayAllocData(pArray);
long lDimension[2];
double m[4][4]={{1,1,3,4},{1,2,3,2},{2,4,1,3},{5,7,1,4}};
double x[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
//为第r行第i列赋值
for(r=0;r<4;++r)
for(i=0;i<4;++i)
{
lDimension[1]=r+1;//第r行
lDimension[0]=i+1;//第i列
//放置数据元素到SafeArray
SafeArrayPutElement(pArray,lDimension,&m[r][i]);//m,x
}
double y[4][4]={0};
for(r=0;r<4;++r)
for(i=0;i<4;++i)
{
//二维SAFEARRAY数组使用的时候下标要注意,这里采用的是列主序的方式,即lDimension[1]代表行,lDimension[0]代表列。
lDimension[1]=r+1;//第r行
lDimension[0]=i+1;//第i列
//读取SafeArray中第r行第i列的数据
SafeArrayGetElement(pArray,lDimension,&y[r][i]);
printf("SafeArray中第r=%d行第i=%d列的数据为%f\n",r,i,y[r][i]);
}
short n=4;
double z=spvbdll->Sum(n,&pArray);//不改变输入的二维数组的值
printf("Sum(m)=%e\n",z);//Sum(m)=4.400000e+001;Sum(x)=1.360000e+002=sum{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}
double det=spvbdll->MDet2(n,&pArray);//可能改变了输入的二维数组的值
printf("det(m)=%e\n",det);//det(m)=4.600000e+001;det(x)=0.000000e+000=det{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}
/*
double ret;
ret=spvbdll->MDet2(4,&mtxA[0][0]);
printf("%lf\n",ret);//-114.000000
*/
SafeArrayDestroy(pArray);
spvbdll.Release();
CoUninitialize();
getchar();
return 0;
}
10.求实系数一元n次方程x^n+a[n-1]x^(n-1)+…+a[0]=0的n个复根xr[k]+xi[k]i,0<=k<n的函数原型:
short CRoot(double *a,int n,double *xr,double *xi);
求实系数一元n次方程a[n]x^n+a[n-1]x^(n-1)+…+a[0]=0的n个复根xr[k]+xi[k]i,0<=k<n的函数原型:
int dqrrt(double a[],int n,double xr[],double xi[],double eps,int jt);//求实系数代数方程全部根的QR方法
或Private Declare Function dqrrt Lib "mathlib37.dll" Alias "_dqrrt@28" (a As Double, ByVal n As Long, xr As Double, xi As Double, ByVal eps As Double, ByVal jt As Long) As Long
一元3次方程1x^3+1x^2+1x+1=0的3个根为:
1: -.999999594237917 + 0j
2: -2.02881041522218E-07 + -1.0000001027837j
3: -2.02881041522218E-07 + 1.0000001027837j
即-1,-i,i。
调用mathlib72.dll中的_ontrt@28求复数z=x+yi=re^(ia)的n个n次根:w_1,w_1(W_n),w_1(W_n)^2,…,w_1(W_n)^(n-1),其中w_1是其中一个根,取w_1=r^(1/n)e^(ia/n),W_n是一个n次本原单位根,可取W_n=e^(i*2pi/n)。函数原型为typedef void(__stdcall *pR)(double x,double y,int n,double *u,double *v);或Private Declare Sub ontrt Lib "mathlib72.dll" Alias "_ontrt@28" (ByVal x As Double, ByVal y As Double, ByVal n As Long, u As Double, v As Double)。
/*
用Cardano公式计算实系数一元三次方程ay^3+by^2+cy+d=0的3个复根,其中y=x-D=x-b/(3a),x^3+px+q=0,p=B-(A^2)/3=c/a-(b^2)/(3a^2),q=2A^3/27-AB/3+C=(2b^3)/(27a^3)-(bc)/(3a^2)+(d/a),x1=a^(1/3)+b^(1/3)=ar+aii+br+bii,ar=u,br=v。
如果delta>=0,a,b是实数,ar,br是实数,ai,bi为0。如果delta>=0,x_1为实根,则x_2,3=[-x_1±sqrt(-3(x_1)^2-4p)]/2。
特殊地,delta=0=>delta2=0,y1=(-4q)^(1/3)-D,y2=y3=(q/2)^(1/3)-D,u=v=(*ar)=(*br)=(-q/2)^(1/3),即delta=0是有3个实根充分不必要条件。一元三次方程经过适当的变量替换后化为如下方程:x^3+px+q=0,三个根是[x1,wx1,w^2x1]:x1=T(P+PQ)+T(P-PQ),x2=wT(P+PQ)+(w^2)T(P-PQ),x3=(w^2)T(P+PQ)+wT(P-PQ),其中w是3次单位根,w≠1,w1=(-1+根号3i)/2,w2=(-1-根号3i)/2,则w1^2=w2,w2^2=w1。令w=w1,则x2=w1T(P+PQ)+w2T(P-PQ),x3=w2T(P+PQ)+w1T(P-PQ);令w=w2,则x3=w1T(P+PQ)+w2T(P-PQ),x2=w2T(P+PQ)+w1T(P-PQ)。无论w是哪个本原根,x的另外两个根都是wT(P+PQ)+(w^2)T(P-PQ),(w^2)T(P+PQ)+wT(P-PQ)。
以上x1,x2,x3中至少有一实根,T(P+PQ)+T(P-PQ)=a^(1/3)+b^(1/3),取delta=q^2/4+p^3/27,a=-q/2+sqrt(delta),b=-q/2+sqrt(delta),取w=w1=(-1+sqrt(3)i)/2。
如果delta<0,a,b是共轭虚数,则a^(1/3)=z,wz,w^2z,取z=|a|e^(i(arga)/3);b^(1/3)=~z,w~z,w^2~z,取~z=|a|e^(i(-arga)/3)。
则x1=a^(1/3)+b^(1/3),x2,x3可表达为:(1)x1=z+~z,x2=wz+w~z,x3=w^2z+w^2~z;(2)x1=z+w~z,x2=wz+w^2~z,x3=w^2z+~z;……;(9)x1=w^2z+w^2~z,x2=z+~z,x3=wz+w~z(是表达1的一个置换);
?表达2不是表达1的一个置换,其中z=re+iim:
(1)x1=z+~z=2re,x2=wz+w~z=2rew,x3=w^2z+w^2~z=2rew^2,
(2)x1=z+w~z=(1+w)re+(1-w)im,x2=wz+w^2~z=-re+(2w+1)im,x3=w^2z+~z=-wre-(w+2)im;
单位的一次根有一个:1。
单位的二次根有两个:+1和-1,只有-1是本原根。
单位的三次根是{1,(-1+根号3i)/2,(-1-根号3i)/2}其中i复数单位;除1外都是本原根。
单位的四次根是{1,+i,-1,-i}其中+i和-i是本原根。
*/
//有问题已修正
void __stdcall Cardano(double a,double b,double c,double d,double *real_y1,double *real_y2,double *real_y3,double *imag_y1,double *imag_y2,double *imag_y3)
{
double p,q,D,u,v,g,delta,fai;
double delta2;
p=(3.0*a*c-b*b)/(3*a*a);//p=B-(A^2)/3=c/a-(b^2)/(3a^2)
q=(2.0*pow(b,3.0)-9*a*b*c+27.0*a*a*d)/(27.0*pow(a,3.0));//q=2A^3/27-AB/3+C=(2b^3)/(27a^3)-(bc)/(3a^2)+(d/a)
D=b/(3.0*a);
delta=pow(q/2.0,2.0)+pow(p/3.0,3.0);//取delta=q^2/4+p^3/27=q*q/2+p*p*p/27
if(delta>=0)
{
g=sqrt(delta);//
if(-q/2.0+g<0)//负数a开立方
u=-pow(fabs(-q/2.0+g),1.0/3.0);
else//正数a开立方
u=pow((-q/2.0+g),1.0/3.0);
if(-q/2.0-g<0)//负数b开立方
v=-pow(fabs(-q/2.0-g),1.0/3.0);
else//正数b开立方
v=pow((-q/2.0-g),1.0/3.0);//
if(delta==0)//delta=0是有3个实根的充分不必要条件
{
//delta=0=>delta2=0,*real_y1=(-4q)^(1/3)-D=2·(-q/2)^(1/3)-D,*real_y2=*real_y3=(q/2)^(1/3)-D
*real_y1=u+v-D;*imag_y1=0;//实根y1,x1
*real_y2=-(u+v)/2-D;*imag_y2=0;//实根y2,x2=-x1/2
*real_y3=-(u+v)/2-D;*imag_y3=0;//实根y3,x3=-x1/2
printf("delta=0,有3个实根,其中y_2和y_3是重根\n");
}
else//delta>0,有3个相异实根或1实2虚
{
*real_y1=u+v-D;*imag_y1=0;
delta2=-3*(u+v)*(u+v)-4*p;
if(delta2>=0)//4*p<=-3*(u+v)*(u+v)
{
*real_y2=-(u+v)/2+sqrt(delta2)/2-D;*imag_y2=0;
*real_y3=-(u+v)/2-sqrt(delta2)/2-D;*imag_y3=0;
}
else
{
*real_y2=-(u+v)/2-D;*imag_y2=sqrt(-delta2)/2;
*real_y3=-(u+v)/2-D;*imag_y3=-sqrt(-delta2)/2;
}
printf("delta>0,有3个相异实根或1实2虚\n");
//*real_y1=u+v-D;*imag_y1=0;
//*real_y2=-(u+v)/2-D;*imag_y2=sqrt(3.0)*(u-v)/2;
//*real_y3=-(u+v)/2-D;*imag_y3=-sqrt(3.0)*(u-v)/2;
}
}
else//delta<0,三个相异的根
{
fai=acos((-q/2)/(sqrt(pow(fabs(p),3)/27)));
*real_y1=2*sqrt(fabs(p)/3.0)*cos(fai/3.0)-D;
*real_y2=-2*sqrt(fabs(p)/3.0)*cos((fai+3.1415926)/3.0)-D;
*real_y3=-2*sqrt(fabs(p)/3.0)*cos((fai-3.1415926)/3.0)-D;
*imag_y1=0;
*imag_y2=0;
*imag_y3=0;
printf("delta<0,有三个相异的根\n");
}
}
11.复数计算的函数原型
struct complex cmu1(struct complex * cpxz1,struct complex * cpxz2 );
struct complex ccpow(struct complex * cpxz,struct complex * cpxn,short * n );
struct complex cpow(struct complex * cpxz,double * w );
struct complex cln(struct complex * cpxz);
复数除法的函数原型为:typedef void(__stdcall *pR)(double a,double b,double c,double d,double *e,double *f);
或Private Declare Sub ocdiv Lib "mathlib72.dll" Alias "_ocdiv@40" (ByVal a As Double, ByVal b As Double, ByVal c As Double, ByVal d As Double, e As Double, f As Double)
复数求模的函数原型为:typedef double(_stdcall *pR)(struct Complex *);
或Private Declare Function CAbs Lib "APICOMPLEX.DLL" Alias "_CAbs@4" (cpxZ As complex) As Double '自定义标准DLL
'Private Declare Function CAbs Lib "MSVCRT.DLL" Alias "_cabs" (cpxZ As complex) As Double 'DLL调用约定错误
求正弦的函数原型为typedef void(__stdcall *pR)(double x,double y,double *u,double *v);
或Private Declare Sub ocsin Lib "mathlib72.dll" Alias "_ocsin@24" (ByVal x As Double, ByVal y As Double, u As Double, v As Double)
求余弦的函数原型为typedef void(__stdcall *pR)(double x,double y,double *u,double *v);或COMPLEX __stdcall CCos(COMPLEX *pz);
或Private Declare Sub occos Lib "mathlib72.dll" Alias "_occos@24" (ByVal x As Double, ByVal y As Double, u As Double, v As Double)
或Private Declare Function CCos Lib "APICOMPLEX.DLL" Alias "_CCos@4" (cpxZ As complex) As complex
Private Function cmu1(cpxz1 As complex, cpxz2 As complex) As complex'复数乘法
Private Function ccpow(cpxz As complex, cpxn As complex, n As Integer) As complex'复数次幂
Private Function cpow(cpxz As complex, w As Double) As complex'实数次幂
Private Function cln(cpxz As complex) As complex'复数对数
//在VC程序中动态注册与取消注册自定义COM并利用智能指针使用自定义COM
#include <afxwin.h>
#import "..\\..\\vbdll0105.dll" no_namespace//自动产生类型库的头文件和实现文件
//在程序中注册某个com
BOOL RegisterDLL(const char * dllname)
{
typedef (WINAPI * REGISTER_FUNC) (void);
REGISTER_FUNC MyFunc = NULL;
HMODULE hModule = ::LoadLibrary(dllname);
if (hModule)
{
MyFunc = (REGISTER_FUNC) GetProcAddress(hModule, "DllRegisterServer");
BOOL pass = (MyFunc != NULL);
if (pass)
{
MyFunc();
}
::FreeLibrary(hModule);
return pass;
}
return FALSE;
}
//在程序中取消注册某个com
BOOL UnRegisterDLL(const char * dllname)
{
typedef (WINAPI * REGISTER_FUNC) (void);
REGISTER_FUNC MyFunc = NULL;
HMODULE hModule = ::LoadLibrary(dllname);
if (hModule)
{
MyFunc = (REGISTER_FUNC) GetProcAddress(hModule, "DllUnregisterServer");
BOOL pass = (MyFunc != NULL);
if (pass)
{
MyFunc();
}
::FreeLibrary(hModule);
return pass;
}
return FALSE;
}
int main(int argc, char* argv[])
{
int *pret;
printf("调用这个DLL之前,需要先拷贝到系统目录并注册或在程序中动态注册。用#import导入类型库,利用VC提供的智能指针包装类\n");
RegisterDLL(".\\vbdll.dll");
CoInitialize(NULL);
_vbdllPtr spvbdll = NULL;
spvbdll.CreateInstance(__uuidof(vbdll));
complex a={3,2};
printf("复数3+2i的自然对数为:%lf,%lf\n",spvbdll->cln(&a).x,spvbdll->cln(&a).y);//复数3+2i的自然对数为:1.282475,0.588003
//复数3+2j的自然对数为:1.28247467873085+.588002603547568j
spvbdll.Release();
CoUninitialize();
UnRegisterDLL(".\\vbdll.dll");
getchar();
return 0;
}
12.
调用_DFT1@24利用定义计算任意点实序列的一维DFT,函数原型为typedef int(__stdcall *pDFT1)(double *pr,double *pi,double *fr,double *fi,int N,int inv);//inv=1表示DFT=~F_n,inv=-1表示IDFT=F_n/n,其中对称阵F_n=(F_n)^T={V_0,V_1,…,V_(n-1)},V_0,V_1,…,V_(n-1)是正交的,且V_0=(1,1,…,1),V_1=(1,W_n,…,(W_n)^(n-1)),V_(n-1)=(1,(W_n)^(n-1),…,(W_n)^[(n-1)(n-1)])。
著名的快速傅里叶变换算法(1965)的函数原型为:typedef void(__stdcall *pDFT1)(double *pr,double *pi,int n,int k,double *fr,double *fi,int l,int il);//kkfft与DFT1的计算结果是一致的,也是~F_n表示正变换DFT,F_n/n表示逆变换IDFT。pr存放变换输入序列的实部,返回变换的模(度);pi存放变换输入序列的虚部,返回变换的幅角;n=2^k,int k=(int)(log((double)n)/log((double)2)),例如n=64,k=6;fr返回变换输出序列的实部;fi返回变换输出序列的虚部;l=0表示计算DFT,l=1表示计算IDFT;il=0表示变换后的输入序列不被覆盖,il=1表示变换后的输入序列被覆盖(以便返回模与幅角的数据),即要计算DFT与IDFT的模与幅角。测试用例:f(t)=e^(-0.1(t+0.5)),采样波形p(t)=e^(-t),t>=0,取n=64,k=6,周期T=6.4,步长h=T/n=0.1,采样序列为p_i=p((i+0.5)h),i=0,1,…,63。
请输入N:4
请输入复4维矢量的实部:1
2
3
4
经过DFT和IDFT后的复4维矢量为,这里取DFT=~F_n,IDFT=F_n/n:
10.000000
-2.000000
-2.000000
-2.000000
0.000000
2.000000
-0.000000
-2.000000
1.000000
2.000000
3.000000
4.000000
-0.000000
0.000000
-0.000000
-0.000000
例如:n=4时,F_4={{1,1,1,1},{1,i,-1,-i},{1,-1,1,-1},{1,-i,-1,i}},~F_4={{1,1,1,1},{1,-i,-1,i},{1,-1,1,-1},{1,i,-1,-i}},DFT{{1},{2},{3},{4}}=~F_4{{1},{2},{3},{4}}={{10},{-2+2i},{-2},{-2-2i}},DFT{{3},{4},{1},{2}}=~F_4{{3},{4},{1},{2}}={{10},{2-2i},{-2},{2+2i}}。
DFT(e_1)={1,1,1,1},DFT(e_2)={1,-i,-1,i},DFT(e_3)={1,-1,1,-1},DFT(e_4)={1,i,-1,-i}。
一维光学DFT(FFT1)与一维DFT的关系:FFT1{{1},{2},{3},{4}}={{-1},{1+i},{5},{1-i}},FFT1{{3},{4},{1},{2}}={{-1},{-1-i},{5},{-1+i}}。
FFT1(e_1)={1/2,-1/2,1/2,-1/2},FFT1(e_2)={-1/2,-i/2,1/2,i/2},FFT1(e_3)={1/2,1/2,1/2,1/2},FFT1(e_4)={-1/2,i/2,1/2,-i/2}。记DFT=(DFT(e_1),DFT(e_2),DFT(e_3),DFT(e_4)),则FFT1=(FFT1(e_1),FFT1(e_2),FFT1(e_3),FFT1(e_4))=(DFT(e_3)/2,-DFT(e_4)/2,DFT(e_1)/2,-DFT(e_2)/2)={{1/2,-1/2,1/2,-1/2},{-1/2,-i/2,1/2,i/2},{1/2,1/2,1/2,1/2},{-1/2,i/2,1/2,-i/2}}。
调用_FFT1@16进行一维光学DFT的计算,函数原型为typedef int(*pFFT1)(float a_rl[], float a_im[], int ex, int inv);pFFT1 FFT1;调用者需申请字节大小为4N*2的两块堆内存,用以存放输入输出的实部数据和虚部数据——N=2^K个float型浮点数实部和N=2^K个float型浮点数虚部(同址运算)。
利用图形用户界面输入2N个实数,输出2N个实数
float p1[N],p2[N];//float *p1,*p2;
//p1=(float *)malloc(N*sizeof(float));
//p2=(float *)malloc(N*sizeof(float));
CString rString;
m_Editi.GetWindowText(rString);
*(p1+i)=(float)atof((LPCTSTR)rString);
m_Editj.GetWindowText(rString);
*(p2+j)=(float)atof((LPCTSTR)rString);
int a=FFT1(p1,p2,K,1);//正变换
int a=FFT1(p1,p2,K,-1);//逆变换
CString sResult;
sResult.Format("实部=%f,%f,%f,%f;虚部=%f,%f,%f,%f",*(p1),*(p1+1),*(p1+2),*(p1+3),*(p2),*(p2+1),*(p2+2),*(p2+3));
AfxMessageBox(sResult);
//实部
sResult.Format("%f",*(p1+i));
m_Editi.SetWindowText((LPCTSTR)sResult);
//虚部
sResult.Format("%f",*(p2+j));
m_Editj.SetWindowText((LPCTSTR)sResult);
C++风格的原型
#include <complex>
using namespace std;
VOID FFT(complex<double> *TD, complex<double> *FD,int r);//采用蝶形算法进行FFT,TD代表实域点
VOID IFFT(complex<double> *FD, complex<double> *TD,int r);//求频域点FD的共轭,调用FFT,再求时域点的共轭得到TD的最终输出,申请了堆内存X存放count=1<<r个complex<double>对象
BOOL Fourier(complex<double> *TD,complex<double> *newFD,LONG lWidth,LONG lHeight);//计算图像每行的字节数long lLineBytes=(((lWidth*8)+31)/32*4);调用FFT对y方向进行FFT,调用FFT对x方向进行FFT,申请了堆内存FD存放w*h个complex<double>对象
void FFT(complex<double> * TD, complex<double> * FD, int r)
{
double angle=-i*PI*2/count;
complex<double> *W,*X1,*X2,*X;//需要申请的堆内存
long count=1<<r;
W=new complex<double>[count/2];//计算加权系数W[i]=complex<double>(cos(angle),sin(angle));
X1=new complex<double>[count];//将时域点写入X1,memcpy(X1, TD, sizeof(complex<double>)*count);
X2=new complex<double>[count];
……
delete W;
delete X1;
delete X2;
}
算法集成在CDib类中去:
#include<complex>
using namespace std;
class CDib
{
public:
void DrawIFFTDib(CDC *pDC,int x,int y);//用SetPixel把pIFFTData中数据绘制出来
void BmpIFFT();//调用Fourier后,数据保存至pIFFTData
void DrawFFTDib(CDC *pDC,int x,int y);//用SetPixel把pFFTData中数据绘制出来
BOOL BmpFFT();//调用Fourier后,数据保存至pFFTData。要求所导入的图像宽度和长度相等,即m_pBIH->biWidth==m_pBIH->biHeight。
CDib();
~CDib();
BITMAPINFOHEADER *m_pBIH;
BYTE *m_pDibBits;//保存了Fourier的实部输入数据,即width*width=m_pBIH->biWidth=m_pBIH->biHeight个complex<double>对象。
complex<double> *pFFTData;//傅立叶变换后数据
complex<double> *pIFFTData;//逆傅立叶变换后数据
BOOL Load(const char * );
BOOL Save(const char * );
BOOL Draw(CDC *, int nX = 0, int nY = 0, int nWidth = -1, int nHeight = -1 );
BOOL SetPalette(CDC * );
private:
CPalette m_Palette;
unsigned char *m_pDib;
DWORD m_dwDibSize;
RGBQUAD *m_pPalette;
int m_nPaletteEntries;
};
13.解一阶ODEy'=f(x,y)的数值算法
/*
RK4(比Solver1D要一般)计算泛函:输入:初值X=t,y,函数f(x,y);输出一阶常微分方程y'=f(x,y)的特解y(x)在x处的值(这里步长h=1e-2)
Solver1D计算泛函:输入:初值X=t,y,函数f(x);输出一阶常微分方程y'=f(y)的特解y(x)的采样值(这里步长dt=0.1)
Solver1D不针对的常微分方程:y'=-0.05x=f(x)=>y=-0.025x^2+c,满足初值条件x=0,y=1,c=1的解为y=-0.025x^2+1
Solver1D针对的常微分方程:y'=-0.05=f(y)=>y=-0.05x+c,满足初值条件x=0,y=1,c=1的解为y=-0.05x+1
Solver1D针对的常微分方程:y'=-0.05y=f(y)=>y=ce^(-0.05x),满足初值条件x=0,y=1,c=1的解为y=e^(-0.05x)
X=t,y,y理论计算值
0 1,1
0.1 0.995012,.995012479192682
0.2 0.99005,.990049833749168
0.3 0.985112
0.4 0.980199
0.5 0.97531
0.6 0.970446
0.7 0.965606
0.8 0.96079
0.9 0.955998
1 0.95123
1.1 0.946485
1.2 0.941765
1.3 0.937068
1.4 0.932394
1.5 0.927744
1.6 0.923117
1.7 0.918513
1.8 0.913932
1.9 0.909373
2 0.904838
2.1 0.900325
2.2 0.895835
2.3 0.891367
2.4 0.886921
2.5 0.882497
2.6 0.878096
2.7 0.873716
2.8 0.869359
2.9 0.865023
3 0.860709
3.1 0.856416
3.2 0.852144
3.3 0.847894
3.4 0.843665
3.5 0.839458
3.6 0.835271
3.7 0.831105
3.8 0.82696
3.9 0.822835
4 0.818731
4.1 0.814648
4.2 0.810585
4.3 0.806542
4.4 0.80252
4.5 0.798517
4.6 0.794534
4.7 0.790572
4.8 0.786629
4.9 0.782705
5 0.778802,.778800783071405
*/
#include "stdafx.h"
#include <stdio.h>
#include <math.h>
extern "C" _declspec(dllexport)double __stdcall RK4(double(__stdcall *f)(double x, double y), double x0, double y0, double xn, double h);
#pragma comment(lib,"mathlib37.lib")//mathlib37.dll中的_RK4@36
/*y'=f(x,y)*/
double __stdcall f(double x, double y)
{
//return -0.05*y;//同Solver1D的f(y)=-0.05*y的情形
return -0.05*exp(-0.05*x);
}
int main()
{
//y(0)=1,y(2)=0.904837418035960,y(1)=0.951229424500714,y(5)=0.778800783071406
double ret = RK4(f, 0, 1.0, 5.0, 1e-2);
printf("%.15f\n", ret);//y(5.0)=0.778800783071405
for(int i=0;i<=5;i++)
printf("%.15f\n",exp(-0.05*i));
return 0;
}
利用LUA脚本后的运行结果和代码为:
--fun.lua
function fsxy(x,y)
return -0.05*math.exp(-0.05*x)
end
调用RK4(比Solver1D要一般)计算泛函——即输出一阶常微分方程y'=f(x,y)的特解y(x)在
x处的值,f(x,y)在LUA脚本里面修改,请输入y(x)的初值x0、y0,xn以及包含f(x,y)的LUA脚
本文件名,这里步长h=1e-2:
0
1
5
fun.lua
RK4fun(x0,y0,xn,luafile)=7.788008e-001
continue with pressing 'y':y
调用RK4(比Solver1D要一般)计算泛函——即输出一阶常微分方程y'=f(x,y)的特解y(x)在
x处的值,f(x,y)在LUA脚本里面修改,请输入y(x)的初值x0、y0,xn以及包含f(x,y)的LUA脚
本文件名,这里步长h=1e-2:
0
1
2
fun.lua
RK4fun(x0,y0,xn,luafile)=9.048374e-001
#include "stdafx.h"
#include <stdio.h>
//以下函数原型避免了函数指针,将泛函的自变量二元实函f(x,y)写在LUA脚本里,在VB/VC中均可以调用
extern "C" _declspec(dllexport)double __stdcall RK4fun(double x0, double y0, double xn,const char *luafile);
#pragma comment(lib,"..\\mathlualib.lib")//mathlualib.dll中的_RK4fun@28
int main()
{
//y(0)=1,y(2)=0.904837418035960,y(1)=0.951229424500714,y(5)=0.778800783071406
/*
double ret = RK4(f, 0, 1.0, 5.0, 1e-2);
printf("%.15f\n", ret);//y(5.0)=0.778800783071405
for(int i=0;i<=5;i++)
printf("%.15f\n",exp(-0.05*i));
*/
for(;;)
{
double x0,y0,xn;
char sz[100];
printf("调用RK4(比Solver1D要一般)计算泛函——即输出一阶常微分方程y'=f(x,y)的特解y(x)在x处的值,f(x,y)在LUA脚本里面修改,请输入y(x)的初值x0、y0,xn以及包含f(x,y)的LUA脚本文件名,这里步长h=1e-2:\n");
fflush(stdin);//add
scanf("%lf%lf%lf%s",&x0,&y0,&xn,sz);
fflush(stdin);//add
//printf("RK4(luafsxy,x0,y0,xn,h)=%e\n",t);
printf("RK4fun(x0,y0,xn,luafile)=%e\n",RK4fun(x0,y0,xn,sz));
printf("continue with pressing 'y':");
if(getchar()!='y')
break;
}
return 0;
}
常用算法程序集(C语言描述第三版 徐士良含盘)
本书针对工程中常用的行之有效的算法而编写,其主要内容包括多项式的计算、复数运算、随机数的产生 、矩阵特征值与特征向量的计算、线性代数方程组的求解、非线性方程与方程组的求解、插值与逼近、数值积分、常微分方程组的求解、数学变换与滤波、特殊函数的计算、排序和查找。
书中所有的算法均用C语言描述,并存放在一张光盘上。
本书可供广大科研人员、工程技术人员以及管理工作者阅读使用,也可作为高等院校师生的参考书。
目录介绍
第1章 多项式的计算
1.1 一维多项式求值
1.2 一维多项式多组求值
1.3 二维多项式求值
1.4 复系数多项式求值
1.5 多项式相乘
1.6 复系数多项式相乘
1.7 多项式相除
1.8 复系数多项式相除
第2章 复数运算
2.1 复数乘法
2.2 负数除法
2.3 复数乘幂
2.4 复数的n次方根
2.5 复数指数
2.6 复数对数
2.7 复数正弦
2.8 复数余弦
第3章 随机数的产生
3.1 产生0到1之间均匀分布的一个随机数
3.2 产生0到1之间均匀分布的随机数序列
3.3 产生任意区间内均匀分布的一个随机整数
3.4 产生任意区间内均匀分布的随机整数序列
3.5 产生任意均值与方差的正态分布的一个随机数
3.6 产生任意均值与方差的正态分布的随机数序列
第4章 矩阵运算
4.1 实矩阵相乘
4.2 复矩阵相乘
4.3 一般实矩阵求逆
4.4 一般复矩阵求逆
4.5 对称正定矩阵的求逆
4.6 托伯利兹矩阵求逆的特兰持方法
4.7 求一般行列式的值
4.8 求矩阵的值
4.9 对称正定矩阵的乔里斯基分解与列式求值
4.10 矩阵的三角分解
4.11 一般实矩阵的QR分解
4.12 一般实矩阵的奇异值分解
4.13 求广义逆的奇异值分解法
第5章 矩阵特征值与特征向量的计算
5.1 约化对称矩阵为对称三对角阵的豪斯荷尔德变换法
5.2 求对称三对角阵的全部特征值与特征向量
5.3 约化一般实矩阵为赫申伯格矩阵的初等相似变换法
5.4 求赫身伯格矩阵全部特征的QR方法
5.5 求实对称矩阵特征值与特征向量的雅可比法
5.6 求实对称矩阵特征值与特征向量的雅可比过关法
第6章 线性代数方程组的求解
6.1 求解实系数方程组的全选主元高斯消去法//函数原型为typedef int (*FunctionPionter)(double *a,double *b,int n); 方程组的阶数n,a存放方程组的系数矩阵,返回时将被破坏,b存放方程组右端的常数向量,返回方程组的解向量,若返回的标志不为0,则正常返回
6.2 求解实系数方程组的全选主元高斯-约当消去法
6.3 求解复系数方程组的全选主元高斯消去法
6.4 求解复系数方程组的全选主元高斯-约当消去法
6.5 求解三对角线方程组的追赶法
6.6 求解一般带型方程组
6.7 求解对称方程组的分解法
6.8 求解对称正定方程组的平方根法
6.9 求解大型系数方程组
6.10 求解托伯利兹方程组的列文逊方法
6.11 高斯-塞德尔失代法
6.12 求解对称正定方程组的共岿梯度法
6.13 求解线性最小二乘文体的豪斯伯尔德变换法
6.14 求解线性最小二乘问题的广义逆法
6.15 求解病态方程组
第7章 非线性方程与方程组的求解
7.1 求非线性方程一个实根的对分法
7.2 求非线性方程一个实根的牛顿法
7.3 求非线性方程一个实根的埃特金矢代法
7.4 求非线性方程一个实根的连分法
7.5 求实系数代数方程全部的QR方法
7.6 求实系数方程全部的牛顿下山法
7.7 求复系数方程的全部根牛顿下山法
7.8 求非线性方程组一组实根的梯度法
7.9 求非线性方程组一组实根的拟牛顿法
7.10 求非线性方程组最小二乘解的广义逆法
7.11 求非线性方程一个实根的蒙特卡洛法
7.12 求实函数或复函数方程一个复根的蒙特卡洛法
7.13 求非线性方程组一组实根的蒙特卡洛法
第8章 插值与逼近
8.1 一元全区间插值
8.2 一元三点插值
8.3 连分式插值
8.4 埃尔米特插值
8.5 特金逐步插值
8.6 光滑插值
8.7 第一种边界条件的三次样条函数插值
8.8 第二种边界条件的三次样条函数插值
8.9 第三种边界条件的三次样条函数插值
8.10 二元三点插值
8.11 二元全区间插值
8.12 最小二乘曲线拟合
8.13 切比雪夫曲线拟合
8.14 最佳一致逼近的里米兹方法
8.15 矩形域的最小二乘曲线拟合
第9章 数值积分
9.1 变补长梯形求积法
9.2 变步长辛卜生求积法
9.3 自适应梯形求积法
9.4 龙贝格求积法
9.5 计算一维积分的连分式法
9.6 高振荡函数求积法
9.7 勒让德-高斯求积法
9.8 拉盖尔-高斯求积法
9.9 埃尔米特-高斯求积法
9.10 切比雪夫求积法
9.11 计算一维积分的蒙特卡洛法
9.12 变步长辛卜生二重积分方法
9.13 计算多重积分的高斯方法
9.14 计算二重积分的连分方式
9.15 计算多重积分的蒙特卡洛法
第10章 常微分方程组的求解
10.1 全区间积分的定步长欧拉方法
10.2 积分一步的变步长欧拉方法
10.3 全区间积分维梯方法
10.4 全区间积分的定步长龙格-库塔方法
10.5 积分一步的变步长龙格-库塔方法
10.6 积分一步的变步长基尔方法
10.7 全区间积分的变步长默森方法
10.8 积分一步的连分方式
10.9 全区间积分的双边法
10.10 全区间积分的阿当姆斯预报校正法
10.11 全区间积分的哈明方法
10.12 积分一步的特雷纳方法
10.13 积分刚性方程组的吉尔方法
10.14 二阶微分方程边值问题的数值解法
第11章 数据处理
11.1 随机样本分析
11.2 一元线性回归分析
11.3 多元线性回归分析
11.4 逐步回归分析
11.5 半对数数据相关
11.6 对数数据相关
第12章 极值问题的求解
12.1 一维极值连分式法
12.1 n维维极值连分式法
12.3 不等式约束线性规划问
12.4 求n维极值的单行条优法
12.5 求约束条件下n维极值的复形调优法
第13章 数学变换与滤波
13.1 傅立叶级数逼近
13.2 快速傅立叶变换
13.3 快速沃什变换
13.4 五点三次平滑
13.5 离散随机线性系统的卡尔曼滤波
13.6 α-β-γ滤波
第14章 特殊函数的计算
14.1 伽马函数
14.2 不完全伽马函数
14.3 误差函数
14.4 第一类整数阶贝塞尔函数
14.5 第二类整数阶贝塞尔函数
14.6 变形第一类整数阶贝塞尔函数
14.7 变形第二类整数阶贝塞尔函数
14.8 不完全贝塞尔函数
14.9 正态分布函数
14.10 t-分布函数
14.11 χ-分布函数
14.12 F-分布函数
14.13 正弦积分
14.14 余弦积分
14.15 指数积分
14.16 第一类椭圆积分
14.17 第二类椭圆积分
第15章 排序
15.1 冒泡排序
15.2 快速排序
15.3 希尔排序
15.4 堆排序
15.5 结构排序
15.6 磁盘文件排序
15.7 拓扑分类
第16章 查找
16.1 结构体数组的顺序查找
16.2 磁盘随机文本文件对分查找
16.3 有序数组的对分查找
16.4 按关键字成员有序的结构体数组的对分查找
16.5 按关键字有序的磁盘随机文本文件的对分查找
16.6 磁盘随机文本文件的字符串匹配
参考文献
数据压缩43
1.压缩与解压缩字符串
C风格的原型:不用引用,用字节指针/常字节指针表示字符串输出/输入参数,用整数指针/整数表示长度输出/输入参数
……
#include"zconf.h"
#include "zlib.h"
#pragma comment(lib, "zlib1.lib")
const unsigned char strSrc[]="……";
unsigned char buf[1024]={0},strDst[1024]={0};
unsigned long srcLen=sizeof(strSrc),bufLen=sizeof(buf),dstLen=sizeof(strDst);
//压缩
compress(buf,&bufLen,strSrc,srcLen);
……
//解压缩
uncompress(strDst,&dstLen,buf,bufLen);
……
C++风格的原型:
bool encrypt(char * desc, int & desc_len, char * src, int src_len)
{
……
}
C++风格的原型:
bool CompressHuffman(BYTE *pSrc, int nSrcLen, BYTE *&pDes, int &nDesLen);
bool DecompressHuffman(BYTE *pSrc, int nSrcLen, BYTE *&pDes, int &nDesLen);
文件压缩比较粗糙的接口:
extern "C" _declspec(dllexport)int __stdcall huf(char *infile_name,char *outfile_name);
自定义API列表(今后可根据此列表进行"接口不变,实现改变"的测试)
Private Declare Function factorial& Lib "MATHLIB.DLL" Alias "_factorial@4" (ByVal a&)
'extern "C" _declspec(dllexport)int __stdcall factorial(int value);//阶乘
Private Declare Function FileExists Lib "MATHLIB.DLL" Alias "_FileExists@4" (ByVal strFileName As String) As Long
'extern "C" _declspec(dllexport)int __stdcall FileExists(char *str);//判断一个文件是否存在,1存在,0不存在
'int __stdcall FileExists(char *str)
'{FILE *stream;
'if ((stream=fopen(str, "r"))!=NULL)
'{fclose(stream);return(1);}
'else if(stream)
'return(0);
'}
Private Declare Function BitReverse& Lib "MATHLIB.DLL" Alias "_BitReverse@8" (ByVal src&, ByVal size&)
'int __stdcall BitReverse(int src, int size);//二进制倒序操作,可以作为简单的加密解密手段
BOOL SnMulTable(int n,char *strvec[],int Matrixvec[][][],int MulTable[][]);//根据恒等置换的置换文字"1,2,3,4"或"{{1,2,3,4},{1,2,3,4}}"生成n!=4!=24个置换文字以及表示它的24个n阶置换阵。strvec[0]和Matrixvec[][][0]即恒等置换。MulTable[][]存储大小为(n!)*(n!)的乘法表,乘法结果即所得n阶置换阵在Matrixvec中的索引。