2012-05-15 2 views
0

Простой вопрос относительно выражения лямбдаC# сложность выражения лямбда

Я хотел получить среднее значение по всем сделкам в следующем коде. Формула, которую я использую, равна ((цена 1 * qty 1+ (цена 2 * qty 2) .... + (цена n * qty n)/(qty 1 + qty 2 + ... + qty n)

В следующем коде я использую функцию sum, чтобы вычислить общее количество (цена * qty), а сложность будет равна O (n) и еще раз, чтобы добавить все значения сложности O (n). есть ли способ, можно найти суммирование как с использованием сложности O (N) означает одно выражение лямбды, которое может вычислить оба результата.

Используя цикл, можно рассчитать оба результата в O (N) сложность.

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Trade> trades = new List<Trade>() 
     { 
      new Trade() {price=2,qty=2}, 
      new Trade() {price=3,qty=3} 
     }; 

     ///using lambda 
     int price = trades.Sum(x => x.price * x.qty); 
     int qty = trades.Sum(x => x.qty); 

     ///using for loop 
     int totalPriceQty=0, totalQty=0; 

     for (int i = 0; i < trades.Count; ++i) 
     { 
      totalPriceQty += trades[i].price * trades[i].qty; 
      totalQty += trades[i].qty; 
     } 
     Console.WriteLine("Average {0}", qty != 0 ? price/qty : 0); 
     Console.Read(); 
    } 
} 

class Trade 
{ 
    public int price; 
    public int qty; 
} 

Редактировать: Я знаю, что коэффициент не учитывается. Позвольте мне перефразировать вопрос, сказав, что с лямбдой мы будем проходить через каждый элемент в списке дважды, а в цикле for мы будем проходить через каждый элемент только один раз. Есть ли какое-либо решение с лямбдой, поэтому ему не нужно дважды перебирать элементы списка?

+0

Если у вас есть две процедуры, которые являются O (n), то один за другим выполняется O (n). – recursive

+0

Я знаю это, но я просто хотел обратить внимание на двойную петлю через элемент списка, когда мы используем lambda – mchicago

+0

. Вам также может быть интересно прочитать эту статью [Parallel Aggregation] (http://msdn.microsoft.com/en-us/ библиотека/ff963547.aspx). Просто потому, что вы добавляете целую кучу цифр, это не значит, что вы должны делать это последовательно. – R0MANARMY

ответ

3

Как уже упоминалось, Big-O не изменяется, повторяете ли вы один или два раза. Если вы хотите использовать Linq для перебора только раз вы могли использовать пользовательский агрегатор (так как вы уменьшаете к тем же свойствам мы можем просто использовать экземпляр Trade для агрегации):

var ag = trades.Aggregate(new Trade(), 
          (agg, trade) => 
          { 
          agg.price += trade.price * trade.qty; 
          agg.qty += trade.qty; 
          return agg; 
          }); 
int price = ag.price; 
int qty = ag.qty; 

В этом Я бы просто использовал цикл foreach или простые lambdas, которые у вас уже есть, - если производительность здесь не важна (измерьте это!)

3

Сложность Big-O не учитывает постоянные коэффициенты. O (n) + O (n) все еще дает O (n).

Если вы решили, что хотите это в лямбда, вот пример использования оператора Aggregate. Это выглядит надуманным, и я бы не рекомендовал его в традиционном цикле.

var result = trades.Aggregate(
    Tuple.Create(0, 0), 
    (acc, trade) => 
     Tuple.Create(acc.Item1 + trade.price * trade.qty, acc.Item2 + trade.qty)); 

int totalPrice = result.Item1; 
int totalQuantity = result.Item2; 
3

Чтобы расширить ответ на ответ BrokenGlass, вы также можете использовать анонимный тип в качестве агрегатора:

var result = trades.Aggregate( 
       new { TotalValue = 0L, TotalQuantity = 0L }, 
       (acc, trade) => new 
       { 
        TotalValue = acc.TotalValue + trade.price, 
        TotalQuantity = acc.TotalQuantity + trade.qty 
       } 
      ); 

Есть два маленьких расквитаться с этим подходом:

  1. Если вы делаете этот вид расчета на большом количестве сделок (или на крупных сделках), вполне возможно, что вы бы переполнить int которые отслеживают общую стоимость сделок и общих акций. Этот подход позволяет указать long как ваш тип данных (так что для переполнения потребуется больше времени).

  2. Объект, который вы получите от этой агрегации, будет иметь более значимые свойства, чем если бы вы просто вернули объект Trade.

Большой недостаток в том, что это странно смотреть.

+0

Я бы изменил и вернул существующий накопитель, но вместо того, чтобы создавать * новый * один для каждой сделки - все это нужно было бы собрать в мусор (!) - поскольку это невозможно с анонимными типами, которых вам не повезло, единственной альтернативой является создание совершенно нового типа для хранения ваших накопленных значений. – BrokenGlass

+0

@BrokenGlass: С точки зрения практической эффективности, да, вы правы. Однако создание нового значения каждый раз ближе к функциональным корням, из которых был получен LINQ. Изменение объекта, который был передан функции аккумулятора, немного взломан. Самый чистый подход, вероятно, заключался бы в объявлении структуры. – Douglas

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