C# 如何使用大数据集最小化运行时间(从93773对象列表中列出唯一对象)
我们正在从EVE在线API中提取大量JSON对象,并使用Newtonsoft.JSON.JsonConvert将它们反序列化为EveObjModel对象。在此基础上,我们希望创建一个独特对象的列表,即每种类型id中最昂贵的对象。我也在下面粘贴了dataContract 问题:下面的代码可以处理较小的数据集,但无法处理较大的数据量。目前,我们正在运行它,它需要超过50分钟(并且正在计算)。我们可以做些什么来将运行更大数据集所需的时间减少到可承受的水平 谢谢你抽出时间。祝你好运C# 如何使用大数据集最小化运行时间(从93773对象列表中列出唯一对象),c#,algorithm,json.net,runtime,C#,Algorithm,Json.net,Runtime,我们正在从EVE在线API中提取大量JSON对象,并使用Newtonsoft.JSON.JsonConvert将它们反序列化为EveObjModel对象。在此基础上,我们希望创建一个独特对象的列表,即每种类型id中最昂贵的对象。我也在下面粘贴了dataContract 问题:下面的代码可以处理较小的数据集,但无法处理较大的数据量。目前,我们正在运行它,它需要超过50分钟(并且正在计算)。我们可以做些什么来将运行更大数据集所需的时间减少到可承受的水平 谢谢你抽出时间。祝你好运 // The
// The buyList contains about 93,000 objects.
public void CreateUniqueBuyList(List<EveObjModel> buyList)
{
List<EveObjModel> uniqueBuyList = new List<EveObjModel>();
foreach (EveObjModel obj in buyList)
{
int duplicateCount = 0;
for (int i = 0; i < uniqueBuyList.Count; i++)
{
if (uniqueBuyList[i].type_id == obj.type_id)
duplicateCount++;
}
if (duplicateCount == 1)
{
foreach (EveObjModel objinUnique in uniqueBuyList)
{
if (obj.type_id == objinUnique.type_id && obj.price > objinUnique.price)
{
// instead of adding obj, the price is just changed to the price in the obj.
objinUnique.price = obj.price;
}
else if (obj.type_id == objinUnique.type_id && obj.price == objinUnique.price)
{
//uniqueBuyList.RemoveAll(item => item.type_id == obj.type_id);
}
else
{
// Hitting this mean that there are other objects with same type and higher price OR its not the same type_id
}
}
}
else if (duplicateCount > 1)
{
// shud not happn...
}
else
{
uniqueBuyList.Add(obj);
}
continue;
}
foreach (EveObjModel item in uniqueBuyList.OrderBy(item => item.type_id))
{
buyListtextField.Text += $"Eve Online Item! Type-ID is: {item.type_id}, Price is {item.price}\n";
}
}
您应该能够使用
Enumerable.GroupBy()
相当有效地执行此操作:
var grouped = buyList.GroupBy(item => item.type_id);
var uniqueBuyList = new List<EveObjModel>();
foreach (var group in grouped)
{
var combined = group.First();
combined.price = group.Max(item => item.price);
uniqueBuyList.Add(combined);
}
这一过程很慢并不奇怪,因为您使用的算法(使用嵌套循环)至少具有二次O(N*N)时间复杂度,这对于如此大的数据集来说是非常慢的 一种方法是使用LINQ
GroupBy
运算符,它在内部使用基于哈希的查找,因此理论上具有O(N)时间复杂度。因此,您可以按类型\u id
进行分组,并针对每个组(具有相同键的元素列表),选择具有最大价格的一个:
var uniqueBuyList = buyList
.GroupBy(e => e.type_id)
.Select(g => g.OrderByDescending(e => e.price).First())
.ToList();
var comparer = Comparer<EveObjModel>.Create((e1, e2) =>
{
int result = e1.type_id.CompareTo(e2.type_id);
if (result == 0) // e1.type_id == e2.type_id
result = e2.price.CompareTo(e1.price); // e1, e2 exchanged to get descending order
return result;
});
buyList.Sort(comparer);
var uniqueBuyList = new List<EveObjModel>();
EveObjModel last = null;
foreach (var item in buyList)
{
if (last == null || last.type_id != item.type_id)
uniqueBuyList.Add(item);
last = item;
}
当然,您不需要对列表进行排序,就可以获取具有maxprice
的元素。更好的版本是使用Aggregate
方法(基本上是foreach
循环)来实现:
var uniqueBuyList = buyList
.GroupBy(e => e.type_id)
.Select(g => g.Aggregate((e1, e2) => e1.price > e2.price ? e1 : e2))
.ToList();
另一种非基于LINQ的方法是按type\u id
升序、price
降序对输入列表进行排序。然后在已排序的列表上执行单个循环,并获取每个类型\u id
组的第一个元素(它将具有最大价格
):
var comparer=comparer.Create((e1,e2)=>
{
int result=e1.type\u id.CompareTo(e2.type\u id);
if(result==0)//e1.type\u id==e2.type\u id
result=e2.price.CompareTo(e1.price);//e1,e2交换以获得降序
返回结果;
});
buyList.Sort(比较器);
var uniqueBuyList=新列表();
EveObjModel last=null;
foreach(采购清单中的var项目)
{
if(last==null | | last.type_id!=item.type_id)
唯一购买清单。添加(项目);
最后一项=项目;
}
该算法的复杂度为O(N*log(N)),因此它比基于散列的算法差(但比原始算法好得多)。好处是它使用更少的内存,并且生成的列表已经按类型id
排序,因此您不需要使用排序依据
,我们可以按升序类型id
对给定列表进行排序,然后按升序价格
对其进行反向排序。因此,EveObjModel
价格更高的对象
对于每个唯一的类型_id
都是第一位的然后,我们可以再次浏览对象列表,然后选择第一个唯一的type\u id,然后跳过相同的type\u id
由于我们只进行一次排序,这将导致时间复杂度为
O(n*logn)
。因为,n=93773
,所以以2为底的93773的对数几乎等于17
。因此,排序将采取总体n*log n=93773*17=1594141
操作,这可以在非常短的时间内完成。希望下面的代码能帮助你
public void CreateUniqueBuyList(List<EveObjModel> buyList)
{
//sort by ascending type_id and then by ascending price and reverse it. so that,
// object with higher price come first
List<EveObjModel>tempList = buyList.OrderBy(x => x.type_id).ThenBy(x => x.price).Reverse().ToList();
List<EveObjModel> uniqueBuyList = new List<EveObjModel>();
for (int i = 0; i < tempList.Count; ++i) {
if ((i > 1) && tempList[i - 1].type_id == tempList[i].type_id) continue; // if duplicate type_id then don't take it again
uniqueBuyList.Add(tempList[i]);
}
foreach (EveObjModel item in uniqueBuyList.OrderBy(item => item.type_id))
{
buyListtextField.Text += $"Eve Online Item! Type-ID is: {item.type_id}, Price is {item.price}\n";
}
}
public void CreateUniqueBuyList(列表buyList)
{
//按升序类型\u id排序,然后按升序价格排序,并将其反转。因此,
//价格较高的物品优先购买
ListtempList=buyList.OrderBy(x=>x.type_id).ThenBy(x=>x.price.Reverse().ToList();
List uniqueBuyList=新列表();
for(int i=0;i1)&&tempList[i-1]。type\u id==tempList[i]。type\u id)继续;//如果type\u id重复,则不要再使用它
添加(圣堂武士[i]);
}
foreach(uniqueBuyList.OrderBy(item=>item.type_id)中的EveObjModel项)
{
buyListtextField.Text+=$“Eve在线项目!类型ID为:{Item.Type_ID},价格为{Item.Price}\n”;
}
}
什么是耗时较长的部分,对象有多大?下载近94000个对象可能需要一段时间(取决于它们的大小)@MindSwipe下载这些对象大约需要2,5分钟。我们也必须对此进行研究,但上述问题仍然占运行时间的99%以上。谢谢你的时间。非常感谢你的回答。这很有帮助!非常感谢你的回答。我们将尝试这两种解决方案并计时。非常感谢您的回答。我们将尝试您的解决方案。
var comparer = Comparer<EveObjModel>.Create((e1, e2) =>
{
int result = e1.type_id.CompareTo(e2.type_id);
if (result == 0) // e1.type_id == e2.type_id
result = e2.price.CompareTo(e1.price); // e1, e2 exchanged to get descending order
return result;
});
buyList.Sort(comparer);
var uniqueBuyList = new List<EveObjModel>();
EveObjModel last = null;
foreach (var item in buyList)
{
if (last == null || last.type_id != item.type_id)
uniqueBuyList.Add(item);
last = item;
}
public void CreateUniqueBuyList(List<EveObjModel> buyList)
{
//sort by ascending type_id and then by ascending price and reverse it. so that,
// object with higher price come first
List<EveObjModel>tempList = buyList.OrderBy(x => x.type_id).ThenBy(x => x.price).Reverse().ToList();
List<EveObjModel> uniqueBuyList = new List<EveObjModel>();
for (int i = 0; i < tempList.Count; ++i) {
if ((i > 1) && tempList[i - 1].type_id == tempList[i].type_id) continue; // if duplicate type_id then don't take it again
uniqueBuyList.Add(tempList[i]);
}
foreach (EveObjModel item in uniqueBuyList.OrderBy(item => item.type_id))
{
buyListtextField.Text += $"Eve Online Item! Type-ID is: {item.type_id}, Price is {item.price}\n";
}
}