【问题标题】:Array sorting in CC中的数组排序
【发布时间】:2013-12-14 20:04:51
【问题描述】:

我不是 C 专家,我已经阅读了论坛,但我仍然需要一些关于 C 排序问题的建议。

我在 C 中有 4 个动态双精度数组。它们的大小都相同,可以说是 n。我想要做的是使用其中一个数组作为第一顺序和第二个数组作为我的第二顺序对它们进行排序。因此,如果数组是 *x、*y、*w 和 *z。我想根据 *x 的值对它们进行排序,然后是 *y。

我必须高效地执行此操作,因为数组非常大。

任何帮助将不胜感激。

【问题讨论】:

  • 问题是如何对数组进行排序?或者你知道如何对一个数组进行排序?
  • 这里:qsort。尝试一些东西,如果你有问题,那么我们会谈谈。关于大数组的排序,见:stackoverflow.com/questions/5588041/…
  • @user20679 你实际上并没有问具体问题。这在此处不受欢迎。对于您的情况:采用任何体面的开源快速排序实现(或其他算法)。然后找到他们交换元素的部分。所以在那个地方,而不是交换原始数组,用相应的索引交换其他数组的元素。
  • @Andrey:问题非常清楚x[i]y[i]w[i]z[i] 中的每一个都代表一条记录,他想按 xy 对每条记录进行排序。如果他有一个表示四个数量的结构,这将非常容易,但他没有。

标签: c arrays sorting


【解决方案1】:

简单的方法是将四个单独的数组映射到一个结构类型的数组上

struct rec {
  double x;
  double y;
  double w;
  double z;
};

struct rec *arr = malloc( sizeof *arr * N ); // where N is the number of
                                             // elements in each array

if ( !arr )
  // malloc failed, handle error somehow

for ( size_t i = 0; i < N; i++ )
{
  arr[i].x = x[i];
  arr[i].y = y[i];
  arr[i].w = w[i];
  arr[i].z = z[i];
}

然后创建一个比较函数传递给qsort

int cmpRec( const void *lhs, const void *rhs )
{
  struct rec *l = lhs;
  struct rec *r = rhs;

  if ( l->x < r->x )
    return -1;
  else if ( l->x > r->x )
    return 1;
  else
  {
    if ( l->y < r->y )
      return -1;
    else if ( l->y > r->y )
      return 1;
    else
      return 0;
  }

  return 0;
}

现在您可以使用qsort 库函数对该结构数组进行排序:

qsort( arr, N, sizeof *arr, cmpRec );

对该数组进行排序后,您可以将结果映射回您的四个原始数组。

【讨论】:

  • OP 说:我知道这不是处理数据的最佳方法,但不能改变它。
  • @Artur 我确定它可以暂时更改。
  • 比较 func 的条件看起来会更好: if ( l->x x ) return -1;如果 ( l->x > r->x ) 返回 1;如果 ( l->y y ) 返回 -1;如果 ( l->y > r->y ) 返回 1;返回 0;
【解决方案2】:

显然,使用标准qsort() 对其进行排序是行不通的;没有传递四个数组的机制。

同样清楚的是,如果数据被结构化为结构数组,那么使用qsort() 将是可行的。

问题1:创建一个结构数组,加载,排序,然后卸载回原始数组是否可行?

问题 2:另一种选择是对整数数组进行排序:

int indexes[n];
for (int i = 0; i < n; i++)
    indexes[i] = i;

qsort(indexes, n, sizeof(indexes[0]), comparator);

comparator 函数必须能够访问 xy 数组作为文件范围变量:

int comparator(void const *v1, void const *v2)
{
    int i1 = *(int *)v1;
    int i2 = *(int *)v2;
    extern double *x, *y;
    if (x[i1] > x[i2])
        return +1;
    else if (x[i1] < x[i2])
        return -1;
    else if (y[i1] > y[i2])
        return +1;
    else if (y[i1] < y[i2])
        return -1;
    else
        return 0;
}

然后您就可以使用x[indexes[i]] 等访问数组,从而按排序顺序访问ith 元素。

这可以接受吗?

如果这也不方便,那么你最终会编写自己的排序;不是很痛,但需要一些护理。


我花了一些时间调整现有的排序测试框架以适应这种情况。完整的代码非常大,因为它包含了大量的测试支持代码。核心功能(比较、交换、分区和快速排序)在这里(122行,包括注释和空白行):

/* SO 20271977 - sort arrays x, y, z, w (type double, size n) in parallel based on values in x and y */

