插入排序、归并排序和递归算法的复杂性分析

转载请声明出处:http://blog.csdn.net/zhongkelee/article/details/44490315

说在前面

      这学期正在学习《计算机算法设计与分析》,所以用博客的形式记录自己的学习心得,与大家交流分享。

一、算法渐进复杂性及其相关记号

1. 复杂度概念

      算法的复杂性分析,说白了也就是关于计算机程序的性能和算法所用计算机资源的理论分析。通常包括时间复杂性T(n)和空间复杂性S(n),其中n是问题的输入规模。一般来说算法需要的时间与输入的规模同步增长,所以通常把一个程序的运行时间描述成输入规模的函数。

   对许多问题,比如排序或计算离散傅里叶变换最自然的量度是输入中的项数,例如带排序数组的规模n。对许多其他问题,比如两个整数相乘,输入规模的最佳量度是用通常的二进制记号表示输入所需的总位数。

   通常一个算法的运行时间依赖于输入,而我们想要获取的是运行时间的上界(实际上每个人都更倾向于获得一种保证)。

   最坏情况下的时间复杂性:

   

   最好情况下的时间复杂性:

   

   平均情况下的时间复杂性:

   

   其中 I 是问题的规模为 n 的实例,p(I) 是实例I出现的概率。

2. 渐近复杂性

     如果我们忽略机器相关的参数,只考察 T(n) 随着 n 趋于无穷大的增长,则可以引入渐近复杂性的概念:

  

   t(n) 是 T(n) 的渐进性态,是 T(n) 略去低阶项后留下的主项,我们称作算法的渐进复杂性。

   例如,当 n 足够大时,一个的算法总是能够打败一个的算法。

   在后文中,我们使用如下渐进分析记号:

       

   其实,这5个记号可以理解为:

   

   若对所有的,函数f(n)在一个常量因子内等于g(n),就称g(n)是f(n)的一个渐进紧确界,如下图所示:

     有关算法分析中渐进函数、常用函数的若干条性质,我这里就不再赘述了,可以参考《算法导论》第三版29~34页相关内容及证明。

3. 最优算法

     如果问题的计算时间下界为,则计算时间复杂性为的算法,就可以称为最优算法。例如,比较排序问题的计算时间下界为,计算时间复杂性为的排序算法是最优算法(比如堆排序算法,后续博客会详细讲解该算法)。

4.常见时间复杂度

   常见时间复杂度从低到高为:常数 < 对数< 多项式 < 指数 < 阶乘

二、普通算法复杂度分析

1. 顺序搜索算法

    例如,顺序搜索算法:

[cpp]  view plain  copy
 print ?
  1. Template<class Type>  
  2.     Int seqSearch(Type *a, int n, Type k){  
  3.         for(int i = 0; i < n; i++)  
  4.             if(a[i] == k)  
  5.                 return i;  
  6.         return -1;  
  7. }  

   它的三种时间复杂性计算如下:

   

    一般来说,对于非递归算法的复杂性分析,有以下几个基本法则:

  (1)for/while循环:循环体内计算时间*循环次数;

  (2)嵌套循环:循环体内计算时间*所有循环次数;

  (3)顺序语句:个语句计算时间相加;

 (4)if-else语句:if语句计算时间和else语句计算时间的较大者。

 2. 插入排序算法

    再举一个例子,比如插入排序算法的代码如下:

[cpp]  view plain  copy
 print ?
  1. template<class type>  
  2. void insertion_sort(Type *a, int n){  
  3.     Type key;  
  4.     for(int i = 1; i < n; i++){  
  5.         key = a[i];  
  6.         int j = i-1;  
  7.         while(j >= 0 && a[j] > key){  
  8.             a[j+1] = a[j];  
  9.             j--;  
  10.         }     
  11.         a[j+1] = key;  
  12.     }     
  13. }  

    下图可以很形象地描述插入这一过程(左手总是持有排好序的牌,右手每次启一张新牌插入):

     时间复杂度计算如下:

    

