今天我寫一篇關於查重算法的例子,查重算法一般在網上資源比較少,如果搜索的話我建議搜索關鍵字“查重算法+空間向量+余弦定理”;為啥這么搜索呢,接下來我先講一下空間向量和余弦定理跟查重算法的關系:
原文地址:http://www.cnblogs.com/sixiangqimeng/p/3304768.html
相信很多學習向量空間模型(Vector Space Model)的人都會被其中的余弦定理公式所迷惑

因為一看到余弦定理,肯定會先想起初中時的那條最簡單的公式cosA=a/c(鄰邊比斜邊),見下圖:

但是,初中那條公式是只適用於直角三角形的,而在非直角三角形中,余弦定理的公式是:
cosA=(c2 + b2 - a2)/2bc
不過這條公式也和向量空間模型中的余弦定理公式不沾邊,迷惑..
-------------------引用開始分界線------------------------
假定三角形的三條邊為 a, b 和 c,對應的三個角為 A, B 和 C,那么角 A 的余弦
![]()
如果我們將三角形的兩邊 b 和 c 看成是兩個向量,那么上述公式等價於

其中分母表示兩個向量 b 和 c 的長度,分子表示兩個向量的內積。
舉一個具體的例子,假如新聞 X 和新聞 Y 對應向量分別是x1,x2,...,x64000 和y1,y2,...,y64000,
那么它們夾角的余弦等於
-------------------引用完畢分界線------------------------
高中那條公式又怎么會等價於向量那條公式呢?
原來它從高中的平面幾何跳躍到大學的線性代數的向量計算..
在線性代數的向量計算的余弦定理中,
* 分子是兩個向量的點積(wiki),點積的定理和計算公式:
The dot product of two vectors a = [a1, a2, … , an] and b = [b1, b2, … , bn] is defined as:

點積(dot product),又叫內積,數量積..(Clotho注: product常見的是產品的意思,但在數學上是乘積的意思.)
* 分母是兩個向量的長度相乘.這里的向量長度的計算公式也比較難理解.
假設是二維向量或者三維向量,可以抽象地理解為在直角坐標軸中的有向線段,如圖:

d2 = x2 + y2 -> d = sprt(x2 + y2)

d2 = x2 + y2 + z2 -> d = sprt(x2 + y2 + z2)
三維以上的維度很難用圖來表示,但是再多維度的向量,也仍然可以用這條公式來計算:
dn2 = x12 + x22 + .. + xn2 -> dn = sprt(x12 + x22 + .. + xn2)
在文本相似度計算中,向量中的維度x1,x2..xn其實就是詞項(term)的權重,一般就是詞項的tf-idf值.
而這條看上去很抽象的公式,其實就是為了計算兩篇文章的相似度.
![]()
文本相似度計算的處理流程是:
1.對所有文章進行分詞
2.分詞的同時計算各個詞的tf值
3.所有文章分詞完畢后計算idf值
4.生成每篇文章對應的n維向量(n是切分出來的詞數,向量的項就是各個詞的tf-idf值)
5.對文章的向量兩篇兩篇代入余弦定理公式計算,得出的cos值就是它們之間的相似度了
當兩條新聞向量夾角的余弦等於一時,這兩條新聞完全重復(用這個辦法可以刪除重復的網頁);當夾角的余弦接近於一時,兩條新聞相似,從而可以歸成一類;夾角的余弦越小,兩條新聞越不相關。
余弦定理是常見的相似度衡量方法之一,
7. 夾角余弦(Cosine)
有沒有搞錯,又不是學幾何,怎么扯到夾角余弦了?各位看官稍安勿躁。幾何中夾角余弦可用來衡量兩個向量方向的差異,機器學習中借用這一概念來衡量樣本向量之間的差異。
(1)在二維空間中向量A(x1,y1)與向量B(x2,y2)的夾角余弦公式:

(2) 兩個n維樣本點a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夾角余弦
類似的,對於兩個n維樣本點a(x11,x12,…,x1n)和b(x21,x22,…,x2n),可以使用類似於夾角余弦的概念來衡量它們間的相似程度。

