В дополнение к вашему текущему ответу у вас есть ряд проблем, но ваша проблема с идентификацией выброса заключается в том, что вы используете '&&'
вместо '||'
, что препятствует обнаружению какого-либо выброса, поскольку ваше тестовое условие всегда оценивает FALSE
Например,
if((measurements[i] < (0.5*median)) && (measurements[i] > (1.5*median))){
(элемент массива никогда не может быть одновременно меньше (0.5*median)
и больше (1.5*median)
одновременно)
Помимо того, что вы определили выбросы, как отмечено в комментариях и в ответе @ paddy's , вам не нужно копировать или размещать в своей функции удаления выбросов.Вместо этого удалите выбросы, перетасовав все элементы выше выброса на единицу, удалив выброс с помощью memmove
, и перед возвратом из функции, если выбросы были удалены, вы можете (необязательно) realloc
один раз в конце, чтобы урезать распределениеразмер.
(что на самом деле не нужно, если вы не работаете со встроенной системой с ограниченным объемом памяти или имеете миллионы элементов, с которыми вы имеете дело)
Уборка вашей функции удаления и передача адрес вашего массива из main()
, чтобы разрешить перераспределение в функции без необходимости присваивания возврата, вы можете сделать что-то вроде:
/* remove outliers from array 'a' given 'median'.
* takes address of array 'a', address of number of elements 'n',
* and median 'median' to remove outliers. a is reallocated following
* removal and n is updated to reflect the number of elements that
* remain. returns pointer to reallocated array on success, NULL otherwise.
*/
double *rmoutliers (double **a, size_t *n, double median)
{
size_t i = 0, nelem = *n; /* index, save initial numer of elements */
while (i < *n) /* loop over all elements indentifying outliers */
if ((*a)[i] < 0.5 * median || (*a)[i] > 1.5 * median) {
if (i < *n - 1) /* if not end, use memmove to remove */
memmove (&(*a)[i], &(*a)[i+1],
(*n - i + 1) * sizeof **a);
(*n)--; /* decrement number of elements */
}
else /* otherwise, increment index */
i++;
if (*n < nelem) { /* if outliers removed */
void *dbltmp = realloc (*a, *n * sizeof **a); /* realloc */
if (!dbltmp) { /* validate reallocation */
perror ("realloc-a");
return NULL;
}
*a = dbltmp; /* assign reallocated block to array */
}
return *a; /* return array */
}
Далее, не сортируйте свою собственную функцию сортировки,Библиотека C предоставляет qsort
, что на несколько порядков с меньшей вероятностью будет содержать ошибки, чем ваши (не говоря уже о порядках быстрее).Все, что вам нужно сделать, это написать qsort
функцию сравнения, которая получает указатели на соседние элементы из вашего массива и затем возвращает -1
, если первая сортируется перед вторым, 0
, если элементы равны, и 1
если второе сортирует перед первым.Для числовых сравнений вы можете вернуть результат в два неравенства, чтобы избежать потенциального переполнения / недополнения, например,
/* qsort compare to sort numbers in ascending order without overflow */
return (a > b) - (a < b);
Отметив, что a
и b
будут указателями на double
(или * 1040)*) в вашем случае, для сравнения значений типа double, правильное приведение перед разыменованием будет выглядеть следующим образом:
/* qsort compare function for doubles (ascending) */
int cmpdbl (const void *a, const void *b)
{
return (*((double *)a) > *((double *)b)) -
(*((double *)a) < *((double *)b));
}
Это единственная сложность при использовании qsort
после этого для сортировки массива в порядке возрастания больше ничего не требуетсячем:
qsort (array, n, sizeof *array, cmpdbl); /* use qsort to sort */
(готово ...)
В целом это краткий пример, который просто читает ваши массивы как строки ввода (1024
chars max), а затем преобразует каждыйЗначение double
с использованием sscanf
, хранящим любое количество значений в динамически изменяемой переменной array
перед сортировкой, захватом медианы и вызовом функции удаления, можно записать следующим образом.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* max characters to read per-line (per-array) */
#define MAXD 8 /* initial number of doubles to allocate */
/* qsort compare function for doubles (ascending) */
int cmpdbl (const void *a, const void *b)
{
return (*((double *)a) > *((double *)b)) -
(*((double *)a) < *((double *)b));
}
/* remove outliers from array 'a' given 'median'.
* takes address of array 'a', address of number of elements 'n',
* and median 'median' to remove outliers. a is reallocated following
* removal and n is updated to reflect the number of elements that
* remain. returns pointer to reallocated array on success, NULL otherwise.
*/
double *rmoutliers (double **a, size_t *n, double median)
{
size_t i = 0, nelem = *n; /* index, save initial numer of elements */
while (i < *n) /* loop over all elements indentifying outliers */
if ((*a)[i] < 0.5 * median || (*a)[i] > 1.5 * median) {
if (i < *n - 1) /* if not end, use memmove to remove */
memmove (&(*a)[i], &(*a)[i+1],
(*n - i + 1) * sizeof **a);
(*n)--; /* decrement number of elements */
}
else /* otherwise, increment index */
i++;
if (*n < nelem) { /* if outliers removed */
void *dbltmp = realloc (*a, *n * sizeof **a); /* realloc */
if (!dbltmp) { /* validate reallocation */
perror ("realloc-a");
return NULL;
}
*a = dbltmp; /* assign reallocated block to array */
}
return *a; /* return array */
}
int main (void) {
char buf[MAXC];
int arrcnt = 1;
while (fgets (buf, MAXC, stdin)) { /* read line of data into buf */
int offset = 0, nchr = 0;
size_t n = 0, ndbl = MAXD, size;
double *array = malloc (ndbl * sizeof *array), /* allocate */
dbltmp, median;
if (!array) { /* validate initial allocation */
perror ("malloc-array");
return 1;
}
/* parse into doubles, store in dbltmp (should use strtod) */
while (sscanf (buf + offset, "%lf%n", &dbltmp, &nchr) == 1) {
if (n == ndbl) { /* check if reallocation requierd */
void *tmp = realloc (array, 2 * ndbl * sizeof *array);
if (!tmp) { /* validate */
perror ("realloc-array");
break;
}
array = tmp; /* assign reallocated block */
ndbl *= 2; /* update allocated number of doubles */
}
array[n++] = dbltmp; /* assign to array, increment index */
offset += nchr; /* update offset in buffer */
}
qsort (array, n, sizeof *array, cmpdbl); /* use qsort to sort */
median = array[n / 2]; /* get median */
/* output original array and number of values */
printf ("\narray[%d] - %zu values\n\n", arrcnt++, n);
for (size_t i = 0; i < n; i++) {
if (i && i % 10 == 0)
putchar ('\n');
printf (" %5.2f", array[i]);
}
printf ("\n\nmedian: %5.2f\n\n", median);
size = n; /* save orginal number of doubles in array in size */
if (!rmoutliers (&array, &n, median)) /* remove outliers */
return 1;
if (n < size) { /* check if outliers removed */
printf ("%zu outliers removed - %zu values\n\n", size - n, n);
for (size_t i = 0; i < n; i++) {
if (i && i % 10 == 0)
putchar ('\n');
printf (" %5.2f", array[i]);
}
printf ("\n\n");
}
else /* otherwise warn no outliers removed */
fputs ("warning: no outliers found.\n\n", stderr);
free (array); /* don't forget to free what you allocate */
}
}
( примечание: вы действительно должны использовать strtod
, поскольку sscanf
не обеспечивает обработки ошибок, кроме сообщения об успешном / неудачном преобразовании, но это на другой день или оставлено вам как упражнение)
Пример входного файла
Примечание. Я не использовал информацию size: X
в моем файле данных.Это было не нужно.Я просто использовал схему динамического размещения для определения размера массивов по мере необходимости.Формат входного файла, который я использовал, содержал значения измерений для каждого массива в отдельной строке, например
23.0 21.5 27.6 2.5 19.23 21.0 23.5 24.6 19.5 19.23 26.01 22.5 24.6 20.15 ... 18.93
11.12 10.32 9.91 14.32 12.32 20.37 13.32 11.57 2.32 13.32 11.22 12.32 ... 13.32
Пример использования / Вывод
$ ./bin/rmoutliers <dat/outlierdata.txt
array[1] - 25 values
2.50 5.23 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15
21.00 21.50 22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60
24.60 26.01 26.60 27.60 45.50
median: 22.25
3 outliers removed - 22 values
18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15 21.00 21.50
22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60 24.60 26.01
26.60 27.60
array[2] - 20 values
2.32 8.32 9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32
12.32 12.58 12.91 13.32 13.32 13.32 14.32 14.56 20.37 35.32
median: 12.32
3 outliers removed - 17 values
8.32 9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32 12.32
12.58 12.91 13.32 13.32 13.32 14.32 14.56
( примечание: в любом коде, который динамически выделяет память, вы должны запускать программу через программу проверки ошибок памяти, такую как valgrind
для Linux, в других ОС есть аналогичные инструменты. Это просто, просто запустите add valgrind
дляначало вашей команды, например, valgrind ./bin/rmoutliers <dat/outlierdata.txt
и подтвердите, что вы освободили всю выделенную память и что ошибок памяти нет.)
Просмотрите все и дайте мне знать, если у вас есть вопросы.
Использование памяти / проверка ошибок
В своем комментарии вы, похоже, обеспокоены тем, что то, что я делаю, может привести к утечке памяти - это не так.Как уже упоминалось в вопросе, вы можете проверить использование памяти и проверить наличие ошибок памяти с помощью таких инструментов, как valgrind
, например,
$ valgrind ./bin/rmoutliers <dat/outlierdata.txt
==28383== Memcheck, a memory error detector
==28383== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28383== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28383== Command: ./bin/rmoutliers
==28383==
array[1] - 25 values
2.50 5.23 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15
21.00 21.50 22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60
24.60 26.01 26.60 27.60 45.50
median: 22.25
3 outliers removed - 22 values
18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15 21.00 21.50
22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60 24.60 26.01
26.60 27.60
array[2] - 20 values
2.32 8.32 9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32
12.32 12.58 12.91 13.32 13.32 13.32 14.32 14.56 20.37 35.32
median: 12.32
3 outliers removed - 17 values
8.32 9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32 12.32
12.58 12.91 13.32 13.32 13.32 14.32 14.56
==28383==
==28383== HEAP SUMMARY:
==28383== in use at exit: 0 bytes in 0 blocks
==28383== total heap usage: 8 allocs, 8 frees, 1,208 bytes allocated
==28383==
==28383== All heap blocks were freed -- no leaks are possible
==28383==
==28383== For counts of detected and suppressed errors, rerun with: -v
==28383== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Если вы заметили выше, было выделено "8 выделений и8 освобождений " связано с памятью, используемой выше, например:
==28383== total heap usage: 8 allocs, 8 frees, 1,208 bytes allocated
Вы также можете подтвердить, что вся память была освобождена и в следующей строке не было утечек:
==28383== All heap blocks were freed -- no leaks are possible
И, наконец, вы можете подтвердить, что не было ошибок памяти, связанных с использованием памяти во время программывыполнение:
==28383== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Если есть часть кода, из-за которой у вас возникают проблемы с освобождением памяти, дайте мне знать, и я буду рад помочь вам в дальнейшем.