三、递归算法实例

1. 递归与分治

     直接或者间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数。有些数据结构,如二叉树等,由于其本身固有的递归特性,特别适合用递归的形式来描述,许多有用的算法在结构上都是递归的,这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。原问题与子问题唯一的区别就是输入规模不同。

   注:迭代与递归的区别,迭代是指循环的过程,递归是指函数不断调用自身的过程。

   例如,阶乘函数递归求解代码为:

[cpp]  view plain  copy
 print ?
  1. int factorial(int n){  
  2.     if (n == 0)  
  3.         return 1;  
  4.     return n*factorial(n-1);  
  5. }  

     复杂性分析:

   

   很简单的就能得出 T(n) = n。 

   一般来说分治模式在每层递归时都有三个步骤:

   1. 分解原问题为若干子问题,这些字问题是原问题的规模较小的实例。

   2. 解决这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。

   3. 合并这些子问题的解成原问题的解。

2. 归并排序算法

     归并排序算法完全遵循分治模式。直观上其操作如下:

   1. 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。

   2. 解决:使用归并排序递归地排序两个子序列。

   3. 合并:合并两个已排序的子序列以产生已排序的答案。

   边界条件为,当待排序的问题规模为1时,递归开始“回升”,这种情况下不要做任何工作,因为长度为1的每个序列都已排好序。

   归并排序算法步骤如下:

     

     其中,合并步骤的伪代码如下(设置哨兵):

   

     子序列排序步骤的伪代码如下:

    

     由下图可得出,归并排序中,合并两个已排序子序列这一步的复杂度为线性时间 O(n):

     复杂度分析:

     

   当 时,我们通常不陈述足够小n下的基础情况,但是要求对迭代的渐进解没有影响。

   得到递推公式后,我们使用递归树的方法来求T(n)的上界。

   也即求解T(n) = 2T(n/2) + cn,这里c > 0 是常数:

     由上图可以看出,整个递归树高度为logn,总共有(log n + 1)层,每层结点复杂度之和为cn,总代价就是cn logn + cn,忽略低阶项和常量c,所以总的归并算法复杂度就是

   下图说明了当n为2的幂时,归并排序在数组A = {5,2,4,7,1,3,2,6}上的操作,随着算法自底向上地推进,待合并的已排好序的各序列的长度不断增加。

3. 插入排序和归并排序算法的比较

     从前文关于最优算法的定义可以得出,因为的增长速度慢于,所以在最坏情况下,归并排序算法渐进意义上优于插入排序算法。但是,在实际应用中,通常是在 n>30 左右后归并排序才开始打败插入排序!

4. 代码实现和论证

     为此,我用c++实现了插入排序和归并排序算法,并进行了对比,实验结果论证了上述结论,代码如下:

