2010-03-31 7 views
2

У меня есть следующий код:Linq, чтобы найти пару точек с самой длинной длиной?

foreach (Tuple<Point, Point> pair in pointsCollection) 
     { 
      var points = new List<Point>() 
         { 
          pair.Value1, 
          pair.Value2 
         }; 

     } 

В этом Еогеаспе, я хотел бы быть в состоянии определить, какую пару точек имеют наиболее значительную длину между координатами для каждой точки внутри пары.

Итак, допустим, что точки состоят из следующих пар:

(1) var points = new List<Point>() { new Point(0,100), new Point(100,100) };

(2) var points = new List<Point>() { new Point(150,100), new Point(200,100) };

Поэтому у меня есть два набора пар, упомянутых выше. Оба они построят горизонтальную линию. Мне интересно узнать, какой лучший подход - найти пару точек с наибольшим расстоянием между ними, будь то вертикально или горизонтально. В двух приведенных выше примерах первая пара точек имеет разницу в 100 между координатой X, так что это будет точка с самой значительной разницей. Но если у меня есть набор пар точек, где некоторые точки будут строить вертикальную линию, некоторые точки будут строить горизонтальную линию, что было бы лучшим подходом для извлечения пары из множества точек, разница которых снова вертикально или горизонтально , является наибольшим среди всех точек в коллекции?

Спасибо!

Chris

+3

Будет ли какой-либо из точек определить линию, которая не по вертикали или горизонтали? Например, есть {(0,0), (3, 4)} возможность? Если да, считаете ли вы, что это имеет «длину» 5 (потому что это евклидово расстояние) или 4 (потому что это большая разница по горизонтали и вертикали)? –

+0

Да, извините, должен был упомянуть об этом, хороший вопрос. – Chris

+0

В результате у меня будет набор точек, которые будут использоваться для построения линии между одной фигурой и другой фигурой. Линия может идти горизонтально, затем вертикально, а затем горизонтально, пока она не закончится в форме назначения. Каждая строка между фигурами имеет текст, такой как «Да» или «Нет», и я пытаюсь найти хороший подход к определению места размещения текста, поэтому я думал найти линию с наибольшим расстоянием, а затем добавьте текст в центр этой строки. – Chris

ответ

3

Используйте OrderBy создать порядок на основе ваших критериев, а затем выберите первый. В этом случае порядок определяется максимальной абсолютной разницей между горизонтальной и вертикальной составляющими в порядке убывания.

EDIT: На самом деле, я думаю, вы должны делать это на самих кортежах, верно? Я буду работать над тем, чтобы приспособить пример к этому.

Для уточнения этой информации, добавьте расширение для Tuple<Point,Point>.

public static class TupleExtensions 
{ 
     public static double Length(this Tuple<Point,Point> tuple) 
     { 
      var first = tuple.Item1; 
      var second = tuple.Item2; 
      double deltaX = first.X - second.X; 
      double deltaY = first.y - second.Y; 
      return Math.Sqrt(deltaX * deltaX + deltaY * deltaY); 
     } 
} 

Теперь мы можем заказать кортежей по их длине

var max = pointCollection.OrderByDescending(t => t.Length()) 
         .FirstOrDefault(); 

Примечание: это быстрее, чтобы просто перебрать коллекцию и следить за максимум, а не сортировки/выбора с помощью LINQ.

Tuple<Point,Point> max = null; 
foreach (var tuple in pointCollection) 
{ 
    if (max == null || tuple.Length() > max.Length()) 
    { 
     max = tuple; 
    } 
} 

Очевидно, что это может быть переработан на IEnumerable расширение, если вы использовали его в более чем одном месте.

+0

Спасибо, но я получаю ошибку компиляции: Аргументы типа для метода 'System.Linq.Enumerable.OrderByDescending (System.Collections.Generic.IEnumerable , System.Func )' не может быть выведено из использования. Попробуйте явно указать аргументы типа. – Chris

+0

Вот что я вошел в код: общественности статической Кортеж <Точка, точка> FindLargestPoint (IList <Кортеж <Точка, точка >> pointsCollection) { вар макс = pointsCollection.OrderByDescending ((х, у) => Math.Max ​​(Math.Abs ​​(x.Value1 - y.Value)), Math.Abs ​​(x.Value2 - y.Value2)) ) .FirstOrDefault(); return max; } – Chris

