数据库有十万条数据,比较的规则是,第一条和第二条后面的所有数据进行比较,第二条和后第三条后面的所有数据进行比较,以此类推。。。比较所有的数据,所比较的数据是根据所选择的几个列的数据进行相应列的对比。这个过程非常慢,据说用哈希可以提高速度,但是针对我们这样的数据结构不知道如何构造哈希表,有没有大神知道怎么样解决这个问题,小弟在这里请教。。。。这个问题困扰了我很久都不能解决,求解决方案?
我们是在程序端(用winform)进行循环对比的,用了双循环。
以下是可能比较的字段:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.ConstrainedExecution; namespace ZuoEr.Collections.Generic { internal static class HashHelpers { internal static readonly int[] primes = { 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] internal static bool IsPrime(int candidate) { if ((candidate & 1) != 0) { int limit = (int)Math.Sqrt(candidate); for (int divisor = 3; divisor <= limit; divisor += 2) { if ((candidate % divisor) == 0) return false; } return true; } return (candidate == 2); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] internal static int GetPrime(int min) { if (min < 0) throw new ArgumentException("Arg_HTCapacityOverflow"); for (int i = 0; i < primes.Length; i++) { int prime = primes[i]; if (prime >= min) return prime; } for (int i = (min | 1); i < Int32.MaxValue; i += 2) { if (IsPrime(i)) return i; } return min; } } }
using System; using System.Collections.Generic; using System.Text; namespace ZuoEr.Collections.Generic { public sealed class HashTree<T> { private int count; private int freeList; private int freeCount; private int[] buckets; private Eny<T, HashTree<T>>[] entries; private IEqualityComparer<T> comparer; private HashTree<T> parent; private struct Eny<K, V> { public K key; public V value; public bool active; public int hashCode; public int next; } private HashTree(HashTree<T> parent, IEqualityComparer<T> comparer) { this.parent = parent; this.comparer = comparer; } private IEqualityComparer<T> Comparer { get { return this.comparer ?? (this.comparer = EqualityComparer<T>.Default); } } private void GetHashIndex(T item, out int num, out int idx) { idx = (num = this.Comparer.GetHashCode(item) & 0x7fffffff) % this.buckets.Length; } private bool GetExists(T item, int num, int idx) { return this.entries[idx].hashCode == num && this.Comparer.Equals(item, this.entries[idx].key); } private void Initialize() { this.entries = new Eny<T, HashTree<T>>[0]; this.Resize(); } private void Resize() { var prime = HashHelpers.GetPrime(this.count * 2); var buckets_temp = new int[prime]; for (var i = 0; i < buckets_temp.Length; i++) buckets_temp[i] = -1; var entries_temp = new Eny<T, HashTree<T>>[prime]; Array.Copy(this.entries, 0, entries_temp, 0, this.count); for (var i = 0; i < this.count; i++) { var p = entries_temp[i].hashCode % prime; entries_temp[i].next = buckets_temp[p]; buckets_temp[p] = i; } this.buckets = buckets_temp; this.entries = entries_temp; } public int Count { get { return this.count - this.freeCount; } } public int Level { get { return this.Parent != null ? this.Parent.Level + 1 : 0; } } public IEnumerable<T> Values { get { int i; if ((i = this.Count) == 0) yield break; foreach (var item in this.entries) { if (item.active) { i--; yield return item.key; } if (i == 0) break; } } } public HashTree<T> Parent { get { return this.parent; } } public HashTree<T> this[T item] { get { HashTree<T> r; if (this.TryGetValue(item, out r)) return r; throw new KeyNotFoundException(); } } public HashTree<T> Add(T item) { return this.Insert(item, false); } public HashTree<T> Insert(T item, bool ignoreRepeat) { if (item == null) throw new ArgumentNullException(); if (this.buckets == null) this.Initialize(); int num, idx, freeList; this.GetHashIndex(item, out num, out idx); for (var i = this.buckets[idx]; i >= 0; i = this.entries[i].next) { if (this.GetExists(item, num, i)) { if (ignoreRepeat) return this.entries[i].value; throw new ArgumentException(); } } if (this.freeCount > 0) { freeList = this.freeList; this.freeList = this.entries[freeList].next; this.freeCount--; } else { if (this.count == this.entries.Length) { this.Resize(); idx = num % this.buckets.Length; } freeList = this.count; this.count++; } var r = new HashTree<T>(this, this.comparer); this.entries[freeList].active = true; this.entries[freeList].hashCode = num; this.entries[freeList].next = this.buckets[idx]; this.entries[freeList].key = item; this.entries[freeList].value = r; this.buckets[idx] = freeList; return r; } public bool TryGetValue(T item, out HashTree<T> result) { if (item == null) throw new ArgumentNullException(); if (this.buckets != null) { int num, idx; this.GetHashIndex(item, out num, out idx); for (var i = this.buckets[idx]; i >= 0; i = this.entries[i].next) { if (this.GetExists(item, num, i)) { result = this.entries[i].value; return true; } } } result = null; return false; } public bool Contains(T item) { if (item == null) throw new ArgumentNullException(); if (this.buckets != null) { int num, idx; this.GetHashIndex(item, out num, out idx); for (var i = this.buckets[idx]; i >= 0; i = this.entries[i].next) { if (this.GetExists(item, num, i)) return true; } } return false; } public bool Remove(T item) { if (item == null) throw new ArgumentNullException(); if (this.buckets != null) { int num, idx, fit = -1; this.GetHashIndex(item, out num, out idx); for (var i = this.buckets[idx]; i >= 0; i = this.entries[i].next) { if (this.GetExists(item, num, i)) { if (fit < 0) this.buckets[idx] = this.entries[i].next; else this.entries[fit].next = this.entries[i].next; this.entries[i].active = false; this.entries[i].hashCode = -1; this.entries[i].next = this.freeList; this.entries[i].key = default(T); this.entries[i].value = null; this.freeList = i; this.freeCount++; return true; } fit = i; } } return false; } public HashTree() : this(null, null) { } public HashTree(IEqualityComparer<T> comparer) : this(null, comparer) { } } }
// 获得差级 public static IEnumerable<DynamicObject> Except<TKey>(this IEnumerable<DynamicObject> owner, IEnumerable<DynamicObject> child, IEnumerable<string> properties, Func<object, TKey> keySelector, IEqualityComparer<TKey> comparer) { var hash = new HashTree<TKey>(comparer); // 将比较的属性 依次存入 foreach (var item in child) { var node = hash; foreach (var meta in properties) { node = node.Insert(keySelector.Invoke(((IDictionary<string, object>)item)[meta]), true); } } // 取出差级 foreach (var item in owner) { var node = hash; var resl = true; foreach (var meta in properties) { if (!(resl &= node.TryGetValue(keySelector.Invoke(((IDictionary<string, object>)item)[meta]), out node))) break; } if (!resl) yield return item; } }
另外还有 简单的方法
1. 使用 IDictionary<string, IDictionary<string, ....>>
2. 使用 System.Data.DataTable + System.Data.DataRelation
@过于执著: 我先研究下你的代码是否符合我的需求,不明白的再问下你。
@枫轻: 其实使用 System.Data.DataTable + System.Data.DataRelation
就可实现你的需求 ( 和 在数据库 表连接 效果是一样的)
上面的代码 用来装逼不错,
@过于执著: 呵呵,看着你给的代码都不知道怎么装逼,不知道如何下手。
@枫轻: 你先把 第一段代码, 第二段代码 拷贝,
然后 把你定义的对象, 和比较的属性 给出来
@过于执著: 我修改了问题,添加了表结构和可能要比较的列,你看下怎么定义对象和比较的属性。
@枫轻: 两个数组? 属性都一样? 和谁比写以下,
@过于执著: 两两比较相应的列,这个比较的时候会通过一个算法计算出相似度,然后通过一个公式计算所有的列的综合值。这个计算我们已经定义好了,关键就是那个对比过程特别慢。
@过于执著: 比如这样比较:单个字段的时候,第一行的字段1和第二行的字段2比较;多个字段的时候就类似相应列的比较。我已经修改了问题,贴出了表结构,你看一下。
@枫轻: 没听懂 也没看懂,
@枫轻: 把你的代码贴出来
1. Model
2. 循环
3. 所谓的 相似度 的算法.
@过于执著:
try
{
string sqlGroup = string.Format("{0}='{1}'", HCZB.DBDefinition.GroupTable.Column_Active.Name, "1");
List<HCZB.Business.Group> groupList = HCZB.Business.Group.GetAllListBy(sqlGroup, "");
if (groupList[0].Active == 1)
{
string sql = string.Format("{0}='{1}'", HCZB.DBDefinition.MatchFieldTable.Column_GroupId.Name, groupList[0].GroupId);
List<HCZB.Business.MatchField> ds_matchfield = HCZB.Business.MatchField.GetAllListBy(sql, "");
string[] c = new string[ds_matchfield.Count];
string[] d = new string[ds_matchfield.Count];
string[] e = new string[ds_matchfield.Count];
for (int i = 0; i < ds_matchfield.Count; i++)
{
c[i] = ds_matchfield[i].MatchThreshold.ToString();
d[i] = ds_matchfield[i].Weight.ToString();
e[i] = ds_matchfield[i].Field.ToString();
}
DataSet ds = HCZB.Business.CustomerInfo.GetAllCustomerInfo();//获取表的所有数据
//DateTime dt1 = DateTime.Now;
if (ds != null && ds.Tables[0].Rows.Count > 0)
{
int i = 0;
int j = 0;
int masterindex = 0;
while (i < ds.Tables[0].Rows.Count)//第一个循环
{
masterindex++;
List<HCZB.Business.OutPutTable> outPutTableList = HCZB.Business.OutPutTable.GetAllListBy(string.Format("{0}='{1}'", HCZB.DBDefinition.OutPutTableTable.Column_Identifier.Name, int.Parse(ds.Tables[0].Rows[i]["id"].ToString())), "");
if (outPutTableList.Count == 0)
{
HCZB.Business.OutPutTable outPutTable = new HCZB.Business.OutPutTable();
outPutTable.Identifier = int.Parse(ds.Tables[0].Rows[i]["id"].ToString());
outPutTable.Masterindex = masterindex;
outPutTable.Linkedtime = DateTime.Now;
outPutTable.MatchRate = "0%";
outPutTable.Insert();
string[] a = new string[e.Length];
for (int k = 0; k < e.Length; k++)
{
a[k] = ds.Tables[0].Rows[i][e[k]].ToString();
}
j = i + 1;
string MemGender = ds.Tables[0].Rows[i]["MemGender"].ToString();
DataRow[] dr = ds.Tables[0].Select(string.Format("MemGender='{0}' or ISNULL(MemGender,'')='{0}'", MemGender));
DataTable dt2 = ToDataTable(dr);
while (j < dt2.Rows.Count)//第二个循环
{
string[] b = new string[e.Length];
for (int k = 0; k < e.Length; k++)
{
b[k] = dt2.Rows[j][e[k]].ToString();
}
float value = StringCompareOthers(a, b, c, d);//这里是用算法
if (value > groupList[0].IntegrateValue)
{
List<HCZB.Business.OutPutTable> outPutTableList1 = HCZB.Business.OutPutTable.GetAllListBy(string.Format("{0}='{1}'", HCZB.DBDefinition.OutPutTableTable.Column_Identifier.Name, int.Parse(dt2.Rows[j]["id"].ToString())), "");
if (outPutTableList1.Count == 0)
{
HCZB.Business.OutPutTable outPutTable1 = new HCZB.Business.OutPutTable();
outPutTable1.Identifier = int.Parse(dt2.Rows[j]["id"].ToString());
outPutTable1.Masterindex = masterindex;
outPutTable1.Linkedtime = DateTime.Now;
outPutTable1.MatchRate = (value * 100).ToString("0.00") + "%";
outPutTable1.Insert();
}
else
{
List<HCZB.Business.OutPutTable> list = HCZB.Business.OutPutTable.GetAllListBy("", "Masterindex desc");
masterindex = int.Parse(list[0].Masterindex.ToString());
}
}
j++;
}
}
else
{
List<HCZB.Business.OutPutTable> list = HCZB.Business.OutPutTable.GetAllListBy("", "Masterindex desc");
masterindex = int.Parse(list[0].Masterindex.ToString());
}
i++;
}
ds.Dispose();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
@枫轻: StringCompareOthers 方法也贴出来下,
@枫轻: 我还是仅给你, 你的 "解决方案" 吧,
1.只需要访问一条数据的地方, 不要把整个集合都拿过来 如 : groupList , list 变量
2.把需要访问 Count 的地方, 不要把整个集合都拿过来 如 : outPutTableList, outPutTableList1 变量
3.把 循环内 的数据库访问 提到外面来 如: OutPutTable
这样可以稍稍缓解下,
你不给出 你的 "相似度" 算法, 根本不不可能 用数据结构能解决它,
若真是你数据上万 即使没有 数据库操作,
万行 * 万行 * 字段数 的字符串比较都需要好几分钟 乃至 十来分钟,
@过于执著: 那个相似度算法是levenshtein distance,你可以查查,两个字符串的相似度的计算。
你的那些建议,我会参考。这个程序主要是循环次数过多导致过慢。
@过于执著: 说实话 你只要告诉他怎么降低时间复杂度就好了
例如字典 其他的 让他自己写被
你具体的需求是什么?是取出所有重复的数据?
就是循环对比,然后通过算法找出相似度比较高的数据。
@枫轻: 对比列加索引之后直接用sql语句要快的多
@nicky0227: 试过用存储过程,加了索引速度还是没有程序端快,现在的主要问题是,因为用了双循环导致循环对比次数过程过多而导致程序过慢。
@nicky0227: 我们的那些比较列是动态获取的。
@枫轻: “通过算法找出相似度比较高的数据” 这应该是你想出来的解决方案吧!你这个方案究竟是想解决什么问题?
@Launcher: 通过算法已经可以找出相似度比较的内容。就是找出相似的数据。
@枫轻: 你提问题的方式有问题,根据你目前提供的信息,我给你梳理下。
已知集合 CustomerInfo :{C1,C2,C3,...Cn} ( 0 < n < 100000)
已知相似度函数:Match
如果 Match(Ci,Cj)(0 < i < n,0 < j < n) 为 true,则表示 Ci 与 Cj 相似。
求所有 Match(Ci,Cj)。
那么针对此问题的最简单的解决方式就是遍历集合,对每一个元素同除它自身之外的其它元素应用 Match 方法。这才是你应该提出的问题,但是你却提出了一个规则:比较的规则是,第一条和第二条后面的所有数据进行比较,第二条和后第三条后面的所有数据进行比较,以此类推。。。很明显,你这个规则是对前面算法的一个优化举措,因为 Match(Ci,Cj) == Match(Cj,Ci),也就是说这是组合问题,不是排列问题。
所以我才接着追问,因为我怕就连这都只是你提出的解决实际问题的方案。如果你的实际问题就是“求所有 Match(Ci,Cj)”的话,那么我们可以讨论如何优化此解决方案,而不是去讨论针对你的实际要解决的问题的其它可选解决方案。
@Launcher: 目前的规则是,第一条数据和其他数据进行对比,匹配的记录存储在output表中,第一个循环完成之后,把所有未匹配的找到,从未匹配的数据中又执行第一次的规则从第一条开始和其他剩余的比较,以此类推,我们现在需要能够提高效率的解决方案.
例如:
第一行: a
第二行: b
第三行: c
第四行: a
第五行: e
结果:第一行和第四行匹配上就插入到output表中。
剩下的二、三、五行重新循环比较。
@枫轻: 按照 a,b,c,d,e,f 来举例:
拿出 a,有匹配为 b,d,那么得到未匹配集合: c,e,f
拿出 c,有匹配 f,那么得到未匹配集合:e
结束,是这样吗?
@Launcher: 嗯,是这样的。我们程序是这样设计的,双循环跑(内循环外循环),
int[] arr=new int[a,b,c,d,e,f];
for(int i=0;i<arr.length;i++)
{
string a=arr[i];
for(int j=i+1;j<arr.length;j++)
{
string b=arr[j];
//算法比较
int value=算法(a,b);
if(value>0.8)
{
入库
}
}
}
@枫轻:
按照 a,b,c,d,e,f 来举例:
拿出 a,有匹配为 b,d,那么得到未匹配集合: c,e,f
拿出 c,有匹配 f,那么得到未匹配集合:e
你再好好看看,是不是这样,因为你写的那段代码跟上面的描述是不符合的。
@Launcher: 就是这样的逻辑,但是实现起来方式不是,我们那个代码虽然那样循环,但是如果已经插入过的数据就不再插入的,有做判断。
@枫轻: 按照你写的代码,当先拿出 arr[0](a)匹配完后,第二次你拿出的是 arr[1](b),而不是从未匹配集合(c,e,f)中取出 c 来匹配。
@Launcher: 不好意思,那个代码不是完整的,如果第二次你拿出是arr[1](b),则判断数据库中是否已有匹配,有则直接跳过,进入下一次循环。
@枫轻: 我们假设在集合{a,b,c,d,e,f}中,所有元素的相似度都是 1,那么第一次循环就能得到最终结果,但是按照你写的代码,你还需要做 5 次查询。
@枫轻:
根据你写的代码,最终结果是否同集合中记录的顺序有关?
根据你提供的相似度算法,如果 dist(a,b) > 0.8,dist(b,c) > 0.8,能否推导出 dist(a,c) > 0.8?
@Launcher: 是的。
@Launcher: 没有考虑到这个推导,感觉应该是可以的。
@枫轻: 你不用感觉,你写的代码就默认了这个推导是成立的,因此你写的算法与集合中记录的顺序无关;否则,你的算法就依赖集合中记录的顺序,那么你写的算法就是错误的。如果是前者,那么你的算法就会产生 5 次无效的查询操作,这是可以优化的地方。
这种问题取得突破的关键在于并行。
排序一下,遍历一下
所比较的数据是根据所选择的几个列的数据进行相应列的对比
关键是要做什么比较?大数据操作的问题自己写算法肯定不靠谱,我的经验是交给数据库去操作