数据结构与算法-进阶(十九)分治

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法-进阶(十九)分治相关的知识,希望对你有一定的参考价值。

参考技术A


分治,可以理解为分而治之,核心逻辑就是将原问题分解成若干个小问题,原问题和小问题在结构上是一致的,唯一的区别就是规模不同。然后再将小问题分解出更小的问题,直到无法再分解为止,也就是可以直接或者简单的计算得出问题的答案。最后利用小问题推导出原问题的解。

这个和 递归的思想类似 ,所以分治策略非常适合使用递归来处理,需要特别注意的是,分治策略中的子问题都是相互独立的。

可以应用到分治策略的有快速排序、归并排序和大数乘法等。

分治策略通常遵循这样的通用模式,解决规模为 n 的问题,就分解成 a 个规模为 fracnb 的子问题,然后在 O(n^d) 时间内将子问题的解给合并起来。

总结下来,算法运算的时间 T(n) = aT(fracnb) + O(n^d),a > 0,b > 1,d geq 0。 继续简化运算时间的公式,有以下三个公式:

接下来通过求最大连续子序列和来进一步了解下分治策略。

当给定一个长度为 n 的整数序列,求它的最大连续子序列和,比如整数序列为 -2、1、-3、4、-1、2、1、-5、4,那么这个整数序列的最大连续子序列和是 4 + (-1) + 2 + 1 = 6。

那么最简单的实现,就是暴力求解,找出所有连续子序列的和,比较出最大的那个。如下代码所示,用三个 for 循环来找出所有的连续子序列,并比较出最大值。

看上面的代码,可以计算出空间复杂度是 O(1),时间复杂度是 O(n^3)。函数中可以继续优化,重复使用前面计算出的 sum 值,代码如下所示:

优化后的函数,它的空间复杂度是 O(n),时间复杂度减少为 O(n^2)。

现在用分治策略来求最大子序列和。先将序列均匀的分割成两个子序列 [begin, end) = [begin, mid) + [mid, end),mid = (begin + end) >> 1。

假设 [begin, end) 的最大子序列是 S[i, j),那么它就有三种可能情况:

当出现第3种情况时,继续往下分析:

所以用代码实现时,就是将这三种情况都给求解出来,然后比较这3种情况的解,其中最大的就是这个序列的连续子序列和。

先创建一个对外调用的函数,设置 nums 数组异常的处理,如下代码所示:

然后创建一个求连续子序列和的函数,传入 nums 的同时,也要传入 begin 和 end,在这个函数中要实现 S[i, j) 的三种情况,第一种情况用 maxSubArray(nums, begin, mid) 处理,第二种情况用 maxSubArray(nums, mid, end) 来处理。这种处理方式就是递归思想。

最后处理第三种情况,那么就要分别求解出 [i, mid) 和 [mid, j)中的连续子序列和,因为第三种情况是分布在 mid 左右,并且是连续的,所以这种情况的连续子序列的和就是 leftMax + rightMax。如下代码所示:

函数中最后的 return 后的代码就是比较这三种情况,并返回其中最大的值。因为是递归的方式,所以函数中一定要有结束递归的条件,if (end - begin < 2) return nums[begin]; 就是递归基。

通过分治策略求连续最大子序列和的空间复杂度是 O(logn),时间复杂度是 O(nlogn),低于暴力求解的复杂度。

Python进阶(三十九)-数据可视化の使用matplotlib进行绘图分析数据

#Python进阶(三十九)-数据可视化の使用matplotlib进行绘图分析数据
  matplotlib 是python最著名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件,嵌入GUI应用程序中。
  它的文档相当完备,并且 Gallery页面 中有上百幅缩略图,打开之后都有源程序。因此如果你需要绘制某种类型的图,只需要在这个页面中浏览/复制/粘贴一下,基本上都能搞定。
  在Linux下比较著名的数据图工具还有gnuplot,这个是免费的,Python有一个包可以调用gnuplot,但是语法比较不习惯,而且画图质量不高。
  而 Matplotlib则比较强:Matlab的语法、python语言、latex的画图质量(还可以使用内嵌的latex引擎绘制的数学公式)。
  matplotlib中的快速绘图的函数库可以通过如下语句载入:

import matplotlib.pyplot as plt

  matplotlib还提供了名为pylab的模块,其中包括了许多numpy和pyplot中常用的函数,方便用户快速进行计算和绘图,可以用于IPython中的快速交互式使用。
  接下来调用figure创建一个绘图对象,并且使它成为当前的绘图对象。

