Набор математических функций

Небольшой набор математических функций для работы с массивами чисел.

Быстрый поиск по запросу «математи*» по ИС выдаёт всего одну статью с набором полезных математических функций:

Библиотека математических функций 1.1

Отличная статья, но перечень функций в основном предназначен для матриц. В моём случае понадобилось обрабатывать одномерные массивы чисел. Получился небольшой набор простых функций, которыми хочу поделиться со всеми нуждающимися и достаточно ленивыми, чтобы писать самостоятельно.

Функция Сумма(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат 0 КонецЕсли;

ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Сумма");

Для ё = 1 по Массив.Количество() Цикл
ТЗ.Добавить();
КонецЦикла;

ТЗ.ЗагрузитьКолонку(Массив, "Сумма");
Сумма = ТЗ.Итог("Сумма");

Возврат Сумма;
КонецФункции

Функция Минимум(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат Неопределено КонецЕсли;

УпорядочитьМассив(Массив);

Возврат Массив[0];
КонецФункции

Функция Среднее(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат Неопределено КонецЕсли;

Возврат Сумма(Массив) / Массив.Количество();
КонецФункции

Функция Максимум(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат Неопределено КонецЕсли;

УпорядочитьМассив(Массив);

Возврат Массив[Массив.Количество() - 1];
КонецФункции

Функция Медиана(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат Неопределено КонецЕсли;
КоличествоЭлементов = Массив.Количество();

Если КоличествоЭлементов = 1 Тогда
Возврат Массив[0];
ИначеЕсли КоличествоЭлементов = 2 Тогда
Возврат Среднее(Массив);
КонецЕсли;

УпорядочитьМассив(Массив);

Середина = КоличествоЭлементов / 2;

Если Середина = Цел(Середина) Тогда
Медиана = (Массив[Середина - 1] + Массив[Середина]) / 2;
Иначе
Медиана = Массив[Середина - 0.5]
КонецЕсли;

Возврат Медиана;
КонецФункции

Функция Дисперсия(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат Неопределено КонецЕсли;

Среднее = Среднее(Массив);

Сумма = 0;

Для каждого Значение из Массив Цикл
Сумма = Сумма + Pow((Значение - Среднее), 2);
КонецЦикла;

Дисперсия = Сумма / Массив.Количество();

Возврат Дисперсия;
КонецФункции

Функция СреднеКвадратичноеОтклонение(Массив) Экспорт
Если Массив.Количество() = 0 Тогда Возврат Неопределено КонецЕсли;

Дисперсия = Дисперсия(Массив);

Возврат ?(Дисперсия < 0, 0, Sqrt(Дисперсия));
КонецФункции

Процедура УпорядочитьМассив(Массив) Экспорт
СЗ = Новый СписокЗначений;
СЗ.ЗагрузитьЗначения(Массив);
СЗ.СортироватьПоЗначению(НаправлениеСортировки.Возр);
Массив = СЗ.ВыгрузитьЗначения();
КонецПроцедуры

Для получения крайних значений использую упорядочивание массива, так как в результате тестирования на разных количествах реальных данных такой вариан выдал лучшие показатели, чем перебор со сравнением текущего значения с минимальным/максимальным.

Также возникла необходимость проверить входят ли значения  одного массива в крайние положения другого с учётом допустимого процента отклонения:

Функция МинимумКП(МассивПолный, МассивТекущий, ПроцентОтклонения, ВернутьКоличество = Ложь) Экспорт
Минимум = Минимум(МассивПолный);
ПроцентХ = Минимум / 100 * (100 + ПроцентОтклонения);
сч = 0;

Для каждого Значение из МассивТекущий Цикл
Если Значение >= Минимум и Значение <= ПроцентХ Тогда
Если ВернутьКоличество Тогда
сч = сч + 1;
Иначе
Возврат Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;

Если ВернутьКоличество Тогда Возврат сч КонецЕсли;

Возврат Ложь;
КонецФункции

Функция МаксимумКП(МассивПолный, МассивТекущий, ПроцентОтклонения, ВернутьКоличество = Ложь) Экспорт
Максимум = Максимум(МассивПолный);
ПроцентХ = Максимум / 100 * (100 - ПроцентОтклонения);
сч = 0;

Для каждого Значение из МассивТекущий Цикл
Если Значение >= ПроцентХ и Значение <= Максимум Тогда
Если ВернутьКоличество Тогда
сч = сч + 1;
Иначе
Возврат Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;

Если ВернутьКоличество Тогда Возврат сч КонецЕсли;

Возврат Ложь;
КонецФункции

У данных функций есть параметр ВернутьКоличество, при установке значения которого в Истина функция возвращает количество значений, попавших в диапазон, иначе вернёт булево (есть пападания/нет).

В дополнение прикладываю обработку с примером использования и возможностью поиграться. Источником данных является таблица значений. необходимые данные с отбором удобно получать при помощи функции ПолучитьМассивДанных(); 

Надеюсь, кому-то может оказаться полезным.

17 Comments

  1. Caliban

    Зачем такие сложности с суммой?

    Функция Сумма(Массив) Экспорт
    Cумма = 0;
    
    Для Каждого Число Из Массив Цикл
    Сумма = Сумма + Число;
    КонецЦикла;
    
    Возврат Сумма;
    КонецФункции

    Показать

    Reply
  2. v3rter

    Мне показалось

    https://its.1c.ru/db/metod8dev#content:2606:hdoc

    Передача параметров по ссылке

    По умолчанию, во встроенном языке 1С:Предприятия 8 передача параметров в процедуры и функции осуществляется по ссылке. Это означает, что изменение формального параметра внутри процедуры или функции будет отражаться на значении фактического параметра, переданного при вызове процедуры или функции.

    или надо было передавать по значению?

    Функция Сумма(Знач Массив) Экспорт
    Reply
  3. ice-net

    (2)

    Знач Массив — породит новую переменную Массив (в текущем контексте), что не есть Массив — передаваемый в параметр

    => пришлось бы писать что-нибудь типа

    Массив = Сумма(Массив);

    Функция Сумма(Знач Массив)

    …..

    Возврат Массив;

    КонецФункции

    Reply
  4. SvoyakMartin

    (1) ситуация аналогична использованию упорядочивания массива для получения крайних вместо перебора.

    Несмотря на то, что логично предположить, что получение итога по колонке выполняет ту же операцию сложения в цикле, а я предлагаю дополнительно тратить время и память на создание объекта и его заполнение, по замерам производительности в моём случае выигрывает тот вариант, что я предложил.

    Reply
  5. v3rter

    Ну если изменения в процессе вызова функции исходного массива некритичны, то да.

    Хотя считаю, что сумму и минимум лучше было делать «дедовскими» способами — циклом с условием и вспомогательной переменной, как в

    (1)

    Идея понравилась, за идею «звездану».

    Reply
  6. ice-net

    (4)

    Ладно время, а как Вы проверяли производительность по памяти?

    Reply
  7. Alias

    На всякий случай напоминаю о наличии в платформе объекта

    Новый АнализДанных

    в котором уже есть встроенные механизмы расчёта различных параметров, в том числе максимума/минимума, медианы, среднего, стандартного отклонения и др.

    Надеюсь автор сравнивал свою разработку с использованием платформенных методов и выяснил, что она работает быстрее и лучше?…

    Reply
  8. SvoyakMartin

    (6) Конкретно в моём случае в приоритете была скорость выполнения отчёта. С памятью же вроде всё очевидно: создавая новую сущность с целью копирования в неё данных можно рассчитывать, что памяти придётся выделить как минимум в два раза больше(например при передаче по значению), а при использовании списка значений и тем более таблицы значений и подавно!

    Reply
  9. ice-net

    (8)

    Я не совсем это имел ввиду)) Я хотел узнать почему в функции Сумма используется ТЗ, а не перебор по массиву как в (1)?

    Reply
  10. SvoyakMartin

    (5) Изменения в исходный массив вносит только упорядочивание. Если важна сохранность не только данных, но и их индексов, тогда да, стоит озадачиться данным вопросом.

    Reply
  11. Alias

    Сравнил с платформенным методом.

    За счет того что платформа получает сразу несколько параметров (напомню, для типа анализа АнализДанныхОбщаяСтатистика это Количество, Максимум,

    Медиана, Минимум, Размах, Среднее, СтандартноеОтклонение), время выполнения анализа платформой больше, чем каждое из вычислений с использованием приведённых функций.

    Для примера:

    Анализ данных платформой: 100 мс (получены 7 параметров)

    Расчет приведёнными функциями: медиана — 25 мс, среднее — 80 мс, макс/мин — по 20 мс

    Таким образом, возможно, для получения единичного параметра (той же медианы) выгоднее использовать приведённую функцию. Ну это если не брать в расчёт то что она работает только с числами и не умеет, например, посчитать медиану по датам 🙂

    Если нужно получить сразу пакет параметров — то нужно или переделывать функции, оптимизируя получение за один проход, или всё же использовать платформенный механизм (т.к. судя по времени исполнения 100 всё же меньше чем 25+80+20+20).

    Reply
  12. SvoyakMartin

    (7) Признаюсь, с данным классом был не знаком, и при поиске способа получения дисперсии и ср.кв. отклонения средствами платформы он мне так же не попадался. На досуге озадачусь изучением сего.

    (11) За сравнение — спасибо, думаю, если объединить повторяющиеся вызовы, то общее время чуть сократится.

    Reply
  13. v3rter

    В инженерных калькуляторах статистические функции сохраняют в памяти устройства количество элементов, сумму и сумму квадратов. Для расчета дисперсии этих данных достаточно. Я это к тому, что если надо считать ещё быстрее, то может каким-то образом сохранять или кешировать эти данные, вместе с минимумом и максимумом?

    Reply
  14. vasilev2015

    Предлагаю:

    рядом с медианой добавить моду ))

    вместо возведения во вторую степень POW использовать умножение на себя

    вместо Количество()-1 использовать ВГраница()

    Reply
  15. SvoyakMartin

    (14) С модой не всё так просто, что делать, если попадётся мультимодальный массив? Можно посчитать по интервальной, но какой брать интервал?

    POW(x, 2) vs x * x — повторюсь, что у меня в приоритете время выполнения, в единичных случаях простое умножение выигрывает, но в среднем наоборот.

    ВГраница() — согласен! На 6 символов короче запись и быстрее в среднем в 5 раз!

    Reply
  16. Probot1c

    с набором функций и обработкой данных удобно в екселе работать, хорошо что в 1с что-то подобное есть.

    Reply
  17. Diego_Iv

    Огромное спасибо за СреднеКвадратичноеОтклонение и Дисперсию !

    Пригодилось…

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *