1

I'm practicing some optimization problems and I'm stuck.

I have a list of tuples and I am doing the following:

private static int CalculateMinimumTotalCost(List<Tuple<int, int>> tuples)
{
    int minimumCost = 0;

    for(int i=0;i<tuples.Count()-1;i++)
    {
        minimumCost += Math.Max(Math.Abs(tuples[i].Item1 - tuples[i + 1].Item1), Math.Abs(tuples[i].Item2 - tuples[i + 1].Item2));
    }
    return minimumCost;
}

The idea is that given a list of tuples and this mathematical equation, I need to find the minimum cost. The catch is that the order of the tuples can be rearranged. My job is to find the LEAST costly arrangement of tuples.

So what I would like to do is loop through all possible combination of Tuples and return the combination with the minimum cost.

For example:

(1,2)(1,1)(1,3) = 3

(1,1)(1,2)(1,3) = 2

So in this case, i would return 2 because that arrangement is less costly.

I understand that when there are N tuples, the number of combinations is N!.

How do I get all the combinations possible for a list of tuples?

Thanks!

mo alaz
  • 4,529
  • 8
  • 30
  • 36
  • 1
    Is the given formula just an example or the *real* formula. – Willem Van Onsem May 03 '16 at 21:30
  • @WillemVanOnsem it is the real formula I need to use. – mo alaz May 03 '16 at 21:31
  • 2
    Use a backtrack recursive algorithm. – jdweng May 03 '16 at 21:32
  • 1
    `Tuple` seems much less appropriate for this then `Point`. This problem is better represented by a (x,y) coordinate plot than an abstract. Wouldn't you agree? – Max Sorin May 03 '16 at 21:32
  • This sounds like an homework exercise or assignment. – pmcilreavy May 03 '16 at 21:34
  • @fallenidol I am not doing homework, I'm practicing the problems on hackerrank to freshen skills. – mo alaz May 03 '16 at 21:35
  • @MaxSorin I always thought that we use Point when Drawing. This is a console app, would it make sense to use Point? – mo alaz May 03 '16 at 21:36
  • a point with an Int x and Int y seems a little easier to work with then tuple. You could define your own class and implement IComparable to reduce amount of code getting inside the loop. – Max Sorin May 03 '16 at 21:39
  • @MaxSorin OH you mean implement my own object Point that has properties int x and int y. Yeah you're right, I think that would be clearer and easier to work with. But I would still need to find all combinations of Points possible. – mo alaz May 03 '16 at 21:43
  • I give an elegant algorithm for generating permutations of a sequence in my series of articles here: https://ericlippert.com/2013/04/15/producing-permutations-part-one/ – Eric Lippert May 03 '16 at 21:50
  • In this exercise, are you allowed to use your knowledge of the formula to take mathematical shortcuts? – Max Sorin May 03 '16 at 21:53
  • @MaxSorin I don't see why not! The question is here if you'd like to take a look: https://www.hackerrank.com/challenges/oil-well – mo alaz May 03 '16 at 21:58
  • An interesting variation on your problem is to use the metric that is the *sum* of the absolute values of the differences, rather than the *maximum*. That is the "manhattan distance metric"; if the points are corners in a city grid then the metric gives the distance you must walk from point to point. Your problem then becomes "find the shortest path that hits every point exactly once." – Eric Lippert May 03 '16 at 22:00
  • @EricLippert, exactly what I was thinking when I suggested `point`. Except in this case moving from (0,0) to (1,1) appears to only cost 1 rather than 2 blocks. – Max Sorin May 03 '16 at 22:07
  • @MaxSorin: Right, it is a nice coincidence that my articles are on generating permutations that are a Hamiltonian of the permutahedron, and this problem is *almost* generating a minimal cost Hamiltonian of a set of points. Speaking of which, that is also a variation that the OP might want to try; find the tour which generates the minimum cost where you have to also add in the cost of going from the last point back to the first! – Eric Lippert May 03 '16 at 22:12
  • @moalaz: You would do well to name your methods more clearly. The method you have implemented here is "compute cost", not "compute minimum cost". You'll want to use this method in your actual solution, so make sure it is named correctly. Otherwise it will just be confusing. – Eric Lippert May 03 '16 at 22:16

2 Answers2

2

As other have suggested you should create the Point class:

public partial class Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

And, let's encapsulate the functions for computing distance and total cost :

public partial class Point
{
    public static int CalculateDistance(Point p0, Point p1)
    {
        return Math.Max(
            Math.Abs(p0.X - p1.X),
            Math.Abs(p0.Y - p1.Y)
            );
    }
}

public static class PointExtensions
{
    public static int GetTotalCost(this IEnumerable<Point> source)
    {
        return source
            .Zip(source.Skip(1), Point.CalculateDistance)
            .Sum();
    }
}

