基于接近平均值对数组进行排序

时间:2014-03-08 19:36:15

标签: c arrays sorting mean qsort

我有一系列双打。我计算了数组的平均值。

我现在需要根据每个值与均值的接近程度对数组进行排序。

如果我可以将一个特殊的比较器函数传递给qsort,它将均值作为第三个参数,那么它将解决我的问题:

int compareValues(double a, double b, double mean)
{
    double aValue = fabs(mean - a);
    double bValue = fabs(mean - b);
    if (aValue > bValue)
       return 1;
    else if (aValue < bValue)
       return -1;
    else return 0;
}

但是,从我读过的所有内容中,你都无法将这样的比较器传递给qsort。

有没有一种简单的方法可以做到这一点,我没有看到?

6 个答案:

答案 0 :(得分:2)

构建您自己的专业快速排序功能:

int comp(double a, double b, double mean)
{
    a = fabs(mean - a);
    b = fabs(mean - b);
    if (a > b)
       return 1;
    else if (a < b)
       return -1;
    else return 0;
}

void swap(double *v, int a, int b)
{
    double temp;

    temp = v[a];
    v[a] = v[b];
    v[b] = temp;
}

void sort(double *v, int left, int right, double mean, int (*comp)(double, double, double))
{
    int i, last;

    if (left >= right) return;
    swap(v, left, (left + right) / 2);
    last = left;
    for (i = left + 1; i <= right; i++) {
        if (comp(v[i], v[left], mean) < 0)
            swap(v, ++last, i);
    }
    swap(v, left, last);
    sort(v, left, last - 1, mean, comp);
    sort(v, last + 1, right, mean, comp);
}

答案 1 :(得分:1)

我只是抛出一个想法,我没有想到这一点,但你可以尝试为比较器构建一个类,并在初始化中将mean定义为全局成员。

此外,您可以按如下方式创建并行数组:     newArray [i] = fabs(mean-oldArray [i]);

并根据新数组对旧数组进行排序。

答案 2 :(得分:1)

虽然你无法将这样的比较器传递给qsort,但你也可以将均值的值存储在比较器和函数调用qsort之外的静态变量中。 ,并使用一个常规的,双参数比较器,&#34;知道&#34;从静态变量中得到均值:

static double mean;

int compareValues(const void *pa, const void *pb)
{
    double a = *((double*)pb);
    double b = *((double*)pa);
    double aValue = fabs(mean - a);
    double bValue = fabs(mean - b);
    if (aValue > bValue)
       return 1;
    else if (aValue < bValue)
       return -1;
    else return 0;
}

void call_sort(double *data, size_t count) {
    mean = find_mean(data, count);
    qsort(data, count, sizeof(double), compareValues)
}

这种方法的不幸结果是你的排序功能不再是可重入的。

答案 3 :(得分:1)

@ merlin2011现在删除的答案有了一个好主意的细菌。

void qsort(void *base, size_t nmemb, size_t size, 
    int (*compar)(const void *a, const void *b));

传递给qsort的比较函数需要指针(而不是双精度数)。此外,qsort()永远不会使用compar()参数调用NULL。因此,首先可以使用a == NULL调用compare函数,b可以指向状态信息。

int compar(const void *a, const void *b) {
  static void *state = NULL;
  if (a == NULL || b == NULL) {
    return Setup_State(&state, a, b);
  }
  return Do_normal_compare(state, a, b);
}

Setup_State()的复杂性和可重入兼容性可能非常复杂,但下面是一个简单的非重复性示例。

int Setup_State(void **statep, void* a, void *b) {
  *statep = b;
  return 0;
}

int Do_normal_compare(void *state, void* a, void *b) {
  double da = *((double *) a);
  double db = *((double *) b);
  double dmean = *((double *) state);
  return OP_compareValues(da, db, dmean);
}

// usage 

double mean;
compar(NULL, &mean);
qsort(d_array, N, sizeof(double), compar);

