开发者

Iterating over unordered pairs of elements stored in a collection

开发者 https://www.devze.com 2023-04-04 16:06 出处:网络
In C#, I have got a collection of unique elements and I wa开发者_高级运维nt to efficeiently execute some code for each unordered pair. For instance, if my container holds {a,b,c}, the unordered pairs

In C#, I have got a collection of unique elements and I wa开发者_高级运维nt to efficeiently execute some code for each unordered pair. For instance, if my container holds {a,b,c}, the unordered pairs are (a,b), (a,c) and (b,c). The problem arises in the scope of performing a 2-opt optimization, thus efficency is an issue.

  • My current solution looks like:

    foreach(var a in container) 
    {
        foreach(var b in container)
        {
            if (a < b)
            {
                 // execute code    
            }
        }
     }
    

    Obviously, this can be modified easily if the operator [] is available to get the i-th element (i.e. if the underlying data structure is a list.) But for all other containers, the solution is dependent on the existence of some comparison function and not very efficient.

  • I've also tried a formulation based on a LINQ statement that generates each desired pair exactly once. However, as expected, this was much slower than the first approach. This applies to a solution using ElementAt too.

Edit: here is the (improved) LINQ code that was used:

var x = from a in container
        from b in container
        where a < b 
        select new KeyValuePair<int,int>(a,b);

Still, execution is slower by a factor of 3-5 compared to the other solutions.

  • Here is the way I would do it in C++ (obtaining good efficiency):

    for(auto it1 = container.begin(); it1!=container.end(); ++it1) 
    {
        auto it2 = it1;
        for(++it2; it2!=container.end(); ++it2) 
        {
            // execute code    
        }
     }
    

    Unfortunatelly, to transform this into C#, it would be required to clone the (internally used) Enumerator, which is not supported by the language itself.

Has anyone a better idea / solution?


Did you try to copy the elements into a list first and then do the algorithm with the indexer ([i]) operator? Since the algorithm has quadratic runtime anyways it may be negligible to have a linear copy operation in front of it. You would have to find out the actual runtime for small, middle and large containers yourself...

I think it may be worth a try, this may well be a lot faster than working with the comparison operator each time.

You could also check if the container is of type IList<T> and jump past the copy operation.


If you don't care about the order, you can do it like this:

int i = 0;
foreach (var a in list)
{
    int j = 0;
    foreach (var b in list)
    {
        if (i <= j)
            break;

        // execute code    

        j++;
    }

    i++;
}

If you do care about the order, you can limit yourself to collections that implement IList<T>, which contains the [] operator. Or you could copy the collection into a List<T> first, and then work with that.


Enumerators in C# are not the same with C++ enumerators from your question. In C# you have neither begin nor end elements of container. You only have Current element and Next() method. It allows you to yield much more sequences, for example - you can enumerate through infinity sequence of random numbers, which obviously has not begin or end.

So - you can't do it in C# like in your C++ code using only IEnumerable classes. The best way to do it is to use System.Collection.Generics.IList<T> interface. Many of types (such as arrays) inherit this interface.

If you use IEnumerable then inside your type you (in most cases) iterate through some collection. If you do this - you can just implement IList<T> interface.

There is another solution - in C# lists and arrays of reference types contains only reference to object. So - you can copy your data to local list and operate with it. But it depends on your memory and performance requirements.


Put your items into a List or IList, and then you can access them using indices in a very similar pattern to your C++ code.

for (int i = 0; i < container.Count(); i++)
for (int j = i+1; j < container.Count(); j++)
{
    var item1 = container.Item[i];
    var item2 = container.Item[j];
}

I'd expect it to be more efficient to iterate an ordered collection using indices rather than the n^2 comparisons. The efficiency of the comparison operator is important but you shouldn't need to compare at all.

0

精彩评论

暂无评论...
验证码 换一张
取 消