我有一系列双打。我计算了数组的平均值。
我现在需要根据每个值与均值的接近程度对数组进行排序。
如果我可以将一个特殊的比较器函数传递给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。
有没有一种简单的方法可以做到这一点,我没有看到?
答案 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 Mann在answer中给出的代码是混合的,半泛型的排序函数。以下是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版本在比较器之后传递它。