[cpp]  view plain  copy
 print ?
  1. #include <iostream>  
  2. #include <fstream>  
  3. #include <stdlib.h>  
  4. #include <time.h>  
  5. #include <assert.h>  
  6.   
  7. using namespace std;  
  8.   
  9. template<typename T>//插入排序  
  10. void InsertSort(T* ptr, int length){  
  11.     typedef T type;  
  12.     typedef type* ptr_type;  
  13.   
  14.     type key;  
  15.   
  16.     for(int i = 1; i < length; i++){  
  17.         key = ptr[i];  
  18.         int j = i-1;  
  19.         while(j>=0 && ptr[j]>key){  
  20.             ptr[j+1] = ptr[j];  
  21.             j--;  
  22.         }  
  23.         ptr[j+1] = key;  
  24.     }  
  25. }  
  26.   
  27. template<typename T>//归并排序合并操作  
  28. void Merge(T* ptr, int begin, int mid, int end){  
  29.     typedef T type;  
  30.     typedef type* ptr_type;  
  31.   
  32.     /*使用哨兵位*/  
  33.     type imax = std::numeric_limits<type>::max();//哨兵牌  
  34.     int lsize = mid - begin +1;//左堆牌数  
  35.     int rsize = end - mid;//右堆牌数  
  36.     ptr_type left = new type[lsize+1];//增加哨兵牌的新左堆  
  37.     ptr_type right = new type[rsize+1];//增加哨兵牌的新右堆  
  38.     for (int i = 0; i < lsize; i++)//原左堆牌放入新左堆  
  39.         left[i] = ptr[begin+i];  
  40.     left[lsize] = imax;//新左堆最下面放置哨兵牌  
  41.     for (int i = 0; i < rsize; i++)//右堆同理  
  42.         right[i] = ptr[mid+1+i];  
  43.     right[rsize] = imax;  
  44.   
  45.     /*比较新左右堆顶牌大小,较小者放入新堆ptr[]中*/  
  46.     int i = 0, j = 0;  
  47.     for (int k = begin; k <= end; k++){  
  48.         if (left[i] <= right[j])  
  49.             ptr[k] = left[i++];  
  50.         else  
  51.             ptr[k] = right[j++];  
  52.     }  
  53.   
  54.     delete[] left;  
  55.     left = NULL;  
  56.     delete[] right;  
  57.     right = NULL;  
  58.   
  59. }  
  60.   
  61. template<typename T>//归并排序子序列排序操作  
  62. void MergeSort(T* ptr, int begin, int end){  
  63.     if (begin >= end)  
  64.         return;  
  65.     int mid = (begin+end)/2;  
  66.     MergeSort(ptr, begin, mid);  
  67.     MergeSort(ptr, mid+1, end);  
  68.     Merge(ptr, begin, mid, end);  
  69. }  
  70.   
  71. int main()  
  72. {  
  73.     const int TEST_NUM = 100;//测试数组大小  
  74.   
  75.     ofstream fout;  
  76.     fout.open("SortResult.txt", ios::out|ios::app);  
  77.     if (fout.fail()){  
  78.         cout<<"can't open SortResult.txt"<<endl;  
  79.         getchar();  
  80.         return 0;  
  81.     }  
  82.   
  83.     srand((int)time(NULL));//以当前时间对应的int值为随机序列起点  
  84.   
  85.     fout<<"Test Array Number:\t"<<TEST_NUM<<endl;  
  86.   
  87.     int* testPtr1 = new int [TEST_NUM];  
  88.     int* testPtr2 = new int [TEST_NUM];  
  89.     for (int i = 0; i < TEST_NUM; i++)  
  90.         testPtr1[i] = testPtr2[i] = rand();//产生随机数  
  91.   
  92.     clock_t t = clock();//当前时间  
  93.     InsertSort(testPtr1, TEST_NUM);  
  94.     fout<<"Insert Sort Time:\t"<<clock()-t<<endl;  
  95.   
  96.     t = clock();  
  97.     MergeSort(testPtr2, 0, TEST_NUM-1);  
  98.     fout<<"Merge Sort Time:\t"<<clock()-t<<endl;  
  99.   
  100.     for (int i = 0; i < 10; i++)//判断是否正确排序  
  101.         assert(testPtr1[i] == testPtr2[i]);  
  102.   
  103.     cout<<"sort done!"<<endl;  
  104.   
  105.     delete[] testPtr1;  
  106.     testPtr1 = NULL;  
  107.     delete[] testPtr2;  
  108.     testPtr2 = NULL;  
  109.   
  110.     fout.close();  
  111.     return 0;  
  112. }  

     实验对比结果:

     从实验代码和以上算法分析可以总结出如下结论:

  (1)插入排序算法时间复杂度是O(n^2),空间复杂度是1。

  (2)归并排序算法时间复杂度是O(nlogn),空间复杂度是n。

四、递归算法的复杂性分析

     接下来,将递归算法分析的三种求解渐近紧确界方法,归纳如下:

1.代入法

     实质上就是数学归纳法,先对一个小的值做假设,然后推测更大的值得正确性。由于是数学归纳法,那么我们就需要对值进行猜测。

   代入法求解递归式一般分为两步:

  (1)猜测解的形式。

  (2)验证解的形式,并计算常数。

   优势:将归纳假设应用于较小的值时,我们将猜测的解带入函数,单凭经验直接猜出复杂度,有没有相当强大?

   劣势:并不存在通用的方法来猜测递归式的正确解,很有可能猜错。

   注意:代入法的证明过程中,要严格按照完整定义给出进行证明,不可以使用渐进表达法!

   示例一

    

     

    (经验:在证明过程中,当常数无法获取时,通常是减去一个低阶项而不是增加。)

   示例二

   针对T(n) = 2T(n/2)+n,我们猜测T(n) = O(nlogn),有:

   

   故假设成立,T(n) = O(nlogn)。

2. 递归树法

     前提:当声明、求解递归式时,我们常常忽略向下取整、向上取整及边界条件。

   虽然你可以用代入法简洁地证明一个解确是递归式的正确解,但想出一个好的猜测可能会很困难。画出递归树,如我们前面分析归并排序的递归式时所做的那样,是设计好的猜测的一种简单而直接的方法。递归树模型刻画了一个算法在递归执行时的时间消耗。

   在递归树中,每个结点表示一个单一子问题的代价,子问题对应某次递归函数调用。我们将树中每层中的代价求和,得到每层代价,然后将所有层的代价求和,得到所有层次的递归调用的总代价。所以,我们利用递归树求解代价,需要知道什么呢,一个是每一层的代价,一个是层数,就是这两个。这个模式一般是典型的等差或等比级数。

   利用递归树方法求算法复杂度我想最好的例子就是归并排序了,但是前面第二部分已经做了说明,这里用课堂上的一个更形象的例子来说明:

   对递归方程:

   有如下递归树:

     于是:

   

  (因为等比数列的比例因子)。

   注解:

   当出现等比数列,整个递推式的量级由最大项决定,

   

3. 主定理

     主定理方法应用于如下的递归形式:

   

   为了使用主定理,需要牢记三种情况,但随后你就可以很容易地求解很多递归式,通常不需要纸和笔的帮助!

     

    理解:将n规模的问题拆分成a个n/b规模的问题,并且剩下的操作为f(n)。

   主定理主要来自递归树方法,如果我们画T(n)的递归树T(n)=aT(n/b)+f(n),我们会发现根节点的值为f(n),所有的叶子节点的和为,递归树的高度为。(求解方法为,得出迭代次数(树高度)为,叶子节点有个,又)。

   

      在递归树方法中,我们计算所有节点的和。

   (1)如果在叶子节点的值是多项式的,那么叶子是占主导地位的一部分,而我们的结果变成叶子节点的值(主定理情况1)。

   (2)如果叶和根是渐近一样的,那么结果就变成树高度乘以所有层的和(主定理情况2)。

   (3)如果根节点的值是渐近多,那么我们的结果变成在根节点的值(主定理情况3)。

    示例:

     注意:

     

五、总结

     我们分别对普通算法和递归算法进行了复杂度分析,但在最后我还是要强调一点,就是我们在分析之前做出了两个假设:

   1. 输入规模为n。

   2. 我们是按照最坏情况来分析算法的时间复杂度。

   上面重点描述了三种求解递归式的方法,即得出算法的渐进界的方法:

  (1)代入法 我们猜测一个界,然后用数学归纳法证明这个界是正确的。

  (2)递归树法 将递归式转换为一棵树,其结点表示不同层次的递归调用产生的代价。然后采用边界和技术来求解递归式。

  (3)主方法 可求解形如下面公式的递归式的界:T(n) = aT(n/b) + f(n),其中,是一个给定的函数。这种形式的递归式很常见,它刻画了这样一个分治方法:生成a个子问题,每个子问题的规模是原问题规模的1/b,分解和合并步骤共花费时间为f(n)。为了使用主方法,必须要熟记三种情况,但是一旦你掌握了这种方法,确定很多简单递归式的渐进界就变得很容易。

