分享
 
 
 

经济普查产品目录审核的分析及优化

王朝other·作者佚名  2006-02-01
窄屏简体版  字體: |||超大  

经济普查产品目录审核的分析及优化

ePRAS是第一次全国经济普查数据处理软件,它引入了公式的概念并建立了一套自成体系的语言结构,因此它具有第四代语言(4GL)的特征,可以方便地对ePRAS系统的各种元素,比如基层表、目录、分组等进行有效的操作。特别是它引入了自定义函数的功能后,更大大扩展了本身的能力,给应用系统的开发人员提供了广阔的发挥空间,能适用于目前多个统计专业业务工作的需要,也为将来新的统计应用打下了基础。

ePRAS软件的审核关系用公式描述,这次经济普查中,《工业企业产品生产、销售、库存》(规模以上为603表,规模以下为611表,下面以规模以上为例,简称603表)的产品目录审核是其中最复杂的审核公式之一,这个审核公式的执行时间也比一般的公式要长得多。这一方面是统计业务的要求造成的,从这个公式包含的审核关系式的条数就能看出,总共768条主产品和子产品的平衡关系和134条的不同计量单位和主项与其中项之间的关系,这种审核还要在表的8个列上分别进行。每个填报单位的产品个数虽然可能只有几种或者十几种,但是不同单位的产品不同,要在900多条审核关系中找到本单位应该审核的关系,这本身就是一件繁重、耗时的工作。另一方面,虽然这个公式在编写时已经根据产品目录和表的特点采用了相当多的技巧,但是此公式中仍有一些值得改进之处,还有相当大的潜力可挖。本文就此公式的的编写思路进行分析,根据的排序和查找的算法,对该公式进行优化设计。

一、对公式编写思路和执行流程的分析

603表审核公式(以下简称CPML_603)的大体执行流程分为以下三步:

1.对所有审核关系式做预处理,形成一个清单A,该清单可被所有参加审核单位利用;

2.读某一个填报单位的表,对它填报的所有产品通过在清单A中查找、变换等处理,形成该单位要执行的审核关系式的清单B;

3.对清单B中的审核关系式进行审核。然后转步骤2,直到所有填报单位的表审核完成。

(一)主产品和子产品的平衡关系的两种清单的形成过程

主产品和子产品的平衡关系分为三种,分别是主产品的值“等于”、“大于等于”或“大于”若干子产品的值之和。

以前4组平衡关系为例:

"原煤=无烟煤+烟煤+褐煤"

"2.烟煤=(1)炼焦烟煤+(2)一般烟煤"

"洗精煤=1.炼焦用洗精煤+2.其他用洗精煤"

"1.炼焦用洗精煤>=其中:冶炼用洗精煤"

用产品代码表述则是:

0600003==0610002+0610003+0620002

0610003==0610004+0610005

0610006==0610007+0610009

0610007>=0610008

从上面的关系式可以看出,每组审核关系都包括一种主产品和对应的若干种子产品,主产品和子产品是相对的,如:烟煤(0610003)在第一组审核关系中是子产品,而在第二组审核关系中是主产品。有时候一种主产品可以有多个审核关系,例如:

1711011>=1711012,

1171101>=1711014+1711015+1711018

我们的目标是,对于某个单位填报的603表中出现的产品,不论它出现在哪个关系式中,不论它是主产品还是子产品(出现在审核关系式中关系运算符的左侧或右侧)都要进行审核,而且各单位应对每个审核关系对表的每列只进行一次审核。

公式设计者采用了几个一维数组来达到这个目的,一个数组是rplist,它是用来记录所有关系的产品代码。

var rplist = [0600003,0610002,0600003,0610003,0600003,0620002,

0610003,0610004,0610003,0610005,

0610006,0610007,0610006,0610009,

0610007,0610008,…]

初看这个数组的元素排列式有些特别,它的第奇数个元素都是某个关系式中的主产品代码,第偶数个元素都是在某个关系式中的子产品代码。一个关系式中有n个子产品,则数组的元素就有n×2个,包括n个填充在奇数格的同样主产品代码,n个填充在偶数格的不同的子产品代码。

