编程题四大算法思想(一)——分治法:最大子数组问题、矩阵乘法的Strassen算法、最近点对问题、凸包问题

news2025/1/15 6:50:50

文章目录

  • 分治法
            • 分治策略
            • 分治算法的效率分析
            • 归并排序
  • 具体应用问题
    • ==最大子数组问题==
      • 蛮力法
      • 分治法
            • 找跨越中间位置的最大子数组
            • 时间复杂度
    • 矩阵乘法
      • 蛮力算法
      • 分治法
      • Strassen矩阵乘法

分治法

  • 方法
    • 分治策略
    • 分治法效率分析——迭代法(递归树法)
    • 分治法效率分析——主定理方法

注:这两个效率分析的方法,详见另一篇博客“数据结构与算法(一)——绪论”。

https://blog.csdn.net/weixin_44421143/article/details/132193847

  • 问题

    • 最大子数组问题

    • 矩阵乘法的Srassen算法

    • 最近点对问题

    • 凸包问题

分治策略
  • 分治法思想

  将原问题分成n个规模较小而结构与原问题相似的子问题,递归地解这些子问题,然后合并其结果就得到原问题的解。

  • 分而治之(divide - and - conquer)

    • 分解(Divide)

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

    • 解决(Conquer)

      递归地求解各子问题。若子问题足够小,则直接求解。

    • 合并(Combine)

      将子问题的解合并成原问题的解。

      关键在于合并处理。

分治算法的效率分析
  • 递归式分析分治算法的运行时间。
  • 一个递归式是一个函数,它由一个或多个基本情况(base case),它自身,以及小参数组成。
  • 递归式的解可以用来近似算法的运行时间。
image-20230829162635357

T ( n ) T(n) T(n)本身表示的就是时间代价。对于函数 T ( n ) T(n) T(n)而言,它的时间代价是基于 T ( n − 1 ) T(n-1) T(n1)的基础之上的,但是加 1 1 1

为什么要加1,因为它是在 T ( n − 1 ) T(n-1) T(n1)所需要花费的时间代价之外还需多执行一个乘法操作。

这就形成了一个递归关系式。

递归关系式体现的是,我的大问题,和小问题,之间的关系。

image-20230829162917661

那我知道 T ( n ) = T ( n − 1 ) + 1 T(n)=T(n-1)+1 T(n)=T(n1)+1,但是 T ( n − 1 ) T(n-1) T(n1)我也不知道啊,我还是不知道啊?

你一直往下递推,一直递归到 T ( 1 ) T(1) T(1)。最后 T ( n ) T(n) T(n)具体的时间代价不就求出来了。

归并排序

image-20230829163331805

  初始序列我不断地分成两个子问题。直到分解到最后,每个子序列里只剩一个了。

  然后开始合并,两两合并。

具体应用问题

最大子数组问题

问题:

  • 输入:数值数组A[1..n]
    • 假设数组中存在负数
    • 如果数组中全是非负数,该问题很简单。
  • 输出:数组下标ij使得子数组A[i..j]A[1..n]的和最大的非空连续子数组。

啥意思呢?

  • 考虑下面的情景:

    • 一只股票连续n天的交易价格。
    • 什么时候该买入?什么时候该卖出?
  • 这个问题就可以转换成最大子数组问题。

定义:A[i] = (第i天的价格) - (第i-1天的价格)

即,我今天的价格,比昨天涨了多少(正数)、跌了多少(负数)。

  • 如果最大子数组是A[i..j]
    • 第i天买入
    • 第j天卖出

即,我到底从哪天买入、到哪天卖出,我从而能够赚的最多。


例子1

  • 一支股票连续n天的交易价格:
image-20230829165525358

看右边的表格,它的最后一行,就对应于整个数组A[1..n]

什么是最大子数组,就是,我到底是哪天买入(i)、哪天卖出(j),才是最赚的情况,对应于最大子数组A[i..j]

进一步想一下,“最赚的”是啥意思。——就是最终卖出价格与最初买入价格之间的差最大。——或者说,是A[i]+...+A[j]这些数求和为最大值。

  • 对于这个例子,其最大子数组为A[3..3]。即第三天当天买入,当天卖出。

例子2

  • 一支股票连续n天的交易价格:
image-20230829165952168
  • 最大子数组是A[8..11]

