基本思想

建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

算法原理

归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

设定两个指针,最初位置分别为两个已经排序序列的起始位置

比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针到达序列尾

将另一序列剩下的所有元素直接复制到合并序列尾

特点

数据结构:数组

稳定性:稳定

过程

https://blog.csdn.net/weixin_43272781/article/details/85013461

排序过程            宏观过程

复杂度与辅助空间

最差时间复杂度:O(nlogn)

最优时间复杂度:O(nlogn)

平均时间复杂度:O(nlogn)

所需辅助空间:O(n)

源程序

//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + ;
int m = mid, n = last;
int k = ; while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
} while (i <= m)
temp[k++] = a[i++]; while (j <= n)
temp[k++] = a[j++]; for (i = ; i < k; i++)
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / ;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + , last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
} bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, , n - , p);
delete[] p;
return true;
}

一、圆周率π计算

  1.  
    /*
  2.  
    *@Author: STZG
  3.  
    *@Language: C++
  4.  
    */
  5.  
    #include <bits/stdc++.h>
  6.  
    using namespace std;
  7.  
    long a=10000,b,c=56000,d,e,f[56001],g;
  8.  
    int main(){
  9.  
    for(;b-c ; )f[b++]=a/5;
  10.  
    for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)
  11.  
    for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);
  12.  
    //cout << "Hello world!" << endl;
  13.  
    return 0;
  14.  
    }

二、数学公式

π = 2 + 1/3 * (2 + 2/5 * (2 + 3/7 * (2 + ...  (2 + k/2k+1 * (2 + ... ))...)))

三、分析

  1.  
    #include <stdio.h>
  2.  
    int a=10000,b,c=2800,d,e,f[2801],g;
  3.  
    main() {
  4.  
    int i;
  5.  
    for(i=0;i<c;i++)
  6.  
         f[i]=a/5;
  7.  
    while(c!=0)
  8.  
         {
  9.  
             d=0;
  10.  
             g=c*2;
  11.  
             b=c;
  12.  
             while(1)
  13.  
                {
  14.  
                    d=d+f[b]*a;
  15.  
                    g--;
  16.  
                    f[b]=d%g;
  17.  
                    d=d/g;
  18.  
                    g--;
  19.  
                    b--;
  20.  
                    if(b==0) break;
  21.  
                    d=d*b;
  22.  
                }
  23.  
             c=c-14;
  24.  
             printf("%.4d",e+d/a);
  25.  
             e=d%a;
  26.  
        }
  27.  
    }

要想计算出无限精度的PI,我们需要上述的迭代公式运行无数次,并且其中每个分数也是完全精确的,这在计算机中自然是无法实现的。那么基本实现思想就是迭代足够多次,并且每个分数也足够精确,这样就能够计算出PI的前n位来。上面这个程序计算800位,迭代公式一共迭代2800次。

int a=10000,b,c=2800,d,e,f[2801],g;

这句话中的2800就是迭代次数。

由于float或者double的精度远远不够,因此程序中使用整数类型(实际是长整型),分段运算(每次计算4位)。我们可以看到输出语句printf("%.4d",e+d/a); 其中%.4就是把计算出来的4位输出,我们看到c每次减少14( c=c-14;),而c的初始大小为2800,因此一共就分了200段运算,并且每次输出4位,所以一共输出了800位。

由于使用整型数运算,因此有必要乘上一个系数,在这个程序中系数为1000,也就是说,公式如下:

1000*pi = 2K+ 1/3 * (2K+ 2/5 * (2K+ 3/7 * (2K+ ... (2K+ k/2k+1 * (2K+ ... ))...)))

这里的2K表示2000,也就是f[2801]数组初始化以后的数据,a=10000,a/5=2000,所以下面的程序把f中的每个元素都赋值为2000:
for(i=0;i<c;i++)
f[i]=a/5;

你可能会觉得奇怪,为什么这里要把一个常数储存到数组中去,请继续往下看。我们先来跟踪一下程序的运行:

  1.  
    while(c!=0)  //假设这是第一次运行,c=2800,为迭代次数
  2.  
         {
  3.  
             d=0;
  4.  
             g=c*2;  //这里的g是用来做k/(2k+1)中的分子
  5.  
             b=c;  //这里的b是用来做k/(2k+1)中的分子
  6.  
             while(1)
  7.  
             while(1)
  8.  
                {
  9.  
                    d=d+f[b]*a;  //f中的所有的值都为2000,这里在计算时又把系数扩大了a=10000倍。这样做的目的稍候介绍,你可以看到输出的时候是d/a,所以这不影响计算
  10.  
                    g--;
  11.  
                    f[b]=d%g;  //先不管这一行
  12.  
                    d=d/g;  //第一次运行的g为2*2799+1,你可以看到g做了分母
  13.  
                    g--;
  14.  
                    b--;
  15.  
                    if(b==0) break;
  16.  
                    d=d*b;  //这里的b为2799,可以看到b做了分子。
  17.  
                }
  18.  
             c=c-14;
  19.  
             printf("%.4d",e+d/a);
  20.  
             e=d%a;
  21.  
        }
  22.  
     

