算法设计:实验三动态规划法

news2024/9/20 0:22:06

【实验目的】

应用动态规划算法思想求解矩阵连乘的顺序问题。

【实验要求】         

应用动态规划算法的最优子结构性质和子问题重叠性质求解此问题。分析动态规划算法的基本思想,应用动态规划策略写出算法及相应的程序,求解此题。要读懂读透A[i,j],

A[1,n]=A[1,k] ×A[k+1,n],m[i][j],s[i][j]各式所表达的含义并正确加以应用。m[i][j]的递归定义:

要求完成:⑴算法描述⑵写出程序代码⑶完成调试⑷进行过程与结果分析。

【实验内容】

对于下列所描述的问题,给出相应的算法描述,并完成程序实现与时间复杂度的分析。该问题描述为:一般地,考虑矩阵A1,A2,… ,An的连乘积,它们的维数分别为d0,d1,…,dn,即Ai的维数为di-1×di  (1≤i≤n)。确定这n个矩阵的乘积结合次序,使所需的总乘法次数最少。对应于乘法次数最少的乘积结合次序为这n个矩阵的最优连乘积次序。按给定的一组测试数据对根据算法设计的程序进行调试:6个矩阵连乘积A=A1×A2×A3×A4×A5×A6,各矩阵的维数分别为:A1:10×20,A2:20×25,A3:25×15,A4:15×5,A5:5×10,A6:10×25。完成测试。

【算法思想及处理过程】

动态规划(Dynamic Programming,简称 DP)是一种通过将原问题分解为相互重叠的子问题,并通过对这些子问题的解进行存储和复用,从而避免重复计算,以提高算法效率的方法。其基本思想是利用子问题的最优解构建原问题的最优解,通过填表格或者使用递归加记忆化的方法来实现。

在矩阵连乘最优化问题中,动态规划的基本思想是:

问题拆解:

将原问题拆解为子问题。对于矩阵连乘问题,给定一系列矩阵 A1, A2, ..., An,每个矩阵 Ai 的维度为 di-1 x di,目标是找到一种矩阵相乘的顺序,使得总的乘法次数最少。这个问题可以拆解为子问题:对于任意子链 Ai x ... x Aj,找到其最优的连乘次序。

定义状态:

定义状态 m[i][j],表示矩阵链 Ai x ... x Aj 的最优乘法次数。同时定义 s[i][j],表示最优的分割点,即在哪里将链 Ai x ... x Aj 分割成两部分进行连乘。

递推关系:

通过递推关系式来计算 m[i][j] 的值,即:

其中 d_{i-1} x d_k x d_j 是将两部分子链 Ai x ... x Ak 和 Ak+1 x ... x Aj 连乘的乘法次数。

填表:

使用动态规划的方法填充表格 m[i][j] 和 s[i][j],首先填充长度为 1 的链(单个矩阵的情况),再逐步增加长度,直至填满整个表格。

构造解:

根据填表得到的 s[i][j],通过递归或迭代的方式构造出最优的连乘次序。

综上所述,动态规划解决矩阵连乘最优化问题的基本思路是利用子问题的最优解构建原问题的最优解,通过填表格并使用递推关系来计算最优解的值,最终通过构造解得到最优的矩阵连乘次序。

以下是确定最优解的流程图:

主要思路为计算m时只需要m的左边与下边的数组值,所以可以从下至上,从左至右计算。

以下是打印最优连乘矩阵次序函数流程图:

递归终止条件:

首先,递归函数 printOptimal 需要处理递归的终止条件。当 i == j 时,表示当前子链只有一个矩阵,此时直接打印该矩阵的名称即可,因为不需要再进行连乘,已经是最小单位了。

利用 s 数组确定分割点:

对于子链 Ai x ... x Aj,s[i][j] 记录了使得乘法次数最少的分割点 k。因此,在递归调用中,我们首先根据 s[i][j] 将当前子链分成两部分:

第一部分:从 i 到 s[i][j] 的子链

第二部分:从 s[i][j] + 1 到 j 的子链

这样可以保证递归地构建出最优的连乘次序。

递归调用:

在分割好子链之后,递归地调用 printOptimal 函数:

对于第一部分:调用 printOptimal(matrices, s, i, s[i][j])

对于第二部分:调用 printOptimal(matrices, s, s[i][j] + 1, j)

