Код для вычисления "медианы пяти" в C#

Примечание: пожалуйста, не интерпретируйте это как "вопрос домашней работы."Это просто вещь, которую мне любопытно знать:)

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

каков наилучший способ реализовать это "медиана пяти с использованием 6 сравнений" в C# ? Все мои попытки, похоже, приводят к неудобному коду : (мне нужен хороший и читаемый код, пока все еще используя только 6 сравнений.

public double medianOfFive(double a, double b, double c, double d, double e){
    //
    // return median
    //
    return c;
}

Примечание: Я думаю, что я должен найти алгоритм"":

я оказался не в состоянии четко объяснить алгоритм как Azereal сделал в своем посте на форуме. Поэтому я буду ссылаться на его пост здесь. От http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1061827085

Ну я поставил эту проблему в один моей задания и я обратился к этому форум для помощи, но никакой помощи не было. В конце концов я понял, как это делается.

  1. запустите mergesort с первыми 4 элементами и упорядочьте каждую пару (2 сравнения)

  2. Сравните два нижних из каждой пары и исключить самый низкий из возможности (3 сравнения)

  3. добавить в 5-й номер, выделенный в число без пары и сравнить пара (4 сравнения)

  4. сравните две самые низкие из двух новых пар и исключите нижнюю (5 сравнения)

  5. сравните один сам по себе и нижний из последней пары и Нижний число-медиана

    возможная медиана находится в пределах parentesis

(54321)

5:4 3:2 2 сравнение