即:

夾角余弦取值范圍為[-1,1]。夾角余弦越大表示兩個向量的夾角越小,夾角余弦越小表示兩向量的夾角越大。當兩個向量的方向重合時夾角余弦取最大值1,當兩個向量的方向完全相反夾角余弦取最小值-1。
原文地址:http://www.cnblogs.com/sixiangqimeng/p/3304768.html
下面我將就這自己的項目進行講解,而且我的項目中由於業務需求,需要對試題進行查重,這樣的話一般的科目沒問題但是我們公司數學里面的公式,都是圖片格式存儲的,在用這個查重就不行了。我又自己創新了一個
圖片查重程序,下面我將進行講解:
public class Program { static void Main(string[] args) { FindDuplicate(68); } public static List<int> FindDuplicate(int id) { ModelOPE.Controller controller = new ModelOPE.Controller(); string sql = "select ID,BankID,QuesBody from " + Model.QuesModel.TableName + " where ID=@id"; ModelOPE.SqlParams parameters = new ModelOPE.SqlParams(); parameters.Add("@id", id); List<Model.QuesModel> modelList = new List<QuesModel>(); modelList = controller.Query<Model.QuesModel>(sql, parameters.toArray()); if (modelList.Count == 0) Library.Debug("ID=" + id.ToString() + "的試題不存在。"); Model.QuesModel Ques = modelList[0]; int bankid = Ques.BankID; List<int> idList = new List<int>(); string text1 = string.Empty; string text2 = string.Empty; TextCompare compare; text1 = Library.RemoveHtml(Ques.QuesBody); compare = new TextCompare(text1, ""); string freqWord = compare.GetHighFreqWord(); modelList = HubbleQues.QuesSearch(freqWord, bankid, 0, 0, "", "", 1, 5, true, false); //string msg = "提取的關鍵字【" + freqWord + "】"; //msg += "雷同試題個數" + modelList.Count + ";"; //System.Web.HttpContext.Current.Response.Write(msg); //if (modelList.Count > 0) //{ // foreach (Model.QuesModel ques in modelList) msg += "," + ques.ID; //} foreach (Model.QuesModel model in modelList) { text2 = Library.RemoveHtml(model.QuesBody); compare = new TextCompare(text1, text2); double similar = compare.GetSimilar(); if (similar > 0.9) idList.Add(model.ID); } //if (idList.Count > 0) //{ // foreach (int idL in idList) // { // msg += ",雷同試題ID=" + idL; // } //} //System.Web.HttpContext.Current.Response.Write(msg); return idList; } } /// <summary> /// 文本相似度計算及提取高頻關鍵字類 /// </summary> public class TextCompare { private string TextStr1; private string TextStr2; /// <summary> /// 構造函數 /// </summary> /// <param name="RefStr1"></param> /// <param name="RefStr2"></param> public TextCompare(string RefStr1, string RefStr2) { TextStr1 = RefStr1; TextStr2 = RefStr2; } /// <summary> /// 獲取相似度 /// </summary> /// <returns></returns> public double GetSimilar() { string UniteQuesText = TextStr1 + TextStr2; string TextVector1 = GetTextVector(TextStr1, UniteQuesText); string TextVector2 = GetTextVector(TextStr2, UniteQuesText); return CalculateVectorCos(TextVector1, TextVector2); } public string GetHighFreqWord() { string UniteQuesText = TextStr1 + TextStr2; Hubble.Analyzer.PanGuAnalyzer analyzer = new Hubble.Analyzer.PanGuAnalyzer(); IEnumerable<Hubble.Core.Entity.WordInfo> words = analyzer.Tokenize(UniteQuesText); StringBuilder result = new StringBuilder(); List<string> wordList = new List<string>(); foreach (Hubble.Core.Entity.WordInfo word in words) { if (word.Word.Length <= 1) continue; if (word.Rank <= 1) continue; if (Regex.Matches(word.Word, "[0-9]").Count > 0) continue; wordList.Add(word.Word); } if (wordList.Count < 10) { foreach (string word in wordList) { if (result.ToString().IndexOf(word) < 0) result.Append((result.Length > 0) ? " " + word : word); } } else { var res = Convert.ToInt32(Math.Floor(Convert.ToDouble(wordList.Count / 3)));//前中后三段 for (var i = 0; i < 3; i++)//3段 { for (var j = 0; j < 3; j++)//每一段取前3個 { var word = wordList[i * res + j]; if (result.ToString().IndexOf(word) < 0) result.Append((result.Length > 0) ? " " + word : word); } } } return result.ToString(); } /// <summary> /// 清理 /// </summary> public void Close() { TextStr1 = ""; TextStr2 = ""; TextStr1 = null; TextStr2 = null; } /// <summary> /// 獲得文本的空間向量 /// </summary> /// <param name="QuesTextFull"></param> /// <returns></returns> private string GetTextVector(string QuesText, string UniteQuesText) { //PanGuTokenizer Tokenizer = new PanGuTokenizer(); //ICollection<WordInfo> words = Tokenizer.SegmentToWordInfos(UniteQuesText);//所有的單詞 //Tokenizer.Close(); Hubble.Analyzer.PanGuAnalyzer analyzer = new Hubble.Analyzer.PanGuAnalyzer(); IEnumerable<Hubble.Core.Entity.WordInfo> words = analyzer.Tokenize(UniteQuesText); List<string> wordList = new List<string>(); List<double> wordWeight = new List<double>(); StringBuilder result = new StringBuilder(); MatchCollection mc = null; double tmpweight = 0; double maxweight = 0; foreach (Hubble.Core.Entity.WordInfo word in words) { if (word.Word.Length <= 1 || wordList.Contains(word.Word)) continue; if (word.Word.Contains("+") || word.Word.Contains("-") || word.Word.Contains("+") || word.Word.Contains("—")) continue;//2013.2.21添加 mc = Regex.Matches(QuesText, word.Word); tmpweight = mc.Count; wordList.Add(word.Word); wordWeight.Add(tmpweight); if (tmpweight > maxweight) maxweight = tmpweight; } double calu = 0; foreach (double weight in wordWeight) { calu = Math.Round((weight / maxweight), 3); if (result.Length == 0) result.Append(calu.ToString()); else result.Append("," + calu.ToString()); } wordList.Clear(); wordWeight.Clear(); return result.ToString(); } //計算兩空間向量的相似度(余弦定理) private double CalculateVectorCos(string vector1, string vector2) { string[] vector1List = vector1.Split(','); string[] vector2List = vector2.Split(','); List<double> v1 = new List<double>(); List<double> v2 = new List<double>(); double tmp = 0; foreach (string v in vector1List) { tmp = double.Parse(v); if (tmp >= 0) v1.Add(tmp); } foreach (string v in vector2List) { tmp = double.Parse(v); if (tmp >= 0) v2.Add(tmp); } if (v1.Count < v2.Count) { int s1 = v2.Count - v1.Count; for (int i = 0; i < s1; i++) v1.Add(0); } else if (v1.Count > v2.Count) { int s2 = v1.Count - v2.Count; for (int i = 0; i < s2; i++) v2.Add(0); } double a = 0, b = 0, c = 0; for (int i = 0, j = v1.Count; i < j; i++) { a += v1[i] * v2[i]; b += v1[i] * v1[i]; c += v2[i] * v2[i]; } return a / (Math.Sqrt(b) * Math.Sqrt(c));//使用余弦定理計算相似度 } }
這些代碼就是一般的查重程序,都是根據上面的公式進行計算,計算出重復數據;沒有什么特別注意的,只要按照公式來就行
下面主要講一下帶圖片的查重程序:這是網上流行的幾種圖片查重算法,第一種是我自創的。
public static int BitmapCompare1(Bitmap bitmap1, Bitmap bitmap2) { int result = 0; int result1 = 0; if (bitmap1 == null || bitmap2 == null) return -1; for (int i = 0; i < bitmap1.Width; i++) { for (int j = 0; j < bitmap1.Height; j++) { Color color1 = bitmap1.GetPixel(i, j); if (color1 == Color.FromArgb(0, 0, 0, 0)) { result++; } } } for (int i1 = 0; i1 < bitmap2.Width; i1++) { for (int j1 = 0; j1 < bitmap2.Height; j1++) { Color color2 = bitmap2.GetPixel(i1, j1); if (color2 == Color.FromArgb(0, 0, 0, 0)) { result1++; } } } return result - result1; } /// <summary> /// 比較兩幅圖片是否一致(使用memcmp方式) /// </summary> /// <param name="bitmap1">圖片1</param> /// <param name="bitmap2">圖片2</param> /// <returns>如果兩幅圖片相同,返回0;如果圖片1小於圖片2,返回小於0的值;如果圖片1大於圖片2,返回大於0的值。</returns> public static int BitmapCompare2(Bitmap bitmap1, Bitmap bitmap2) { int result = 0; //假設兩幅圖片相同 if (bitmap1 == null || bitmap2 == null) return -1; if (bitmap1.Width == bitmap2.Width && bitmap1.Height == bitmap2.Height) { BitmapData bmd1 = bitmap1.LockBits(new Rectangle(0, 0, bitmap1.Width, bitmap1.Height), ImageLockMode.ReadOnly, bitmap1.PixelFormat); BitmapData bmd2 = bitmap2.LockBits(new Rectangle(0, 0, bitmap2.Width, bitmap2.Height), ImageLockMode.ReadOnly, bitmap2.PixelFormat); int bytes = bmd1.Stride * bitmap1.Height; byte[] buff1 = new byte[bytes]; byte[] buff2 = new byte[bytes]; Marshal.Copy(bmd1.Scan0, buff1, 0, Marshal.SizeOf(typeof(byte)) * bytes); Marshal.Copy(bmd2.Scan0, buff2, 0, Marshal.SizeOf(typeof(byte)) * bytes); result = MemoryCompare(buff1, buff2); bitmap1.UnlockBits(bmd1); bitmap2.UnlockBits(bmd2); } else if (bitmap1.Width != bitmap2.Width) { result = bitmap1.Width - bitmap2.Width; } else if (bitmap1.Height != bitmap2.Height) { result = bitmap1.Height - bitmap2.Height; } return result; } /// <summary> /// 比較兩幅圖片是否一致(使用Marshal.ReadByte方式) /// </summary> /// <param name="bitmap1">圖片1</param> /// <param name="bitmap2">圖片2</param> /// <returns>如果兩幅圖片相同,返回0;如果圖片1小於圖片2,返回小於0的值;如果圖片1大於圖片2,返回大於0的值。</returns> public static int BitmapCompare3(Bitmap bitmap1, Bitmap bitmap2) { int result = 0; //假設兩幅圖片相同 if (bitmap1 == null || bitmap2 == null) return -1; if (bitmap1.Width == bitmap2.Width && bitmap1.Height == bitmap2.Height) { BitmapData bmd1 = bitmap1.LockBits(new Rectangle(0, 0, bitmap1.Width, bitmap1.Height), ImageLockMode.ReadOnly, bitmap1.PixelFormat); BitmapData bmd2 = bitmap2.LockBits(new Rectangle(0, 0, bitmap2.Width, bitmap2.Height), ImageLockMode.ReadOnly, bitmap2.PixelFormat); IntPtr start1 = bmd1.Scan0; IntPtr start2 = bmd2.Scan0; int sizeOfByte = Marshal.SizeOf(typeof(byte)); for (int i = 0; i < sizeOfByte * bmd1.Stride * bitmap1.Height; i++) { byte b1 = Marshal.ReadByte(start1, i); byte b2 = Marshal.ReadByte(start2, i); if (b1 != b2) { result = (int)(b1 - b2); break; } } bitmap1.UnlockBits(bmd1); bitmap2.UnlockBits(bmd2); } else if (bitmap1.Width != bitmap2.Width) { result = bitmap1.Width - bitmap2.Width; } else if (bitmap1.Height != bitmap2.Height) { result = bitmap1.Height - bitmap2.Height; } return result; } /// <summary> /// 比較兩幅圖片是否一致(使用自定義字節數組比較) /// </summary> /// <param name="bitmap1">圖片1</param> /// <param name="bitmap2">圖片2</param> /// <returns>如果兩幅圖片相同,返回0;如果圖片1小於圖片2,返回小於0的值;如果圖片1大於圖片2,返回大於0的值。</returns> public static int BitmapCompare4(Bitmap bitmap1, Bitmap bitmap2) { int result = 0; //假設兩幅圖片相同 if (bitmap1 == null || bitmap2 == null) return -1; if (bitmap1.Width == bitmap2.Width && bitmap1.Height == bitmap2.Height) { BitmapData bmd1 = bitmap1.LockBits(new Rectangle(0, 0, bitmap1.Width, bitmap1.Height), ImageLockMode.ReadOnly, bitmap1.PixelFormat); BitmapData bmd2 = bitmap2.LockBits(new Rectangle(0, 0, bitmap2.Width, bitmap2.Height), ImageLockMode.ReadOnly, bitmap2.PixelFormat); int bytes = bmd1.Stride * bitmap1.Height; byte[] buff1 = new byte[bytes]; byte[] buff2 = new byte[bytes]; Marshal.Copy(bmd1.Scan0, buff1, 0, Marshal.SizeOf(typeof(byte)) * bytes); Marshal.Copy(bmd2.Scan0, buff2, 0, Marshal.SizeOf(typeof(byte)) * bytes); result = MemoryCompare2(buff1, buff2); bitmap1.UnlockBits(bmd1); bitmap2.UnlockBits(bmd2); } else if (bitmap1.Width != bitmap2.Width) { result = bitmap1.Width - bitmap2.Width; } else if (bitmap1.Height != bitmap2.Height) { result = bitmap1.Height - bitmap2.Height; } return result; } /// <summary> /// 用memcmp比較字節數組 /// </summary> /// <param name="b1">字節數組1</param> /// <param name="b2">字節數組2</param> /// <returns>如果兩個數組相同,返回0;如果數組1小於數組2,返回小於0的值;如果數組1大於數組2,返回大於0的值。</returns> public static int MemoryCompare(byte[] b1, byte[] b2) { IntPtr retval = memcmp(b1, b2, new IntPtr(b1.Length)); return retval.ToInt32(); } /// <summary> /// 比較字節數組 /// </summary> /// <param name="b1">字節數組1</param> /// <param name="b2">字節數組2</param> /// <returns>如果兩個數組相同,返回0;如果數組1小於數組2,返回小於0的值;如果數組1大於數組2,返回大於0的值。</returns> public static int MemoryCompare2(byte[] b1, byte[] b2) { int result = 0; if (b1.Length != b2.Length) result = b1.Length - b2.Length; else { for (int i = 0; i < b1.Length; i++) { if (b1[i] != b2[i]) { result = (int)(b1[i] - b2[i]); break; } } } return result; } /// <summary> /// memcmp API /// </summary> /// <param name="b1">字節數組1</param> /// <param name="b2">字節數組2</param> /// <returns>如果兩個數組相同,返回0;如果數組1小於數組2,返回小於0的值;如果數組1大於數組2,返回大於0的值。</returns> [DllImport("msvcrt.dll")] private static extern IntPtr memcmp(byte[] b1, byte[] b2, IntPtr count);
我自創的根本原因就是發現下面的一些比較方法在有些條件下根本不行,比如說兩個圖片,一個圖片截圖比較大,一個圖片截圖比較小,但是內容是相同的,如圖,我用黑色背景比較容易明白,由於兩幅圖內容是一樣的,但是圖片大小不一樣,所以用網上流行的那些方法根本不靈。所以由於此原因,我自己研究發明了第一種方法(保證原創)。這查重程序結合着圖片查重程序一起使用才完美。

原文地址:http://www.cnblogs.com/sixiangqimeng/p/3304768.html