使用b == NULL的电话可能会发出某种状态后清理信号。

compar(NULL, NULL);

答案 4 :(得分:0)

我更喜欢“建立自己的qsort”答案。但你可以做这样的事情。

我没有对双打和外部均值列表进行排序,而是对结构列表进行排序,每个结构都存储指向double和(相同)均值的指针。

我对那个数组进行排序,然后列出结果的值。

似乎工作,但最终比@AlterMann更糟糕。

gcc -o cmp cmp.c -std=c99 -Lmath

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

int compareValues(double a, double b, double mean)
{
    double aValue = fabs(mean - a);
    double bValue = fabs(mean - b);
    if (aValue > bValue)
       return 1;
    else if (aValue < bValue)
       return -1;
    else return 0;
}

typedef struct dbl_and_mean_t{
    const double* value;
    const double* mean;
} dbl_and_mean;

int cmpToMean(const void* a, const void* b){
    dbl_and_mean* dma = (dbl_and_mean*)a;
    dbl_and_mean* dmb = (dbl_and_mean*)b;

    double aValue = *(dma->value);
    double bValue = *(dmb->value);
    // we assume mean is same in both cases.
    double mean = *(dma->mean);
    return compareValues( aValue, bValue, mean );
}

/*
       void qsort(void *base, size_t nmemb, size_t size,
                  int(*compar)(const void *, const void *));
*/

int main(int argc, char* argv[]) {
    unsigned int numlen = 100;
    double numbers[100] = {
 20.11267454572858, 27.00916748845121, 41.69273886807976, 59.574594859929206, 5.148795948275042, 25.0952600949092, 7.490782458661016, 46.82558348995649, 99.1505635434539, 80.82884752229698, 34.562195425918965, 12.83419004171462, 65.21675903144343, 60.939055397544415, 59.716932510808405, 68.58214712201324, 16.96903326566488, 40.890117173893096, 43.24295370982686, 49.74083920053604, 51.879578222761545, 85.55201465227584, 0.9988146850440804, 72.9927624741183, 24.544352584593764, 38.07294449540915, 89.21601198806061, 46.242113823416055, 34.77013261276065, 75.50489987606491, 68.73075063359678, 34.11250399830412, 33.03497314824547, 50.356507540969574, 43.44185408674688, 1.3480391491077937, 17.87324689034111, 27.521463721587523, 65.36478555088043, 89.08983557487836, 91.20949281863321, 36.15883451406319, 64.71929705431249, 96.51660222081459, 84.3925334284804, 9.273474377948954, 20.7970994809055, 81.63173897647613, 34.54178336906219, 69.17908602857048, 34.49554788014096, 27.658713128591337, 37.84762296714004, 32.47882877578083, 80.33388365434217, 12.535403896961606, 1.6177858463917616, 58.492589297744544, 4.996882418234216, 0.6516899504362961, 94.14913555948795, 45.01455399721226, 91.13032884578304, 0.9747543756017163, 87.73797888418335, 17.05103955970504, 34.9990215191348, 32.132359722564175, 51.39141618413181, 90.41510433921886, 70.85275376557709, 60.81740079574899, 56.276844928014334, 96.84741168778665, 38.587969750110915, 26.93423429759396, 56.28727064877738, 40.69208717486249, 30.893466414304214, 54.69704130793473, 8.422991004598423, 42.51756379315109, 6.109299688810255, 97.5321480398511, 76.34912536352495, 83.6200551607522, 19.447640061947336, 29.659746702311896, 72.24996303415246, 7.992406225268933, 57.09202659164654, 60.782606246000036, 60.398430869817474, 41.77937462471086, 47.28376403551421, 54.31179044336384, 39.837395485680894, 39.301123086537395, 71.8438289228498, 49.209926974123285
    };
    double mean, sum = 0;
    dbl_and_mean* doubles_and_mean = calloc( numlen, sizeof(dbl_and_mean) );
    for( unsigned i = 0; i < numlen; i++){
        sum = sum + numbers[i];
        doubles_and_mean[i].value = &(numbers[i]);
        doubles_and_mean[i].mean = &mean;
    }
    mean = sum / numlen;
    printf("Mean is %f\n", mean);
    qsort( doubles_and_mean , 100, sizeof(dbl_and_mean), &cmpToMean);
    for( unsigned int i=0; i< numlen; i++){
        printf("%03d: %f\n", i, *(doubles_and_mean[i].value) );
    }
}

