提高双循环的性能

时间:2016-10-31 12:19:13

标签: c# performance for-loop

我正在研究一种算法,作为向客户提供餐馆的建议。这些建议基于一些过滤器,但主要是通过比较人们在餐馆留下的评论。 (我饶有你的详细信息)。

为了计算皮尔逊相关性(一个确定用户与彼此之间的适合程度的数字),我必须检查用户在同一家餐厅留下评论的位置。为了增加匹配数量,我在主题的价格范围内包含了一个匹配项。我试着解释一下,这是我的餐厅课程:

public class Restaurant
{
    public Guid Id { get; set; }
    public int PriceRange { get; set; }
}

这是一个简化版本,但对我的例子来说已经足够了。价格范围可以是1-5的整数,这决定了餐厅的价格。

这是for循环我用来检查他们是否在同一家餐厅留下评论,或者在同一价格范围的餐馆评论。

//List<Review> user1Reviews is a list of all reviews from the first user
//List<Review> user2Reviews is a list of all reviews from the second user
Dictionary<Review, Review> shared_items = new Dictionary<Review, Review>();
    foreach (var review1 in user1Reviews)
        foreach (var review2 in user2Reviews)
            if (review1.Restaurant.Id == review2.Restaurant.Id || 
                review1.Restaurant.PriceRange == review2.Restaurant.PriceRange)
                if (!shared_items.ContainsKey(review1))
                    shared_items.Add(review1, review2);

现在这是我的实际问题。你可以看到我为第一个用户留下的每个评论循环第二个列表。有没有办法改善这些循环的性能?我曾尝试使用hashset和.contains()函数,但我需要包含更多条件(即价格范围)。我无法弄清楚如何在hashset中包含它。

我希望它不会太混乱,并提前感谢您的帮助!

编辑:在测试了linq和for循环后,我得出结论,for循环的速度是使用linq的两倍。谢谢你的帮助!

2 个答案:

答案 0 :(得分:0)

您可以尝试使用外部循环的条件通过Linq查询替换内部循环:

foreach (var review1 in user1Reviews)
{
    var review2 = user2Reviews.FirstOrDefault(r2 => r2.Restaurant.Id == review1.Restaurant.Id ||
                                            r2.Restaurant.PriceRange == review1.Restaurant.PriceRange);
    if (review2 != null)
    {
        if (!shared_items.ContainsKey(review1))
            shared_items.Add(review1, review2);
    }
}

如果有多个匹配项,您应该使用Where并处理潜在的结果列表。

我不确定它会更快,因为你仍然需要检查所有针对user1评论的user2评论。

不过,如果你为你的餐馆课程编写了一个自定义比较器,你可以使用Intersect的重载来回复你的常见评论:

var commonReviews = user1Reviews.Intersect(user2Reviews, new RestaurantComparer());

如果RestaurantComparer看起来像这样:

// Custom comparer for the Restaurant class
class RestaurantComparer : IEqualityComparer<Restaurant>
{
    // Products are equal if their ids and price ranges are equal.
    public bool Equals(Restaurant x, Restaurant y)
    {
        //Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        //Check whether the properties are equal.
        return x.Id == y.Id && x.PriceRange == y.PriceRange;
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.

    public int GetHashCode(Product product)
    {
        //Check whether the object is null
        if (Object.ReferenceEquals(product, null)) return 0;

        //Get hash code for the Id field.
        int hashId product.Id.GetHashCode();

        //Get hash code for the Code field.
        int hashPriceRange = product.PriceRange.GetHashCode();

        //Calculate the hash code for the product.
        return hashId ^ hashPriceRange;
    }
}

答案 1 :(得分:0)

您基本上需要通过Id PriceRange快速查找评论。通常,您将使用基于快速哈希的查找结构(如Dictionary<TKey, TValue>)作为单个键,或者如果匹配操作为,则使用复合键。不幸的是,您的,因此Dictionary不起作用。

嗯,不是真的。单字典不起作用,但你可以使用两个字典,并且由于字典查找是O(1),操作仍然是O(N)(而不是O(N * M)和内循环/天真LINQ)

由于密钥不是唯一的,您可以使用lookups而不是字典,保持相同的效率:

var lookup1 = user2Reviews.ToLookup(r => r.Restaurant.Id);
var lookup2 = user2Reviews.ToLookup(r => r.Restaurant.PriceRange);
foreach (var review1 in user1Reviews)
{
    var review2 = lookup1[review.Restaurant.Id].FirstOrDefault() ??
                  lookup2[review.Restaurant.PriceRange].FirstOrDefault();
    if (review2 != null)
    {
        // do something
    }
}