Ответ 1
Вы не можете сделать это в одном, и вы используете только два или три, поэтому я бы сказал, что у вас уже есть минимальное количество сравнений.
Я реализовал quicksort, и я хотел установить опорную точку в качестве медианного или трех чисел. Три числа - это первый элемент, средний элемент и последний элемент.
Могу ли я найти медиана меньше, чем нет. сравнений?
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];
}
Вы не можете сделать это в одном, и вы используете только два или три, поэтому я бы сказал, что у вас уже есть минимальное количество сравнений.
Если проблема связана только с сравнениями, то это необходимо использовать.
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];
}
Если вы не боитесь, что ваши руки немного грязны с внутренними функциями компилятора, вы можете сделать это ровно с 0 ветвями.
Тот же вопрос обсуждался ранее:
Самый быстрый способ найти среднее значение тройки?
Хотя, я должен добавить, что в контексте наивной реализации quicksort с большим количеством элементов сокращение количества ветвей при поиске медианы не так важно, потому что предсказатель ветвления будет задыхаться в любом случае, когда вы будете начать бросать элементы вокруг стержня. Более сложные реализации (которые не ветвятся на операцию раздела и не избегают опасностей WAW) выиграют от этого значительно.
Существует действительно умный способ изолировать медианный элемент из трех, используя тщательный анализ 6 возможных перестановок (низкого, среднего, высокого). В 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]
Половина времени, когда у вас есть два сравнения, в противном случае у вас есть 3 (avg 2.5). И вы только обмениваете срединный элемент один раз, когда это необходимо (2/3 времени).
Полная быстрая сортировка python с помощью этого:
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;
}
Вы можете написать все перестановки:
1 0 2
1 2 0
0 1 2
2 1 0
0 2 1
2 0 1
Затем мы хотим найти положение 1
. Мы могли бы сделать это с помощью двух сравнений, если бы наше первое сравнение могло разделить группу равных позиций, таких как первые две строки.
Проблема заключается в том, что первые две строки различаются при любом сравнении, которое у нас имеется: a<b
, a<c
, b<c
. Следовательно, мы должны полностью идентифицировать перестановку, которая требует 3 сравнений в худшем случае.
Я знаю, что это старый поток, но мне пришлось решить именно эту проблему на микроконтроллере, который имеет очень мало оперативной памяти и не имеет единицы умножения 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])]];
}