答案 5 :(得分:0)

Alter Mannanswer中给出的代码是混合的,半泛型的排序函数。以下是BSD qsort_r()函数的重新实现,它是qsort()的完全通用变体,为比较器函数提供“上下文”(或“thunk”)。

代码包括测试数据和输出:

Mean:   3.4940
Before:
[0] =   3.1416 (delta =   0.3524)
[1] =   2.7813 (delta =   0.7127)
[2] =   1.6130 (delta =   1.8810)
[3] =   9.8126 (delta =   6.3186)
[4] =   0.1213 (delta =   3.3727)
After:
[0] =   3.1416 (delta =   0.3524)
[1] =   2.7813 (delta =   0.7127)
[2] =   1.6130 (delta =   1.8810)
[3] =   0.1213 (delta =   3.3727)
[4] =   9.8126 (delta =   6.3186)
Mean:   5.1282
Before:
[0] =   1.2300 (delta =   3.8982)
[1] =   3.2900 (delta =   1.8382)
[2] =   8.1800 (delta =   3.0518)
[3] =   2.5100 (delta =   2.6182)
[4] =   4.1800 (delta =   0.9482)
[5] =   9.9900 (delta =   4.8618)
[6] =   6.7500 (delta =   1.6218)
[7] =   1.0100 (delta =   4.1182)
[8] =   4.6800 (delta =   0.4482)
[9] =   8.2100 (delta =   3.0818)
[10] =   6.3800 (delta =   1.2518)
After:
[0] =   4.6800 (delta =   0.4482)
[1] =   4.1800 (delta =   0.9482)
[2] =   6.3800 (delta =   1.2518)
[3] =   6.7500 (delta =   1.6218)
[4] =   3.2900 (delta =   1.8382)
[5] =   2.5100 (delta =   2.6182)
[6] =   8.1800 (delta =   3.0518)
[7] =   8.2100 (delta =   3.0818)
[8] =   1.2300 (delta =   3.8982)
[9] =   1.0100 (delta =   4.1182)
[10] =   9.9900 (delta =   4.8618)

这部分基于我一直在玩的一些代码,它们从快速排序的其余部分的各种实现中对分区函数进行参数化,因此它比其它方式分割得更多。

#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

/*
** Implement BSD qsort_r() under the name Qsort_r()
**
** void qsort_r(void *base, size_t nel, size_t width, void *thunk,
**              int (*compar)(void *, const void *, const void *));
**
** The qsort_r() function behaves identically to qsort(), except that it
** takes an additional argument, thunk, which is passed unchanged as the
** first argument to function pointed to compar.  This allows the
** comparison function to access additional data without using global
** variables, and thus qsort_r() is suitable for use in functions which
** must be reentrant.
*/

typedef int (*Comparator)(void *, const void *, const void *);

extern void Qsort_r(void *base, size_t num, size_t size, void *thunk, Comparator cmp);

static
void swap(void *val1, void *val2, size_t size)
{
    char temp[size];

    memmove(temp, val1, size);
    memmove(val1, val2, size);
    memmove(val2, temp, size);
}

/* Generate random number in range p..r inclusive */
static size_t random_int(size_t p, size_t r)
{
    assert(p <= r);
    size_t q = rand() % (r - p + 1) + p;
    return q;
}