/*
** To apply this to the real code, where there are 4 arrays to be sorted
** in parallel, you might write:
**
**    Array4 a;
**    a.x = x;
**    a.y = y;
**    a.z = z;
**    a.w = w;
**    a.n = n;
**    quicksort_random(&a);
**
** Or even:
**
**    quicksort_random((Array4){ .n = n, .x = x, .y = y, .z = z, .w = w });
**
** combining designated initializers and compound literals.  Or you could write a
** trivial wrapper so that you can call:
**
**    quicksort_random_wrapper(n, x, y, z, w);
*/

/* SOF so-20271977.h */
#include <stddef.h>
typedef struct Array4
{
    size_t  n;
    double *x;
    double *y;
    double *z;
    double *w;
} Array4;

extern void quicksort_random(Array4 *A);
/* EOF so-20271977.h */

#include <assert.h>
#include <stdlib.h> /* lrand48() */

/*
** Note that a more careful implementation would use nrand48() instead
** of lrand48() to prevent its random number generation from interfering
** with other uses of the x-rand48() functions.
*/

typedef size_t (*Part)(Array4 *A, size_t p, size_t r);

static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition);
static size_t partition_random(Array4 *A, size_t p, size_t r);

/* Quick Sort Wrapper function - specifying random partitioning */
void quicksort_random(Array4 *A)
{
    quicksort_partition(A, 0, A->n - 1, partition_random);
}

/* Main Quick Sort function */
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition)
{
    if (p < r)
    {
        size_t q = (*partition)(A, p, r);
        assert(p <= q && q <= r);
        if (q > 0)
            quicksort_partition(A, p, q-1, partition);
        quicksort_partition(A, q+1, r, partition);
    }
}

static inline int compare(Array4 const *A, size_t p, size_t r)
{
    if (A->x[p] < A->x[r])
        return -1;
    else if (A->x[p] > A->x[r])
        return +1;
    if (A->y[p] < A->y[r])
        return -1;
    else if (A->y[p] > A->y[r])
        return +1;
    else
        return 0;
}

static inline size_t random_int(size_t p, size_t r)
{
    return(lrand48() % (r - p + 1) + p);
}

static inline void swap(Array4 *A, size_t i, size_t j)
{
    double d;
    d = A->x[i];
    A->x[i] = A->x[j];
    A->x[j] = d;
    d = A->y[i];
    A->y[i] = A->y[j];
    A->y[j] = d;
    d = A->z[i];
    A->z[i] = A->z[j];
    A->z[j] = d;
    d = A->w[i];
    A->w[i] = A->w[j];
    A->w[j] = d;
}

static size_t partition_random(Array4 *A, size_t p, size_t r)
{
    size_t pivot = random_int(p, r);
    swap(A, pivot, r);
    size_t i = p-1;
    size_t j = p;

    while (j <= r)
    {
        if (compare(A, j, r) <= 0)
            swap(A, j, ++i);
        j++;
    }
    return i;
}

测试框架(如果不是我手头已经有它的变体的话,这个框架相当可笑)是 369 行,包括空白行和注释行——以及上面的所有代码:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define FLTFMT "%13.6f"

typedef struct Array4
{
    size_t  n;
    double *x;
    double *y;
    double *z;
    double *w;
} Array4;

static int trace = 0;

static void *xmalloc(size_t size)
{
    void *space = malloc(size);
    if (space == 0)
    {
        fprintf(stderr, "Out of memory (%zu)\n", size);
        exit(1);
    }
    return space;
}

void quicksort_last(Array4 *A);
void quicksort_random(Array4 *A);
void selectionsort(Array4 *A);

static inline int compare(Array4 const *A, size_t p, size_t r)
{
    if (A->x[p] < A->x[r])
        return -1;
    else if (A->x[p] > A->x[r])
        return +1;
    if (A->y[p] < A->y[r])
        return -1;
    else if (A->y[p] > A->y[r])
        return +1;
    else
        return 0;
}

static void dump_array(char const *tag, Array4 const *A)
{
    printf("%s [%zu..%zu]:\n", tag, (size_t)0, A->n-1);
    for (size_t i = 0; i < A->n; i++)
        printf("(" FLTFMT ", " FLTFMT ", " FLTFMT ", " FLTFMT ")\n",
               A->x[i], A->y[i], A->z[i], A->w[i]);
}

