Рассчитать медиану в c # - PullRequest
46 голосов
/ 10 ноября 2010

Мне нужно написать функцию, которая будет принимать массив десятичных дробей и найдет медиану.

Есть ли функция в библиотеке .net Math?

Ответы [ 12 ]

58 голосов
/ 28 марта 2014

Похоже, что другие ответы используют сортировку. Это не оптимально с точки зрения производительности, потому что это занимает O(n logn) время. Вместо этого можно рассчитать медиану в O(n) времени. Обобщенная версия этой проблемы известна как «статистика n-порядка», что означает нахождение элемента K в наборе, в котором у нас n элементов меньше или равно K, а остальные больше или равны K. Таким образом, статистика 0-го порядка будет минимальной элемент в наборе (примечание: в некоторых источниках используется индекс от 1 до N вместо 0 до N-1). Медиана - это просто (Count-1)/2 -статистическая статистика.

Ниже приведен код, принятый из Введение в алгоритмы Кормена и др., 3-е издание .

/// <summary>
/// Partitions the given list around a pivot element such that all elements on left of pivot are <= pivot
/// and the ones at thr right are > pivot. This method can be used for sorting, N-order statistics such as
/// as median finding algorithms.
/// Pivot is selected ranodmly if random number generator is supplied else its selected as last element in the list.
/// Reference: Introduction to Algorithms 3rd Edition, Corman et al, pp 171
/// </summary>
private static int Partition<T>(this IList<T> list, int start, int end, Random rnd = null) where T : IComparable<T>
{
    if (rnd != null)
        list.Swap(end, rnd.Next(start, end+1));

    var pivot = list[end];
    var lastLow = start - 1;
    for (var i = start; i < end; i++)
    {
        if (list[i].CompareTo(pivot) <= 0)
            list.Swap(i, ++lastLow);
    }
    list.Swap(end, ++lastLow);
    return lastLow;
}

/// <summary>
/// Returns Nth smallest element from the list. Here n starts from 0 so that n=0 returns minimum, n=1 returns 2nd smallest element etc.
/// Note: specified list would be mutated in the process.
/// Reference: Introduction to Algorithms 3rd Edition, Corman et al, pp 216
/// </summary>
public static T NthOrderStatistic<T>(this IList<T> list, int n, Random rnd = null) where T : IComparable<T>
{
    return NthOrderStatistic(list, n, 0, list.Count - 1, rnd);
}
private static T NthOrderStatistic<T>(this IList<T> list, int n, int start, int end, Random rnd) where T : IComparable<T>
{
    while (true)
    {
        var pivotIndex = list.Partition(start, end, rnd);
        if (pivotIndex == n)
            return list[pivotIndex];

        if (n < pivotIndex)
            end = pivotIndex - 1;
        else
            start = pivotIndex + 1;
    }
}

public static void Swap<T>(this IList<T> list, int i, int j)
{
    if (i==j)   //This check is not required but Partition function may make many calls so its for perf reason
        return;
    var temp = list[i];
    list[i] = list[j];
    list[j] = temp;
}

/// <summary>
/// Note: specified list would be mutated in the process.
/// </summary>
public static T Median<T>(this IList<T> list) where T : IComparable<T>
{
    return list.NthOrderStatistic((list.Count - 1)/2);
}

public static double Median<T>(this IEnumerable<T> sequence, Func<T, double> getValue)
{
    var list = sequence.Select(getValue).ToList();
    var mid = (list.Count - 1) / 2;
    return list.NthOrderStatistic(mid);
}