咋看出来的??——若从图像上直观来看,可以理解为,图像的某一个极小值点与某一个极大值点之间的差为最大的情况(前提:极小值点的选取要从时间上位于极大值点前)。所以从上图,若直观的观察,也能观察出,第7天对应的一个很低价买入,第11天对应的很高价卖出,获利会是最大了。——为啥数组是A[8],这有可能是因为,我们具体的数组存放,和我们所谓的第7天,它不是完全对照的,它可能是有个对应关系。

关键就看计算机咋实现了。

我们在解决问题的时候,一般都是从蛮力法开始。我们不会一上来就想象出来一种很精妙的办法。

蛮力法

  你不是有n个元素么,我不就是要找出最大差值么?

image-20230829173025843

  那我就从第一个元素开始:A[1..1]是多少,A[1..2]是多少,A[1..3]是多少,……A[1..n]是多少。——n次

  然后从第二个元素开始:A[2..2]A[2..3]A[2..4],……,A[2..n]。——n-1次

  …………

  从第n-1个元素开始:A[n-1..n-1]A[n-1..n]。——2次

  从第n个元素开始:A[n..n]。——1次

  蛮力法是啥,我就是把所有情况列出来么。

  总共有 1 + 2 + . . . + n = n ( n + 1 ) 2 1+2+...+n=\frac{n(n+1)}{2} 1+2+...+n=2n(n+1)种可能性。

  但是,对于其中的操作,我们总是要做一个求和的操作。对于“从第一个元素开始”下的情况,我们对A[1..1],一直求到A[1..n],分别为1、2、3、…n个数求和。——我们对于n个数字,前1、2、…、n个数之和的求和,要获取这n个值,共需要进行 n 2 n^2 n2次的操作。而我们又不仅仅需要算“第一个元素开始”的情况,也要算其他的情况,所以总的时间代价还要再乘上 n n n,为 O ( n 3 ) O(n^3) O(n3)

  这个求和操作,能否优化一下?——求前缀和。

  实际上,你求完A[1..1]得到的和(第一个数字),之后你在算A[1..2]之和时(前两个数之和)就完全不必从头加起,而是由上一个的结果基础之上加一个数即可。因此,我们对于n个数字,获取前1、2、…、n个数之和,这n个值,只需要进行 n n n次的操作。因此,总共的算法为 O ( n 2 ) O(n^2) O(n2)


  我们既然蛮力法已经会求了——时间复杂度 O ( n 2 ) O(n^2) O(n2)

  那么我们再来看看,这个问题用分治法怎么求?

分治法

  分治法,同样也是个,n个元素的数组。

  但是,我对这个数组,我考虑的是,把它从中间分成两半。——三个位置: l o w , h i g h , m i d low,high,mid low,high,mid

image-20230829173305088

  我的一个初步的想法:左半边数组,找到它的最大子数组A[i1, j1],右半边数组它的最大子数组A[i2,j2]。然后再看看这两个最大子数组谁更大,最终就是整个数组的最大子数组了。

  但是,稍加思考,这个思路就明显有个问题:我的最大子数组凭什么就一定是从左半边某处起、左半边某处停止;或者右半边某处起、右半边某处止?——我完全有可能是横跨mid的情况啊。

image-20230829173638472

  所以对这个问题的分治法处理,还是有点麻烦的了。因为它除了对左右两边分别处理之外,还有一个对于“中间”处理的过程。


该问题的分治解法总结如下:

  • 子问题:找出 A [ l o w . . h i g h ] A[low..high] A[low..high]的最大子数组。
    • 参数初始值: l o w = 1 , h i g h = n . low=1,high=n. low=1,high=n.
    • 分解。将子数组分解成两个大小基本相同的子数组。
      • 找到子数组的中间位置 m i d mid mid,将子数组分成两个更小的子数组 A [ l o w . . m i d ] A[low..mid] A[low..mid] A [ m i d + 1.. h i g h ] A[mid+1..high] A[mid+1..high]
    • 求解。找数组 A [ l o w . . h i g h ] A[low..high] A[low..high] A [ m i d + 1.. h i g h ] A[mid+1..high] A[mid+1..high]的最大子数组。
    • 组合。找出跨越中间位置的最大子数组。
      • 三种情况取和最大的子数组(跨越中间位置的最大子数组和“求解”步骤中找到的那两个最大子数组)。

这时候会发现,问题在于,跨越中间位置的最大子数组咋找啊?

找跨越中间位置的最大子数组
  • 子数组必须跨越中间位置。
  • 解决思路:
    • 任何一个跨越中间位置 A [ m i d ] A[mid] A[mid]的子数组 A [ i . . j ] A[i..j] A[i..j]由两个更小的子数组 A [ i . . m i d ] A[i..mid] A[i..mid] A [ m i d + 1.. j ] A[mid+1..j] A[mid+1..j]组成,其中 l o w ≤ i ≤ m i d ≤ j ≤ h i g h low≤i≤mid≤j≤high lowimidjhigh
    • 只要找到最大子数组 A [ i . . m i d ] A[i..mid] A[i..mid] A [ m i d + 1.. j ] A[mid+1..j] A[mid+1..j],然后把它们合并。
    • 注意: m i d mid mid是固定的,左右分别扫描即可。这个问题可以用 θ ( n ) \theta(n) θ(n)的时间解决。

image-20230829174443320

(可能上面的说法还是有点抽象,下面形象一点来解释)

  实际上对于这一块,也是靠蛮力了。

   m i d mid mid是一个已经固定好的位置。我从 m i d mid mid出发,往左边找:我往左1个、往左2个、往左3个、……一直往左到 l o w low low的位置为止。我们找啥?——我们找,这所有情况里,哪种是最大的。往右边找是同理的。

image-20230829174921322

  对于这两边的蛮力找法,总共操作也就是 O ( n ) O(n) O(n)的时间。

image-20230829174443320

  这样一来,我们通过对以 m i d mid mid为终点,左边起点所有情况的遍历,找出最大的那一情况: A [ i . . m i d ] A[i..mid] A[i..mid]。通过对以 m i d mid mid为起点,右边终点所有情况的遍历,找出最大的那一情况: A [ m i d + 1.. j ] A[mid+1..j] A[mid+1..j]。把这两个连起来,就是一个跨越 m i d mid mid的最大子数组了。


时间复杂度

时间复杂度为: T ( n ) = 2 T ( n / 2 ) + O ( n ) T(n)=2T(n/2)+O(n) T(n)=2T(n/2)+O(n)

其中, O ( n ) O(n) O(n)是蛮力解决跨界子数组的时间。

中间的推理过程不写了,最后结果如下图所示。

image-20230829175843146

矩阵乘法

image-20230829180329324

  具体的矩阵乘法定义就不说了,学过线性代数的都知道。

蛮力算法

Matrix operator*(const Matrix &m, const Matrix &n) {
    if(m.l_size()!=n.h_size()) return Matrix();	//非法运算返回空矩阵
    
    Matrix ans(m.h_size(), n.l_size());
    
    for(int i=0; i!=ans.h_size(); ++i) {
        for(int j=0; j!=ans.l_size(); ++j) {
            for(int k=0; k!=m.l_size(); ++k) {
                ans[i][j] += m[i][k]*n[k][j];
            }
        }
    }
    return ans;
}

  通过看这个代码,或者根据矩阵乘法的定义来想一下,很容易能想到,蛮力法是 O ( n 3 ) O(n^3) O(n3)

  那既然蛮力法不好,那怎么写比较好呢?——还是分治呗。

分治法

  • 分治法:
    • 将矩阵 A A A B B B C C C中每一矩阵都分块成 4 4 4个大小相等的子矩阵。由此,可将方程 C = A B C=AB C=AB重写为:

image-20230829181025848

  由此可得:

image-20230829181039519

注意:只关心乘法的执行次数。

  我们把一个矩阵,分解成8个子矩阵的相乘问题。(只关心乘法操作)至于其他的操作,如相加操作,它们可视为一个常量时间 O ( 1 ) O(1) O(1)

由此可得: T ( n ) = 8 T ( n / 2 ) + O ( 1 ) T(n)=8T(n/2)+O(1) T(n)=8T(n/2)+O(1)


时间复杂度

  具体的推导过程不写了,最后得到,这种方法得到的时间复杂度为 T ( n ) = θ ( n 3 ) T(n)=\theta(n^3) T(n)=θ(n3)

  我们会发现,它跟蛮力法时间复杂度一样了,没改进啊。

  想一下,我们都用分治法了,为啥没有改进。因为乘8。——具体原因,看一看主定理分析时间复杂度那里,有个 l o g b a log_ba logba的问题。

