3つの変数の中央値を求める。

クイックソートでPivotを決めるときに3つの値の真ん中をPivotにする手法がある。
それで中央値(Median)を求めるための処理を考えたのだが思ったよりややこしい。


最大値を求めるときは簡単だ。

int getMax(int tmpMax, int x, int y)
{
    if (tmpMax < x) {
        tmpMax = x;
    }

    if (tmpMax < y) {
        tmpMax = y;
    }

    return tmpMax;
}

最小値は不等号の向きを逆にするだけである。あ、変数名はtmpMinにすべきだ。

ところが中央値だと最初の直感より難しい。
素直にコーディングするとこうなった。

int getMedian(int x, int y, int z)
{
    if (x < y) {
        if (y < z) {
            /* x < y < z */
            return y;
        } else {
            /* y is max of three */
            return (x < z)? z: x;
        }
    } else {
        if (z < y) {
            /* z < y < x */
            return y;
        } else {
            /* y is min of three */
            return (x < z)? x: z;
        }
    }
    /* never reach */
    assert(0);
}

ぱっと見でこのコードが正しいかどうかわからない。
もうちょっとうまくかけないかと思い次のようなコードを仕立てた。

#define SWAP(type, x, y)    do {type tmp; tmp = x; x = y; y = tmp;} while(0)

/* 3つの引数の中央値を返す */
int getMedian(int tmpMax, int x, int y)
{
    if (tmpMax < x) {
        SWAP(int, tmpMax, x);
    }
    if (tmpMax < y) {
        SWAP(int, tmpMax, y);
    }
    /* x,yのうち大きいほうが中央値 */
    return (x < y) ? y : x;
}

最大値、最小値を求めるときは上書きして気にしない、気にしないなのだが、
このコードでは後で最大の方を捨ててしまう。


とりあえず個人で行き着いたのはこんなコードだ。
これは自分に対する今後の課題にしておく。