static inline size_t thin_partition_random(void *base, size_t size,
    size_t lo, size_t hi, void *thunk, Comparator cmp)
{
    /* Use a random element as the pivot */
    assert(hi > 0);
    size_t m = random_int(lo, hi - 1);
    swap((char *)base + size * m, (char *)base + size * lo, size);

    /*
    ** Set l and r and move the array elements such
    ** that for all i in [lo..r), a[i] <= pivot,
    ** and for all i in [r+1..hi), a[i] >= pivot.
    */

    size_t l = lo + 1;
    size_t r = hi;
    while (l < r)
    {
        if (cmp(thunk, (char *)base + size * l, (char *)base + size * lo) <= 0)
            l++;
        else
            swap((char *)base + size * l, (char *)base + size * --r, size);
    }

    /* Put pivot element into place */
    swap((char *)base + size * lo, (char *)base + size * --l, size);
    return r;
}

/* Sorts the elements of a between low and high inclusive */
/* Sort short sub-partition recursively and iterate to sort longer */
void Qsort_r(void *base, size_t num, size_t size, void *thunk, Comparator cmp)
{
    size_t low = 0;
    size_t high = num;

    while (low < high)
    {
        size_t r = thin_partition_random(base, size, low, high, thunk, cmp);
        size_t l = r - 1;
        assert(l >= low && high >= r);
        if (l - low > high - r)
        {
            if (high - r > 1)
                Qsort_r((char *)base + size * r, high - r + 1, size, thunk, cmp);
            high = l;
        }
        else
        {
            if (l - low > 1)
                Qsort_r((char *)base + size * low, l - low + 1, size, thunk, cmp);
            low = r;
        }
    }
}

/* -- Test harness -- */

#include <stdio.h>

static void dump_array(int n, double *d, double mean)
{
    for (int i = 0; i < n; i++)
        printf("[%d] = %8.4f (delta = %8.4f)\n", i, d[i], fabs(d[i] - mean));
}

static
int comp(void *thunk, const void *pa, const void *pb)
{
    double aValue = fabs(*(double *)thunk - *(double *)pa);
    double bValue = fabs(*(double *)thunk - *(double *)pb);
    if (aValue > bValue)
       return 1;
    else if (aValue < bValue)
       return -1;
    else return 0;
}

static void test(int n, double *d)
{
    double sum = 0;
    for (int i = 0; i < n; i++)
        sum += d[i];
    double mean= sum / n;

    printf("Mean: %8.4f\n", mean);
    printf("Before:\n");
    dump_array(n, d, mean);
    Qsort_r(d, n, sizeof(double), &mean, comp);
    printf("After:\n");
    dump_array(n, d, mean);
}

int main(void)
{
    double d[] = { 3.1416, 2.7813, 1.613, 9.8126, 0.1213 };
    enum { NUM_D = sizeof(d) / sizeof(d[0]) };
    test(NUM_D, d);

    double data[] =
    {
        1.23, 3.29, 8.18, 2.51, 4.18, 9.99,
        6.75, 1.01, 4.68, 8.21, 6.38,
    };
    enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
    test(NUM_DATA, data);

    return 0;
}

请注意,它不是生产就绪,因为它使用标准库函数rand(),并且一个好的库函数不应该这样做 - 调用此函数可能会破坏程序的可重复性。一个不错的替代方案使用drand48()系列函数,并使用其自己的种子和状态运行,使用:

  • long nrand48(unsigned short xsubi[3]);

返回[0,2 31 )范围内的值。


请注意,Linux(或GNU C Library)也实现了qsort_r(),但该接口与BSD版本不同:

void qsort_r(void *base, size_t nmemb, size_t size,
             int (*compar)(const void *, const void *, void *),
             void *arg);

两者相同但不同。 BSD版本将辅助数据作为第一个参数传递给比较器,其中Linux将其作为比较器的最后一个参数传递。此外,在调用qsort_r()时,BSD版本在比较器之前传递辅助数据指针,Linux版本在比较器之后传递它。