这样可以递归地将整个矩阵连乘问题拆解为更小规模的子问题,直到达到终止条件为止。

输出格式控制:

在递归调用结束后,根据递归的层级,添加括号和矩阵名称,以构建出最优的连乘次序表达式。

【程序代码】

#include <stdio.h>
#include <limits.h>
#include <string.h>

#define MAX_SIZE 100
#define MAX 9999999 

typedef struct {
    char name[20];
    int rows;
    int cols;
} Matrix;

// 计算最优连乘矩阵次序
void matrixOptimal(Matrix matrices[], int num, int m[MAX_SIZE][MAX_SIZE], int s[MAX_SIZE][MAX_SIZE]) {
    int i,j,k,l,flag;

    for (i = 1; i <= num; i++) {
        m[i][i] = 0;
    }
    
    // 计算子问题的最优解 
    
    for(j = 1; j <= num; j++){
    	for(i = j-1;i > 0; i--){
    		m[i][j] = MAX;
    		for (k = i; k < j; k++) {
	    		flag = m[i][k] + m[k+1][j] + matrices[i].rows * matrices[k].cols * matrices[j].cols;
	            if (flag < m[i][j]) {
	                m[i][j] = flag;
	                s[i][j] = k;
	            }
        	}
    	}
    }
    /*for (l = 2; l <= num; l++) {  // l 为连乘链的长度
        for (i = 1; i <= num - l + 1; i++) {
            j = i + l - 1;
            m[i][j] = MAX;
            for (k = i; k < j; k++) {
                flag = m[i][k] + m[k+1][j] + matrices[i].rows * matrices[k].cols * matrices[j].cols;
                if (flag < m[i][j]) {
                    m[i][j] = flag;
                    s[i][j] = k;
                }
            }
        }
    }*/
}

// 打印最优连乘矩阵次序
void printOptimal(Matrix matrices[], int s[MAX_SIZE][MAX_SIZE], int i, int j) {
    if (i == j) {
        printf("%s", matrices[i].name);
    } else {
        printf("(");
        printOptimal(matrices, s, i, s[i][j]);
        printOptimal(matrices, s, s[i][j] + 1, j);
        printf(")");
    }
}

int main() {
    Matrix matrices[MAX_SIZE];
    int num;
	int i,j;
    printf("输入矩阵个数:");
    scanf("%d", &num);
    printf("输入每个矩阵的名称和维度:\n");
    for (i = 1; i <= num; i++) {
        printf("矩阵 %d名称: ", i);
        scanf("%s", matrices[i].name);
        printf("维数: ");
        scanf("%d %d", &matrices[i].rows, &matrices[i].cols);
        // 对第一个矩阵不进行维度检查 
        if (i > 1) {
            if (matrices[i-1].cols != matrices[i].rows) {
                printf("错误:矩阵维度不匹配,请重新输入。\n");
                i--;
            }
        }
    }

    int m[MAX_SIZE][MAX_SIZE];  // 存储最小乘法次数
    int s[MAX_SIZE][MAX_SIZE];  // 存储最优分割点
    matrixOptimal(matrices, num, m, s);
    
    printf("------------------------------------");
    printf("\n数组 m:\n");
    for (i = 1; i <= num; i++) {
        for (j = 1; j <= num; j++) {
            printf("%d\t", m[i][j]);
        }
        printf("\n");
    }
    printf("\n数组 s:\n");
    for (i = 1; i <= num; i++) {
        for (j = 1; j <= num; j++) {
            printf("%d\t", s[i][j]);
        }
        printf("\n");
    }

	printf("------------------------------------\n");
    printf("所需的总乘法次数为:%d\n", m[1][num]);
    printf("最优连乘积次序为:");
    printOptimal(matrices, s, 1, num);
    printf("\n");

    return 0;
}

【运行结果】

【算法分析】

算法思路:

这个算法利用动态规划的思想解决矩阵连乘最优化问题。首先,通过 matrixOptimal 函数计算出最小乘法次数 m[i][j] 和最优分割点 s[i][j],然后通过 printOptimal 函数根据 s 数组打印出最优的连乘次序。

时间复杂度分析:

计算 m[i][j] 和 s[i][j] 的时间复杂度:

外层循环 for (j = 1; j <= num; j++) 循环次数为 num,内层循环 for (i = j-1; i > 0; i--) 的平均循环次数约为 num/2。内部的循环 for (k = i; k < j; k++) 的循环次数约为 j - i,因此计算 m[i][j] 和 s[i][j] 的时间复杂度大致为 O(num^3)。

打印最优连乘次序的时间复杂度:

printOptimal 函数的时间复杂度主要取决于矩阵链的长度 num,递归调用的次数是 O(num),因此其时间复杂度也是 O(num)。

总体时间复杂度:

综合考虑,整个算法的时间复杂度主要由计算 m[i][j] 和 s[i][j] 的过程决定,即 O(num^3)。其余部分包括输入矩阵信息和打印结果的时间复杂度较小,对总体复杂度影响较小。

因此,该算法的时间复杂度为 O(num^3),其中 num 是矩阵的个数。对于较大的矩阵个数,算法的执行时间会随着 num 的增加而增加。需要注意的是,在实际应用中,可以考虑优化算法以降低时间复杂度,例如通过记忆化搜索等方法减少重复计算,或者使用更高效的算法来解决矩阵连乘问题。

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

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

相关文章

stm32-SD卡实验

1. SD简介 SD卡&#xff0c;Secure Digital Card&#xff0c;称为安全数字卡&#xff08;安全数码卡&#xff09;。 SD卡系列主要有三种&#xff1a;SD卡(full size)、MiniSD卡和MicroSD卡&#xff08;原名 TF卡&#xff09;。 特点&#xff1a;容量大、高安全性、体积小、传…

threejs绘制带箭头的坐标系

效果图&#xff1a; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9319baf1e01946b8919490704e97532a.pn 实现思路&#xff1a; AxesHelper实现坐标系&#xff0c;但是是没有箭头的&#xff1b;这个对象会显示三个彩色的箭头&#xff0c;这些箭头分别表示x, y, z轴…

Android Studio Koala下载并安装,测试helloworld.

1、下载&#xff1a; 下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 2、滚动条拉到近最后&#xff0c;各个系统的下载地址&#xff1a; 3、下载完成以后&#xff0c;我们双击运行安装&#xff1a; 如果有路径要修改&#xff0c;则修改下就可以了&a…

直播平台直播API集成之twitch篇

前言&#xff1a;     本篇我们来介绍如何使用twitch的直播API创建直播。 准备工作&#xff1a; 1、你首先得有个twitch账号&#xff1b; 2、创建twitch应用&#xff0c;主要是给自己的应用取名并配置授权回调地址&#xff08;可配多个&#xff09;&#xff0c;如下图所示&…

Ae基础概念与界面讲解

目录 Ae软件的用途 Ae界面介绍 预设 界面介绍 首选项概念 导入与导出 Ae软件的用途 Ae是一款专业特效合成软件&#xff0c;通过对收集到的素材进行数字化的编辑组合到一起&#xff0c;进行艺术性的再加工后得到的最终作品。 Ae界面介绍 画面中最显眼的图标是新建合成&am…

RoboCat: A Self-Improving Generalist Agent for Robotic Manipulation

发表时间&#xff1a;22 Dec 2023 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4836882796542689281&noteId2413286807916664832 作者单位&#xff1a;Google DeepMind Motivation&#xff1a;受视觉和语言基础模型的最新进展的启发&#xff0c…

第三十九篇-TeslaP40+CosyVoice-安装

环境 系统&#xff1a;CentOS-7 CPU: 14C28T 内存&#xff1a;32G 显卡&#xff1a;Tesla P40 24G 驱动: 535 CUDA: 12.2克隆 git clone --recursive https://github.com/FunAudioLLM/CosyVoice.git cd CosyVoicegit submodule update --init --recursive下载 Matcha-TTS cd…

DWF 支持的 TON 链 Telegram 免费宠物游戏 Gatto_game,推出 “Paws Up! 世界锦标赛”

TON 链在这轮牛市里无疑是一匹脱缰的黑马&#xff0c;创造了一个又一个爆款&#xff0c;为持有者带来了不菲的收益。 Gatto_game 是一款 TON链 Tamagotchi 电子宠物风格的 P2E web3 游戏。可以通过喂养升级&#xff0c;参加比赛赚取 $TON 或者 $GTON &#xff0c;或许就是下一个…

四大名著改编的ip大作,一个巨亏2亿,一个狂赚20亿!选择决定成败!

