Гуглинг и инфостартинг не выдали автору статьи ответа – был ли этот способ когда-либо реализован на языке запросов 1С или нет. Если описание реализации такого способа для 1С уже есть – прошу ссылку казать в комментариях.
В начале, как и положено -
Сухая теория
Согласно википедии, у понятия «медиана» есть два определения:
- Медиана (от лат. mediāna «середина») набора чисел — число, которое находится в середине этого набора, если его упорядочить по возрастанию, то есть такое число, что половина из элементов набора не меньше него, а другая половина не больше.
- Медиана набора чисел — это число, сумма расстояний (или, если более строго, модулей) от которого до всех чисел из набора минимальна
В дальнейших рассуждениях будем опираться на первое определение. Но не только потому, что оно проще в восприятии.
Алгоритм вычисления медианы по этому определению прост и состоит из следующих шагов:
- пусть нам дан некоторый числовой ряд из N чисел
- упорядочиваем этот ряд по возрастанию значений
- если N – нечетное, то
- вычисляем индекс i = Целое(1 + N/2)
- находим число ni, стоящее на месте с индексом i
- найденное число и есть искомая медиана
- если N – четное
- вычисляем два индекса – i и j, где i = N / 2, j = i + 1
- и вот тут есть три варианта:
- объявляем медианой число ni
- объявляем медианой число nj
- объявляем медианой среднее арифметическое этих чисел
В этих примерах будем считать, что индексация ряда начинается с 1
Пример ряда с нечетным количеством элементов
- Пусть дан числовой ряд {7, 15, 6, 8, 7, 7, 15}
- упорядочим ряд по возрастанию значений: {6, 7, 7, 7, 8, 15, 15}
- ряд содержит 7 чисел (N = 7), значит i = Целое (1 + 7/2) = 4
- медиана ряда - число i4 = 7
Пример ряда с четным количеством элементов
- Пусть дан числовой ряд {7, 15, 6, 8, 7, 7, 20, 15}
- упорядочим ряд по возрастанию значений: {6, 7, 7, 7, 8, 15, 15, 20}
- ряд содержит 8 чисел (N = 8), значит
- i = 8/2 = 4
- j = i + 1 = 5
- медианой этого числового ряда могут быть следующие числа:
- i4 = 7
- i5 = 8
- Среднее(7, 8) = (7 + 8) / 2 = 7.5
Теоретически вроде просто.
Но на практике при попытке реализации этого алгоритма методом «в лоб» на языке запросов 1С возникает одна непростая задача – индексация элементов ряда: в языке запросов 1С нет легких и быстрых способов пронумеровать элементы числового ряда. Задача нумерации элементов ряда еще более осложняется, если в этом ряду есть дубликаты.
Способ, предлагаемый вниманию сообщества, избавляет нас от необходимости индексировать числовой ряд.
Описание предлагаемого способа
Даже больше - этот способ не предусматривает упорядочивание ряда по возрастанию значений его элементов!
Что же такого хитрого в этом способе?
Все дело в индексах элементов упорядоченного числового ряда.
Возможно, это звучит парадоксально – ряд мы не упорядочиваем, но опираемся на индексы упорядоченного ряда. Но это так.
Для рассмотрения возьмем числовой ряд из первого примера - {7, 15, 6, 8, 7, 7, 15}.
И проделаем с ним следующие манипуляции:
- упорядочим - {6, 7, 7, 7, 8, 15, 15}.
- проиндексируем упорядоченный ряд:
n1 = 6
n2 = 7
n3 = 7
n4 = 7
n5 = 8
n6 = 15
n7 = 15
- выделим уникальные элементы ряда: {6, 7, 8 ,15}
- определим диапазоны индексов, которые занимают уникальные элементы ряда в упорядоченном ряду:
6 – [1, 1]
7 – [2, 4]
8 – [5, 5]
15 – [6, 7].
И вот тут мне кажется, что Вы, мой внимательный читатель, уже поняли основную суть!
Совершенно верно: зная индекс среднего элемента ряда нам необходимо всего лишь определить:
- в какой диапазон этот индекс попадает
- и какой элемент ряда занимает этот диапазон.
Т.е. для нашего примера индекс среднего элемента упорядоченного ряда равен 4.
Этот индекс входит в диапазон [2, 4].
Число, занимающее этот диапазон – 7.
Это число – и есть медиана нашего числового ряда.
Подытожим – в предлагаемом способе для вычисления медианы нам необходимо всего лишь:
- иметь диапазоны индексов, которые занимают уникальные элементы ряда в упорядоченном ряду
- знать индекс среднего элемента ряда!
И то, и другое уже можно реализовать на языке запросов 1С.
От теории к практике
Настало время продемонстрировать запрос 1С, реализующий предложенный способ.
Небольшое уточнение: для ряда, содержащего четное количество элементов, предлагаемый запрос в качестве медианы вычисляет среднее арифметическое элементов ni и nj
Вот он:
ВЫБРАТЬ 7 КАК Элемент
ПОМЕСТИТЬ Ряд
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 15
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 15
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
КОЛИЧЕСТВО(Ряд.Элемент) КАК Длина
ПОМЕСТИТЬ ДлинаРяда
ИЗ
Ряд КАК Ряд
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВЫРАЗИТЬ((ДлинаРяда.Длина - 1) / 2 - 0.1 КАК ЧИСЛО(19, 0)) КАК Индекс
ПОМЕСТИТЬ ИндексМедианногоЭлемента
ИЗ
ДлинаРяда КАК ДлинаРяда
ОБЪЕДИНИТЬ
ВЫБРАТЬ
ВЫРАЗИТЬ((ДлинаРяда.Длина - 1) / 2 КАК ЧИСЛО(19, 0))
ИЗ
ДлинаРяда КАК ДлинаРяда
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Ряд.Элемент КАК Элемент,
КОЛИЧЕСТВО(Ряд.Элемент) КАК Количество
ПОМЕСТИТЬ СтатистикаРяда
ИЗ
Ряд КАК Ряд
СГРУППИРОВАТЬ ПО
Ряд.Элемент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Окончание.Элемент КАК Элемент,
ЕСТЬNULL(СУММА(Начало.Количество), 0) КАК НачальныйИндекс,
ЕСТЬNULL(СУММА(Начало.Количество), 0) + Окончание.Количество - 1 КАК КонечныйИндекс
ПОМЕСТИТЬ ИндексыЭлементов
ИЗ
СтатистикаРяда КАК Начало
ПРАВОЕ СОЕДИНЕНИЕ СтатистикаРяда КАК Окончание
ПО Начало.Элемент < Окончание.Элемент
СГРУППИРОВАТЬ ПО
Окончание.Элемент,
Окончание.Количество
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СРЕДНЕЕ(ИндексыЭлементов.Элемент) КАК Элемент
ИЗ
ИндексМедианногоЭлемента КАК ИндексМедианногоЭлемента
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ИндексыЭлементов КАК ИндексыЭлементов
ПО (ИндексМедианногоЭлемента.Индекс МЕЖДУ ИндексыЭлементов.НачальныйИндекс И ИндексыЭлементов.КонечныйИндекс)
Запрос достаточно прост и вряд ли нуждается в комментировании.