plt.figure(figsize=(8,4))

  也可以不创建绘图对象直接调用接下来的plot函数直接绘图,matplotlib会为我们自动创建一个绘图对象。如果需要同时绘制多幅图表的话,可以是给figure传递一个整数参数指定图标的序号,如果所指定序号的绘图对象已经存在的话,将不创建新的对象,而只是让它成为当前绘图对象。
  通过figsize参数可以指定绘图对象的宽度和高度,单位为英寸;dpi参数指定绘图对象的分辨率,即每英寸多少个像素,缺省值为80。因此本例中所创建的图表窗口的宽度为880 = 640像素。
  但是用工具栏中的保存按钮保存下来的png图像的大小是800
400像素。这是因为保存图表用的函数savefig使用不同的DPI配置,savefig函数也有一个dpi参数,如果不设置的话,将使用matplotlib配置文件中的配置,此配置可以通过如下语句进行查看:

import matplotlib
matplotlib.rcParams[savefig.dpi]

  下面的两行程序通过调用plot函数在当前的绘图对象中进行绘图:

plt.plot(years, price, 'b*')#,label=$cos(x^2)$)
plt.plot(years, price, 'r')

  plot函数的调用方式很灵活,第一句将x,y数组传递给plot之后,用关键字参数指定各种属性:

  • label : 给所绘制的曲线一个名字,此名字在图示(legend)中显示。只要在字符串前后添加$符号,matplotlib就会使用其内嵌的latex引擎绘制的数学公式。
  • color : 指定曲线的颜色
  • linewidth : 指定曲线的宽度

  第一句直接通过第三个参数b–指定曲线的颜色和线型,这个参数称为格式化参数,它能够通过一些易记的符号快速指定曲线的样式。其中b表示蓝色,–表示线型为虚线。
  在IPython中输入 plt.plot? 可以查看格式化字符串的详细配置。
  接下来通过一系列函数设置绘图对象的各个属性:

plt.xlabel(years(+2000))
plt.ylabel(housing average price(*2000 yuan))
plt.ylim(0, 15)
plt.title('line_regression & gradient decrease')
plt.legend()
  • xlabel : 设置X轴的文字
  • ylabel : 设置Y轴的文字
  • title : 设置图表的标题
  • ylim : 设置Y轴的范围
  • legend : 显示图示

  最后调用plt.show()显示出我们创建的所有绘图对象。
##配置属性
  matplotlib所绘制的图的每个组成部分都对应有一个对象,我们可以通过调用这些对象的属性设置方法set_*或者pyplot的属性设置函数setp设置其属性值。例如plot函数返回一个 matplotlib.lines.Line2D 对象的列表,下面的例子显示如何设置Line2D对象的属性:

import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 5, 0.1)
line, = plt.plot(x, x*x) # plot返回一个列表,通过line,获取其第一个元素
# 调用Line2D对象的set_*方法设置属性值
line.set_antialiased(False)
# 同时绘制sin和cos两条曲线,lines是一个有两个Line2D对象的列表
lines = plt.plot(x, np.sin(x), x, np.cos(x)) 
# 调用setp函数同时配置多个Line2D对象的多个属性值
plt.setp(lines, color=r, linewidth=2.0)

  这段例子中,通过调用Line2D对象line的set_antialiased方法,关闭对象的反锯齿效果。或者通过调用plt.setp函数配置多个Line2D对象的颜色和线宽属性。
  同样我们可以通过调用Line2D对象的get_*方法,或者plt.getp函数获取对象的属性值:

line.get_linewidth()
plt.getp(lines[0], color) # 返回color属性
plt.getp(lines[1]) # 输出全部属性
alpha = 1.0
animated = False
antialiased or aa = True
axes = Axes(0.125,0.1;0.775x0.8)

  注意getp函数只能对一个对象进行操作,它有两种用法:

  • 指定属性名:返回对象的指定属性的值
  • 不指定属性名:打印出对象的所有属性和其值

  matplotlib的整个图表为一个Figure对象,此对象在调用plt.figure函数时返回,我们也可以通过plt.gcf函数获取当前的绘图对象:

f = plt.gcf()
plt.getp(f)
alpha = 1.0
animated = False

  Figure对象有一个axes属性,其值为AxesSubplot对象的列表,每个AxesSubplot对象代表图表中的一个子图,前面所绘制的图表只包含一个子图,当前子图也可以通过plt.gca获得:

plt.getp(f, axes)
plt.gca()

  用plt.getp可以发现AxesSubplot对象有很多属性,例如它的lines属性为此子图所包括的 Line2D 对象列表:

alllines = plt.getp(plt.gca(), lines)
alllines[0] == line # 其中的第一条曲线就是最开始绘制的那条曲线

  通过这种方法我们可以很容易地查看对象的属性和它们之间的包含关系,找到需要配置的属性。

##配置文件
  绘制一幅图需要对许多对象的属性进行配置,例如颜色、字体、线型等等。我们在绘图时,并没有逐一对这些属性进行配置,许多都直接采用了matplotlib的缺省配置。
  matplotlib将这些缺省配置保存在一个名为“matplotlibrc”的配置文件中,通过修改配置文件,我们可以修改图表的缺省样式。配置文件的读入可以使用rc_params(),它返回一个配置字典;在matplotlib模块载入时会调用rc_params(),并把得到的配置字典保存到rcParams变量中;matplotlib将使用rcParams字典中的配置进行绘图;用户可以直接修改此字典中的配置,所做的改变会反映到此后创建的绘图元素。
##绘制多子图(快速绘图)
  Matplotlib 里的常用类的包含关系为 Figure -> Axes -> (Line2D, Text, etc.)一个Figure对象可以包含多个子图(Axes),在matplotlib中用Axes对象表示一个绘图区域,可以理解为子图。
  可以使用subplot()快速绘制包含多个子图的图表,它的调用形式如下:

subplot(numRows, numCols, plotNum)

  subplot将整个绘图区域等分为numRows行* numCols列个子区域,然后按照从左到右,从上到下的顺序对每个子区域进行编号,左上的子区域的编号为1。如果numRows,numCols和plotNum这三个数都小于10的话,可以把它们缩写为一个整数,例如subplot(323)和subplot(3,2,3)是相同的。subplot在plotNum指定的区域中创建一个轴对象。如果新创建的轴和之前创建的轴重叠的话,之前的轴将被删除。
subplot()返回它所创建的Axes对象,我们可以将它用变量保存起来,然后用sca()交替让它们成为当前Axes对象,并调用plot()在其中绘图。
##绘制多图表(快速绘图)
  如果需要同时绘制多幅图表,可以给figure()传递一个整数参数指定Figure对象的序号,如果序号所指定的Figure对象已经存在,将不创建新的对象,而只是让它成为当前的Figure对象。

import numpy as np
import matplotlib.pyplot as plt
plt.figure(1) # 创建图表1
plt.figure(2) # 创建图表2
ax1 = plt.subplot(211) # 在图表2中创建子图1
ax2 = plt.subplot(212) # 在图表2中创建子图2
x = np.linspace(0, 3, 100)
for i in xrange(5):
plt.figure(1)  #? # 选择图表1
plt.plot(x, np.exp(i*x/3))
plt.sca(ax1)   #? # 选择图表2的子图1
plt.plot(x, np.sin(i*x))
plt.sca(ax2)  # 选择图表2的子图2
plt.plot(x, np.cos(i*x))
plt.show()

##在图表中显示中文
  matplotlib的缺省配置文件中所使用的字体无法正确显示中文。为了让图表能正确显示中文,可以有几种解决方案。

  • 在程序中直接指定字体。
  • 在程序开头修改配置字典rcParams。
  • 修改配置文件。

  比较简便的方式是,中文字符串用unicode格式,例如:u’‘测试中文显示’’,代码文件编码使用utf-8 加上 # coding = utf-8。
  但以上方法只是解决了标题部分显示中文的问题,并未解决图例中文显示的问题。可采用修改配置字典的方式设置图例显示中文,代码如下:

# 指定中文字体
mpl.rcParams['font.sans-serif'] = ['SimHei'] #指定默认字体

  配置好配置字典之后即可实现图例显示中文。

![这里写图片描述](https://img-blog.csdnimg.cn/img_convert/b753f298918e8eff2f45fd7c6abeb162.png)   matplotlib API包含有三层,Artist层处理所有的高层结构,例如处理图表、文字和曲线等的绘制和布局。通常我们只和Artist打交道,而不需要关心底层的绘制细节。   直接使用Artists创建图表的标准流程如下:

以上是关于数据结构与算法-进阶(十九)分治的主要内容,如果未能解决你的问题,请参考以下文章

从程序员进阶到架构师,6大核心技能要领详解

408数据结构与算法—快速排序(十九)

408数据结构与算法—快速排序(十九)

Python进阶(三十九)-数据可视化の使用matplotlib进行绘图分析数据

大数据ClickHouse进阶(十九):ClickHouse字典的数据源

数据结构与算法——分治算法