
给定一整数n,要按照字典序从小到大的顺序枚举输出前n个数(1-n)的全排列。例如:n=3时,枚举排列结果是:(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)。以下是c语言源码实现该算法:
------------------------------------c源码----------------------------------------
#include<stdioh>
#include<timeh>
void print_permutation(int n,int A,int cur){
int i,j;
if(cur==n){
for(i=0;i<n;i++)
printf(%d,A[i]);
printf(\n);
}
else for(i=1;i<=n;i++){
int ok=1;
for(j=0;j<cur;j++){
if(A[j]==i)
ok=0;
}
if(ok){
A[cur]=i;
print_permutation(n,A,cur+1);//递归调用
}
}
}
void main(){
int n;
int A[1000];
printf(input the n:);
scanf(%d,&n);
print_permutation(n,A,0);
printf(time used:%2lf\n,(double)clock()/CLOCKS_PER_SEC);//测试程序运行时间
}
------------------------------------c源码---------------------------------------- 输入数组P,并按字典序输出数组P各元素的全排列到A(注意:P有序),例如P序列为:1 1 2,则对应的排序结果为:(1,1,2)、(1,2,1)、(2,1,1)。以下是该算法的c源码,P数组长度可以自己调整。
------------------------------------c源码----------------------------------------
#include<stdioh>
#include<timeh>
void print_permutation(int n,int P,int A,int cur){
int i,j;
if(cur==n){
for(i=0;i<n;i++)
printf(%d,A[i]);
printf(\n);
}
else for(i=0;i<n;i++){
if(!i||P[i]!=P[i-1]){
int c1=0,c2=0;
for(j=0;j<cur;j++){
if(A[j]==P[i])
c1++;
}
for(j=0;j<n;j++){
if(P[i]==P[j])
c2++;
}
if(c1<c2){
A[cur]=P[i];
print_permutation(n,P,A,cur+1);//递归调用
}
}
}
}
void main(){
int n,m;
int A[1000];
int P[4];//可以自己修改
int i=0;
while(scanf(%d,&m)==1){
P[i]=m;
i++;
}
n=i;
print_permutation(n,P,A,0);
printf(time used:%2lf\n,(double)clock()/CLOCKS_PER_SEC);
}
------------------------------------c源码----------------------------------------
while getopts ":l:c:" opt;do case $opt in l) dict_length=$OPTARG;; c) dict_char=$OPTARG;; \) echo 'Usage: dictsh -l dict_length -c "dict_char"' exit 1;; esacdoneshift $(($OPTIND - 1)) #根据字典的长度产生单词,象这样:${pi}${pi-1}${p1}key_str(){ for((i=$1;i!=0;i--));do pswd=$pswd\${p$i} done} #用字典替换${pi}char_sub(){ idx=$1 for char in $dict_char;do eval p${idx}=$char if [ $idx -gt 1 ];then ((--idx)) char_sub $idx ((++idx)) continue fi eval echo $pswd done}
枚举类型,在C语言中求sizeof,可以先定义一个枚举类型,然后对枚举类型中的元素,或者对类型求size,获取到内存长。
代码如下
#include<stdioh>
enum TEST
{
TEST0,
TEST1,
TEST2
};
int main()
{
printf("size of TEST is %d\n", sizeof(TEST));//用类型求大小。
printf("size of TEST is %d\n", sizeof(TEST1));//用元素求大小。
return 0;
}
对于枚举类型,区分是否有负数值,系统会按照unsigned int或int来处理,而是否带符号,并不影响int的占用空间。所以任意枚举类型或枚举变量,占用的空间都是和平台实现时的int类型相同。对于主流的32位/64位平台,枚举类型均占4字节。即sizeof结果为4。
1
因11CM为底边,所以另两边的和一定要大于11CM才能组成一个三角形,所以有以下20个不同的三角形:2+10,3+9,3+10,4+8,4+9,4+10,5+7,5+8,5+9,5+10,6+7,6+8,6+9,6+10,7+8,7+9,7+10,8+9,8+10,9+10
2
算盘中下面一排的珠子1个表示1,上面的珠子1个表示的是5,这个三位数是
百位一颗,十位没有,个位两颗的是:102,106,502,506
百位一颗,十位一颗,个位一颗的是:111,115,151,155,511,515,551,555
百位一颗,十位两颗,个位没有的是:120,160,520,560
百位两颗,十位没有,个位一颗的是:201,205,601,605
百位两颗,十位一颗,个位没有的是:210,250,610,650
百倍三颗,十位没有,个位没有的是:300,700
3
单个法码能称的重量是1g,2g,4g,7g,13g
两个法码能称的重量是1+2=3g,1+4=5g,1+7=8g,1+13=14g,2+4=6g,2+7=9g,2+13=15g,4+7=11g,4+13=17g,7+13=20g
三个法码能称的重量是1+2+4=7g,1+2+7=10g,1+2+13=16g,2+4+7=13g,2+4+13=19g,4+7+13=24g
四个法码能称的重量是1+2+4+7=14g,1+2+4+13=20g,2+4+7+13=26g
五个法码能称的重量是1+2+4+7+13=27g
因为其中7g,13g,14g,20g均有重复,所以能称的重量有21种,分别是:1g,2g,3g,4g,5g,6g,7g,8g,9g,10g,11g,13g,14g,15g,16g,17g,19g,20g,24g,26g,27g
4
由题可知小明第一天和第四天都是做语文,所以第二天和第三天都不做语文,
若第二天做数学的话,第三天可做英语或科学,
若第二天做英语的话,第三天可做数学或科学,
若第二天做科学的话,第三天可做数学或英语
所以这4天的作业安排有6种,分别是语+数+英+语,语+数+科+语,语+英+数+语,语+英+科+语,语+科+数+语,语+科+英+语。
物理算子构成的树状结构,每个节点都是一个物理算子(也就用于实现该算子功能的代码),称为执行计划,最后会交给执行引擎去执行。这个树状结构也表示了数据的流动方向。
查询优化
执行计划的复杂性体现在,算术表达式的等价转换以及物理算子的多种实现上。不同的执行计划的执行效率差别十分的大,所以谨慎的选择一个执行计划是一个非常重要和非常难的问题。通过上述表达,我们可以知道查询优化是一个搜索问题。
为了解决这个问题我们需要从以下三方面入手。
image
搜索空间
代价估计
枚举算法
System-R
SPJ queries 搜索空间
引入 System-R 解释上述问题的实现方案,使用 Select-Project-Join (SPJ) queries,因为这个模型被广泛的应用于数据库系统的研究中。
SPJ 语句涉及到一个 linear scqucncc of join operations,如下图所示:
image
根据 Join 算子的 associative 和 commutative 性质, Figure2(a), (b) 等价。
image
代价模型
System-R 对于算子的代价评估主要考虑一下方面:
image
如果使用自底向上的方法遍历计划,那么上层的代价会依赖于下层算子的代价。
枚举算法
System-R 展示了两种重要的技术,动态规划和 interesting orders。
使用动态规划算法的基础是代价模型满足最优化原理。
动态规划
对于 SPJ queries 当我们要计算 k-join 的最优解时,需要知道 (k-1)-join 的最优解。
image
求 Join{R1, R2, R3, R4} 的最优解,我们可以从:
Join{R1, R2, R3} 的最优解 + R4 的代价来获得
Join{R1, R2, R4} 的最优解 + R3 的代价来获得
Join{R1, R3, R4} 的最优解 + R2 的代价来获得
Join{R2, R3, R4} 的最优解 + R1 的代价来获得
这样我们就不需要再去枚举 Join{R1, R2, R3, R4} 的所有情况了。
该算法的思路使用如下代码描述:
R = [R1, R2, R3, , Rn]
optjoin[i][j] # 表示第 j 个 i 长度的子集的最优代价。
opt = MAX_COST # 保存最后的最优方案
for i in enumerate{ R 的所有非空子集的长度}:
for j in enumerate{所有长度为 i 的子集}:
# Rk 为 i 长度的子集的所有 R 中,不在 i-1 长度中的那个 Rk
optjoin[i][j] = min(optjoin[i-1][j]+cost(Rk), optjoin[i-1][j])
opt = optjoin[n][1]
因此可以将枚举规模:O(n!),降到枚举所有子集的问题规模: O(n2^{n-1})
Interesting Oders
对于 Join{R1, R2, R3},如果 Join{R1, R2} 如果使用 sort-merge join,那么其结果是有序的,可以在对 R3 进行 Join 时提供额外的信息,加快 R3 的 Join 速度。如果 Join{R1, R2} 使用 nest-loop join 那其对 R3 的 Join 并没有帮助。Interesting Order 就是指考虑这样的情况。
System-R 的缺陷
System-R 框架同时也存在一些问题,不容易处理 Join 之外的代数变换。
Search Space
搜索空间取决于代数变换以及物理算子的多种实现。这个部分将介绍一些重要的代数变换,这些代数变换并不一定会减少代价,需要靠枚举算法来保证最后计划是最优的。
优化器的主要工作就是将 SQL parser 之后的语法树,经过一系列的转换,变成执行计划,也就是 Operator Tree。在这个变换的过程中,使用 logical operator tree (query trees) 来表示中间的数据结构,Figure2 就是 query tree。
Starburst 使用了 QGM(Query Graph Model) 来进行表示 SPJ queries。并介绍了其存的在问题,如下图所示:
img
Exodus 使用了 query tree 和 operator tree 在所有的优化阶段。
Commuting Between Operators
Join Sequence
System-R 它只能处理 linear sequences, 并且笛卡尔积被推迟到所有 Join *** 作完成。
使用 bushy join 也就是 Figure2(b), 需要物化中间结果,可以得到更高效的执行计划,但是其也大大的增加了需要枚举的 search space。
推迟笛卡尔积可能会导致性能的下降尤其是在 OLAP 系统中。因为在 AP 系统中存在大量的维度表。
Outerjoin and Join
outerjoin 与 join 构成的 joins 序列是不能进行交换的。在特定条件下存在如下公式:
img
有了这个公式,我们就可以将大量的 Join *** 作放到 Outerjoin *** 作之前执行,因此可以对前置的 Join 进行变换来优化性能。
Group-By and Join
提前进行 Group-By,对于 SELECT DISTINCT 这样的语句,提前进行 Group-By 可以大量减少所需处理的 Tuple 数量。
img
如图所示,Group-By *** 作不仅仅能在特定情况下进行下推,如 Figure4(b) 所示,还可以在特定场景下进行分阶段 Group-By。如 Figure4(c) 所示。
Reducing Multi-Block Queries to Single-Block
这部分介绍了如何在特定条件下进行子查询"打平"。
Merging Views
有如下图所示公式:
img
如果 V 是一个 View,我们可以将其直接展开,然后应用各种 Join 的优化手段。这个过程要注意对 View 中的对象进行重命名。
Merging Nested Subqueries
考虑嵌套查询的情况,有如下 SQL
SELECT EmpName
FROM Emp
WHERE EmpDept# IN
(
SELECT DeptDept#
FROM Dept
WHERE DeptLoc='Denver' AND EmpEmp#=DeptMgr
)
在这个查询中外层的列 EmpEmp# 出现在嵌套的子查询中,所以这个子查询称为关联子查询。
如果这个查询是非关联子查询,这个查询在遍历每个 Tuple 时,其子查询都会执行一次,可以看到的一个优化点是,我们可以只执行一次这个子查询。
但是如果碰到关联的子查询,如上 SQL,根据一些研究(参考引用论文)。我们可将其”打平“后得到如下 SQL
SELECT EName
FROM EMP E, Dept D
WHERE EDept# = DDept#
AND DLoc='Denver' AND EEmp#=DMgr
这个 query 等价于:
Semijoin(Emp, Dept, EmptDept#=DeptDept#) = Project(Join(Emp, Dept), Emp)
需要注意的是,当子查询中有 Agg 的时候就比较难以处理。有如下查询:
SELECT Deptname
FROM Dept
WHERE Deptnum-of-machine >= (
SELECT COUNT(Emp) FROM Emp
WHERE Deptname=EmpDept_name
);
这个查询需要从 Agg 中拉取数据还要不违反子查询的语义。保留 duplicate 和 nulls 是非常 tricky 的事情。当这个子查询中的谓词一直为假,这个子查询还是会返回一个 tuple,最后导致这个查询可能会返回tuple。如果采用第一部分,将子查询转成 Join 的方法,那么就不会返回 tuple。这样这个转换使得原 query 的语义发生了改变。
因此,当子查询中有 Agg 我们需要使用 outjoin 来进行转换。转换结果为
SELECT Deptname
FROM Dept left outer join Empt
ON (Deptname=Emptdept_name)
GROUP BY Deptname
HAVING Deptnum-of-machine < count(Emp);
除了上述描述的方法,另一种“打平”的技术是使用 table-expressions 或者 views。
Using Semijoin Like Techniques for Optimizing Multi-Block Queries
提供一种补充方法,有如下 SQL
CREATE VIEW DepAvgSal As (
SELECT Edid, Avg(ESal) AS avgsal FROM Emp E
GROUP BY Edid);
SELECT Edid, ESal
HID修改报告长度后导致枚举失败可能是因为修改报告长度导致了与设备驱动程序不兼容的问题。如果报告长度被修改为一个不受支持的值,那么 *** 作系统就无法正确地解析设备发送的数据。这可能会导致设备无法正常工作或无法被识别。为了解决此问题,你可以尝试恢复默认的报告长度或者更新设备驱动程序以支持所做的更改。另外,确保在对 HID 设备进行任何更改之前备份设备的原始设置和配置文件也非常重要
枚举顶层(top-level)窗口枚举桌面顶层窗口相对于枚举进程来说可能要容易一些。枚举桌面顶层窗口的方法是用 EnumWindows() 函数。不要用 GetWindow()来创建窗口列表,因为窗口之间复杂的父子及同胞关系(Z-Order)容易造成混乱而使得枚举结果不准确。EnumWindows()有两个参数,一个是指向回调函数的指针,一个是用户定义的 LPARAM 值, 针对每个桌面窗口(或者顶层窗口)它调用回调函数一次。然后回调函数用该窗口句柄做一些处理,比如将它添加到列表中。这个方法保证枚举结果不会被窗口复杂的层次关系搞乱,因此,一旦有了窗口句柄,我们就可以通过 GetWindowText() 得到窗口标题。枚举进程建立系统进程列表比枚举窗口稍微复杂一些。这主要是因为所用的 API 函数对于不同的 Win32 *** 作系统有依赖性。在 Windows 9x、Windows Me、Windows 2000 Professional 以及 Windows XP 中,我们可以用 ToolHelp32 库中的 APIs 函数。但是在 Windows NT 里,我们必须用 PSAPI 库中的 APIs 函数, PSAPI 库是 SDK 的一部分。本文我们将讨论上述所有平台中的实现。附带的例子程序将对上述库中的 APIs 进行包装,以便包装后的函数能支持所有 Win32 *** 作系统。使用 ToolHelp32 库枚举进程ToolHelp32 库函数在 KERNEL32dll 中,它们都是标准的 API 函数。但是 Windows NT 40 不提供这些函。ToolHelp32 库中有各种各样的函数可以用来枚举系统中的进程、线程以及获取内存和模块信息。其中枚举进程 只需用如下三个的函数:CreateToolhelp32Snapshot()、Process32First()和 Process32Next()。使用 ToolHelp32 函数的第一步是用 CreateToolhelp32Snapshot() 函数创建系统信息“快照”。这个函数可以让你选择存储在快照中的信息类型。如果你只是对进程信息感兴趣,那么只要包含 TH32CS_SNAPPROCESS 标志即可。 CreateToolhelp32Snapshot() 函数返回一个 HANDLE,完成调用之后,必须将此 HANDLE 传给 CloseHandle()。接下来是调用一次 Process32First 函数,从快照中获取进程列表,然后重复调用 Process32Next,直到函数返回 FALSE 为止。这样将遍历快照中进程列表。这两个函数都带两个参数,它们分别是快照句柄和一个 PROCESSENTRY32 结构。调用完 Process32First 或 Process32Next 之后,PROCESSENTRY32 中将包含系统中某个进程的关键信息。其中进程 ID 就存储在此结构的 th32ProcessID。此 ID 可以被传给 OpenProcess() API 以获得该进程的句柄。对应的可执行文件名及其存放路径存放在 szExeFile 结构成员中。在该结构中还可以找到其它一些有用的信息。注意:在调用 Process32First() 之前,一定要记住将 PROCESSENTRY32 结构的 dwSize 成员设置成 sizeof(PROCESSENTRY32)。使用 PSAPI 库枚举进程在 Windows NT 中,创建进程列表使用 PSAPI 函数,这些函数在 PSAPIDLL 中。这个文件是随 Platform SDK 一起分发的,最新版本的 Platform SDK 可以从这里下载:使用这个库所需的 PSAPIh 和 PSAPIlib 文件也在该 Platform SDK 中。为了使用 PSAPI 库中的函数,需将 PSAPIlib 添加到代码项目中,同时在所有调用 PSAPI API 的模块中包含 PSAPIh 文件。记住一定要随可执行文件一起分发 PSAPIDLL,因为它不随 Windows NT 一起分发。你可以点击这里单独下载 PSAPIDLL 的可分发版本(不用完全下载 Platform SDK)。与 ToolHelp32 一样,PSAPI 库也包含各种各样有用的函数。由于篇幅所限,本文只讨论与枚举进程有关函数:EnumProcesses()、EnumProcessModules()、GetModuleFileNameEx()和 GetModuleBaseName()。创建进程列表的第一步是调用 EnumProcesses()。该函数的声明如下:BOOL EnumProcesses( DWORD lpidProcess, DWORD cb, DWORD cbNeeded );EnumProcesses()带三个参数,DWORD 类型的数组指针 lpidProcess;该数组的大小尺寸 cb;以及一个指向 DWORD 的指针 cbNeeded,它接收返回数据的长度。DWORD 数组用于保存当前运行的进程 IDs。cbNeeded 返回数组所用的内存大小。下面算式可以得出返回了多少进程:nReturned = cbNeeded / sizeof(DWORD)。注意:虽然文档将返回的 DWORD 命名为“cbNeeded”,实际上是没有办法知道到底要传多大的数组的。EnumProcesses()根本不会在 cbNeeded 中返回一个大于 cb 参数传递的数组值。结果,唯一确保 EnumProcesses()函数成功的方法是分配一个 DWORD 数组,并且,如果返回的 cbNeeded 等于 cb,分配一个较大的数组,并不停地尝试直到 cbNeeded 小于 cb现在,你获得了一个数组,其元素保存着系统中每个进程的ID。如果你要想获取进程名,那么你必须首先获取一个句柄。要想从进程 ID 得到句柄,就得调用 OpenProcess()。一旦有了句柄,则需要得到该进程的第一个模块。为此调用 EnumProcessModules() API:EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbReturned );调用之后,hModule 变量中保存的将是进程中的第一个模块。记住进程其实没有名字,但进程的第一个模块既是该进程的可执行模块。现在你可以用 hModule 中返回的模块句柄调用 GetModuleFileNameEx() 或 GetModuleBaseName() API 函数获取全路径名,或者仅仅是进程可执行模块名。两个函数均带四个参数:进程句柄,模块句柄,返回名字的缓冲指针以及缓冲大小尺寸。用 EnumProcesses() API 返回的每一个进程 ID 重复这个调用过程,你便可以创建 Windows NT 的进程列表。16位进程的处理方法在 Windows 95,Windows 98 和 Windows ME 中,ToolHelp32 对待16位程序一视同仁,它们与 Win32 程序一样有自己的进程IDs。但是在 Windows NT,Windows 2000 或 Windows XP 中情况并不是这样。在这些 *** 作系统中,16位程序运行在所谓的 VDM 当中(也就是DOS机)。为了在 Windows NT,Windows 2000 和 Windows XP 中枚举16位程序,你必须使用一个名为 VDMEnumTaskWOWEx()的函数。在源代码模块中必须包含 VDMDBGh,并且 VDMDBGlib 文件必须与项目链接。这两个文件都在 Platform SDK 中。该函数的声明如下:INT WINAPI VDMEnumTaskWOWEx( DWORD dwProcessId, TASKENUMPROCEX fp,LPARAM lparam );此处 dwProcessId 是 NTVDM 中拟枚举的16位任务进程标示符。参数 fp 是回调枚举函数的指针。参数 lparam 是用户定义的值,它被传递到枚举函数。枚举函数应该被定义成如下这样:BOOL WINAPI Enum16( DWORD dwThreadId,WORD hMod16,WORD hTask16,PSZ pszModName,PSZ pszFileName,LPARAM lpUserDefined );该函数针对每个运行在 NTVDM 进程中的16位任务调用一次,NTVDM 进程ID将被传入 VDMEnumTaskWOWEx()。如果想继续枚举则返回 FALSE,终止枚举则返回 TRUE。注意这是与 EnumWindows()相对的。关于代码本文附带的代码例子将 PSAPI 和 ToolHelp32 封装到一个名为 EnumProcs() 的函数中。该函数的工作原理类似 EnumWindows(),有一个指向回调函数的指针,并要对该函数进行重复调用,针对系统中的每个进程调用一次。另一个参数是用户定义的 lParam。下面是该函数的声明:BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam );使用该函数时,要象下面这样声明回调函数:BOOL CALLBACK Proc( DWORD dw, WORD w16, LPCSTR lpstr, LPARAM lParam );参数 dw 包含 ID,“w16”是16位任务的任务号,如果为32位进程则为0(在 Windows 95 中总是0),参数lpstr 指向文件名,lParam 是用户定义的,要被传入 EnumProcs()。EnumProcs() 函数通过显示链接使用 ToolHelp32 和 PSAPI,而非通常所用的隐式链接。之所以要这样做,主要是为了让代码能够在二进制一级兼容,从可以在所有 Win32 *** 作系统平台上运行。
以上就是关于枚举的枚举排列全部的内容,包括:枚举的枚举排列、shell编程如何迅速枚举、利用sizeof() 函数计算C语言中各种数据类型所占用的存储空间的大小等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)