说到SIM,真真的被Dick Grune这老教授折服了,以前一直以为自己的C语言跟编译原理学的蛮好的,现在才察觉,无知真的很可怕。所谓的大学课堂,就好像一本书的绪论。应该没有几个人在看完了一本书的绪论后就对这本书的内容了如指掌了吧!更何况我们所学的是一门发展了几十甚至上百千年的学科。
SIM是一个检测计算机程序相似性的实用程序,当然在后期的发展中也有扩展到对文本相似度的判别。其核心的思想其实就是我们常说的LCS(最长公共子序列)。
它对于检测一大部分的程序作业抄袭有用。这个软件是一个项目构建工具,以协助计算机科学教学的一部分。
1. 介绍
三十多年前计算机科学教育开始建立了第一个CS部门,但其从业者在为他们的贸易开发软件工具方面已落后于其他CS子域的同事。今天在软件系统处理中有一大批卓越的软件设计师,电路设计师、网络管理员、数值分析和数字艺术家。相比之下,计算机科学教育工作者仍依靠传统工具和技术:他们的主要思想交流的工具,最近才转变为课程网页,实现则仍然是粉笔和黑板, 而评价他们的主要工具依然是依靠人力劳动。
我们相信这个软件工具的缺乏并不是由于CS教育者不够勤奋,而是缺乏一套成熟的理论。关于计算学习理论的研究,调查的局限性和不同的学习策略的成本,开始只在1980年代中期。同样,在程序校验研究中,通过测量检查他们的行为[ 3,4,5 ]来判断程序正确性,是从1980年代后期开始的。这些发展中地区产生的深刻而令人吃惊的结果,在不久的将来肯定会有助于改善计算机科学的实践教育。
然而,在这一点上,并不是所有的软件开发的CS教育都需要进一步的基础性工作。看看在较低层次的编程课程作业和考试评价任务的情况吧!这样的课程往往有高入学率和大量的编程作业,却只能用简单的解决方案评估其正确性以及他们的风格和独特性。实现这一任务的软件工具需要检查程序的结构,并将得益于已经成熟的字符串算法理论。
在本文中,我们描述一个叫做 SIM的程序的设计和实现,它测度两个C语言计算机程序之间的结构相似性,并评估其正确性,风格,和独特性。这个程序是一个字符串对齐技术的直接应用,最近应用于检测DNA之间的相似子串(6、12)(参见[7,13、14])。对于给定的两个程序, SIM首先使用一个标准的词法分析器建立他们的解析树。我们将解析树看作字符串, SIM接着在彼此间依次添加空格以获得最长的公共子序列。两个程序之间的相似程度用一个0.0~1.0之间分数来表示。在其目前的情况分析,
SIM运行时间复杂度是O(s^2), s是解析树的最大高度。 SIM大部分是基于c++实现的, 它的图形用户界面是由Tcl/Tk实现的。
基于 SIM的第一个应用,我们用来检测剽窃。真实的实验数据表明,SIM可以检测到标识符的更改,语句、函数的重新排序,以及对空格和注释的增删、修改。对于小型的项目SIM是相当快速的:依次两两比较56个平均长度为3415字节的程序只需三分半钟。
本文的其余部分组织如下:第二节讲了基于SIM的底层字符串对齐算法的使用,第三节描述了SIM的设计和实现,第四节介绍了一个实验及其结果,第五节讨论了未来如何改进。
接下来通过来回顾检测程序的相似性来总结一下本节内容。Unix下的diff命令同样使用字符串对齐方法检测两个项目之间的相似性,但它是由行作为基本的文本单元的,不像SIM以解析树来构造; 并且diff不能检测到系统名称的更改或文本的重新排序。Baker的dup程序可以检测所有两两程序之间长度超过某个阈值的最大精确匹配对; 它可以检测系统(参数化)名称变更和重新排序的大模块,但是它却检测不到插入注释,或者某个小范围的语句重新排序。Aiken的MOSS工程是在加州大学伯克利分校开发用于的剽窃检测的,也检查程序结构,但其基本算法比较尚未公开。其他用于剽窃检测的工具[10、11]是基于启发式方法的,测量统计变量和标识符的出现频率,从而可能具有较高的误报率。
2. 字符串匹配算法
本节我们通过SIM比较两个程序来介绍字符串匹配算法,对齐的两个字符串s和t(可能是不同长度)是通过插入空格使得他们的长度变成一样。请注意,有许多可能的排列。比如,两个字符串masters和stars的两种情况:
masters masters
sta rs stars
考虑双字符匹配:匹配分数m,不匹配分数d和差距分数g, m、d、g是选择的值。每一组的得分是各个得分的总和,排名最高的分数作为该组匹配的值。在上面的例子中,如果m = 1,d = -1,g = -2,然后“rs/rs”和“sters/stars” 分别是第一和第二组匹配中最高分。那么匹配得分分别是2和3。序列匹配分数用于测量两个几乎相同的对象之间的相似性,并与编辑距离有关。字符串匹配适合不精确匹配并广泛用于计算生物学检测DNA链[6、12]之间的关系。
两个字符串之间的最优匹配的得分是所有两两匹配中的最高得分。这个值可以使用动态规划算法来计算。形式上,我们给定两个字符串s和t,定义D(i,j)为两个字符串s[1..i] 和 t[1..j]之间的最优匹配分数。max(D(i,j))(其中1=<i<=|s|,1=<j<=|t|)是我们所期望的值。定义
下面的递归关系给了我们一个计算方法来得到解决方案:
边界条件由D(1,i)=i*g和D(j,1)= j*g给出。矩阵D的元素可以通过边界条件的第一行和列初始化,然后按照由左到右,从上到下的元素进行计算得到。这是可能的,因为D(i,j)的值仅取决于D(i- 1,j - 1),D(i - 1,j)和D(i,j- 1)。该匹配的最好时间复杂度为O(|s||t|)。因为每次计算只需要两排,故空间复杂度是O(max(|s|,|t|))。
3.设计和实现
SIM是由1780行C++代码和393行Tcl / Tk代码实现。SIM的设计显示在一个方框图中,如图一:
每个输入C程序首先通过一个词法分析器产生紧凑的结构形式的一个整数流称为标记。词法分析器由Unix命令flex自动生成并给出C语法的一个适当的子集,所以SIM程序可以很容易地修改以用于其他语言。每个标记代表一个算术或逻辑操作,一个标点符号,一个C宏,一个字,一个数字或字符串常量,注释,或一个标识符。例如for语句for(i = 0; i < max; i++)将会被一下标记流TKN_FOR TKN_LPAREN TKN_ID_I TKN_EQUALS
TKN_ZERO . . .取代。
标记符关键字和特殊符号是预定义的,那些标识符使用一个符号表(所有程序共用)动态分配,使得两次出现的变量名是由两个整数替换。每个注释,无论多长,都被替换为固定标记,所有空格将被丢弃。这个标记化过程的目的是减少转换为解析树的代码,这通常要舍弃一大部分,并且执行比较之前除去非必需的信息,如空格和注释。
当两个源文件都标记化后,第二程序的标记流被分成段,每一段表示原始程序的一个模块。然后每个这样的模块,与所述第一程序的标记流分别匹配。这种技术允许SIM检测程序模块的位置排列打乱情况下的相似性。实际的匹配使用以下方案进行:
l 一个涉及两个标识符标记匹配记2分;其他匹配记1分;
l 有产生记-2分;
l 涉及两个标识符不匹配记0分;其它不匹配记-2分。
该方案的理由很明显:标识符匹配被认为是非凡的,因而得分最高,而我们认为两个标识符之间的名称不匹配是显然低于一个标识符和一个操作符之间的结构性不匹配的。总匹配得分由各个块得分累加,然后归一化到一个0.0~1.0之间的数,通过匹配分数之和除以它的第一个和第二个程序本身。即:S=(2*score(p1,p2))/(score(p1,p1)+score(p2,p2)).
Tcl / Tk提供了图形用户界面允许比较的参考文件的集合文件和结果的显示并以一个条形图的形式打印出来。图2和图3展示的示例输出屏幕和一个文件选择框。红色、黄色和绿色条状根据用户指定的阈值减少参考程序的相似性。另外还使用GNUPlot在一个单独的界面提供显示结果。
4.实验设置和结果
下面一个简单的实验中,我们将一个简单的C程序,尽可能改变所有的变量名和函数名,删除注释,并尽可能颠倒相邻语句的顺序。程序如图4和5所示。在这个例子中,SIM得分是0.4,一般情况下足以检查低年级学生的程序了。
为了解释得分值,我们发现它根据每组程序把所有的SIM分数绘制成曲线,比使用一套固定的阈值更可靠。为了说明这一点,我们用一组由最近我们部门提交的36个低年级的计算机科学课程的实际作业程序来对SIM进行测试。每个程序分别被用作参考程序对其他程序比对,并将分数表列于一柱状图中。从一个这样的组比较的结果示于图6中。
在该图中,最左边栏是针对自己作为参考程序,得分是1.0。最右边两个程序(37和38)中,修改引用程序的作者为了减少分数通过删除或移动注释,重新排列块,改变变量名,添加冗余双“{}”。 正如所料, 早些时候五个由老师决定要修改的参考程序项目,取得了显著的结果。
整组630个比较统计数据涉及有关文件大小,标记数组大小,运行时间如图7所示。程序大小以字节为单位,时间以秒为单位。使用的平台是一台奔腾200Mhz工作站,运行在Debian GNU / Linux 1.3下。
我们从不同的低级别反复收集一大批的作业程序用于测试,最后教师没有发现任何剽窃。一个典型的组针对一个参考方案的比较示于图8,整个组的统计示于图9。
5. 讨论
我们设计和实现了一个软件工具,用于测量两个C程序之间的相似性,可以用来检测计算机科学课程编程作业低级剽窃。我们发现我们的工具对于检测常见修改变量名, 语句和函数重排列,添加/删除注释和空白等方面是足够的。关于构建工具来协助评估作业还有许多工作有待完成。
正如我们前面提到的,SIM可以不仅扩展到评估代码风格独特性,而且可以判断计算机程序的正确性,这一工具可以用于测试交互式初级编程课程。
通过已知的方法来减少二次底层字符串匹配算法的运行时间,我们计划在未来版本的SIM中实现,不过似乎也没有必要对每一组可能的程序运行SIM来检测剽窃。由于作弊事件的数量通常是很小范围的,SIM足以帮助我们发现排名相似分数最高的一组程序。如果没有作弊的证据来证明这组程序作假,老师则不需要检查其他的了。目前,我们正在致力于二次分段(sub-quadratic)算法寻找最高匹配对。