Strassen矩阵乘法

  • 为了降低时间复杂度,必须减少乘法的次数。

image-20230829181816641

  对于矩阵的加法,我们可以理解为一个 O ( n ) O(n) O(n)时间的操作。

  他这个人研究的这个算法,为啥好?就是因为他只做了 7 7 7次矩阵乘法,其余的均为 O ( 1 ) O(1) O(1)的矩阵加法操作。

由此可得: T ( n ) = 7 T ( n / 2 ) + O ( 1 ) T(n)=7T(n/2)+O(1) T(n)=7T(n/2)+O(1)

具体推理过程不写了,最终得到它这个算法的时间复杂度 ≈ n 2.807 ≈n^{2.807} n2.807

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/944751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习JAVA打卡第四十六天

Date和Calendar类 Date类 ⑴使用无参数构造方法 使用Date 类的无参数构造方法创建的对象可以获取本机的当前日期和时间,例如: Date nowtime new Date(); ⑵使用带参数的构造方法 计算机系统将其自身的时间的设“…

redis实战-项目集成git及redis实现短信验证码登录

目录 IDEA集成git 传统session存在的问题 redis方案 业务流程 选用的数据结构 整体访问流程 发送短信验证码 获取校验验证码 配置登录拦截器 拦截器注册配置类 拦截器 用户状态刷新问题 刷新问题解决方案 IDEA集成git 远程仓库采用码云,创建好仓库&…

js定位到元素底部

文字的一行一行添加的&#xff0c;每次添加要滚动条自动定位到元素底部 <div class"An">//要父元素包裹&#xff0c;父元素设置max-height&#xff0c;overflow啥的<div class"friendly_pW"></div></div>//添加文字时找子元素的高…

数据管理,是企业管理的上帝视角

在早期&#xff0c;企业整体规模较小&#xff0c;市场以及产品也比较匮乏&#xff0c;用户对商品没有过多的需求&#xff0c;更多是出于对必需品的需要&#xff0c;选择购买商品。伴随着经济的飞速发展&#xff0c;企业规模在扩大的同时&#xff0c;市场和用户对商品的需求日益…

【pyqt5界面化工具开发-8】窗口开发-QDialog对话框

目录 一、调用父类的菜单 二、添加更多的布局在对话框内 一、调用父类的菜单 和前面Qwedget一样的结构&#xff08;不做过多介绍&#xff09; 可以参考代码中的注释 import sys from PyQt5.QtWidgets import QApplication, QPushButton, QDialog# 对话框&#xff08;多运用…

Windows服务器使用Mysqldump备份MySQL数据库方法

Windows服务器使用Mysqldump备份MySQL数据库方法 1.进入到MySQL安装目录的bin目录下&#xff0c;进入cmd F:\20220601\dev_software\mysql-8.0.11-winx64 2.执行备份命令&#xff1a; mysqldump -u root -p zj_bak test_bak -r D:\backup.sql3.导入备份 数据&#xff1a; m…

DDS器件原理与应用(AD9833 AD9834)

1、DDS模块的主要构成 DDS模块原理框图 2、DDS器件的工作原理 直接数字合成是生成模拟信号的一种常用方法&#xff0c;简单意义上的DDS&#xff0c;主要由相位调制器、波形查找表和DAC组成。相位调制器产生一个相位信息&#xff0c;使用该相位信息去波形查找表中查找对应的幅…

智慧安全3.0这两年:数智破冰 护航千行百业安全发展

数智化时代下&#xff0c;网络安全作为网络强国、数字中国的底座&#xff0c;是我国现代化产业发展中不可或缺的部分。在安全实战上&#xff0c;传统的基于边界防护&#xff0c;单次静态安全策略配置的安全措施已无法满足业务发展的安全需求&#xff0c;真正的安全防护必须是主…

如何使用敏捷开发方法管理项目

敏捷开发方法是一种灵活且高效的项目管理方法&#xff0c;旨在应对不断变化的需求和快速发展的项目环境。使用敏捷开发方法可以帮助团队更好地应对不确定性&#xff0c;提高项目的质量和效率。以下是使用敏捷开发方法管理项目的具体步骤&#xff1a; 明确项目目标和范围 在项…

2023 天府杯“ 室温超导+可再生能源应用国赛预测模拟专题赛“——参考代码答案与解析

