首页 > 编程语言 >常见的排序算法——归并排序(六)

常见的排序算法——归并排序(六)

时间:2024-05-25 09:18:23浏览次数:21  
标签:__ 归并 int double 算法 time 排序

本文记述了多向归并排序的基本思想并给出了一份参考实现代码。在说明了算法的性能后用随机数据进行了验证。

◆ 思想

归并排序归并排序(二)归并排序(三)归并排序(四)中记述的归并排序,都是把待排序范围分成两个部分分别排序的。而多向归并排序是把待排序范围分为 K 个部分,把它们分别排序然后进行归并。

◆ 实现

排序代码采用《算法(第4版)》的“排序算法类模板”实现。(代码中涉及的基础类,如 Array,请参考算法文章中涉及的若干基础类的主要API

// merge6.hxx

...

class Merge6
{

    ...
    
    template
    <
        class _T,
        class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type
    >
    static
    void
    sort(int K, Array<_T> & a)                     // #1
    {
        Array<_T> aux = Array<_T>(a.size());
        __sort__(K, a, 0, a.size()-1, aux);
    }
    
    ...

    template
    <
        class _T,
        class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type
    >
    static
    void
    __sort__(int K, Array<_T> & a, int lo, int hi, Array<_T> & aux)
    {
        if (hi <= lo) return;
        int N = hi - lo + 1;
        Array<int> s(K), e(K);  // start and end indices for subarrays
        int sz = int(std::ceil(double(N) / K));
        for (int i = 0; i < K; ++i) {                                   // #2
            s[i] = lo + i * sz;
            e[i] = (s[i] + sz - 1 < hi) ? s[i] + sz-1 : hi;
        }
        for (int i = 0; i < K; ++i)                         // #3
            __sort__(K, a, s[i], e[i], aux);
        __merge__(K, a, s, e, aux);                         // #4
    }
    
    ...

    template
    <
        class _T,
        class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type
    >
    static
    void
    __merge__(int K, Array<_T> & a, Array<int> & s, Array<int> & e, Array<_T> & aux)
    {
        for (int i = s[0]; i <= e[K-1]; ++i)                 // #5
            aux[i] = a[i];
        for (int i = s[0], min; i <= e[K-1]; ++i) {
            min = s[0];
            for (int k = 0; k < K; ++k)
                if (s[k] <= e[k] && __less__(aux[s[k]], aux[min]))
                    min = s[k], ++s[k];
            a[i] = aux[min];                      // #6
        }
    }

    ...
    
    template
    <
        class _T,
        class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type
    >
    static
    bool
    __less__(_T const& v, _T const& w)
    {
        return v.compare_to(w) < 0;         // #7
    }

    ...

向数 K 作为参数被传递给排序算法(#1)。在排序前,先计算每个子范围的开始和结束位置(#2),把它们分别排序(#3),然后把各个排序的结果归并起来(#4)。在归并前,先利用辅助空间临时存放 K 个子范围的元素(#5),再逐一找到 K 个子范围中的当前最小元素并将其放回到排序范围内(#6),直至 K 个子范围都归并完。将 '<' 改为 '>',即得到逆序的结果(#7)。

◆ 性能

时间复杂度 空间复杂度 是否稳定
N*log(N) N

◆ 验证

测试代码采用《算法(第4版)》的倍率实验方案,用随机数据验证其正确性并获取时间复杂度数据。

// test.cpp
    
...

time_trial(int K, int N)
{
    Array<Double> a(N);
    for (int i = 0; i < N; ++i) a[i] = Std_Random::random();    // #1
    Stopwatch timer;
    Merge6::sort(K, a);                     // #2
    double time = timer.elapsed_time();
    assert(Merge6::is_sorted(a));            // #3
    return time;
}

...

test(char * argv[])
{
    int T = std::stoi(argv[1]);          // #4
    Std_Out::printf("%10s%10s%10s%10s%10s%10s%10s%10s%10s%10s%10s\n", "N", "K = 2", "K = 3", "K = 4", "K = 5", "K = 6", "K = 7", "K = 8", "K = 9", "K = 10", "K = 11");
    for (int i = 0, N = 1024; i < T; ++i, N += N) {            // #5
        double time2  = time_trial(2,  N);
        double time3  = time_trial(3,  N);
        double time4  = time_trial(4,  N);
        double time5  = time_trial(5,  N);
        double time6  = time_trial(6,  N);
        double time7  = time_trial(7,  N);                 // #6
        double time8  = time_trial(8,  N);
        double time9  = time_trial(9,  N);
        double time10 = time_trial(10, N);
        double time11 = time_trial(11, N);
        Std_Out::printf("%10d%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f%10.3f\n", N, time2, time3, time4, time5, time6, time7, time8, time9, time10, time11);
    }
}

...

用 [0,1) 之间的实数初始化待排序数组(#1),打开计时器后执行排序(#2),确保得到正确的排序结果(#3)。整个测试过程要执行 T 次排序(#4)。每次执行排序的数据规模都会翻倍(#5),并得到不同 K 值下的排序时间(#6),

此测试在实验环境一中完成,

$ g++ -std=c++11 test.cpp std_out.cpp std_random.cpp stopwatch.cpp type_wrappers.cpp

         N     K = 2     K = 3     K = 4     K = 5     K = 6     K = 7     K = 8     K = 9    K = 10    K = 11
      1024     0.028     0.023     0.018     0.022     0.018     0.021     0.025     0.025     0.026     0.020
      2048     0.060     0.043     0.047     0.040     0.050     0.039     0.044     0.049     0.052     0.059
      4096     0.128     0.104     0.082     0.097     0.089     0.107     0.085     0.093     0.099     0.107
      8192     0.272     0.232     0.214     0.198     0.219     0.195     0.240     0.252     0.198     0.213
     16384     0.575     0.428     0.380     0.458     0.423     0.381     0.426     0.484     0.547     0.571
     32768     1.208     0.956     0.951     0.898     0.814     1.037     0.833     0.932     1.009     1.114
     65536     2.541     2.146     1.707     1.693     2.019     1.881     2.267     2.419     1.984     2.146
    131072     5.323     4.072     4.192     4.207     4.025     4.567     4.145     4.614     5.127     4.289
    262144    11.132     9.178     7.587     7.649     7.590     8.870     8.134     8.823     9.945    11.181
    524288    23.245    17.141    18.181    18.439    18.972    17.017    20.447    17.565    18.952    20.757
   1048576    48.281    39.075    33.000    35.410    34.756    40.479    38.386    44.446    46.330    40.997
   2097152   100.685    84.436    78.072    80.730    82.781    80.011    74.419    82.705    90.530   101.835
   4194304   208.772   158.872   142.965   157.102   159.215   152.687   186.003   160.461   178.676   196.547
   8388608   433.488   352.724   338.606   303.316   309.389   364.163   350.672   398.539   352.995   387.393
  16777216   898.020   734.325   618.355   693.583   738.794   709.554   684.961   755.231   861.721   764.983

在给定的 N 下,将 K = 2, 3, ..., 11 对应的时间按照从短到长的顺序排名,得到下表中黑粗线上边的排名数据。然后在针对某个 K 下的所有排名,计算平均数、中位数和众数,得到下表中黑粗线下边的结果。

test_data

根据这些结果可知,对于随机数据而言,当 K = 4 时多向归并表现最佳。

◆ 最后

完整的代码请参考 [gitee] cnblogs/18210127

写作过程中,笔者参考了《算法(第4版)》的归并排序、练习题 2.2.25、“排序算法类模板”和倍率实验。致作者 Sedgwick,Wayne 及译者谢路云。

标签:__,归并,int,double,算法,time,排序
From: https://www.cnblogs.com/green-cnblogs/p/18210127

相关文章

  • Mysql自增id、uuid、雪花算法id的比较
    MySQL自增id:优点:1.简单易用​MySQL自增id由数据库自动生成。2.效率高自增id是按顺序递增的,可以提高插入和查询的效率。3.索引效率高自增id可以作为主键或索引列,提高查询效率。缺点:1.不适用于分布式系统在分布式环境下,多个节点生成的自增id可能会冲突,需要额外的处理机......
  • m基于GA-GRU遗传优化门控循环单元网络的电力负荷数据预测算法matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下: 优化前:    优化后:    对比:   2.算法涉及理论知识概要      基于遗传算法(GeneticAlgorithm,GA)优化的长门控循环单元(GatedRecurrentUnit,GRU)网络,是一种结合了进化计算与深度学习的混合预测模......
  • 代码随想录算法训练营第一天 | 977.有序数组的平方;
    代码随想录算法训练营第一天|977.有序数组的平方;977题链接:https://leetcode.cn/problems/squares-of-a-sorted-array/代码随想录链接:https://programmercarl.com/0977.有序数组的平方.html#思路209题链接:https://leetcode.cn/problems/minimum-size-subarray-sum/submission......
  • 基于BP神经网络的16QAM解调算法matlab性能仿真
    1.算法运行效果图预览  2.算法运行软件版本MATLAB2022a 3.算法理论概述     16QAM(QuadratureAmplitudeModulation,正交幅度调制)是一种高效的数字调制技术,能够在相同的带宽内传输比传统调制方式更多的信息。解调是通信系统中从接收到的信号中恢复原始信息的关......
  • 代码随想录算法训练营第第17天 | 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子
    三道题都没想出来,还是要加强递归的练习110.平衡二叉树(优先掌握递归)再一次涉及到,什么是高度,什么是深度,可以巩固一下。题目链接/文章讲解/视频讲解:https://programmercarl.com/0110.平衡二叉树.htmlfunctiongetHeight(node){if(node===null)return0;letleftH......
  • 【智能算法应用】遗传算法求解车间布局优化问题
    目录1.问题背景2.车间布局数学模型3.算法过程4.结果展示5.参考文献6.代码获取1.问题背景工厂设施布置的规划一直是工业工程领域不断研究和探索的内容,其中最具代表性之一的是系统布置设计(systemlayoutplanning,SLP)方法。作为一种经典且有效的方法,其为设施布......
  • 【智能算法应用】白鲸优化算法求解二维路径规划问题
    目录1.算法原理2.路径规划数学模型3.结果展示4.参考文献5.代码获取1.算法原理【智能算法】白鲸优化算法(BWO)原理及实现2.路径规划数学模型优化目标路径规划问题需要考虑三点:全局总路径最优避免碰撞到障碍物路径平滑性全局总路径最优考虑路径规划问题的全局最......
  • [自动驾驶技术]-7 Tesla自动驾驶方案之算法(AI Day 2022)
    特斯拉在2022年AIDay上更新了感知规控算法模型,核心引入了Occupancy技术。下图是特斯拉活动日展示的主题内容,本文主要解读Planning和NeuralNetwork部分。1规划决策Interactionsearch-交互搜索特斯拉在自动驾驶规划中使用了一种高度复杂和优化的搜索算法,结合了多种先进的......
  • 实验21-正向最大匹配算法
    出现UnicodeDecodeError:'gbk'codeccan'tdecodebyte0x9finposition16:illegalmultibytesequence 修改为 即可实验结果: ......
  • Jpeg算法压缩
    Jpeg算法压缩JPEG格式图片文件背后的算法:色彩空间转换(ColorSpaceConversion"),将RGB转换为YUV色彩空间,YUV的数据更好处理色度缩减采样(ChromenanceDownsampling),将蓝红色度层的“分辨率”变小,因为人眼对颜色不敏感离散余弦变换(DiscreteCosineTransform),找出人眼不敏感的高频......