对于重复的主产品代码,公式设计者采用了在原来的代码前加重复次数序号的办法。比如1711011第一次出现就是1711011,第2次出现就是11711011,以此类推。

另一个数组repeatPList,以第奇数个元素为重复主产品代码、第偶数个元素为出现次数,记录了重复的产品代码情况。

var repeatPList = [1711011,2,1712002,2,2911003,2,3220003,3,3230008,2,3230018,3,

3230033,3,3230048,2,3230058,3,3230089,2,3230098,2,3230107,2, …]

将rplist按照偶数位代码数值增序排序,其对应左侧奇数格代码也跟着改变位置。

通过sortArray( rplist )排序后形成的数组是按照子产品代码从小到大排列的,如果某个子产品在多个关系式中出现,那么这些关系式的主产品代码都被集中在了一起。

在此基础上,下一步的工作就是对每一个单位形成一个检查表cplist,在表中列出所有应该审核的产品关系式的关系代码(主产品代码)。cplist是一个二维数组,其第一列记录产品代码,用来和审核关系式的产品代码作判断,第二列记录主产品代码在603表中的记录号,方便在数组中读取数据, 如果主产品漏录,就表明这个审核不成立。

依次读取各填报单位的603表的每一行,就可以取得该单位填报的每一种产品的产品代码,把它们逐个添加到cplist中。为了检查出有些单位只填子产品,而漏填了主产品的错误,最后还要对该单位填写的产品在数组rplist中作一次检索,把尚未列入的主产品补充到cplist中,得出需要检查的审核关系。检索办法是在数组rplist的偶数格检索出有没有这个产品需要检查的审核关系,如果有,则其左侧奇数格代码就是关系式的主产品代码,再在数组cplist中查找该主产品代码,如果没有查到,则将它添加到cplist的末尾。因为rplist排序后每个子产品代码对应的主产品代码都被集中在了一起,所以如果子产品代码有多个要检查的审核关系,那么它的各个主产品代码都可依次轮询得到,当轮询到一个不同子产品代码时,表明涉及前一个子产品的审核关系式已列举完成。

值得注意的是,cplist中不但记录了主产品代码,也记录了子产品代码,如果该子产品代码不是其他审核关系式的主产品代码(即:“纯”子产品代码),则将来在和审核关系式的主产品代码作比较时必然是找不到的,因而是多余的。

(二)不同计量单位的主项与其中项逻辑关系的两种清单的形成过程

这种审核关系的数量较少,共134条。数组erplist用来保存需检查的产品代码,用0来分隔不同的审核关系式。

var erplist = [2440003,2440004,0,2672003,2672005,2672006,2672007,0,3050003,3050004,0,…]

数组extmsg用来保存同一个审核关系式中第一个产品代码(主项代码)和提示信息。这里利用了ePRAS数组的一个特性,即数组的不同元素可以是不同的数据类型。

var extmsg = [2440003,"1.供儿童乘骑的带轮玩具的不同计量单位辆和千元必须同时出现",

2672003,"清洁类化妆品的其中项已填写,请填写本项." ,…]

数组epplist用来保存不同计量单位的主项与其中项关系的主项代码,通过对某个单位填报的每一个产品代码在erplist中的检索,在形成前面的检查表cplist的同时,也构造完成了epplist。同样,epplist是一个二维数组,其第一列记录主项代码,第二列记录主项代码在603表中的记录号,这样可以方便地定位,如果主项漏录,就表明这个审核不成立。

(三)对清单中的产品依次进行两种审核关系式的检查

在前面工作的基础上,只要简单地对检查表cplist进行遍历就可以不重不漏地对603表主产品和子产品的平衡关系进行检查;同样,对检查表epplist进行遍历,就可以不重不漏地对603表不同计量单位、主项与其中项关系进行检查。