static void chk_sort(Array4 const *A)
{
    for (size_t i = 0; i < A->n - 1; i++)
    {
        //if (compare(A, i, i+1) > 0)
        {
            if (A->x[i] > A->x[i+1])
            {
                printf("Out of order: A.x[%zu] = " FLTFMT ", A.x[%zu] = " FLTFMT "\n",
                        i, A->x[i], i+1, A->x[i+1]);
            }
            else if ((A->x[i] == A->x[i+1] && A->y[i] > A->y[i+1]))
            {
                printf("Out of order: A.x[%zu] = " FLTFMT ", A.x[%zu] = " FLTFMT ", "
                        "A.y[%zu] = " FLTFMT ", A.y[%zu] = " FLTFMT "\n",
                        i, A->x[i], i+1, A->x[i+1], i, A->y[i], i+1, A->y[i+1]);
            }
        }
    }
}

static inline void set(Array4 *A, size_t p, double d)
{
    A->x[p] = d;
    A->y[p] = d + drand48() - 0.5;
    A->z[p] = d / 2.0;
    A->w[p] = d * 2.0;
}

static void load_random(Array4 *A)
{
    size_t size = A->n;
    for (size_t i = 0; i < size; i++)
    {
        A->x[i] = drand48() * size;
        A->y[i] = drand48() * size + drand48() - 0.5;
        A->z[i] = drand48() * size / 2.0;
        A->w[i] = drand48() * size * 2.0;
    }
}

static void load_ascending(Array4 *A)
{
    for (size_t i = 0; i < A->n; i++)
        set(A, i, i);
}

static void load_descending(Array4 *A)
{
    for (size_t i = 0; i < A->n; i++)
        set(A, i, A->n - i);
}

static void load_uniform(Array4 *A)
{
    for (size_t i = 0; i < A->n; i++)
        set(A, i, A->n);
}

static void load_organpipe(Array4 *A)
{
    for (size_t i = 0; i <= A->n / 2; i++)
        set(A, i, i);
    for (size_t i = A->n / 2 + 1; i < A->n; i++)
        set(A, i, A->n - i);
}

static void load_invorganpipe(Array4 *A)
{
    size_t range = A->n / 2;
    for (size_t i = 0; i < A->n / 2; i++)
        set(A, i, range - i);
    for (size_t i = A->n / 2 + 1; i < A->n; i++)
        set(A, i, i - range);
}

typedef void (*Load)(Array4 *A);
typedef void (*Sort)(Array4 *A);
typedef size_t (*Part)(Array4 *A, size_t p, size_t r);

static void test_one_sort(Array4 *A, Sort sort, char const *s_tag,
                          char const *l_tag, char const *z_tag)
{
    if (trace)
    {
        printf("%s-%s-%s:", z_tag, l_tag, s_tag);
        dump_array("Before", A);
    }
    clock_t start = clock();
    (*sort)(A);
    clock_t finish = clock();
    double sec = (finish - start) / (double)CLOCKS_PER_SEC;
    printf("%s-%s-%s: %13.6f\n", z_tag, l_tag, s_tag, sec);
    chk_sort(A);
    if (trace)
    {
        printf("%s-%s-%s:", z_tag, l_tag, s_tag);
        dump_array("After", A);
    }
    fflush(stdout);
}

static Array4 *alloc_array(size_t size)
{
    Array4 *A = xmalloc(sizeof(*A));
    A->n = size;
    A->x = xmalloc(size * sizeof(A->x[0]));
    A->y = xmalloc(size * sizeof(A->y[0]));
    A->z = xmalloc(size * sizeof(A->z[0]));
    A->w = xmalloc(size * sizeof(A->w[0]));
    return A;
}

static Array4 *dup_array(Array4 *A)
{
    size_t size = A->n;
    Array4 *B = alloc_array(size);
    if (B != 0)
    {
        B->n = size;
        memmove(B->x, A->x, size * sizeof(A->x[0]));
        memmove(B->y, A->y, size * sizeof(A->y[0]));
        memmove(B->z, A->z, size * sizeof(A->z[0]));
        memmove(B->w, A->w, size * sizeof(A->w[0]));
    }
    return B;
}

static void free_array(Array4 *A)
{
    free(A->x);
    free(A->y);
    free(A->z);
    free(A->w);
    free(A);
}

static void test_set_sorts(Array4 *A, char const *l_tag, char const *z_tag)
{
    struct sorter
    {
        Sort function;
        char const *tag;
    } sort[] =
    {
        { quicksort_last, "QS.L" },
        { quicksort_random, "QS.R" },
        { selectionsort, "SS.N" },
    };
    enum { NUM_SORTS = sizeof(sort) / sizeof(sort[0]) };
    for (int i = 0; i < NUM_SORTS; i++)
    {
        Array4 *B = dup_array(A);
        test_one_sort(B, sort[i].function, sort[i].tag, l_tag, z_tag);
        free(B);
    }
}