Несколько заметок:

  1. Этот код заменяет хвостовой рекурсивный код из исходной версии книги в итерационный цикл.
  2. Это также устраняет ненужную дополнительную проверку из исходной версии, когда start == end.
  3. Я предоставил две версии Median, одна из которых принимает IEnumerable, а затем создает список. Если вы используете версию, которая принимает IList, имейте в виду, что она изменяет порядок в списке.
  4. Вышеуказанные методы вычисляют медиану или любую статистику i-порядка в O(n) ожидаемое время . Если вы хотите O(n) худшее время , то есть методика использования медианы медианы. Хотя это улучшило бы производительность в худшем случае, оно ухудшает средний случай, потому что константа в O(n) теперь больше. Однако, если вы рассчитываете медиану в основном на очень больших данных, стоит посмотреть.
  5. Метод NthOrderStatistics позволяет пропустить генератор случайных чисел, который затем будет использоваться для выбора случайного центра во время разделения. Как правило, в этом нет необходимости, если только вы не знаете, что у ваших данных есть определенные шаблоны, так что последний элемент не будет достаточно случайным или если каким-то образом ваш код будет открыт снаружи для целевого использования.
  6. Определение медианы понятно, если у вас нечетное количество элементов. Это просто элемент с индексом (Count-1)/2 в отсортированном массиве. Но когда у вас четное число элемента (Count-1)/2 больше не является целым числом, и у вас есть две медианы: Нижняя медиана Math.Floor((Count-1)/2) и Math.Ceiling((Count-1)/2). Некоторые учебники используют более низкую медиану в качестве «стандарта», в то время как другие предлагают использовать в среднем два. Этот вопрос становится особенно критичным для набора из 2 элементов. Выше код возвращает нижнюю медиану. Если вы хотите вместо усреднения нижнего и верхнего значений, вам нужно вызвать вышеуказанный код дважды. В этом случае не забудьте измерить производительность ваших данных, чтобы решить, следует ли использовать приведенный выше код VS просто для прямой сортировки.
  7. Для .net 4.5+ вы можете добавить атрибут MethodImplOptions.AggressiveInlining в метод Swap<T> для немного улучшенной производительности.
34 голосов
/ 30 ноября 2011

Спасибо, Рэйф, это учитывает проблемы, которые отправили ваши ответчики.

public static double GetMedian(double[] sourceNumbers) {
        //Framework 2.0 version of this method. there is an easier way in F4        
        if (sourceNumbers == null || sourceNumbers.Length == 0)
            throw new System.Exception("Median of empty array not defined.");

        //make sure the list is sorted, but use a new array
        double[] sortedPNumbers = (double[])sourceNumbers.Clone();
        Array.Sort(sortedPNumbers);

        //get the median
        int size = sortedPNumbers.Length;
        int mid = size / 2;
        double median = (size % 2 != 0) ? (double)sortedPNumbers[mid] : ((double)sortedPNumbers[mid] + (double)sortedPNumbers[mid - 1]) / 2;
        return median;
    }
18 голосов
/ 10 ноября 2010

Есть ли функция в библиотеке .net Math?

номер

Нетрудно написать свой собственный. Наивный алгоритм сортирует массив и выбирает средние (или средние из двух средних) элементов. Однако этот алгоритм имеет значение O(n log n), в то время как его можно решить за O(n) раз. Вы хотите взглянуть на алгоритмы выбора , чтобы получить такой алгоритм.

17 голосов
/ 10 ноября 2010
decimal Median(decimal[] xs) {
  Array.Sort(xs);
  return xs[xs.Length / 2];
}

Нужно сделать свое дело.

- РЕДАКТИРОВАТЬ -

Для тех, кто хочет получить полную монету, вот полное, короткое, чистое решение (непустоеподразумевается входной массив):

decimal Median(decimal[] xs) {
  var ys = xs.OrderBy(x => x).ToList();
  double mid = (ys.Count - 1) / 2.0;
  return (ys[(int)(mid)] + ys[(int)(mid + 0.5)]) / 2;
}
11 голосов
/ 11 декабря 2017

Math.NET - это библиотека с открытым исходным кодом, которая предлагает метод вычисления Медиана . Пакет nuget называется MathNet.Numerics .

.

Использование довольно просто:

using MathNet.Numerics.Statistics;

IEnumerable<double> data;
double median = data.Median();
3 голосов
/ 20 сентября 2017