通过前面的分析,我们可以看出,公式作者为了保证审核的完备性,提高审核效率,应用了诸多的技巧。试想一下,如果不通过这些技巧提供的审核关系过滤、筛选,对每一个603表的填报单位直接运用所有的审核关系进行检查,将会有多少无用的数据读取和对具体单位不涉及产品的审核关系执行,将造成多少的时间消耗。

二、数据结构中排序和查找的算法

(一)排序算法

排序算法是一种基本并且常用的算法。由于实际工作中要处理的数据量巨大,所以对排序算法有很高的速度要求。而一般我们所谓的算法的性能主要是指算法的复杂度,一般用O方法来表示。O方法的定义:

若存在一个常量K和起点n0,使当n>=n0时,有f(n)<=K×g(n),则f(n) = O(g(n))。

我们将按照算法的复杂度,从简单到复杂来分析算法。影响我们算法性能的主要部分是比较和交换,显然,比较和交换次数越多,性能就越差。按照关键字相同的元素在排序前后的相对位置是否改变,排序算法可以分为稳定的和不稳定的。

第一部分是简单排序算法,它们的共同点是算法复杂度为O(N2)。

第二部分是高级排序算法,复杂度为O(N×Log2(N))或小于O(N2)。这里我们只介绍3种算法。另外还有几种算法因为涉及树与堆的概念,这里不再详细讨论。

1、简单排序算法

(1)冒泡法

这是最原始,也是众所周知的最慢的算法。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上“飘浮”。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。

(2)选择法

这种方法提高了一点性能(某些情况下),这种方法类似我们人为的排序习惯:从数据中选择最小的同第一个值交换,再从剩下的部分中选择最小的与第二个交换,这样往复下去。

(3)插入法

插入法较为复杂,它的基本工作原理是抽出牌,在前面的牌中寻找相应的位置插入,然后继续下一张。

2、高级排序算法

(1)快速排序

快速排序的基本思想是基于分治策略的。将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。首先我们选择一个中间值middle(程序中我们使用数组中间值),然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程。

(2)Shell排序(希尔排序)

首先需要一个递减的步长gap,最后的步长必须是1。工作原理是首先对相隔较远的元素的所有内容排序,然后再使用同样的方法对相隔近些的元素的排序,以此类推。

(3)归并排序

把数据等分成两部分, 各自用归并排序排好后再合并,它在归并时耗费O(n)的空间。

3、对排序算法的总结

(1)若n较小(如n≤40),可采用插入排序或选择排序。当记录规模较小时,插入排序较好;反之,因为选择移动的记录数少于插人,应选选择排序为宜。(2)若文件初始状态基本有序(指正序),则应选用插人、冒泡或随机的快速排序为宜;(3)若n较大,则应采用时间复杂度为O(N×Log2(N))的排序方法:快速排序、堆排序或归并排序。

快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;快速排序不适合用于“几乎已排好序”或“几乎正好倒序”的数据。在此最坏情况下,时间复杂度为O(N2)。堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。归并排序是稳定的,而且适用于数据量特别大以至于无法在内存中容纳,需要通过外存来进行的外部排序。

Shell排序的时间复杂度在数学上没有解决,大致可以认为是O(n(1+£)), 其中£是介于0和1之间的常数。

(二)查找算法

1.顺序查找法

即从第一个元素顺序到最后一个元素依次与待查的值进行比较,如果相等,查找成功,否则继续比较,直到所有元素都比较过了,如果还没有匹配的值,查找失败。查找适用于数据量少、不要求已经排序的数据,它的时间复杂度为O(N);

2.二分查找

又称折半查找,适用于数据量大、已经排序的数据,它的时间复杂度为O(Log2(N))。

二分查找的基本思想是(设R[low..high]是当前的查找区间) :

(1)首先确定该区间的中点位置:mid=(low+high)/2

(2)然后将待查的K值与R[mid].key比较:若相等,则查找成功并返回此位置,否则须确定新的查找区间,继续二分查找,具体方法如下:

①若R[mid].key>K,则由表的有序性可知R[mid..n].keys均大于K,因此若表中存在关键字等于K的结点,则该结点必定是在位置mid左边的子表R[1..mid-1]中,故新的查找区间是左子表R[1..mid-1]。

②类似地,若R[mid].key<K,则要查找的K必在mid的右子表R[mid+1..n]中,即新的查找区间是右子表R[mid+1..n]。下一次查找是针对新的查找区间进行的。

因此,从初始的查找区间R[1..n]开始,每经过一次与当前查找区间的中点位置上的结点关键字的比较,就可确定查找是否成功,不成功则当前的查找区间就缩小一半。这一过程重复直至找到关键字为K的结点,或者直至当前的查找区间为空(即查找失败)时为止。

3.分块查找

分块查找(Blocking Search)又称索引顺序查找。它是一种性能介于顺序查找和二分查找之间的查找方法。

分块查找表的存储结构由“分块有序”的线性表和索引表组成。

(1) “分块有序”的线性表

表R[1..n]均分为b块,前b-1块中结点个数为s=n/b,第b块的结点数小于等于s;每一块中的关键字不一定有序,但前一块中的最大关键字必须小于后一块中的最小关键字,即表是“分块有序”的。

(2)索引表

抽取各块中的最大关键字及其起始位置构成一个索引表ID[l..b],即:ID[i](1≤i≤b)中存放第i块的最大关键字及该块在表R中的起始位置。由于表R是分块有序的,所以索引表是一个递增有序表。

分块查找的基本思想是:

(1)首先查找索引表,索引表是有序表,可采用二分查找或顺序查找,以确定待查的结点在哪一块。

(2)然后在已确定的块中进行顺序查找,由于块内无序,只能用顺序查找。

4.对查找算法的总结

(1)若n较小(如n≤40),可采用顺序查找。 (2)若文件初始状态有序,且n较大,则应采用时间复杂度为O(Log2(N))的二分查找方法。(3) 若文件初始状态分块有序 ,且n较大,则应采用分块查找。

三、对公式的进一步的优化

有了上边的结论,回到我们的问题,不难发现公式编写中的一些欠缺之处。我们将逐个分析它们,并提出改进的方法,以使公式的执行效率达到更优。

(一)排序算法的选择

执行过CPML_603公式时可以注意到,无论单位个数多少(大于1),在进度显示中,从开始执行到单位数变成1,中间大约有20秒(在PIII700Mhz服务器上测试)的间隔,这个时间基本上是耗费在sortArray( rplist )了,这里,数组rplist中参加排序的元素有2568个(即偶数格的子产品代码)。

从sortArray的源代码:

function sortArray( rplist )

var pid=0;

var totalrpnum = length(rplist,1)/2;

for i=1 to totalrpnum-1 do

for j=i+1 to totalrpnum do

if rplist[i*2]>rplist[j*2] then

pid = rplist[j*2];

rplist[j*2] = rplist[i*2];

rplist[i*2] = pid;

endif

endfor

endfor

end

可以看出,这是一种选择排序,时间复杂度为O(N2),比较次数为N×N /2,如果把它改成时间复杂度为O(N×Log2(N))的排序,将可以缩短排序时间为原来的大约Log2(N)/N×2,即大约9/1000。采用递归算法的快速排序或采用Shell排序来代替原来的sortArray()函数,经过测试,大约1秒都可以完成排序。

根据总运行时间函数f(u)=t0+t1u,其中u是参加审核的单位个数,t0是固定的,也就是开始处理每个单位前都要用的时间,实际上主要是rplist排序和系统对数据表建立索引的时间。下面的主要工作就是如何减少每个单位处理的时间t1,达到缩短总时间的目的。

(二)查找算法的选择

每个单位处理的时间由二部分组成,一部分是形成该单位要执行的审核关系式的清单,另一部分是对清单中的审核关系式进行审核。我们要从这二方面着手减少处理的时间。

findFirstRProdIndex( rplist,cplist[i,1] )函数用来在rplist中找到第一个出现的cplist[i,1],已知元素有2568个,而且已经排好序,符合二分查找的条件,把它改为二分查找,将可以缩短查找时间为原来的大约Log2 (N)/N×2。但是二分查找无法实现对“第一个”出现的元素的查找,为此新增了数组unicodelist用来存放单个子产品代码以及它第一次出现在rplist的位置,通过对unicodelist的二分查找得到它在rplist的位置,由于rplist已排好序,所以从中顺序挑选子产品代码存入unicodelist,unicodelist自然已经排好序了。

尽管如此我们作了这些额外的事,经过测试,此项改动在单位个数为3700,大约可以使总执行时间由180秒(已改用Shell排序)进一步缩短为130秒完成。

getERProdid(prodid, erplist )函数用于查找主产品或者副产品,如果找到是副产品,返回其主产品,erplist有 440个元素,改用二分法也将有不少的收益。但原来erplist 存放需要检查计量单位同时存在的关系的产品号,不同的关系用 0分隔,排序后就失去了分隔标志,需要用一个新代码来代替,这个新代码的值应位于上一种关系最后一个产品和下一种关系第一个产品代码之间,才能在排序后保持原有的相对位置。考虑到有的上一种关系最后一个产品和下一种关系第一个产品代码是连续的,比如:3050003, 3050004, 0, 3050005, 3050006, 0。在这里,用下一种关系第一个产品代码来替代0分隔是很合适的,经过替换以后,erplist已经有序了。查找的时候,只要判断两个连续的代码是相同的,就可以确定它是主产品代码。

取得提示信息的函数有2个,它们目前也都是采用顺序查找,理论上也可以用排序后二分查找的办法提高效率,但经过实验发现,这些函数总的执行次数(时间)很少,可以忽略不计,这可能与我们采用的样本数据只有很少的错误有关。ePRAS公式的设计者在设计verify expr,ErrorMsg()语句的时候,采用了类似C语言多个条件复合表达式的短路技术(比如复合表达式(true or expr2)不管子表达式expr2的值是什么,总是返回true),如果知道前面expr的值为true,就不再调用后面ErrorMsg()函数了。

如果原始数据质量很差,错误很多,那么下面这些语句的优化还是值得的。

getErrorMsg(prodid, plist, pmsg )

getEXTMsg(prodid, mesg )

除了在输出错误信息时对数组pmsg或extmsg二分查找,也可以在形成清单的过程中,在数组记下各主产品的行号rn,直接引用pmsg[rn]或extmsg[rn*2],避免二次查找。

(三)大量case语句的拆分

观察 getCompResult()这个函数,它一共包含了768条审核关系式,都用case语句依次排开,每次读入一个产品代码,都需要和这么多case语句的值进行比较,这会不会是造成执行时间长的主要原因?我把case语句拆分为2组,外面加了一个if.. else语句判断代码的范围。再次试验,公式的总执行时间缩短了20秒(3700单位),猜测得到了证实,下一步该研究如何拆分最优了。

设每产品比较次数y=各组比较次数+组内比较次数=n+768/n,利用微积分的知识来求函数的最小值,等式两边求导数得,y'=1-768/(n×n)=0, ==> n==27.7,即当等距离拆分为28组的时候,总比较次数最少,最坏情况下为28+28=56次,与原来的768次相比,减少为原来的1/14,假设我们等距离拆分为14组,最坏情况下为14+768/14=69次,减少为原来的1/11,已经可以接受了。

以上我们假设各单位填写的产品代码是均匀分布的,实际上各单位填写的产品代码不均匀,有些产品生产的单位多,如果把这些生产的单位多的产品代码排在前边,这样可以减少判断次数。但是这样做影响了程序的通用性,如果一批数据中恰好都不存在这些产品,反而会增加判断次数,降低性能。