(4

4:2 3 сравнения

2(4

1:3 4 сравнения

2(4

4:1 5 сравнения

1,2(4

4:3 6 сравнения

1,2(3)4,5

три медианы

EDIT: как ваш запрос и чтобы предотвратить получение большего количества downvotes, это код C++, который я написал, чтобы найти медиану из пяти. Не против это неловкость:

double StageGenerator::MedianOfFive(double n1, double n2, double n3, double n4, double n5){
    double *a = &n1, *b = &n2, *c = &n3, *d = &n4, *e = &n5;
    double *tmp;

    // makes a < b and b < d
    if(*b < *a){
        tmp = a; a = b; b = tmp;
    }

    if(*d < *c){
        tmp = c; c = d; d = tmp;
    }

    // eleminate the lowest
    if(*c < *a){
        tmp = b; b = d; d = tmp; 
        c = a;
    }

    // gets e in
    a = e;

    // makes a < b and b < d
    if(*b < *a){
        tmp = a; a = b; b = tmp;
    }

    // eliminate another lowest
    // remaing: a,b,d
    if(*a < *c){
        tmp = b; b = d; d = tmp; 
        a = c;
    }

    if(*d < *a)
        return *d;
    else
        return *a;

} 

Он должен быть более компактным, не так ли ?

EDIT:

как указал @pablito в своем ответе. Встроенный список.Sort () не может выполнить это требование, так как он использует до 13 сравнений :]

10 ответов


это в основном просто факторинг кода подкачки и сортировки из вашего примера c++:

private static void Swap(ref double a, ref double b) {
    double t = a;
    a = b;
    b = t;
}

private static void Sort(ref double a, ref double b) {
    if (a > b) {
        double t = a;
        a = b;
        b = t;
    }
}

private static double MedianOfFive(double a, double b, double c, double d, double e){
    // makes a < b and c < d
    Sort(ref a, ref b);
    Sort(ref c, ref d);

    // eleminate the lowest
    if (c < a) {
        Swap(ref b, ref d);
        c = a;
    }

    // gets e in
    a = e;

    // makes a < b
    Sort(ref a, ref b);

    // eliminate another lowest
    // remaing: a,b,d
    if (a < c) {
        Swap(ref b, ref d);
        a = c;
    }

    return Math.Min(d, a);
}

Я нашел этот пост интересным, и в качестве упражнения я создал это, которое делает только 6 сравнений и ничего больше:

static double MedianOfFive(double a, double b, double c, double d, double e)
{
    return b < a ? d < c ? b < d ? a < e ? a < d ? e < d ? e : d
                                                 : c < a ? c : a
                                         : e < d ? a < d ? a : d
                                                 : c < e ? c : e
                                 : c < e ? b < c ? a < c ? a : c
                                                 : e < b ? e : b
                                         : b < e ? a < e ? a : e
                                                 : c < b ? c : b
                         : b < c ? a < e ? a < c ? e < c ? e : c
                                                 : d < a ? d : a
                                         : e < c ? a < c ? a : c
                                                 : d < e ? d : e
                                 : d < e ? b < d ? a < d ? a : d
                                                 : e < b ? e : b
                                         : b < e ? a < e ? a : e
                                                 : d < b ? d : b
                 : d < c ? a < d ? b < e ? b < d ? e < d ? e : d
                                                 : c < b ? c : b
                                         : e < d ? b < d ? b : d
                                                 : c < e ? c : e
                                 : c < e ? a < c ? b < c ? b : c
                                                 : e < a ? e : a
                                         : a < e ? b < e ? b : e
                                                 : c < a ? c : a
                         : a < c ? b < e ? b < c ? e < c ? e : c
                                                 : d < b ? d : b
                                         : e < c ? b < c ? b : c
                                                 : d < e ? d : e
                                 : d < e ? a < d ? b < d ? b : d
                                                 : e < a ? e : a
                                         : a < e ? b < e ? b : e
                                                 : d < a ? d : a;
}

спасибо. Я знаю, что ваши сообщения довольно старые, но это было полезно для моей проблемы.

Мне нужен был способ вычислить медиану 5 регистров SSE / AVX (4 поплавка / 8 поплавков сразу или 2 двойника / 4 двойника сразу):

  • без каких-либо условных переходов

  • только с инструкциями min/max

Если функции min / max запрограммированы для скалярных регистров с условными скачками, мой код не является оптимальным в термин сравнения. Но если функции min/max закодированы с соответствующими инструкциями CPU, мой код очень эффективен, потому что при выполнении CPU не выполняется условный переход.

    template<class V> 
    inline V median(const V &a, const V &b, const V &c)
    {
      return max(min(a,b),min(c,max(a,b))); 
    } 

    template<class V> 
    inline V median(const V &a, const V &b, const V &c, const V &d, const V &e)
    {
      V f=max(min(a,b),min(c,d)); // discards lowest from first 4
      V g=min(max(a,b),max(c,d)); // discards biggest from first 4
      return median(e,f,g);
    } 

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

public double medianOfFive(double a, double b, double c, double d, double e){
    double median;
    // sort a and b
    if(a > b) // comparison # 1
    {
        double temp = a;
        a = b;
        b = temp;
    }

    // sort c and d
    if(c > d)  // comparison # 2
    {
        double temp = c;
        c = d;
        d = temp;
    }

    // replace the lower of a and c with e
    // because the lowest of the first four cannot be the median
    if(a < c) // comparison # 3
    {
        a = e;
        // re-sort a and b
        if(a > b) // comparison # 4
        {
            double temp = a;
            a = b;
            b = temp;
        }
    }
    else
    {
        c = e;
        // re-sort c and d
        if(c > d)  // comparison # 4
        {
            double temp = c;
            c = d;
            d = temp;
        }
    }

    // eliminate a or c, because the lowest
    // of the remaining four can't be the median either
    if(a < c) // comparison #5
    {
         if(b < c) // comparison #6
         {
              median = c;
         }
         else
         {
              median = b;
         }
    }
    else
    {
         if(d < a) // comparison #6
         {
              median = a;
         }
         else
         {
              median = d;
         }
    }
    return median;
}

интересная тема здесь:

http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1061827085

цитата из ниток:

  1. цифры в массив.

  2. используйте три сравнения и перетасуйте числа так, чтобы a[1]

  3. Если a[3] > a[2], то проблема достаточно простой. Если a[2]

  4. Итак, a[3] a[4], то решение является меньшим из A[3] и a[5]. В противном случае решение является меньшим из[2] и[4].


просто чтобы проверить, сколько сравнений:

    class MyComparable : IComparable
{

    public static int NumberOfComparisons = 0;

    public int NumPart { get; set; }

    #region IComparable Members

    public int CompareTo(object obj)
    {
        NumberOfComparisons++; //I know, not thread safe but just for the sample
        MyComparable mc = obj as MyComparable;
        if (mc == null)
            return -1;
        else
            return NumPart.CompareTo(mc.NumPart);
    }

    #endregion
}

class Program
{
    static void Main(string[] args)
    {
        List<MyComparable> list = new List<MyComparable>();
        list.Add(new MyComparable() { NumPart = 5 });
        list.Add(new MyComparable() { NumPart = 4 });
        list.Add(new MyComparable() { NumPart = 3 });
        list.Add(new MyComparable() { NumPart = 2 });
        list.Add(new MyComparable() { NumPart = 1 });
        list.Sort();


        Console.WriteLine(MyComparable.NumberOfComparisons);
    }
}

результат 13.


для полноты, вопрос является частным случаем сеть сортировки, который кнут (Искусство программирования, vol 3) крышки в большой детали. The классической работе Э. К. Батчер на эту тему кратко и стоит прочитать.


это должно помочь

private Double medianofFive(double[] input)
{
    Double temp;
if (input[0] > input[1])//#1 - sort First and Second
{
    temp = input[0];
    input[0] = input[1];
    input[1] = temp;
}
if (input[2] > input[3])//#2 sort Third and Fourth
{
    temp = input[2];
    input[2] = input[3];
    input[3] = temp;
}

// replace the smaller of first and third with 5th, then sort
int smallerIndex = input[0] < input[2] ? 0 : 2;//#3
input[smallerIndex] = input[4];

//sort the new pair
if(input[smallerIndex]>input[smallerIndex+1])//#4
{
    temp = input[smallerIndex];
    input[smallerIndex] = input[smallerIndex+1];
    input[smallerIndex+1] = temp;
}

//compare the two smaller numbers
// then compare the smaller of the two's partner with larger of the two
// the smaller of THOSE two is the median
if (input[2] > input[0])
//#5
{
    temp = input[2] > input[1] ? input[1] : input[2];//#6
}
else
{
    temp = input[0] > input[3] ? input[3] : input[0];//#6
}
    return temp;
}

-- In Haskell the solution could look like

import Data.Function

median5By pred [a,b,c,d,e] = fst $ merge2 c' d' where
  merge2 = merge2By pred
  merge2By pred x y = if x `pred` y then (x,y) else (y,x)
  ((_,b'), de   ) = merge2By (pred `on` fst) (merge2 a  b) (merge2 d e)
  ((_,c'),(d',_)) = merge2By (pred `on` fst) (merge2 b' c)  de

main = print $ median5By (<) [2,5,4,1,3]

интересно, сколько сравнений в образце MSDN...

public static double Median(this IEnumerable<double> source) {
        if (source.Count() == 0)  throw new InvalidOperationException("Cannot compute median for an empty set.");

        var sortedList = from number in source
                         orderby number
                         select number;

        int itemIndex = (int)sortedList.Count() / 2;

        if (sortedList.Count() % 2 == 0) {
            // Even number of items.
            return (sortedList.ElementAt(itemIndex) + sortedList.ElementAt(itemIndex - 1)) / 2; } else {
            // Odd number of items.
            return sortedList.ElementAt(itemIndex); }
    }