在C#中计算“中位数为5”的代码

时间:2009-01-26 19:07:38

标签: c# algorithm readability median

注意:请不要将此解释为“作业问题”。这只是我很想知道的事情:)

中位数有时被用作算法设计中的一项练习,并且已知可以计算仅使用6次比较

在C#中实现“使用6个比较的中位数为5”的最佳方法是什么?我所有的尝试似乎都导致代码笨拙:(我需要漂亮可读的代码,同时仍然只使用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. 使用前4个元素启动mergesort,并为每个元素排序(2   比较)

  2.   
  3. 比较每对中的两个较低的一个并消除最低的一个   可能性(3次比较)

  4.   
  5. 在没有配对的情况下添加留给号码的第5个号码进行比较   两个(4个比较)

  6.   
  7. 比较两个新对中的两个最低对并消除较低对   (5次比较)

  8.   
  9. 单独比较一个和最后一对和较低的一对   数字是中位数

         

    可能的中位数在   parentesis

  10.         

    (54321)

         

    5:4 3:2 2比较

         

    (4 <5 2 <3 1)

         

    4:2 3比较

         

    2(4 <5 3 1)

         

    1:3 4比较

         

    2(4 <5 1 <3)

         

    4:1 5比较

         

    1,2(4 <5 3)

         

    4:3 6比较

         

    1,2(3)4,5

         

    三是中位数

编辑:作为您的请求,并防止自己获得更多的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;

} 

它应该更紧凑,不是吗?

修改

正如@pablito在答案中指出的那样。内置的List.Sort()无法满足此要求,因为它最多使用13次比较:]

11 个答案:

答案 0 :(得分:42)

我觉得这篇文章很有意思,作为一个练习我创造了这个只进行了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;
}

答案 1 :(得分:15)

这基本上只是将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);
}

答案 2 :(得分:10)

感谢。我知道你的帖子很老了,但这对我的问题很有帮助。

我需要一种方法来计算5个SSE / AVX寄存器的中位数(4个浮点数/ 8个浮点数一次,或2个双打/ 4个双打一次):

  • 没有任何条件跳转

  • 仅限最小/最大指示

如果为具有条件跳转的标量寄存器编程最小/最大功能,则我的代码在比较方面不是最佳的。 但是如果使用相应的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);
    } 

答案 3 :(得分:8)

这是一个有趣的主题:

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

引自线程:

  
      
  1. 将数字放在数组中。

  2.   
  3. 使用三个比较并在数字周围进行随机播放,以便a [1]&lt; a [2],a [4]&lt; a [5]和[1]&lt;一个[4]。

  4.   
  5. 如果a [3]&gt; a [2],那么问题就相当容易了。如果a [2]&lt; a [4],中值是a [3]和[4]中的较小者。如果不是,则中值是a [2]和[5]中的较小者。

  6.   
  7. 所以a [3]&lt; a2]。如果a [3]&gt; a [4],那么解是[3]和[5]中较小的一个。否则,解决方案是a [2]和[4]中的较小者。

  8.   

答案 4 :(得分:8)

这非常难看并且可以使用一些重构,但它明确地遍历所有比较和交换,以便您可以看到正在发生的事情。

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;
}

答案 5 :(得分: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。

答案 6 :(得分:3)

为了完整起见,问题是sorting network的具体情况,其中Knuth (Art of Computer Programming, vol 3)详细介绍了classic paper by K.E. Batcher。关于这个主题的{{3}}是简短的,值得一读。

答案 7 :(得分:1)

这应该这样做

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;
}

答案 8 :(得分:0)

其他答案略有不同,与6个比较相比,平均提高了3.33%至66.67%,并在中位数附近完全分配了5个元素,而没有任何额外费用。

通过使用3中位数和quickselect,使用3中位数从3个样本中选择枢轴,可以在5.8个比较的平均值(所有排列的平均值)中找到5个不同元素的中位数。 5个元素中的一个。 “ 3分中位数”对这三个元素进行分区,在对其余2个元素进行分区时,无需将其与数据透视表进行比较。到目前为止,这是4到5的比较,以将5个元素划分为三个中间元素之一(3的中位数不能为5的最小值或最大值)。最多可能需要进行3次以上比较,才能将5个元素围绕其中位数进行划分(严格来说,这比仅仅寻找中位数要耗费更多的精力),总共需要进行4到7次比较,(如上所述)平均为5.8。 5个不同元素的所有可能排列(如果元素不同则比较少)。请注意,这与通常的“总是6比较”解决方案不同,在少数情况下,不同的输入可能需要多达7个比较,但另一方面,不同输入的大多数排列要求不超过6个,并且通常更少;此外,为非唯一输入保存比较的代码非常容易(如果所有输入相等,则仅需要2个比较;当使用常规6比较方法输入不是唯一时,保存比较的代码变得相当复杂(尝试一下) !),即使所有输入均相等,它仍然需要进行6次比较。