其实我们这种手工拆分,在原理上类似于分块查找。但是由于case后边跟的都是不同的语句,无法用二分查找,否则比较次数将可以进一步减少为: log2(N)=log2(768)≈10次。

(四)用数组来存储关系式,让程序自动计算

在getCompResult()中,每个审核关系一条语句,能否用数组来存储关系式,让程序自动计算?我们可以利用已有的主产品代码plist数组,我们需要再建立4个数组,一个son保存按主产品排序的对应审核关系式子产品代码(相当于目前rplist的第偶数个元素形成的数组),一个son_pos保存各主产品第一个对应子产品在son中的起始行号,一个son_num保存各主产品对应子产品个数,一个sign保存审核关系式的算术比较符(如: '>=' , '==' ,'>'或它们的代号,如1,2,3 ),那么可以用下面的方法实现任意一个表达式(右边都是加法运算)的判断:

function judge(proid)

rowno=bfind(proid);// 通过在plist二分查找proid,定位到一个主产品行号

var res= getvalue(proid);

for i= son_pos[rowno] to son_pos[rowno]+son_num[rowno]

res=res-getvalue(son[i]);// getvalue返回某个产品代码的值

endfor

switch(sign[rowno])

case 1:verify res >=0,pmsg[rowno];

case 2:verify res ==0,pmsg[rowno];

case 3:verify res > 0,pmsg[rowno];

endswitch

end

这样做的工作量比较大,要人工增加子产品代码和比较运算符信息,形成上述4个数组。这样做虽然简化了表达式的写法,但却降低了公式的可读性,而且经过测试,这样做的性能提高幅度很小。这与我们已经利用case语句的拆分将公式执行时间缩短到不到30秒,进一步压缩的余地已经很少有关。

能否做到两全其美,既不降低公式的可读性,又简化表达式的写法?这就需要ePRAS程序更进一步,能自动根据存在数组explist或一个二维表当中的每个表达式,通过分析关系运算符和算术运算符,分离出产品代码,形成上述4个数组以及rplist、plist和pmsg,而不需要编写公式的应用人员手工来做,将可以减轻他们的负担,并保证这些数组的正确性。每次要修改审核关系,只要在explist或者二维表中改变,代码部分不需要任何改动。伪码如下:

var explist=["0600003=0610002+0610003+0620002", "0610003=0610004+0610005"...];

gensonarray(explist);// 形成上述4个数组

for i= 1 to length(cqlist,1) do //cqlist是某一单位要审核的主产品清单

for indx=2 to 1+cols do //cols是要审核的二维表列数

cresult = getQty(indx,cqlist[i])//相当于给上述judge函数增加一个indx参数

endfor

endfor

这种思路在其他的应用中,包括ePRAS审核程序将来的改进设计中,有参考的价值。也可以推广到一切具有总体——部分之间特征的审核关系,比如海关进出口目录等的审核,解决数据库对记录行之间审核无能为力的问题。

(五)优化要适度,要适应实际工作需要

代码行数越多,出错的几率越大,正确的重要性永远是第一位的。在目前ePRAS调试公式手段很有限的情况下,在给定的时间内要保证正确性,我们就要尽量少地修改源代码。简明的语言结构是最容易保证和验证正确性的。优化只对最频繁操作并且又耗时的部分进行。这两个条件缺一不可。

比如,经过统计,getQty()函数的执行次数很多,而且它采用的是顺序查找并有很多的多余查找动作,当getCompResult()函数调用getQty()函数时,indx从2到 length(pqlist,2)共8次循环,其实从indx=3开始,

case 0600003:cresult = getQty(indx,prodid,item_num,pqlist) == (getQty(indx,0610002,item_num,pqlist )+getQty(indx,0610003,item_num,pqlist)+getQty(indx,0620002,item_num,pqlist));