只需要粗略的看看上面的程序,我们就大概知道它的确是使用的那个迭代公式来计算Pi的了,不过不知道到现在为止你是否明白了f数组的用处。如果没有明白,请继续阅读。
d=d/g,这一行的目的是除以2k+1,我们知道之所以程序无法精确计算的原因就是这个除法。即使用浮点数,答案也是不够精确的,因此直接用来计算800位的Pi是不可能的。那么不精确的成分在哪里?很明显:就是那个余数d%g。程序用f数组把这个误差储存起来,在下次计算的时候使用。现在你也应该知道为什么d=d+f[b]*a;中间需要乘上a了吧。把分子扩大之后,才好把误差精确的算出来。
d如果不乘10000这个系数,则其值为2000,那么运行d=d/g;则是2000/(2*2799+1),这种整数的除法答案为0,根本无法迭代下去了。
现在我们知道程序就是把余数储存起来,作为下次迭代的时候的参数,那么为什么这么做就可以使得下次迭代出来的结果为接下来的数字呢?
这实际上和我们在纸上作除法很类似:

   0142
  /———
 7/ 1
   10
    7
——————
    30
    28
——————
    20
    14
——————
     6
.....

我们可以发现,在做除法的时候,我们通常把余数扩大之后再来计算,f中既然储存的是余数,而f[b]*a;则正好把这个余数扩大了a倍,然后如此循环下去,可以计算到任意精度。
这里要说明的是,事实上每次计算出来的d并不一定只有4位数,例如第一次计算的时候,d的值为31415926,输出4位时候,把低四位的值储存在e中间,e=d%a,也就是5926。

最后,这个c=c-14不太好理解。事实上没有这条语句,程序计算出来的仍然正确。只是因为如果迭代2800次,无论分数如何精确,最后Pi的精度只能够达到800。
你可以把程序改为如下形式尝试一下:

  1.  
    for(i=0;i<800;i++)
  2.  
         {
  3.  
         {
  4.  
             d=0;
  5.  
             g=c*2;
  6.  
             b=c;
  7.  
             while(1)
  8.  
                {
  9.  
                    d=d+f[b]*a;
  10.  
                    g--;
  11.  
                    f[b]=d%g;
  12.  
                    d=d/g;
  13.  
                    g--;
  14.  
                    b--;
  15.  
                    if(b==0) break;
  16.  
                    d=d*b;
  17.  
                }
  18.  
           //c=c-14;  //不要这句话。
  19.  
             printf("%.4d",e+d/a);
  20.  
             e=d%a;
  21.  
        }

最后的答案仍然正确。
不过我们可以看到内循环的次数是c次,也就是说每次迭代计算c次。而每次计算后续位数的时候,迭代次数减少14,而不影响精度。

最新文章

  1. [No000082]Convert和Parse的区别/Convert.ToInt32()与int.Parse()的区别
  2. jstl
  3. [java基础知识]java安装步骤
  4. Jquery,javascript 的处理机制
  5. Java中字符串的几个实例
  6. [Android]ADB Server didn&#39;t ACK错误的解决方法
  7. Linux Shell 批量更换文件名或后缀名
  8. AndroidManifest修改重打包全过程
  9. grails导入excel
  10. Xcode 中添加 .pch文件
  11. 一个故事讲清楚NIO(转)
  12. ES4:ElasticSearch 使用C#添加和更新文档
  13. js禁止选中(网页复制)
  14. ajax+struts2 实现省份-城市-地区三级联动
  15. 转载:搭建完整的arm-linux-gcc等交叉编译环境(感谢CSDN博主的分享)
  16. [SRM577]BoardPainting
  17. DELL平板如何安装WIN10系统 -PE启动问题
  18. linq to sql之组装where条件下的&#39;或&#39;语句
  19. C# 如何把dataTable以参数的形式传入 sql 存储过程
  20. python常见模块属性与方法

热门文章

  1. if语句基本结构以及基础案例演示
  2. 一步一步学Vue(九) 路由元数据
  3. ES6几大特性,让你的代码更优美
  4. MQ之Kafka
  5. webshell yar
  6. 第5篇K8S创建资源的两种方式
  7. tomcat manager 禁止外网访问 只容许内网访问
  8. foobar2000 频谱给我的win10 任务栏添加一点会动背景
  9. POJ1149PIGS
  10. Windows下Tomcat安装 + eclipse-Server配置