微笑 好了,这部分内容就说到这,希望对读者有所帮助。在后一篇博文中,我还会介绍一种摊还分析方法,来评价算法操作的代价

转载请声明出处:http://blog.csdn.net/zhongkelee/article/details/44490315

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Data Structures, Algorithms, and Applications in C++, Second Edition 出版者的话 译者序 前言 第一部分 预备知识 第1章 C++回顾 1.1 引言 1.2 函数与参数 1.2.1 传值参数 1.2.2 模板函数 1.2.3 引用参数 1.2.4 常量引用参数 1.2.5 返回值 1.2.6 重载函数 1.3 异常 1.3.1 抛出异常 1.3.2 处理异常 1.4 动态存储空间分配 1.4.1 操作符new 1.4.2 一维数组 1.4.3 异常处理 1.4.4 操作符delete 1.4.5 二维数组 1.5 自有数据类型 1.5.1 类currency 1.5.2 一种不同的描述方法 1.5.3 操作符重载 1.5.4 友元和保护性类成员 1.5.5 增加#ifndef、#define和#endif语句 1.6 异常类illegalParameterValue 1.7 递归函数 1.7.1 递归的数学函数 1.7.2 归纳 1.7.3 C++递归函数 1.8 标准模板库 1.9 测试与调试 1.9.1 什么是测试 1.9.2 测试数据的设计 1.9.3 调试 1.10 参考及推荐读物 第2章 程序性能分析 2.1 什么是程序性能 2.2 空间复杂度 2.2.1 空间复杂度的组成 2.2.2 举例 2.3 时间复杂度 2.3.1 时间复杂度的组成 2.3.2 操作计数 2.3.3 最好、最坏和平均操作计数 2.3.4 步数 第3章 渐近记法 3.1 引言 3.2 渐近记法 3.2.1 大Ο记法 3.2.2 渐近记法Ω和Θ 3.3 渐近数学(可选) 3.3.1 大O记法 3.3.2 Ω记法 3.3.3 Θ记法 3.3.4 小ο记法 3.3.5 特性 3.4 复杂度分析举例 3.5 实际复杂度 3.6 参考及推荐读物 第4章 性能测量 4.1 引言 4.2 选择实例的大小 4.3 设计测试数据 4.4 实验设计 4.5 高速缓存 4.5.1 简单计算机模型 4.5.2 缓存未命中对运行时间的影响 4.5.3 矩阵乘法 4.6 参考及推荐读物 第二部分 数据结构 第5章 线性表——数组描述 5.1 数据对象和数据结构 5.2 线性表数据结构 5.2.1 抽象数据类型linearList 5.2.2 抽象类linearList 5.3 数组描述 5.3.1 描述 5.3.2 变长一维数组 5.3.3 类arrayList 5.3.4 C++迭代器 5.3.5 arrayList的一个迭代器 5.4 vector的描述 5.5 在一个数组中实现的多重表 5.6 性能测量 5.7 参考及推荐读物 第6章 线性表——链式描述 6.1 单向链表 6.1.1 描述 6.1.2 结构chainNode 6.1.3 类chain 6.1.4 抽象数据类型linearList的扩充 6.1.5 类extendedChain 6.1.6 性能测量 6.2 循环链表和头节点 6.3 双向链表 6.4 链表用到的词汇表 6.5 应用 6.5.1 箱子排序 6.5.2 基数排序 6.5.3 凸包 6.5.4 并查集 第7章 数组和矩阵 7.1 数组 7.1.1 抽象数据类型 7.1.2 C++数组的索引 7.1.3 行主映射和列主映射 7.1.4 用数组的数组来描述 7.1.5 行主描述和列主描述 7.1.6 不规则二维数组 7.2 矩阵 7.2.1 定义和操作 7.2.2 类matrix 7.3 特殊矩阵 7.3.1 定义和应用 7.3.2 对角矩阵 7.3.3 三对角矩阵 7.3.4 三角矩阵 7.3.5 对称矩阵 7.4 稀疏矩阵 7.4.1 基本概念 7.4.2 用单个线性表描述 7.4.3 用多个线性表描述 7.4.4 性能测量 第8章 栈 8.1 定义和应用 8.2 抽象数据类型 8.3 数组描述 8.3.1 作为一个派生类实现 8.3.2 类arrayStack 8.3.3 性能测量 8.4 链表描述 8.4.1 类derivedLinkedStack 8.4.2 类linkedStack 8.4.3 性能测量 8.5 应用 8.5.1 括号匹配 8.5.2 汉诺塔 8.5.3 列车车厢重排 8.5.4 开关盒布线 8.5.5 离线等价类问题 8.5.6 迷宫老鼠 8.6 参考及推荐读物 第9章 队列 9.1 定义和应用 9.2 抽象数据类型 9.3 数组描述 9.3.1 描述 9.3.2 类arrayQueue 9.4 链表描述 9.5 应用 9.5.1 列车车厢重排 9.5.2 电路布线 9.5.3 图元识别 9.5.4 工厂仿真 9.6 参考及推荐读物 第10章
一、本书的内容 目前,市面上有关计算机算法的书很多,有些叙述严谨但不全面,另外一些则是容量很大但不够严谨。本书将叙述的严谨性以及内容的深度和广度有机地结合了起来。第1版推出后,即在世界范围内受到了广泛的欢迎,被各高等院校用作多种课程的教材和业界的标准参考资料。它深入浅出地介绍了大量的算法及相关的数据结构,以及用于解决一些复杂计算问题的高级策略(如动态规划、贪心算法、平摊分析等),重点在于算法的分析和设计。对于每一个专题,作者都试图提供目前最新的研究成果及样例解答,并通过清晰的图示来说明算法的执行过程。. 本书是原书的第2版,在第1版的基础之上增加了一些新的内容,涉及算法的作用、概率分析和随机化算法、线性规划,以及对第1版中详尽的、几乎涉及到每一小节的修订。这些修订看似细微,实际上非常重要。书中引入了“循环不变式”,并贯穿始终地用来证明算法的正确性。在不改动数学和分析重点的前提下,作者将第1版中的许多数学基础知识从第一部分移到了附录中。 二、本书的特点 本书在进行算法分析的过程中,保持了很好的数学严谨性。书中的分析和设计可以被具有各种水平的读者所理解。相对来说,每一章都可以作为一个相对独立的单元来教授或学习。书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如人类基因项目)、商业和工程等领域。每一小节通常以对相关历史素材的讨论结束,讨论了在每一算法领域的原创研究。 本书的特点可以概括为以下几个方面: 1.概念清晰,广度、深度兼顾。 本书收集了现代计算机常用的数据结构和算法,并作了系统而深入的介绍。对涉及的概念和背景知识都作了清晰的阐述,有关的定理给出了完整的证明。 2.“五个一”的描述方法。 本书以相当的深度介绍了许多常用的数据结构和有效的算法。编写上采用了“五个一”,即一章介绍一个算法、一种设计技术、一个应用领域和一个相关话题。.. 3.图文并茂,可读性强。 书中的算法均以通俗易懂的语言进行说明,并采用了大量插图来说明算法是如何工作的,易于理解。 4.算法的“伪代码”形式简明实用。 书中的算法均以非常简明的“伪代码”形式来设计,可以很容易地把它转化为计算机程序,直接应用。 注重算法设计的效率,对所有的算法进行了仔细、精确的运行时间分析,有利于进一步改进算法。 三、本书的用法 本书对内容进行了精心的设计和安排,尽可能考虑到所有水平的读者。即使是初学计算机算法的人,也可以在本书中找到所需的材料。 每一章都是独立的,读者只需将注意力集中到最感兴趣的章节阅读。 1.适合作为教材或教学参考书。 本书兼顾通用性与系统性,覆盖了许多方面的内容。本书不但阐述通俗、严谨,而且提供了大量练习和思考题。针对每一节的内容,都给出了数量和难度不等的练习题。练习题用于考察对基本内容的掌握程度,思考题有一定的难度,需进行精心的研究,有时还通过思考题介绍一些新的知识。 前言回到顶部↑本书提供了对当代计算机算法研究的一个全面、综合性的介绍。书中给出了多个算法,并对它们进行了较为深入的分析,使得这些算法的设计和分析易于被各个层次的读者所理解。力求在不牺牲分析的深度和数学严密性的前提下,给出深入浅出的说明。. 书中每一章都给出了一个算法、一种算法设计技术、一个应用领域或一个相关的主题。算法是用英语和一种“伪代码”来描述的,任何有一点程序设计经验的人都能看得懂。书中给出了230多幅图,说明各个算法的工作过程。我们强调将算法的效率作为一种设计标准,对书中的所有算法,都给出了关于其运行时间的详细分析。 本书主要供本科生和研究生的算法或数据结构课程使用。因为书中讨论了算法设计中的工程问题及其数学性质,因此,本书也可以供专业技术人员自学之用。 本书是第2版。在这个版本里,我们对全书进行了更新。所做的改动从新增了若干章,到个别语句的改写。 致使用本书的教师 本书的设计目标是全面、适用于多种用途。它可用于若干课程,从本科生的数据结构课程到研究生的算法课程。由于书中给出的内容比较多,只讲一学期一般讲不完,因此,教师们应该将本书看成是一种“缓存区”或“瑞典式自助餐”,从中挑选出能最好地支持自己希望教授的课程的内容。 教师们会发现,要围绕自己所需的各个章节来组织课程是比较容易的。书中的各章都是相对独立的,因此,你不必担心意想不到的或不必要的各章之间的依赖关系。每一章都是以节为单位,内容由易到难。如果将本书用于本科生的课程,可以选用每一章的前面几节内容;在研究生课程中,则可以完整地讲授每一章。 全书包含920多个练习题和140多个思考题。每一节结束时给出练习题,每一章结束时给出一些
尽管人工智能 (AI) 通常与执行惊人壮举的拟人计算机相关联,就像在《黑客帝国》和《少数派报告》这样的电影中一样,但人工智能实际上是一个更广泛的研究领域,其结果并不总是令人兴奋。 那么什么是人工智能? 基本上,人工智能涉及对自动化人类智能的研究。 这包括面向实践的研究,例如构建执行需要人类智能的任务的计算机应用程序,以及基础研究,例如确定如何以计算机可理解的形式表示知识。 一方面是人工智能,另一方面是法律的交叉点是一个致力于将先进计算机技术用于法律目的的领域:人工智能与法律。 本文应用作者在 AI 和法律(谈判决策支持系统和对话论证工具)方面的研究,构建在线争议解决 (ODR) 模型。 在考虑我们的综合 ODR 环境背后的原则和理论时,我们首先评估了最佳解决在线争议的顺序。 我们建议的系统符合以下顺序,我们认为这会产生最有效的 ODR 环境:1) 首先,如果谈判失败,谈判支持工具应提供有关争议可能结果的反馈 -即,“谈判协议的最佳替代方案”(BATNA)。 2) 其次,该工具应尝试使用对话技术解决任何现有冲突。 3) 第三,对于在第二步中没有解决的问题,该工具应采用补偿/权衡策略以促进争议的解决。 4) 最后,如果各方不能接受第三步的结果,则该工具应允许各方返回到第二步并递归重复该过程,直到解决争议或出现僵局。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值