Finally, you will need another extension method to create "all possible combination" :

public static class PermutationExtensions
{
    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> source)
    {
        if (source == null || !source.Any())
            throw new ArgumentNullException("source");

        var array = source.ToArray();

        return Permute(array, 0, array.Length - 1);
    }
    private static IEnumerable<IEnumerable<T>> Permute<T>(T[] array, int i, int n)
    {
        if (i == n)
            yield return array.ToArray();
        else
        {
            for (int j = i; j <= n; j++)
            {
                array.Swap(i, j);
                foreach (var permutation in Permute(array, i + 1, n))
                    yield return permutation.ToArray();
                array.Swap(i, j); //backtrack
            }
        }
    }
    private static void Swap<T>(this T[] array, int i, int j)
    {
        T temp = array[i];

        array[i] = array[j];
        array[j] = temp;
    }
}

Source from Listing all permutations of a string/integer adapted to be more LINQ-friendly


Usage :

void Main()
{
    var list = new List<Point>
    {
        new Point(1, 2),
        new Point(1, 1),
        new Point(1, 3),
    };

    // result: Point[] (3 items) : (1, 1), (1, 2), (1,3)
    list.GetPermutations()
        .OrderBy(x => x.GetTotalCost())
        .First();
}

EDIT : As @EricLippert pointed out, source.OrderBy(selector).First() has some extra cost. This following extension method deals with this issue :

public static class EnumerableExtensions
{
    public static T MinBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, IComparer<TKey> comparer = null)
    {
        IEnumerator<T> etor = null;

        if (source == null || !(etor = source.GetEnumerator()).MoveNext())
            throw new ArgumentNullException("source");

        if (keySelector == null)
            throw new ArgumentNullException("keySelector");

        var min = etor.Current;
        var minKey = keySelector(min);

        comparer = comparer ?? Comparer<TKey>.Default;
        while (etor.MoveNext())
        {
            var key = keySelector(etor.Current);
            if (comparer.Compare(key, minKey) < 0)
            {
                min = etor.Current;
                minKey = key;
            }
        }

        return min;
    }
}

And, we can rewrite the above solution as :

list.GetPermutations().MinBy(x => x.GetTotalCost())
Community
  • 1
  • 1
Xiaoy312
  • 14,292
  • 1
  • 32
  • 44
  • Nice solution. I note that you do not actually need to order the solutions by total cost. All you need to do is extract the solution that has the least cost. – Eric Lippert May 03 '16 at 22:55
  • @EricLippert wouldn't you need all the cost, in order to determine the least costly one? – Xiaoy312 May 03 '16 at 23:02
  • 1
    Yes, you certainly need to know all the costs. But once you have found the *smallest*, **you don't need to order the rest of them**. If I give you a one, a five, a one, a five, a ten, a fifty, a ten, ... then once you've determined that the one is the smallest, you don't have to put the rest of them in order. OrderBy sorts *everything*, but you only need the smallest. That's more work than you need to do. – Eric Lippert May 03 '16 at 23:09
  • @EricLippert Indeed, the lazy way does come at a price. I'll add a `MinBy` for sake for performance. – Xiaoy312 May 04 '16 at 01:41
-4

You can change the for loop to Foreach to make it more readable and rather than using index to fetch values.

private static int CalculateMinimumTotalCost(List<Tuple<int, int>> tuples)
        {
            int minimumCost = 0;
            Tuple<int, int> currentTuple = tuples.First();

            foreach (Tuple<int, int> tuple in tuples)
            {
               minimumCost += Math.Max(Math.Abs(currentTuple.Item1 - tuple.Item1), Math.Abs(currentTuple.Item2 - tuple.Item2));
               currentTuple = tuple;
            }
            return minimumCost;
        }
Avneesh
  • 654
  • 4
  • 6
  • 1
    The question is "How do I get all the combinations possible for a list of tuples?" I am not seeing where in this answer you answer the question. – Eric Lippert May 03 '16 at 22:13
  • I was just telling about making the loop better. I have not mentioned about the permutations. You are getting aggressive by rejecting first rather than making code better. – Avneesh May 03 '16 at 22:16
  • 2
    If you have a comment that addresses the code quality -- as I did -- then leave a comment -- as I did. Don't answer a question that wasn't asked. – Eric Lippert May 03 '16 at 22:16
  • But Eric, you have not answered or suggested anything other than theory of some book. You should have then suggested atleast some code. – Avneesh May 03 '16 at 22:18
  • 3
    I wrote a carefully reasoned six-part blog post clearly laying out how to solve this problem elegantly in C#. My suggestion is: read it. – Eric Lippert May 03 '16 at 22:19