Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Как вычислить последовательность накопленных средних с помощью встроенных функций? / 17 сообщений из 17, страница 1 из 1
29.11.2012, 14:13
    #38057971
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
Под последовательностью накопленных средних я имею ввиду последовательность:

1) равную входящей последовательности по числу элементов;
2) в которой каждый текущий элемент получается нахождением среднеарифметического всех элементов с начала входящей последовательности по её текущий элемент.

Т. е. если А и В - соответственно входящая последовательность и последовательность накопленных средних относительно входящей, то

1) А.Count == B.Count;
2) В[i] = (А[0]+А[1]+... +А[i]) / (i+1).

Я это сделал с помощью обычного for, а теперь хочу узнать, как это можно сделать с помощью встроенных функций, типа Enumerable.Aggregate, Enumerable.Sum.

Вот мой код для обычного цикла for


Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
// Последовательность накопленных средних.
List<double> cumulativeAverages = new List<double>(source.Count);

// Текущее значение накопленной средней.
double cumulativeAverage = 0;

// Вычисление накопленных средних по всей входящей последовательности.
for (int i = 0; i < source.Count; i++)
{
	cumulativeAverage = (cumulativeAverage * i + source[i]) / (i + 1);
	cumulativeAverages.Add(cumulativeAverage);
}
...
Рейтинг: 0 / 0
29.11.2012, 14:50
    #38058060
Abstraction
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
user7320,

Что-то в духе
Код: c#
1.
B = A.Aggregate(new List<double>(), (a,e)=>a.Add(a.Sum()+e).Aggregate(new List<double>(), (a,e)=>a.Add(e/(a.Count+1)));

...к примеру.
...
Рейтинг: 0 / 0
29.11.2012, 19:58
    #38058851
ЕвгенийВ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
Abstractionuser7320,

Что-то в духе
Код: c#
1.
B = A.Aggregate(new List<double>(), (a,e)=>a.Add(a.Sum()+e).Aggregate(new List<double>(), (a,e)=>a.Add(e/(a.Count+1)));

...к примеру.
Кашмар.

Код: c#
1.
2.
3.
4.
5.
6.
 //тестовые данные
            Random r = new Random();
            var list = Enumerable.Range(1, 1000).Select(s => r.NextDouble() * 1000); 
            //сам процесс
            double d = 0;
            var data = list.Select((a, i) => (d +=a ) / (i + 1));
...
Рейтинг: 0 / 0
29.11.2012, 20:37
    #38058887
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
ЕвгенийВ,

спасибо. А ещё узнать хотел бы, какой способ всё же быстрее - мой через for, или ваш через лямбды? А то тут читал, что всякие анонимные методы и их родственники (как я понял) где-то на порядок медленнее, чем непосредственный вызов метода и, как я понимаю, ещё медленнее, если без всяких методов в for прогнать массив со взятием элементов по индексу (особенно, если ещё упростить

cumulativeAverage = (cumulativeAverage * i + source[i]) / (i + 1);
cumulativeAverages.Add(cumulativeAverage);

до

cumulativeAverage += source[i];
cumulativeAverages.Add(cumulativeAverage / (i + 1));
...
Рейтинг: 0 / 0
30.11.2012, 06:06
    #38059158
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
Выше cumulativeAverage уже станет просто cumulative.
...
Рейтинг: 0 / 0
30.11.2012, 10:52
    #38059345
ЕвгенийВ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
Несомненно через лямбды медленнее из за вызова делегатов. Но при использовании лябмд, результат будет считаться только когда его кто то запросит, при использовании for он будет считаться всегда.
Плюс при использовании for есть накладные расходы на создание List<double> cumulativeAverages, как по памяти, так и по вычислениям. С использованием лямбд можно получить очень большие последовательности(бесконечные), на которые в классическом случае не хватит памяти.
Также лямбды в ряде случаев можно легко распараллелить(не в Вашем, так как последующий элемент зависит от предыдущих).
...
Рейтинг: 0 / 0
30.11.2012, 12:38
    #38059551
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
ЕвгенийВ,

что-то не то выдаёт лямбда. Я немного изменил обозначения, чтобы мне было понятнее, но суть осталась той же:

Код: c#
1.
2.
3.
4.
double sum = 0;

var data = 
    source.Select<double, double>((double sourceElement, int index) => (sum += sourceElement) / (index + 1));



Оба моих цикла for выдают то, что слева (см. рис.). Ваша лямбда выдаёт то, что посередине. Последовательность-источник - справа. Очевидно, что первый элементы накопленных средних должен совпадать с первым элементов источника. У меня так, а у лямбды - нет. Ещё странно, что у лямбды одни отрицательные числа. Причём я взял отдельные переменные для сумм для цикла и для лямбды - т. е. они не одну и ту же сумму (sum) считали, т. е. влияния одного куска кода на другой не было.




По идее, вроде, лямбда правильная, а считает не так. Может, она в произвольном порядке берёт очередной элемент из источника, а не по последовательному возрастанию индекса?
...
Рейтинг: 0 / 0
30.11.2012, 17:12
    #38060224
ЕвгенийВ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
user7320,
А Вы попробуйте закрыть мой список и открыть его снова, результаты будут опять другие)))
...
Рейтинг: 0 / 0
30.11.2012, 17:40
    #38060307
ЕвгенийВ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
ЕвгенийВuser7320,
А Вы попробуйте закрыть мой список и открыть его снова, результаты будут опять другие)))
Начнет считать с последнего присвоенного значения sum, а не с нуля.
Можно попробовать сделать такую функцию.
Код: c#
1.
2.
3.
4.
5.
static IEnumerable<double> Compute(IEnumerable<double> src)
        {
            double d = 0;
            return src.Select((a, i) => (d += a) / (i + 1));
        }
...
Рейтинг: 0 / 0
02.12.2012, 07:22
    #38061497
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
ЕвгенийВЕвгенийВuser7320,
А Вы попробуйте закрыть мой список и открыть его снова, результаты будут опять другие)))
Начнет считать с последнего присвоенного значения sum, а не с нуля.
Можно попробовать сделать такую функцию.
Код: c#
1.
2.
3.
4.
5.
static IEnumerable<double> Compute(IEnumerable<double> src)
        {
            double d = 0;
            return src.Select((a, i) => (d += a) / (i + 1));
        }


Так я же говорил, что у меня для каждой суммы своя переменная. Скажем, в лямбде я записываю сумму в sum, а в цикле - в какую-нибудь currentSum. Обе суммы вначале нулём инициализируются. А результат разный.
...
Рейтинг: 0 / 0
02.12.2012, 08:31
    #38061503
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
Извиняюсь за свою недогадливость - там действительно прицина в том, что значения -640 и т. д. получались после ВТОРОГО вычисления этой лямбды. Дело в том, что я привык, что неиспользованная переменная, даже если ей присвоены какие-то значения, не отображается в дебаге и, по-видимому, вообще не инициализируются средой выполнения. Поэтому добавил после data ещё строку data.ToArray(). Вот она и вычислилась второй раз. Причём она так вычисляется даже если лямбду в функцию положить. Если ToString() сделать, то вычисляется только один раз.

Тогда такой вопрос - а что, теперь каждый запрос к переменной, сохраняющей результат вычисления лямбды, вызовет выполнение этой лямбды, причём с запомненным значением её локальных переменных, а не просто считывание данных из переменной результата? Вот те раз! Т. е. чтобы эта лямбда отработала так, как мне нужно, я не должен считывать значения из переменной результата более одного раза?
...
Рейтинг: 0 / 0
02.12.2012, 09:13
    #38061508
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
Вобщем, чтобы этого избежать, добавил в конце ToList() и эта переменная перестала быть делегатом и стала обычным списком. Теперь можно хоть в массив, хоть что с ней делать - делегат применяться к ней больше не будет.
...
Рейтинг: 0 / 0
08.01.2013, 21:49
    #38102570
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
user7320
Код: c#
1.
2.
3.
4.
double sum = 0;

var data = 
    source.Select<double, double>((double sourceElement, int index) => (sum += sourceElement) / (index + 1));


Кажется, это называется "замыканием" - делегат помнит значение переменной окружения sum, поэтому при любом последующем обращении к нему (когда нажимал в дебаге на разворачивание списка значений data) он вычисляет накопленную сумму, начиная с последнего значения sum, а не с нуля.

Подтвердите или поправьте, если я неправ, пожалуйста.
...
Рейтинг: 0 / 0
08.01.2013, 23:31
    #38102670
bazile
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
user7320,

замыкание это механизм с помошью которого анонимнаная функция "захватывает" локальную переменную sum. Разный результат работы Select() отражает отложенную природу вычисления содержимого IEnumerable<>. Так что это две разные вещи.
...
Рейтинг: 0 / 0
09.01.2013, 13:31
    #38103302
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
bazileuser7320,

замыкание это механизм с помошью которого анонимнаная функция "захватывает" локальную переменную sum. Разный результат работы Select() отражает отложенную природу вычисления содержимого IEnumerable<>. Так что это две разные вещи.
Я выше, ещё в ранних постах жаловался, что эта sum запоминается после каждого прохода функции Select. А проход осуществляется при каждом раскрытии значений коллекции data в дебаггере. А поскольку запомниться эта переменная может только в самом делегате, то я и подумал, что это и есть то самое замыкание. Правильно?
...
Рейтинг: 0 / 0
09.01.2013, 13:42
    #38103326
bazile
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
user7320, она не запоминается, а используется благодаря замыканию. Переменная как была одна так одна и останется независимо от того сколько раз она используется в разных анонимных функциях. Подумай над таким примером:
Код: c#
1.
2.
3.
4.
double sum = 0;

var data1 = source.Select<double, double>((double sourceElement, int index) => (sum += sourceElement) / (index + 1));
var data2 = source.Select<double, double>((double sourceElement, int index) => (sum += sourceElement) / (index + 1));
...
Рейтинг: 0 / 0
10.01.2013, 04:12
    #38104313
user7320
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как вычислить последовательность накопленных средних с помощью встроенных функций?
bazileuser7320, она не запоминается, а используется благодаря замыканию. Переменная как была одна так одна и останется независимо от того сколько раз она используется в разных анонимных функциях. Подумай над таким примером:
Код: c#
1.
2.
3.
4.
double sum = 0;

var data1 = source.Select<double, double>((double sourceElement, int index) => (sum += sourceElement) / (index + 1));
var data2 = source.Select<double, double>((double sourceElement, int index) => (sum += sourceElement) / (index + 1));


Ну, здесь в data2 будет использоваться sum, полученная после проходов по source первой функции Select.

А где хранится эта sum? Я так понимаю, что делегаты, т. е. анонимные методы, это те же методы и указатели на них, как и их параметры, хранятся в стеке?

И ещё вопрос. А вот допустим закончился контекст внешнего окружения делегата, переданного в качестве параметра в Select. Тогда что, переменная sum тоже уничтожилась сборщиком мусора, а в стеке вместе с делегатом осталась КОПИЯ этой переменной в качестве параметра делегата? Или это та же переменная? Другими словами, адрес переменной sum из окружения и переменной sum в делегате одинаков?
...
Рейтинг: 0 / 0
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Как вычислить последовательность накопленных средних с помощью встроенных функций? / 17 сообщений из 17, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]