这一行语句中涉及的产品代码均已得到其在pqlist中的行号,而不需要在pqlist中再找一次,因此可增加临时数组变量temppos记录indx=2找到的各产品代码在pqlist中的行号,从indx=3开始,直接从temppos中取得已知的行号,使查找的动作仅为原来的1/8。期望可以使getCompResult()的总执行时间缩短为原来的1/8,但是实际测试结果表明,这样做节省的时间对4万多个单位也只有几秒,而审核一个错误较多的样本时用时急剧上升。可见,经过一系列优化后,最后的时间耗费主要是用在把错误信息写入数据库上,而不是用在执行公式代码本身上。虽然getQty()函数调用次数频繁,但并不耗时,而且根据上文提到的,当欲查找的元素数量较少时,考虑函数调用以及计算、比较等开销,顺序查找比二分查找的效果更好。

必须认识到,之所以我们能对这个审核公式进行优化,都是基于每个单位只生产少量产品的事实,否则假如每个单位都生产产品目录上所有产品,那么直接对所有审核关系式执行就行了。

公式中还有一些小的疏忽,比如,产生一个需要检查的关系(主产品号)表时,需要检查某产品是否已经在列表了,找到以后没有及时用break语句跳出循环,也延长了执行时间。又如,前边提到的“cplist中不但记录了主产品代码,也记录了子产品代码”问题,可通过给二维数组cplist增加一列标志列来解决,在对每个添加进来的产品通过在plist中查找确定是否主产品,如是主产品,标志列值非零,否则,标志列值为零(“纯”子产品代码)。下一步在审核时只审核标志列值非零的产品。根据epras公式未公开的用法,组合数据项(子表)实际上可以当一个二维数组使用,那么从子表复制数据到数组就不必要了,可以不借助二维数组upplist,直接用t603["JB603_sub3"]达到同样的效果。

(六)一些分析问题和提高工效的办法

在对CPML_603的分析和优化过程中,我针对目前ePRAS程序的实际情况,总结出一些经验,可供ePRAS的二次开发人员参考。

1.关于如何找到程序执行的瓶颈,第一种方法是通过加入临时变量记录函数调用或语句执行的次数,对执行最频繁的部分进行改进,第二种方法是注释一些语句,比较注释前后的执行时间,看哪些语句块耗费比较多的时间。

2.关于调试,可以用verify false,str(变量)的办法输出值。用break语句随时中断公式的执行。还可以用“植入错误”的办法,用语句把错误的值填入到内存变量,而不用手工在基层表中输入数据。还可以利用已有的集成开发环境,比如Visual C++等,把代码调试好以后,再改写成等价的ePRAS公式代码。实际上本文涉及的快速排序、希尔排序、二分查找的算法均来自数据结构教科书上的C/C++源代码。time()函数返回格式化的当前时间,比如:08:05:23,debug(a)函数将变量或表达式a的内容输出到程序安装目录下面的ecs.debug文件。

3.关于多种工具的结合使用,Excel软件可以方便地对产品目录、提示信息等进行格式化,还可以用嵌入VBA语言进行更多的数据处理和检查工作,多种工具的结合使用能发挥各自的长处,得到综合的效益。

4.关于验证修改后的执行结果是否和原来公式一致,可以用sql语句从错误清单表中导出不同公式代码的记录进行比对。

四、结论

这样,我们通过对公式代码的分析,采用适当的手段优化,提高了它的执行效率(各步优化效果见附表: 优化测试结果),在数据量大的时候有较大的实用价值。

笔者在经济普查国家数据处理中应用上述办法,提高了审核效率,加快了工作进度。由于CPML_611和CPML_603公式只有表代码和子表代码不同,因此只要改变这些就能用于CPML_611,对规模以下工业企业进行产品目录审核。

优化测试结果(40842单位,单位:秒)

项目

运行

时间

本步节

约时间

原公式

2145

0

sortArray改为希尔排序

2112

33

findFirstRprodIndex改为二分查找

1161

951

GetCompResult的case拆分为14段

720

441

getERProdid改为二分查找

223

497

“纯”子产品代码不参加审核

190

33

用数组来存储关系式

168

22

getQty函数改为查找一次

167

1

不借助二维数组upplist

163

4

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有