繁体   English   中英

以 3 函数的中位数进行的比较次数?

[英]Number of comparisons made in median of 3 function?

截至目前,我的函数找到 3 个数字的中位数并对它们进行排序,但它总是进行 3 次比较。 我在想我可以在某处使用嵌套的 if 语句,这样有时我的函数只会进行两次比较。

int median_of_3(int list[], int p, int r)
{
    int median = (p + r) / 2;

    if(list[p] > list[r])
        exchange(list, p, r);
    if(list[p] > list[median])
        exchange(list, p, median);
    if(list[r] > list[median])
        exchange(list, r, median);

    comparisons+=3;                // 3 comparisons for each call to median_of_3

    return list[r];
}

我不确定我在哪里可以创建嵌套的 if 语句。

如果您只需要中值,这里有一个基于最小/最大运算符的无分支解决方案:

median = max(min(a,b), min(max(a,b),c));

英特尔 CPU 具有 SSE 最小/最大矢量指令,因此根据您或您的编译器的矢量化能力,它可以运行得非常快。

如果我们允许额外的操作,我们最多可以使用 2 次比较来找到中位数。 诀窍是使用排他或查找三个数字之间的关系。

void median3(int A[], int p, int r)
{
    int m = (p+r)/2;
    /* let a, b, c be the numbers to be compared */
    int a = A[p], b = A[m], c = A[r];
    int e = a-b;
    int f = a-c;

    if ((e^f) < 0) {
        med_comparisons += 1;
        /* a is the median with 1 comparison */
        A[m] = a;
        /* b < a < c ? */
        if (b < c) /* b < a < c */ { A[p] = b, A[r] = c; }
        else       /* c < a < b */ { A[p] = c, A[r] = b; }
        comparisons += 2;
    } else {
        med_comparisons += 2;
        int g = b-c;
        if ((e^g) < 0) {
            /* c is the median with 2 comparisons */ 
            A[m] = c;
            /* a < c < b ? */
            if (a < b) /* a < c < b */ { A[p] = a, A[r] = b; }
            else       /* b < c < a */ { A[p] = b, A[r] = a; }
        } else {
            /* b is the median with 2 comparisons */
            A[m] = b;
            /* c < b < a ? */
            if (a > c) /* c < b < a */ { A[p] = c; A[r] = a; }
            else       /* a < b < c */ { /* do nothing */    }
        }
        comparisons += 3;
    }
}

第一个异或(e^f)是找出(ab)和(ac)之间符号位的不同。
如果他们有不同的符号位,那么 a 就是中位数。 否则,a 是最小值或最大值。 在这种情况下,我们需要第二个异或 (e^g)。

同样,我们将找出(ab) 和 (bc)之间符号位的差异。 如果它们的符号位不同,一种情况是 a > b && b < c。 在这种情况下,我们也会得到a > c ,因为a 是这种情况下的最大值 所以我们有a > c > b。 另一种情况是a < b && b > c && a < c 所以我们有a < c < b ; 在这两种情况下,c 都是中位数

如果(ab)(bc)具有相同的符号位则 b 是使用与上述类似参数的中位数 实验表明,随机输入需要1.667 次比较才能找出中位数,需要额外进行一次比较才能获得顺序。

要对 3 个项目进行排序,您需要恰好进行 3 次比较。

要偶然找到中间的那个,你需要 2 个。

要准确找到中间的那个,平均需要 2+2/3 ~= 2.67(具有均匀分布的随机数据)

if (a<b) {
   // partial order = a,b
   if (b<c) {  } // 2 comparisons: order is a,b,c
      else { // order is a,c,b or c,a,b
          if (a<c) { } // order is a,c,b -- 3 comparisons
          else { }     // order is c,a,b -- 3 comparisons
      }
} else {
   // partial order = b,a  
   if (c<b) {  } // 2 comparisons: order is c,b,a
   else {  // order is b,c,a or b,a,c
      if (c>a) { } // order is b,a,c -- 3 comparisons
      else { }   // order is b,c,a -- 3 comparisons
   }
}

作为附加说明:一些语言(Fortran、IIRC)以及一些 ISA(VAX,再次是 IIRC)支持比较,其中下一个 PC 地址是从三个选项中选择的:LT、EQ、GT。 对于足够小的字母表,这种机会会稍微减少所需比较的次数。

此外,这可能没有实际用途,由于过于复杂的嵌套结构而导致的错误分支预测的惩罚可能比保存的比较带来的收益大得多。

int m = (p + r) / 2;
if (list[p] < list[m])
    if (list[p] >= list[r])
        return list[p];
    else if (list[m] < list[r])
        return list[m];
else
    if (list[p] < list[r])
        return list[p];
return list[r];

更多此类

#define MEDIAN(a,b,c) ( (a > b) ? max(b, min(a,c)) :
                                  min(b, max(a,c)) )

Python

#!/usr/bin/env python3

def bigger(a,b):
    if a > b:
       return a
    else:
    return b

def biggest(a,b,c):
    return bigger(a,bigger(b,c))

def median(a,b,c):
    big = biggest(a,b,c)
    if big == a:
       return bigger(b,c)
    if big == b:
       return bigger(a,c)
    else:
       return bigger(a,b)

打印中位数

print(median(20,18,19)) # => 19

仅使用两个比较:

double median_of_three(double left, double middle, double right) 
{
    double med = middle;
    if (left  < med) med = left;
    if (right > med) med = right;
    return med;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM