項目
| 這個作業屬於哪個課程 | https://edu.cnblogs.com/campus/gdgy/Networkengineering1834 |
|---|---|
| 這個作業要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Networkengineering1834/homework/11146 |
| 這個作業的目標 | <實現論文查重算法,學會使用PSP表格估計,學會單元測試> |
PSP表格
| PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
|---|---|---|---|
| Planning | 計划 | 20 | 15 |
| Estimate | 估計這個任務需要多少時間 | 5 | 5 |
| Development | 開發 | 360 | 410 |
| Analysis | 需求分析 (包括學習新技術) | 20 | 20 |
| Design Spec | 生成設計文檔 | 0 | 0 |
| Design Review | 設計復審 | 0 | 0 |
| Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 10 | 10 |
| Design | 具體設計 | 20 | 20 |
| Coding | 具體編碼 | 240 | 280 |
| Code Review | 代碼復審 | 30 | 20 |
| Test | 測試(自我測試,修改代碼,提交修改) | 40 | 60 |
| Reporting | 報告 | 60 | 120 |
| Test Repor | 測試報告 | 20 | 20 |
| Size Measurement | 計算工作量 | 5 | 5 |
| Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 40 | 30 |
| 合計 | 510 | 605 |
模塊接口的設計
項目類圖

Simhash類中自定義了hash方法,實現simhash算法,並且得到了相應論文的數字指紋。ReadTxt類中實現了讀取文件和寫入文件功能。
關鍵算法流程
- 代碼原理
程序首先輸入抄襲論文和原版論文的絕對路徑,並且通過SimHash算法獲取兩個論文相應的Simhash對象hash1和hash2。
在Simhash的方法中,首先通過Java自帶的StringTokenizer實現對論文的分詞,並且定義一個與數字指紋等長的數組用於記錄文檔所有特征的向量和,然后重寫了hash方法對於每段分詞進行hash處理,並將其進行判斷處理得出文檔特征的向量和v[i],最后對數組進行判斷,大於0的記為1,小於等於0的記為0,得到一個 92bit 的數字指紋/簽名.
得到hash1和hash2之后,取出兩者的數字指紋傳入getDistance方法中,通過逐位比較兩個指紋的二進制數據,統計其中相似之處並返回一個整數dis以表示兩個數字指紋中總共相似的地方。最后通過dis/hashbit(hashbit是自定義的數字指紋位數,通過多次修改測試得出92位的數字指紋算出的結果比64位的數字指紋精確,且並不是越大越好)求出論文的相似率。
- 項目代碼流程

- 數字指紋的獲取

- 論文查重的原理

模塊接口部分的性能分析
- JProfiler性能分析

- 內容分析