+0

Да, это должно быть сделано на самих кортежах. Спасибо за вашу помощь. Я буду искать пример. – Chris

2

Вам нужна функция, вероятно, используя теорему Пифагора для вычисления расстояний

а^2 + Ь^2 = с^2

Если бы разница в Point.X, б будет разница в Point.Y, а c будет вашим расстоянием. И как только эта функция была записана, вы можете перейти к LINQ и упорядочить результаты.

Вот что я сделал.(Примечание: я не имею C# 4, так что это не яблоки с яблоками

private double GetDistance(Point a, Point b) 
{ 
    return Math.Pow(Math.Pow(Math.Abs(a.X - b.X), 2) + Math.Pow(Math.Abs(a.Y - b.Y), 2), 0.5); 
} 

Вы можете превратить это в анонимный метод или Func, если вы предпочитаете, очевидно

var query = pointlistCollection.OrderByDescending(pair => GetDistance(pair[0], pair[1])).First(); 

Где pointlistCollection это. List<List<Point>>, каждый внутренний список, имеющие два элемента. Быстрый пример, но он работает.

List<List<Point>> pointlistCollection 
    = new List<List<Point>>() 
    { 
     new List<Point>() { new Point(0,0), new Point(3,4)}, 
     new List<Point>() { new Point(5,5), new Point (3,7)} 
    }; 

*** Вот моя GetDistance функция Func формы.

Func<Point, Point, double> getDistance 
     = (a, b) 
     => Math.Pow(Math.Pow(Math.Abs(a.X - b.X), 2) + Math.Pow(Math.Abs(a.Y - b.Y), 2), 0.5); 

var query = pointlistCollection.OrderByDescending(pair => getDistance(pair[0], pair[1])).First(); 
+1

Обратите внимание: если вы ограничены во времени и все, о чем вы заботитесь, это расстояние * наибольшее *, тогда вам не нужно принимать квадратный корень. Наибольшее расстояние также будет иметь наибольшее квадратное расстояние. –

+0

Хорошая точка. Также обратите внимание, что вызовы Math.Abs ​​излишни, так как я все равно отказываюсь от разницы. Помимо этого, моя функция хороша для повторного использования, когда и если вы когда-нибудь решите отобразить разницу. –

-1

Ответ tvanfosson хорош, однако я хотел бы предложить небольшое улучшение: вам не нужно сортировать коллекцию, чтобы найти max, вам просто нужно перечислить коллекцию и отслеживать максимальное значение , Так как это очень распространенный сценарий, я написал метод расширения для обработки его:

public static class EnumerableExtensions 
{ 
    public static T WithMax<T, TValue>(this IEnumerable<T> source, Func<T, TValue> selector) 
    { 
     var max = default(TValue); 
     var withMax = default(T); 
     bool first = true; 
     foreach (var item in source) 
     { 
      var value = selector(item); 
      int compare = Comparer<TValue>.Default.Compare(value, max); 

      if (compare > 0 || first) 
      { 
       max = value; 
       withMax = item; 
      } 
      first = false; 
     } 
     return withMax; 
    } 
} 

Вы можете сделать что-то вроде этого:

Tuple<Point, Point> max = pointCollection.WithMax(t => t.Length()); 
+0

@downvoter, позаботьтесь, чтобы дать объяснение ?? –

0

Как комментировал выше: Не сортировать список в порядке чтобы получить максимум.

public static double Norm(Point x, Point y) 
{ 
    return Math.Sqrt(Math.Pow(x.X - y.X, 2) + Math.Pow(x.Y - y.Y, 2)); 
} 

Max() нужен только О (п) вместо O (п * § п)

pointsCollection.Max(t => Norm(t.Item1, t.Item2)); 
+1

Вопрос о том, что он интересовался извлечением пары с наибольшим расстоянием, а не просто величиной наибольшего расстояния. –

+0

Извините, я искал что-то вроде MaxBy(). Но похоже, что это только в F # не прямо в LINQ. – forki23

Смежные вопросы