可以通过以下方式找到除中位数以外的订单统计信息:第二个最小的或第二个最大的平均数量可以稍微多一些(5.81666 ...比较),当然可以只找到最小或最大4个比较。

基于此,并根据要求,下面是一个注释严重的函数,该函数使用可变参数比较函数返回5个元素的中值的指针。它是用C语言编写的,但是它在Quadrathorpe-y异常或海波状波状变形中也应同样有效。请注意,这仅返回指向中值元素的指针。它不会对元素进行分区(实际上它不会移动任何元素)。

/* a virtual swap, preserving both pointers for later use */
#define VSWAP(ma,mb,mt) mt=ma,ma=mb,mb=mt
/* a virtual swap only preserving the first pointer */
#define VSWAP2(ma,mb,munused) ma=mb
/* virtual rotation to the left; reverse the first 3 args for right rotation */
#define ROTATE(ma,mb,mc,mt) (mt)=(ma),(ma)=(mb),(mb)=(mc),(mc)=(mt)


/* median of 5 elements, no data movement, minimum (average 5.8) comparisons */
/* This implementation minimizes the number of comparisons made (min. 2) when
   elements compare equal.
*/
/* As no elements are moved, the elements are of course not partitioned around
   the element pointed to by the returned pointer (this differs from selection
   of the median via quickselect).
*/
/* Results are biased toward the middle: pc, then pb or pd, last pa or pe. */
/* The implementation is based on a simulation of quickselect using partition3
   to select a pivot from the middle three elements, with partitioning by
   skipping over the 3 partitioned elements.  For distinct inputs, it uses on
   average 5.8 comparisons (averaged over all permutations of 5 distinct
   elements); fewer for inputs with equal elements.  That's an improvement of
   about 3% over the usual 6-comparison implementation of median-of-5.
*/
void *fmed5(void *pa, void *pb, void *pc, void *pd, void *pe,
    int(*compar)(const void *,const void *))
{
    void *t;
    int c, d;
    /* First compare the 3 middle elements to determine their relative
       ordering; pb will point to the minimum, pc to the median, and pd to
       the maximum of the three.
    */
    /* Ternary median-of-3, biased toward middle element if possible, else
       first element.  Average 8/3 comparisons for 3 elements (distinct values)
       = 0.889 comparisons/element
    */
    c=compar(pb,pc); /* 1 */
    if (0<c) { /* *pb,*pc out-of-order: *pb>*pc */
        /* Before doing anything about pb,pc, compare *pc,*pd. */
        d=compar(pc,pd); /* 2a */
        if (0>d) { /* *pc<*pd: strictly in order */
            /* But *pb might be either larger than or smaller than (or equal
               to) *pd, so they may (i.e. unless it's known from the earlier
               comparison of original *pc and *pd that *pb is larger than
               both) have to be compared,  Possibilities:
               *pc<*pb<=*pd (virtual swap of pb,pc corrects relationship)
               *pc<*pd<*pb (virtual rotation to the left corrects it)
            */
            c=compar(pb,pd); /* 3a (if needed) */
            if (0<c) { /* *pc < *pd < *pb */
                ROTATE(pb,pc,pd,t);
            } else { /* *pc < *pb <= *pd */
                VSWAP(pb,pc,t);
            }
        } else { /* *pd==*pc<*pb or reversed ordering: *pd<*pc<*pb */
            VSWAP(pb,pd,t); /* one (virtual) swap takes care of it */
        } /* *pc:*pd comparisons results if-else */
        /* Note that if pc,pd compare equal, pc remains as the chosen
           median (biased toward the middle element).
        */
    } else if (0==c) { /* *pb,*pc compare equal */
        /* Either pb or pc can be taken as the median; bias here is towards
           pc, which is already in the middle position. But pd might be
           the minimum of the three or the maximum (or it may also be equal
           to both pb and pc).
        */
        d=compar(pb,pd); /* 2b */
        if (0<d) { /* pb,pd are out-of-order */
            VSWAP(pb,pd,t);
        }
    } else { /* *pb,*pc in strict order: *pb < *pc; how about *pc,*pd? */
        d=compar(pc,pd); /* 2c */
        if (0<d) { /* *pc,*pd are strictly out-of-order: *pd < *pc */
            /* But *pb might be either larger than or smaller than (or equal
               to) *pd, so they may (i.e. unless it's known from the earlier
               comparison of original *pc and *pd that *pb is larger than
               both) have to be compared,  Possibilities:
               *pb<=*pd<*pc (virtual swap of pc,pd corrects relationship)
               *pd<*pb<*pc (virtual rotation to the right corrects it)
            */
            c=compar(pb,pd); /* 3b (if needed) */
            if (0<c) { /* *pd < *pb < *pc */
                ROTATE(pd,pc,pb,t);
            } else { /* *pc < *pb <= *pd */
                VSWAP(pc,pd,t);
            }
        } /* *pc:*pd comparisons results if-else */
        /* Note that if pc,pd compare equal, pc remains as the chosen
           median (biased toward the middle element).
        */
    } /* *pb:*pc comparisons results if-else chain */
    /* Now pb points to the minimum of pb,pc,pd; pc to the median, and pd
       to the maximum.
    */
    /* Special case: if all three middle elements compare equal (0==c==d),
       any one can be returned as the median of 5, as it's impossible for
       either of the other two elements to be the median (unless of course
       one or both of them also compares equal to pb,pc,pd, in which case
       returning any of pb,pc,pd is still correct).  Nothing more needs to
       be done in that case.
    */
    if ((0!=c)||(0!=d)) { /* Not all three of pb,pc,pd compare equal */
        int e;
        /* Compare pa and pe to pc. */
        e=compar(pa,pc); /* 3c or 4a (iff needed) */
        /* If three (or more) of the four elements so far compared are
           equal, any of those equal-comparing elements can be chhosen as
           the median of 5.  If all five elements were arranged in order,
           one of the three equal-comparing elements would necessarily be
           in the middle (at most both of the remaining elements might be
           either larger or smaller than the equal elements).  So if pa
           compares equal to pc and pc also compared equal to pb or to pd,
           nothing more need be done; pc can be considered as the median of
           five.
        */
        if ((0!=e)||(0!=c)||(0!=d)) { /* no three elements compare equal */
            int f;
            f=compar(pe,pc); /* 4b or 5a (iff needed) */
            /* Check again for three equal comparisons to avoid doing any
               unnecessary additional work.
            */
            if (
                (0!=f) /* also not equal; still not three */
              ||
                ( /* 0==f && */
                 ((0!=c)&&(0!=d)) /* at most e and f; not three */
               ||
                 ((0!=c)&&(0!=e)) /* at most d and f; not three */
               ||
                 ((0!=d)&&(0!=e)) /* at most c and f; not three */
                )
            ) {
                /* Possibilites are that:
                     one of *pa,*pe is less than (or equal to) *pc and one
                       is greater than (or equal to) *pc; *pc is the median
                       of five.
                     *pa and *pe are both less than *pc; the median of five
                       is then the maximum of *pa,*pb,*pe (*pc and *pd are
                       at least as large as those three).  The ordering of
                       those 3 elements has not been established, and it
                       will take two additional comparisons to do so.
                     *pa and *pe are both greater than *pc; the median of
                       five is the minimum of *pa,*pd,*pe (neither *pb nor
                       *pc can be larger than any of those three).
                */
                if ((0<e)&&(0<f)) { /* *pa,*pe both > *pc; median of five is
                                       the minimum of *pa,*pe,*pd
                                    */
                    /* Bias towards *pd (originally closest of these three
                       to the middle.  Neither *pa nor *pe has yet been
                       compared to *pd.
                    */
                    e=compar(pa,pe); /* 5b or 6a (iff needed) */
                    if (0<e) { /* *pe is smaller */
                        f=compar(pd,pe); /* 6b or 7a (iff needed) */
                        if (0<f) { /* *pe is smaller */
                            VSWAP2(pc,pe,t);
                        } else { /* *pd is smaller or *pd==*pe */
                            VSWAP2(pc,pd,t);
                        }
                    } else { /* *pa==*pe or *pa is smaller */
                        f=compar(pd,pa); /* 6c or 7b (iff needed) */
                        if (0<f) { /* *pa is smaller */
                            VSWAP2(pc,pa,t);
                        } else { /* *pd is smaller or *pd==*pa */
                            VSWAP2(pc,pd,t);
                        }
                    }
                } else if ((0>e)&&(0>f)) { /* *pa,*pe both < *pc; median of
                                       five is the maximum of *pa,*pb,*pe
                                    */
                    /* Bias towards *pb (originally closest of these three
                       to the middle.  Neither *pa nor *pe has yet been
                       compared to *pb.
                    */
                    e=compar(pa,pe); /* 5c or 6d (iff needed) */
                    if (0<e) { /* *pa is larger */
                        f=compar(pa,pb); /* 6e or 7c (iff needed) */
                        if (0<f) { /* *pa is larger */
                            VSWAP2(pc,pa,t);
                        } else { /* *pb is larger or *pa==*pb */
                            VSWAP2(pc,pb,t);
                        }
                    } else { /* *pe is larger or *pa==*pe */
                        f=compar(pe,pb); /* 6f or 7d (iff needed) */
                        if (0<f) { /* *pe is larger */
                            VSWAP2(pc,pe,t);
                        } else { /* *pb is larger or *pe==*pb */
                            VSWAP2(pc,pb,t);
                        }
                    } /* *pa:*pe results if-else */
                } /* median of five: minimum or maximum of three if-else */
            } /* not three equal elements among five */
        } /* not three equal elements among four */
    } /* not three equal elements among three */
    return pc;
}

答案 9 :(得分:-1)

-- 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]

答案 10 :(得分:-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); }
    }