模塊代碼測試及部分代碼展示
public class SimHash {
private String tokens;
private BigInteger intSimHash;
private String strSimHash;
private int hashbits;
public String getTokens() {
return tokens;
}
public void setTokens(String tokens) {
this.tokens = tokens;
}
public BigInteger getIntSimHash() {
return intSimHash;
}
public void setIntSimHash(BigInteger intSimHash) {
this.intSimHash = intSimHash;
}
public String getStrSimHash() {
return strSimHash;
}
public void setStrSimHash(String strSimHash) {
this.strSimHash = strSimHash;
}
public SimHash(String tokens) {
this.tokens = tokens;
this.intSimHash = this.simHash();
}
public SimHash(String tokens, int hashbits) {
this.tokens = tokens;
this.hashbits = hashbits;
this.intSimHash = this.simHash();
}
public BigInteger simHash() {
// 定義特征向量/數組
int[] v = new int[this.hashbits];
// 1、將文本去掉格式后, 分詞.
StringTokenizer stringTokens = new StringTokenizer(this.tokens, ",。!、:“”");
while (stringTokens.hasMoreTokens()) {
String temp = stringTokens.nextToken();
// System.out.println(temp);//查看拆分后的結果
// 2、將每一個分詞hash為一組固定長度的數列.比如 92bit 的一個整數.
BigInteger t = this.hash(temp);
for (int i = 0; i < this.hashbits; i++) {
BigInteger bitmask = new BigInteger("1").shiftLeft(i);
// 3、建立一個長度為92的整數數組(假設要生成92位的數字指紋,也可以是其它數字),
// 對每一個分詞hash后的數列進行判斷,如果是1000...1,那么數組的第一位和末尾一位加1,
// 中間的90位減一,也就是說,逢1加1,逢0減1.一直到把所有的分詞hash數列全部判斷完畢.
if (t.and(bitmask).signum() != 0) {
// 這里是計算整個文檔的所有特征的向量和
// 這里實際使用中需要 +- 權重,而不是簡單的 +1/-1,
v[i] += 1;
} else {
v[i] -= 1;
}
}
}
BigInteger fingerprint = new BigInteger("0");
StringBuffer simHashBuffer = new StringBuffer();
for (int i = 0; i < this.hashbits; i++) {
// 4、最后對數組進行判斷,大於0的記為1,小於等於0的記為0,得到一個 92bit 的數字指紋/簽名.
if (v[i] >= 0) {
fingerprint = fingerprint.add(new BigInteger("1").shiftLeft(i));
simHashBuffer.append("1");
} else {
simHashBuffer.append("0");
}
}
this.strSimHash = simHashBuffer.toString();
// 測試數字指紋
// System.out.println( this .strSimHash + " length " + this .strSimHash.length());
return fingerprint;
}
private BigInteger hash(String source) {
if (source == null || source.length() == 0) {
return new BigInteger("0");
} else {
char[] sourceArray = source.toCharArray();
BigInteger x = BigInteger.valueOf(((long) sourceArray[0]) << 7);
BigInteger m = new BigInteger("1000003");
BigInteger mask = new BigInteger("2").pow(this.hashbits).subtract(new BigInteger("1"));
for (char item : sourceArray) {
BigInteger temp = BigInteger.valueOf((long) item);
x = x.multiply(m).xor(temp).and(mask);
}
x = x.xor(new BigInteger(String.valueOf(source.length())));
if (x.equals(new BigInteger("-1"))) {
x = new BigInteger("-2");
}
return x;
}
}
public int hammingDistance(SimHash other) {
BigInteger x = this.intSimHash.xor(other.intSimHash);
int tot = 0;
// 統計x中二進制位數為1的個數
// 一個二進制數減去1,那么,從最后那個1(包括那個1)后面的數字全都反了,對吧,然后,n&(n-1)就相當於把后面的數字清0,
// 我們看n能做多少次這樣的操作就OK了。
while (x.signum() != 0) {
tot += 1;
x = x.and(x.subtract(new BigInteger("1")));
}
return tot;
}
public int getDistance(String str1, String str2) {
int distance;
if (str1.length() != str2.length()) {
distance = -1;
} else {
distance = 0;
for (int i = 0; i < str1.length(); i++) {
if (str1.charAt(i) == str2.charAt(i)) {
distance++;
}
}
}
return distance;
}
// public List subByDistance(SimHashDemo simHash, int distance) {
// // 分成幾組來檢查
// int numEach = this .hashbits / (distance + 1 );
// List characters = new ArrayList();
//
// StringBuffer buffer = new StringBuffer();
//
// int k = 0 ;
// for ( int i = 0 ; i < this .intSimHash.bitLength(); i++) {
// // 當且僅當設置了指定的位時,返回 true
// boolean sr = simHash.intSimHash.testBit(i);
//
// if (sr) {
// buffer.append( "1" );
// } else {
// buffer.append( "0" );
// }
//
// if ((i + 1 ) % numEach == 0 ) {
// // 將二進制轉為BigInteger
// BigInteger eachValue = new BigInteger(buffer.toString(), 2 );
// System.out.println( "----" + eachValue);
// buffer.delete( 0 , buffer.length());
// characters.add(eachValue);
// }
// }
//
// return characters;
// }
測試
請輸入抄襲版論文的文件的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig_0.8_add.txt
請輸入論文原文的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
論文相似率:0.88
請輸入抄襲版論文的文件的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig_0.8_del.txt
請輸入論文原文的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
論文相似率:0.97
請輸入抄襲版論文的文件的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig_0.8_dis_1.txt
請輸入論文原文的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
論文相似率:0.95
請輸入抄襲版論文的文件的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig_0.8_dis_10.txt
請輸入論文原文的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
論文相似率:0.86
請輸入抄襲版論文的文件的絕對路徑C:\\Users\\10973\\Desktop\\test\\orig_0.8_dis_15.txt
請輸入論文原文的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
論文相似率:0.64
請輸入抄襲版論文的文件的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
請輸入論文原文的絕對路徑:C:\\Users\\10973\\Desktop\\test\\orig.txt
論文相似率:1.00
代碼指紋樣板

模塊異常處理說明
- 路徑出錯(java.io.FileNotFoundException)

