【问题标题】:Swapping elements from array of struct faster更快地从结构数组中交换元素
【发布时间】:2017-01-29 01:32:52
【问题描述】:

我有这个结构:

typedef struct data{
    char name[100], pseudo[100];
    int num0, num1, num2, num3, num4;
    long int lnum0, lnum1, lnum2, lnum3, lnum4;
    double dnum0, dnum1;
}data;
data list[50]

我创建了一个此结构的数组,并使用快速排序算法对它们进行排序。 为此,我必须使用此函数交换元素:

void swap(data list[], i, j){
    data tmp;

    tmp.num1 = list[i].num1
    list[i].num1 = list[j].num1
    list[j].num1 =tmp.num1
    //using memmove to avoid overlaping from the strcpy function
    memmove(temp.name,list[i].name,strlen(list[i].name));
    memmove(list[i].name,list[j].name,strlen(list[j].num1));
    memmove(list[j].name,tmp.name,strlen(tmp.name));
}

我的结构中有 16 个元素,我必须重复此函数 16 次才能将它们全部交换。 我的问题是:是否有另一种更简单、更快或更好的方法来进行,或者我们可以优化这个功能?

【问题讨论】:

  • 也许创建一个指向这些结构的指针数组并交换指针而不是实际的结构?
  • 你的memmove 代码没有意义。
  • 如果你必须交换实际结构而不是使用指针,data tmp; tmp = list[ i ]; list[ i ] = list[ j ]; list[ j ] = tmp; 肯定是更简单的代码。而且它也可能会快很多。
  • 要交换整个结构,需要使用memmove()memcpy() 之类的东西。在整个结构上。注意:@AndrewHenle,仅使用结构实例的名称不会复制整个结构
  • @user3629249:使用结构赋值来复制结构,如 AndrewHenle 所示,对于浅拷贝来说是正确的,这就是这里所需要的。

标签: c arrays struct swap


【解决方案1】:

这是对具有T 类型的N 元素的数组进行排序的典型解决方法,其中假定Nsizeof(T) 都很大。

  1. 创建一个由N 组成的临时数组指针T
  2. 用指向实际数组中元素的指针填充临时数组。
  3. 对临时数组进行排序。 (比较元素时,您必须取消引用指针。交换元素时,您只需交换单个指针。)
  4. 重新排列原始数组中的元素,使它们与临时数组中的指针所指向的顺序相同。
  5. 再次释放临时数组。

此技术的优点是您只需执行TO(N) 交换,而您可以执行T*O(N log(N)) 交换。缺点是您必须分配临时缓冲区并在比较元素时通过额外的指针间接。您必须进行基准测试才能确定这是否适合您的类型。

一个可能的优化是在堆栈上分配临时数组,因为它永远不会超过排序例程。但是,将巨大的数组放入堆栈可能会导致堆栈溢出,因此请注意大小。

【讨论】:

  • 哇,实际上我不得不执行O(N^2)swaps,但多亏了你,我可能能够大大减少处理时间
  • 你的快速排序怎么会导致 O(N^2) 交换?它可能会在最坏的情况下退化,但您通常会平均期望 O(N log(N))。请注意,O(N^2) 和 O(N log(N)) 之间的差异将比 O(N log(N)) 和 O(N) 之间的差异显着得多。
  • 这是因为我随意选择了pivot,这个选择中很少有独特的
  • 在这种情况下,3 路快速排序或合并排序可能是值得研究的有希望的替代方案。仍然可以应用“对指针数组进行排序”的技巧。
  • 请注意,OP 声明该数组有 16 个元素。这很少被称为“大”。
【解决方案2】:

我不确定这是否是您要查找的内容,但加速这些交换的一种简单方法是将指向“结构数据”的指针存储在“列表”中,而不是存储整个结构本身。这样,当你交换时,你一次只交换 4 或 8 个字节(分别为 32 位和 64 位),而不是高达 256 个字节。

如果您打算连续存储和交换这些结构的所有内存,最好的办法是使用向量内在函数 (SIMD)。这是guide for gcc。这是one for msvc

【讨论】:

    【解决方案3】:

    如果不是因为您询问优化这一事实,我会认为这是一项家庭作业。不过,分类种类的家庭作业通常不涉及优化。尽管如此,您的机构会在现实世界中教您永远不要重新发明轮子,除非收益大于成本。在这种情况下,他们不会。

    • 想象一下,如果您的 x86 最快代码也 ARM 最慢。这是一种常见的情况,标准库在<stdlib.h> 中包含两个函数:qsortbsearch。很可能,标准库的作者对如何编写、测试和调整每个平台的排序算法有更好的了解。
    • 想象一下,如果每个进程 在每个给定时间运行都重新发明轮子,导致大量重复的排序函数被保留并在内存中交换......使用标准库代码的一个主要好处是它可以在许多进程之间共享,从而减少重复的资源消耗。更少的资源消耗也很可能会导致代码更快,在这种情况下,在多个进程之间共享此资源也可能会减少缓存抖动。

    要使用qsortbsearch,您首先需要定义一个比较函数。这可以像包装strcmp 一样简单,如果您可以保证要排序的字段实际上是一个字符串(即字符序列以'\0' 结尾)。比较函数需要使用签名int compare(void const *x, void const *y);,例如:

    int compare_data_by_name(void const *x, void const *y) {
        data const *foo = x, *bar = y;
        return strcmp(foo->name, bar->name);
    }
    

    调用qsort(list, sizeof list / sizeof *list, sizeof *list, compare_data_by_name); 将排序list

    调用bsearch(&(data){ .name = "fred" }, list, sizeof list / sizeof *list, sizeof *list, compare_data_by_name); 将检索以"fred" 作为名称的项目。

    【讨论】:

    • 好吧!我不知道qsortbsearch,所以谢谢我会试一试。这不是作业,我也没有重新发明轮子,基本上我只是想在一个结构数组中格式化并快速排序,因为这可能对某些任务有用
    猜你喜欢
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    • 2012-09-21
    • 2019-11-29
    相关资源
    最近更新 更多