室温超导材料在可再生能源传输中的应用研究 题目要求&#xff1a; 假设某国家有多个可再生能源发电站点&#xff0c;分布在不同的地理位置。每个发电站点产生的能量大小和时间段都有所不同。同时&#xff0c;假设已经研发出室温超导材料 LK-6&#xff0c;LK-66&#xff0c;L…

A卡崩溃有救了 AMD推出RGD专用工具:找出报错原因

现在的三大显卡GPU厂商中&#xff0c;谁家的显卡好争议不断&#xff0c;粉丝不仅争硬件性能&#xff0c;软件驱动也是容易被诟病的地方&#xff0c;AMD显卡这方面就吃过亏&#xff0c;好在AMD这次推出了一个RGD工具&#xff0c;可以找出显卡崩溃的原因。 AMD日前发布了一个Rade…

智慧高速领域数字孪生应用思考及实践

近些年&#xff0c;国家和相关部委陆续发布多项政策指导智慧公路建设&#xff0c;逐步制定相关建设标准规范&#xff0c;加快推动全国公路数字化、智能化、协同化升级。对于智慧高速的发展探索在这几年不停提出各类创新性的技术手段&#xff0c;并且进行了局部的试点和示范性应…

高等职业学校物联网实训室建设方案

一、概述 1.1专业背景 物联网&#xff08;Internet of Things&#xff09;被称为继计算机、互联网之后世界信息产业第三次浪潮&#xff0c;它并非一个全新的技术领域&#xff0c;而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升&#xff0c;是随着传感网、通…

..\SYSTEM\delay\delay.h(20): error: #18: expected a “)“

在KEIL中有时编译过程中会出现下面这样的错误。 明明代码里面并没有缺少小括号&#xff0c;但是就是找不到错误。 真正的原因是因为 在20行的地方形参 SYSCLK 与系统的中某个文件的宏名字相同导致这样的问题。 解决方案&#xff1a; 把函数的形参随便修改下就OK了。

危险的套娃:攻击者在 PDF 文件中隐藏恶意Word 文档

据BleepingComputer消息&#xff0c;日本计算机紧急响应小组 (JPCERT) 日前分享了在2023 年 7 月检测到的利用PDF文档的新型攻击——PDF MalDoc攻击&#xff0c;能将恶意 Word 文件嵌入 PDF 来绕过安全检测。 JPCERT采样了一种多格式文件&#xff0c;能被大多数扫描引擎和工具识…

详解JS 和CSS 代码利用率统计利器Coverage以及高版本chrome中coverage的一个致命问题

在做前端性能测试时较大的文件需要更多时间来下载&#xff0c;并可能导致我们的网站加载缓慢&#xff0c;从而导致用户体验欠佳。因此删除JS和CSS中未被使用的代码就很有必要了&#xff01;Coverage工具就可以非常方便的实现这一需求。 Coverage使用方法 Coverage 是chrome开…

已知两地经纬度,计算两地直线距离

文章目录 1 原理公式2 代码实现2.1 JavaScript2.2 C2.3 Python2.4 MATLAB 1 原理公式 在地球上&#xff0c;计算两点之间的直线距离通常使用地理坐标系&#xff08;例如WGS84&#xff09;。计算两地直线距离的公式是根据经纬度之间的大圆距离&#xff08;Great Circle Distanc…

C语言练习题解析:挑战与突破,开启编程新篇章!(1)

&#x1f493;博客主页&#xff1a;江池俊的博客⏩收录专栏&#xff1a;C语言刷题专栏&#x1f449;专栏推荐&#xff1a;✅C语言初阶之路 ✅C语言进阶之路&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐&a…

正则表达式 之 断言详解

正则表达式的先行断言和后行断言一共有 4 种形式&#xff1a; (?pattern) 零宽正向先行断言(zero-width positive lookahead assertion)(?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)(?<pattern) 零宽正向后行断言(zero-width positive lookb…

QML Book 学习基础5(An Image Viewer)

目录 桌面版&#xff08;win端&#xff09; 移动端 下面我们用更有挑战性例子来使用Qt控件&#xff0c;将创建一个简单的图像查看器。 桌面版&#xff08;win端&#xff09; 程序主要由四个主要区域组成&#xff0c;如下所示。菜单栏、工具栏和状态栏&#xff0c;通常由控件…