Код для вычисления "медианы пяти" в С#
Примечание: Пожалуйста, не интерпретируйте это как "вопрос о домашнем задании". Мне просто интересно узнать:)
Медиана пяти иногда используется как упражнение в дизайне алгоритма и, как известно, вычислима , используя только 6 сравнений.
Каков наилучший способ реализовать эту "медиану из пяти, используя 6 сравнений в С#? Все мои попытки, похоже, приводят к неловкому коду:( Мне нужен хороший и читаемый код, хотя мы используем только 6 сравнений.
public double medianOfFive(double a, double b, double c, double d, double e){
//
// return median
//
return c;
}
Примечание: Я думаю, что я должен также предоставить "алгоритм":
Я обнаружил, что не могу четко объяснить алгоритм, как это сделал Азереал на своем форуме. Поэтому я расскажу здесь об этом. Из http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1061827085
Ну, я поставил эту проблему в один моих заданий, и я обратился к этому форум для помощи, но никакой помощи здесь не было. В конце концов я узнал, как это сделать.
-
Начните объединение с четырьмя четырьмя элементами и закажите каждую пару (2 сравнение)
-
Сравните два нижних из каждой пары и устраните самый низкий из возможности (3 сравнения)
-
Добавьте в пятый номер, отведенный на номер без пары, и сравните два (4 сравнения)
-
Сравните две самые низкие из двух новых пар и устраните нижнюю (5 сравнений)
-
Сравните один сам по себе и нижний из последней пары, а нижний число - медиана
Возможная медиана находится в пределах parentesis
(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, это код С++, который я написал, чтобы найти медиану из пяти. Не возражайте против неловкости:
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;
}
Он должен быть более компактным, не так ли?
EDIT:
Как отметил @pablito в своем ответе. Встроенный List.Sort() не может выполнить это требование, так как он использует до 13 сравнений:]
Ответы
Ответ 1
В основном это просто факторинг кода подкачки и сортировки из вашего примера на С++:
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
Я нашел это сообщение интересным, и в качестве упражнения я создал это, которое ТОЛЬКО делает 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;
}
Ответ 3
Спасибо. Я знаю, что ваши сообщения довольно старые, но это было полезно для моей проблемы.
Мне понадобился способ вычислить медиану из 5 регистров SSE/AVX (4 поплавка /8 поплавков за один раз или 2 удваивания /4 удваивается одновременно):
Если функции min/max запрограммированы для скалярных регистров с условными переходами, мой код не является оптимальным для сравнения.
Но если функции min/max закодированы с соответствующими инструкциями CPU, мой код очень эффективен, потому что при выполнении 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);
}
Ответ 4
Интересная тема здесь:
http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1061827085
Цитата из темы:
-
Поместите числа в массив.
-
Используйте три сравнения и перетасовки вокруг чисел, чтобы [1] a [2], a [4] a [5] и a [1] а [4].
-
Если a [3] > a [2], то проблема довольно проста. Если a [2] a [4], медианное значение меньше из [3] и [4]. Если нет, то медианное значение меньше из [2] и [5].
-
Итак, [3] а [2]. Если a [3] > a [4], то решение меньше из [3] и [5]. В противном случае решение меньше из [2] и [4].
Ответ 5
Это довольно уродливо и может использовать некоторый рефакторинг, но он явно просматривает все сравнения и свопы, чтобы вы могли видеть, что происходит.
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;
}
Ответ 6
Просто, чтобы проверить, сколько сравнений:
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.
Ответ 7
Для полноты вопроса речь идет о конкретном случае сети сортировки , которая Knuth (Искусство компьютерного программирования, том 3) подробно описывается. классическая статья К.Е. Дозатор по этому вопросу является кратким и заслуживает внимания.
Ответ 8
Это должно сделать это
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 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;
}
Ответ 9
-- 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
Интересно, сколько сравнений в примере 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); }
}