最近讨论热度比较高的当属《红楼梦》和《西游记》了 胡玫导演的《红楼梦之金玉良缘》耗费了18年的心血&#xff0c;投资了2个多亿 却仅仅只有600万票房&#xff0c;还被网友调侃称“一黛不如一黛” 而由《西游记》改编的游戏《黑神话悟空》&#xff0c;研发10年投资6亿&…

如祺出行发布首份中期业绩,总收入增长13.6%

8月28日&#xff0c;如祺出行发布2024年中期业绩公告。这是如祺出行于7月10日在港交所主板上市后发布的首份业绩公告。 业绩公告显示&#xff0c;2024年上半年如祺出行收入实现10.37亿元&#xff08;单位&#xff1a;人民币&#xff0c;下同&#xff09;、同比增长13.6%&#x…

Avalonia与WPF开发时的差异总结

1.一个控件绑定到另外一个控件的属性 WPF: <TextBox Height"30" Width"100" x:Name"tb"></TextBox><TextBlock Text"{Binding ElementNametb,PathText}" ></TextBlock>Avalonia: <TextBox Height"3…

梯度下降法求解线性回归

文章目录 线性回归损失函数平均绝对误差&#xff08;MAE&#xff09;均方误差&#xff08;MSE&#xff09; 最小二乘法最小二乘法代数推导最小二乘法矩阵推导 线性回归 Python 实现线性回归 scikit-learn 实现 梯度下降法梯度下降法的原理 梯度下降法求解线性回归 线性回归 线…

Java SpringBoot实战:如何构建学生档案管理系统实现信息管理

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

【ocr识别003】动物检疫合格证明单据OCR识别应用案例(已更新)

1.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 2.了解、学习OCR相关技术知识领域,结合日常的场景进行测试、总结。如本文的实践:以动物检疫合格证明单据识别为例,探索OCR文本推理应用示例场景。 3.有其他场景问题,欢迎留言或加微沟通。 文章目录 1.简要介绍2.`O…

【Qt窗口】—— 状态栏

目录 1.1 状态栏的创建 1.2 在状态栏中显示实时消息 1.3 在状态栏中显示永久消息 状态栏是应用程序中输出简要信息的区域。⼀般位于主窗口的最底部&#xff0c;⼀个窗⼝中最多只能有⼀个状态栏。在Qt中&#xff0c;状态栏是通过QStatusBar类来实现的。在状态栏中可以显示的消…

Navicat Premium 自定义字体大小

常用编程软件自定义字体大全首页 文章目录 前言具体操作1. 打开工具对话框2. 设置编辑器字体大小3. 设置查询表格字体大小 前言 Navicat Premium 自定义字体大小&#xff0c;统一设置为 Cascadia Code SemiBold &#xff0c;大小为 12 具体操作 【工具】>【选项】>【编…

内网渗透小知识

下载proxychains这个工具 在下面这里进行代理配置 然后再里面添加内网端口。在设置浏览器的代理&#xff0c;就可以通过内网访问内网资源 然后在浏览器中设置&#xff0c;设置socks后可以访问很多。 如果映射http的话只可以访问一个 然后如果在内网扫描不了IP的话使用上面的代…

AGI系列(8)零门槛信息抓取利器打造,免费自动化抓取推特上的热点内容

应该大家都或多或少的听说过 X/Twitter。它可不只是个普通的社交平台&#xff01;它还是个信息宝库&#xff0c;里面有各种有趣的内容&#xff0c;比如&#xff1a;想知道最新热点&#xff1f;想和全世界的人聊天&#xff1f;Twitter都能搞定&#xff01;它的搜索功能特别厉害&…

测试职场经验 | 缺陷如何分类

说到bug&#xff0c;大家可能更多的认为是错误&#xff0c;有问题的地方&#xff0c;但是它的原意是”臭虫”,是不是有了新的发现&#xff01;而真正找出电脑程序中第一个bug的女程序员&#xff0c;来自于计算机软件第一夫人&#xff1a;Grace Hopper&#xff0c;一次”意外”的…

取模+背包

前言&#xff1a;一开始我想错了&#xff0c;一开始我想的是直接统计每一项模完后的和&#xff0c;我们只要能够取到一半&#xff0c;那么就有解&#xff0c;但是时间复杂度太大了 我们做推导 x y s u m x y sum xysum x − y k ∗ n x - y k * n x−yk∗n 那么我们可…