How to find max. and min. in array using minimum comparisons?

后端 未结 14 1443
深忆病人
深忆病人 2020-12-04 09:08

This is a interview question: given an array of integers find the max. and min. using minimum comparisons.

Obviously, I can loop over the array twice and use ~

14条回答
  •  再見小時候
    2020-12-04 09:37

    After reading the question and answers, I decided to try a few versions (in C#).
    I thought the fastest would be Anton Knyazyev's one (branch free), it isn't (on my box).
    Results:

    /*                comp.  time(ns)
          minmax0     3n/2    855
          minmax1     2n      805
          minmax2     2n     1315 
          minmax3     2n      685          */
    

    Why are minmax1 and minmax3 faster? Probably because the "branch predictor" does a nice job, each iteration the chance, a new min (or max) is found, decreases, so predictions become better.
    All in all it's a simple test. I do realize my conclusions may be:
    -premature.
    -not valid for different platforms.
    Let's say they're indicative.
    Edit: Break-even point minmax0, minmax3: ~100 items,
    10,000 items: minmax3 ~3.5 times faster than minmax0.

    using System;
    using sw = System.Diagnostics.Stopwatch;
    class Program
    {
        static void Main()
        {
            int n = 1000;
            int[] a = buildA(n);
            sw sw = new sw();
            sw.Start();
            for (int i = 1000000; i > 0; i--) minMax3(a);
            sw.Stop();
            Console.Write(sw.ElapsedMilliseconds);
            Console.Read();
        }
    
        static int[] minMax0(int[] a)  // ~3j/2 comp.
        {
            int j = a.Length - 1;
            if (j < 2) return j < 0 ? null :
                j < 1 ? new int[] { a[0], a[0] } :
                a[0] < a[1] ? new int[] { a[0], a[1] } :
                              new int[] { a[1], a[0] };
            int a0 = a[0], a1 = a[1], ai = a0;
            if (a1 < a0) { a0 = a1; a1 = ai; }
            int i = 2;
            for (int aj; i < j; i += 2)
            {
                if ((ai = a[i]) < (aj = a[i + 1]))  // hard to predict
                { if (ai < a0) a0 = ai; if (aj > a1) a1 = aj; }
                else
                { if (aj < a0) a0 = aj; if (ai > a1) a1 = ai; }
            }
            if (i <= j)
            { if ((ai = a[i]) < a0) a0 = ai; else if (ai > a1) a1 = ai; }
            return new int[] { a0, a1 };
        }
    
        static int[] minMax1(int[] a)  // ~2j comp.  
        {
            int j = a.Length;
            if (j < 3) return j < 1 ? null :
                j < 2 ? new int[] { a[0], a[0] } :
                a[0] < a[1] ? new int[] { a[0], a[1] } :
                              new int[] { a[1], a[0] };
            int a0 = a[0], a1 = a0, ai = a0;
            for (int i = 1; i < j; i++)
            {
                if ((ai = a[i]) < a0) a0 = ai;
                else if (ai > a1) a1 = ai;
            }
            return new int[] { a0, a1 };
        }
    
        static int[] minMax2(int[] a)  // ~2j comp.  
        {
            int j = a.Length;
            if (j < 2) return j == 0 ? null : new int[] { a[0], a[0] };
            int a0 = a[0], a1 = a0;
            for (int i = 1, ai = a[1], aj = ai; ; aj = ai = a[i])
            {
                ai -= a0; a0 += ai & ai >> 31;
                aj -= a1; a1 += aj & -aj >> 31;
                i++; if (i >= j) break;
            }
            return new int[] { a0, a1 };
        }
    
        static int[] minMax3(int[] a)  // ~2j comp.
        {
            int j = a.Length - 1;
            if (j < 2) return j < 0 ? null :
                j < 1 ? new int[] { a[0], a[0] } :
                a[0] < a[1] ? new int[] { a[0], a[1] } :
                              new int[] { a[1], a[0] };
            int a0 = a[0], a1 = a[1], ai = a0;
            if (a1 < a0) { a0 = a1; a1 = ai; }
            int i = 2;
            for (j -= 2; i < j; i += 3)
            {
                ai = a[i + 0]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
                ai = a[i + 1]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
                ai = a[i + 2]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
            }
            for (j += 2; i <= j; i++)
            { if ((ai = a[i]) < a0) a0 = ai; else if (ai > a1) a1 = ai; }
            return new int[] { a0, a1 };
        }
    
        static int[] buildA(int n)
        {
            int[] a = new int[n--]; Random rand = new Random(0);
            for (int j = n; n >= 0; n--) a[n] = rand.Next(-1 * j, 1 * j);
            return a;
        }
    }
    

提交回复
热议问题