static void test_set_loads(size_t size, char const *z_tag)
{
    struct loader
    {
        Load function;
        char const *tag;
    } load[] =
    {
        { load_random, "R" },
        { load_ascending, "A" },
        { load_descending, "D" },
        { load_organpipe, "O" },
        { load_invorganpipe, "I" },
        { load_uniform, "U" },
    };
    enum { NUM_LOADS = sizeof(load) / sizeof(load[0]) };
    Array4 *A = alloc_array(size);
    for (int i = 0; i < NUM_LOADS; i++)
    {
        load[i].function(A);
        test_set_sorts(A, load[i].tag, z_tag);
    }
    free_array(A);
}

/* Main Quick Sort function */
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition)
{
    if (p < r)
    {
        size_t q = (*partition)(A, p, r);
        assert(p <= q && q <= r);
        if (q > 0)
            quicksort_partition(A, p, q-1, partition);
        quicksort_partition(A, q+1, r, partition);
    }
}

static size_t partition_random(Array4 *A, size_t p, size_t r);
static size_t partition_last(Array4 *A, size_t p, size_t r);

/* Quick Sort Wrapper function - specifying random partitioning */
void quicksort_random(Array4 *A)
{
    quicksort_partition(A, 0, A->n - 1, partition_random);
}

/* Quick Sort Wrapper function - specifying partitioning about last element */
void quicksort_last(Array4 *A)
{
    quicksort_partition(A, 0, A->n - 1, partition_last);
}

static inline size_t random_int(size_t p, size_t r)
{
    return(lrand48() % (r - p + 1) + p);
}

static inline void swap(Array4 *A, size_t i, size_t j)
{
    double d;
    d = A->x[i];
    A->x[i] = A->x[j];
    A->x[j] = d;
    d = A->y[i];
    A->y[i] = A->y[j];
    A->y[j] = d;
    d = A->z[i];
    A->z[i] = A->z[j];
    A->z[j] = d;
    d = A->w[i];
    A->w[i] = A->w[j];
    A->w[j] = d;
}

static size_t partition_random(Array4 *A, size_t p, size_t r)
{
    size_t pivot = random_int(p, r);
    swap(A, pivot, r);
    size_t i = p-1;
    size_t j = p;

    while (j <= r)
    {
        if (compare(A, j, r) <= 0)
            swap(A, j, ++i);
        j++;
    }
    return i;
}

static size_t partition_last(Array4 *A, size_t p, size_t r)
{
    size_t i = p-1;
    size_t j = p;

    while (j <= r)
    {
        if (compare(A, j, r) <= 0)
            swap(A, j, ++i);
        j++;
    }
    return i;
}

/* Selection Sort algorithm */
void selectionsort(Array4 *A)
{
    size_t r = A->n;
    for (size_t p = 0; p < r; p++)
    {
        for (size_t i = p; i < r; i++)
        {
            if (compare(A, p, i) > 0)
                swap(A, p, i);
        }
    }
}

/*
** To apply this to the real code, where there are 4 arrays to be sorted
** in parallel, you might write:
**
**    Array4 a;
**    a.x = x;
**    a.y = y;
**    a.z = z;
**    a.w = w;
**    a.n = n;
**    quicksort_random(&a);
**
** Or even:
**
**    quicksort_random((Array4){ .n = n, .x = x, .y = y, .z = z, .w = w });
**
** combining designated initializers and compound literals.  Or you could write a
** trivial wrapper so that you can call:
**
**    quicksort_random_wrapper(n, x, y, z, w);
*/

int main(void)
{
    srand48((long)time(0));

    for (size_t i = 10; i <= 40; i += 10)
    {
        char buffer[10];
        snprintf(buffer, sizeof(buffer), "%zuK", i);
        test_set_loads(1000*i, buffer);
    }

    return 0;
}

【讨论】:

    【解决方案3】:

    如果您不能将qsort

    一起使用
    typedef struct Point {
        double x;
        double y;
        double w;
        double z;
    } Point;
    

    使用 qsort 和

     typedef struct UglyThing {
       double x;
       int i;
     } UglyThing;
    

    创建一个大小为 n 的数组,用 x 值填充 x,用索引填充 i。 调用 qsort。最后,我将存储排列顺序。 根据排列顺序交换其他三个数组。 然后在 y 方向上对小数组(“with same x”)做同样的事情。

    如果这个丑陋的把戏是不可能的,那么除了重新发明轮子之外,我看不到任何其他的解决方案。

    (编辑:我刚刚看到安德鲁说了一些非常接近这个答案的话......对不起!) 再见,

    弗朗西斯

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-26
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      • 2015-01-27
      • 2013-06-19
      相关资源
      最近更新 更多