Вот общая версия ответа Джейсона

    /// <summary>
    /// Gets the median value from an array
    /// </summary>
    /// <typeparam name="T">The array type</typeparam>
    /// <param name="sourceArray">The source array</param>
    /// <param name="cloneArray">If it doesn't matter if the source array is sorted, you can pass false to improve performance</param>
    /// <returns></returns>
    public static T GetMedian<T>(T[] sourceArray, bool cloneArray = true) where T : IComparable<T>
    {
        //Framework 2.0 version of this method. there is an easier way in F4        
        if (sourceArray == null || sourceArray.Length == 0)
            throw new ArgumentException("Median of empty array not defined.");

        //make sure the list is sorted, but use a new array
        T[] sortedArray = cloneArray ? (T[])sourceArray.Clone() : sourceArray;
        Array.Sort(sortedArray);

        //get the median
        int size = sortedArray.Length;
        int mid = size / 2;
        if (size % 2 != 0)
            return sortedArray[mid];

        dynamic value1 = sortedArray[mid];
        dynamic value2 = sortedArray[mid - 1];
        return (sortedArray[mid] + value2) * 0.5;
    }
1 голос
/ 10 октября 2017

Когда-нибудь в будущем.Я думаю, это так просто, как только можно.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Median
{
    class Program
    {
        static void Main(string[] args)
        {
            var mediaValue = 0.0;
            var items = new[] { 1, 2, 3, 4,5 };
            var getLengthItems = items.Length;
            Array.Sort(items);
            if (getLengthItems % 2 == 0)
            {
                var firstValue = items[(items.Length / 2) - 1];
                var secondValue = items[(items.Length / 2)];
                mediaValue = (firstValue + secondValue) / 2.0;
            }
            if (getLengthItems % 2 == 1)
            {
                mediaValue = items[(items.Length / 2)];
            }
            Console.WriteLine(mediaValue);
            Console.WriteLine("Enter to Exit!");
            Console.ReadKey();
        }
    }
}
1 голос
/ 15 ноября 2016

Библиотека NMath CenterSpace предоставляет функцию:

double[] values = new double[arraySize];
double median = NMathFunctions.Median(values);

При желании вы можете использовать NaNMedian (если ваш массив может содержать нулевые значения), но вам нужно будет преобразовать массив в вектор:

double median = NMathFunctions.NaNMedian(new DoubleVector(values));

Библиотека NMath CenterSpace не бесплатна, но многие университеты имеют лицензии

1 голос
/ 28 июля 2015

Вот самая быстрая небезопасная реализация, тот же алгоритм, что и раньше, взятый из этого источника

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static unsafe void SwapElements(int* p, int* q)
    {
        int temp = *p;
        *p = *q;
        *q = temp;
    }

    public static unsafe int Median(int[] arr, int n)
    {
        int middle, ll, hh;

        int low = 0; int high = n - 1; int median = (low + high) / 2;
        fixed (int* arrptr = arr)
        {
            for (;;)
            {
                if (high <= low)
                    return arr[median];

                if (high == low + 1)
                {
                    if (arr[low] > arr[high])
                        SwapElements(arrptr + low, arrptr + high);
                    return arr[median];
                }

                middle = (low + high) / 2;
                if (arr[middle] > arr[high])
                    SwapElements(arrptr + middle, arrptr + high);

                if (arr[low] > arr[high])
                    SwapElements(arrptr + low, arrptr + high);

                if (arr[middle] > arr[low])
                    SwapElements(arrptr + middle, arrptr + low);

                SwapElements(arrptr + middle, arrptr + low + 1);

                ll = low + 1;
                hh = high;
                for (;;)
                {
                    do ll++; while (arr[low] > arr[ll]);
                    do hh--; while (arr[hh] > arr[low]);

                    if (hh < ll)
                        break;

                    SwapElements(arrptr + ll, arrptr + hh);
                }

                SwapElements(arrptr + low, arrptr + hh);

                if (hh <= median)
                    low = ll;
                if (hh >= median)
                    high = hh - 1;
            }
        }
    }
0 голосов
/ 11 января 2019

У меня есть гистограмма с переменной: группа
Вот как я вычисляю свою медиану:

int[] group = new int[nbr]; 

// -- Fill the group with values---

// sum all data in median
int median = 0;
for (int i =0;i<nbr;i++) median += group[i];

// then divide by 2 
median = median / 2;

// find 50% first part 
for (int i = 0; i < nbr; i++)
{
   median -= group[i];
   if (median <= 0)
   {
      median = i;
      break;
   }
}

медиана - групповой индекс медианы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...