私はクイックソートを実装していて、ピボットを中央値または3つの数値に設定したいと思いました。最初の要素、中間の要素、最後の要素の3つの数字。
中央値をもっと少なく見つけることができるでしょうか。比較の?
median(int a[], int p, int r)
{
int m = (p+r)/2;
if(a[p] < a[m])
{
if(a[p] >= a[r])
return a[p];
else if(a[m] < a[r])
return a[m];
}
else
{
if(a[p] < a[r])
return a[p];
else if(a[m] >= a[r])
return a[m];
}
return a[r];
}
1つでそれを行うことはできず、2つまたは3つしか使用していないので、比較の最小数はすでにあると思います。
懸念が比較のみである場合は、これを使用する必要があります。
int getMedian(int a, int b , int c) {
int x = a-b;
int y = b-c;
int z = a-c;
if(x*y > 0) return b;
if(x*z > 0) return c;
return a;
}
中央値を計算するだけでなく、それらを適切に配置することもできます。そうすれば、常に3つの比較で逃げることができ、ピボットを適切な場所に近づけることができます。
T median(T a[], int low, int high)
{
int middle = ( low + high ) / 2;
if( a[ middle ].compareTo( a[ low ] ) < 0 )
swap( a, low, middle );
if( a[ high ].compareTo( a[ low ] ) < 0 )
swap( a, low, high );
if( a[ high ].compareTo( a[ middle ] ) < 0 )
swap( a, middle, high );
return a[middle];
}
int32_t FindMedian(const int n1, const int n2, const int n3) {
auto _min = min(n1, min(n2, n3));
auto _max = max(n1, max(n2, n3));
return (n1 + n2 + n3) - _min - _max;
}
合計から最大値と最小値を削除します
int med3(int a, int b, int c)
{
int tot_v = a + b + c ;
int max_v = max(a, max(b, c));
int min_v = min(a, min(b, c));
return tot_v - max_v - min_v
}
コンパイラの組み込み関数で手を少し汚すことを恐れていない場合は、正確に0のブランチでそれを行うことができます。
同じ質問が前に議論されました:
トリプルの中間値を見つける最も速い方法は?
ただし、要素が多いクイックソートのナイーブな実装のコンテキストでは、要素を投げ始めるときに分岐予測子がどちらの方法でもチョークするため、中央値を見つけるときに分岐の量を減らすことはそれほど重要ではありません。ピボットの周り。より洗練された実装(パーティション操作で分岐せず、WAWの危険を回避する)は、これから大きな恩恵を受けます。
実際には、6つの可能な順列(低、中央値、高)の注意深い分析を使用して、中央値要素を3つから分離する賢い方法があります。 Pythonの場合:
def med(a, start, mid, last):
# put the median of a[start], a[mid], a[last] in the a[start] position
SM = a[start] < a[mid]
SL = a[start] < a[last]
if SM != SL:
return
ML = a[mid] < a[last]
m = mid if SM == ML else last
a[start], a[m] = a[m], a[start]
半分の時間で2つの比較があり、そうでない場合は3つ(平均2.5)です。また、必要に応じて中央値要素を1回だけ交換します(時間の2/3)。
フルpythonクイックソートでこれを使用:
これが古いスレッドであることは知っていますが、RAMで、h/w乗算ユニット(:))がほとんどないマイクロコントローラーでこの問題を正確に解決する必要がありました。結局、私は以下がうまく機能することを発見しました:
static char medianIndex[] = { 1, 1, 2, 0, 0, 2, 1, 1 };
signed short getMedian(const signed short num[])
{
return num[medianIndex[(num[0] > num[1]) << 2 | (num[1] > num[2]) << 1 | (num[0] > num[2])]];
}
あなたはすべての順列を書くことができます:
1 0 2
1 2 0
0 1 2
2 1 0
0 2 1
2 0 1
次に、1
の位置を見つけます。最初の比較で最初の2行などの等しい位置のグループを分割できる場合は、2つの比較でこれを行うことができます。
問題は、最初の2行が、利用可能な比較で異なることであるようです:a<b
、a<c
、b<c
。したがって、順列を完全に特定する必要があり、最悪の場合は3回の比較が必要になります。