【算法设计与分析】实验报告c++python实现(TSP问题、哈夫曼编码问题、顾客安排问题、最小生成树问题、图着色问题)

一、实验目的

1.加深学生对贪心算法设计方法的基本思想、基本步骤、基本方法的理解与掌握;
2.提高学生利用课堂所学知识解决实际问题的能力;
3.提高学生综合应用所学知识解决实际问题的能力。

二、实验任务

用贪心算法实现:
1、TSP问题
TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
2、哈夫曼编码问题
a.写一个程序,为给定的英文文本构造一套哈夫曼编码,并对该文本编码。
b.写一个程序,对一段用哈夫曼编码的英文文本进行解码。
c.做一个实验,测试对包含1000个词左右的一段英文文本进行哈夫曼编码时,典型的压缩率位于什么样的区间。
3、设有n个顾客同时等待一项服务,顾客i 需要的服务时间为ti ,i=1,2,3,…,n 。从时刻0开始计时,若在时刻t开始对顾客i服务,那么i的等待时间为t,应该怎么安排n个顾客的服务次序使得总的等待时间(每个顾客等待时间的总和)最少?
假设服务时间分别为{1,3,2,15,10,6,12},用贪心算法给出这个问题的解。
4、最小生成树问题(Prim算法和Kruskal算法)
设G=(V,E)是一个无向连通网,生成树上各边的权值之和称为该生成树的代价,在G的所有生成树中,代价最小的生成树称为最小生成树(Minimal Spanning Trees)。
5、图着色问题
给定无向连通图G=(V, E),求图G的最小色数k,使得用k种颜色对G中的顶点着色,可使任意两个相邻顶点着色不同。

三、实验设备及编程开发工具

笔记本,Windows10操作系统,Dev-C++,Pycharm

四、实验过程设计(算法设计过程)

(一)TSP问题

1、TSP问题

在TSP问题上,假设城市集合为B,我们每次只选择距离当前城市最近的城市移动。同时集合s记录已经遍历过得城市,下次从集合(B-s)中选择。

# 贪心法
import pandas as pd
import numpy as np
import math
import time
dataframe = pd.read_csv("./data/TSP100cities.tsp", sep=" ", header=None)
v = dataframe.iloc[:, 1:3]train_v = np.array(v)
train_d = train_v
dist = np.zeros((train_v.shape[0], train_d.shape[0]))# 计算距离矩阵
for i in range(train_v.shape[0]):for j in range(train_d.shape[0]):dist[i, j] = math.sqrt(np.sum((train_v[i, :] - train_d[j, :]) ** 2))
"""
s:已经遍历过的城市
dist:城市间距离矩阵
sumpath:目前的最小路径总长度
Dtemp:当前最小距离
flag:访问标记
"""
i = 1
n = train_v.shape[0]
j = 0
sumpath = 0
s = []
s.append(0)
start = time.clock()
while True:k = 1Detemp = 10000000while True:l = 0flag = 0if k in s:flag = 1if (flag == 0) and (dist[k][s[i - 1]] < Detemp):j = k;Detemp = dist[k][s[i - 1]];k += 1if k >= n:break;s.append(j)i += 1;sumpath += Detempif i >= n:break;
sumpath += dist[0][j]
end = time.clock()
print("结果:")
print(sumpath)
for m in range(n):print("%s-> " % (s[m]), end='')
print()
print("程序的运行时间是:%s" % (end - start))

(二)哈夫曼编码问题

(1)问题分析:
哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。其构造步骤如下:
(1)哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。
(2)算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。
(3)假设编码字符集中每一字符c的频率是f©。以f为键值的优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入优先队列Q。经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。

(2)算法实现:

import requests
# 实现节点类
class Node:def __init__(self, freq):self.left = Noneself.right = Noneself.father = Noneself.freq = freqdef is_left(self):return self.father.left == self# 为每一个节点赋权值
def create_nodes(frequencies):return [Node(freq) for freq in frequencies]# 创建哈夫曼树
def createHuffmanTree(nodes):queue = nodes[:]while len(queue) > 1:queue.sort(key=lambda item: item.freq)  # 将字符出现的频率视为权值,按权值排序。sort()默认升序排列node_left = queue.pop(0)  # 将弹出的第一个元素(最小)赋给左边节点node_right = queue.pop(0)node_father = Node(node_left.freq + node_right.freq)  # 将左右子树的权值相加赋给父节点# 构建节点之间的关系node_father.left = node_leftnode_father.right = node_rightnode_left.father = node_fathernode_right.father = node_fatherqueue.append(node_father)  # 将父节点插入到队列中queue[0].father = None  # 第一个节点没有父节点# print(type(queue[0]))  queue[0] 中保存的是什么数据return queue[0]# 遍历叶节点
def huffman_encoding(nodes, root):codes = [''] * len(nodes)for i in range(len(nodes)):node_tmp = nodes[i]while node_tmp != root:if node_tmp.is_left():codes[i] = '0' + codes[i]else:codes[i] = '1' + codes[i]node_tmp = node_tmp.fatherreturn codes# 获取字符出现的频数
def count_frequency(input_string):# 用于存放字符char_store = []# 用于存放频数freq_store = []# 解析字符串for index in range(len(input_string)):if char_store.count(input_string[index]) > 0:temp = int(freq_store[char_store.index(input_string[index])])temp = temp + 1freq_store[char_store.index(input_string[index])] = tempelse:char_store.append(input_string[index])freq_store.append(1)# 返回字符列表和频数列表return char_store, freq_store# 获取字符、频数的列表
def get_char_frequency(char_store=[], freq_store=[]):# 用于存放char_frequencychar_frequency = []for item in zip(char_store, freq_store):temp = (item[0], item[1])char_frequency.append(temp)return char_frequency# 解压缩huffman文件
def decode_huffman(input_string,  char_store, freq_store):encode = ''decode = ''for index in range(len(input_string)):encode = encode + input_string[index]for item in zip(char_store, freq_store):if encode == item[1]:decode = decode + item[0]encode = ''return decodeinput_string = input()
a, b = count_frequency(input_string)
char_freqs = get_char_frequency(a, b)
nodes = create_nodes([item[1] for item in char_freqs])
root = createHuffmanTree(nodes)
codes = huffman_encoding(nodes, root)
for item in zip(char_freqs, codes):print('Character:%s freq:%-2d   encoding: %s' % (item[0][0], item[0][1], item[1]))

(三)顾客等待问题

(1) 问题分析:
服务时间最小的顾客先服务,第一位顾客服务时,每一位顾客都等待A[0]个时间;第二个时,有n-1位顾客等待;以此类推,第i个顾客服务时,有n- i个顾客在等待中,由此得出公式 总的等待时间 time += (n - i) * A [ i ] ,最小平均等待时间为 time / n。

(2) 算法实现:

#include <stdio.h>
#include <string.h>
#define SIZE 10
int A[SIZE];
void sort(int A[],int n);
double greedy(int A[],int n);
void swap(int * a,int * b);int  main()
{int n,i;// n个顾客printf("请输入顾客数:\n");scanf("%d",&n);printf("请输入每个顾客的服务时间:\n");for(i = 1;i<=n;i++){printf("No.%d\n",i);scanf("%d",&A[i-1]);}sort(A,n);printf("最小平均等待时间:%.2f",greedy(A,n));return 0;
}double greedy(int A[],int n)
{int i,time = 0;double t = 0;//最小平均等待时间for(i = 0;i < n;i++){time = time +(n-i)*A[i];}t = time/n;return t;
}
void sort(int A[],int n)
{int i,j;for(i = 0;i < n;i++){for(j = i+1;j < n;j++){if(A[i] > A[j])swap(&A[i],&A[j]);}}
}
void swap(int *a,int *b)
{int temp;temp = *a;*a = *b;*b = temp;
}

(四)最小生成树问题

(1) Prim算法
任选一个顶点,并以此建立起生成树,每一步的贪心选择是简单地把不在生成树中的最近顶点添加到生成树中。设最小生成树T=(U,TE),初始化时U={u0}(u0为任意顶点),TE={ }。Prim算法的关键是如何找到连接 U 和 V-U 的最短边来扩充生成树T。
对于每一个不在当前生成树中的顶点v∈ V-U,必须知道它连接生成树中每一个顶点u∈U的边信息,从中选择最短边。
所以,对当前不在生成树中的顶点v∈ V-U,需要保存两个信息:lowcost[v]表示顶点v到生成树中所有顶点的最短边;adjvex[v]表示该最短边在生成树中的顶点。

(2) Kruskal算法
设G=(V,E)是一个无向连通网,令T=(U,TE)是G的最小生成树。最短边策略从TE={ }开始,每一次贪心选择都是在边集E中选择最短边(u,v),如果边(u,v)加入集合TE中不产生回路,则将边(u,v)加入边集TE中,并将它在集合E中删去。

class Graph(object):def __init__(self, maps):self.maps = mapsself.nodenum = self.get_nodenum()self.edgenum = self.get_edgenum()def get_nodenum(self):return len(self.maps)def get_edgenum(self):count = 0for i in range(self.nodenum):for j in range(i):if self.maps[i][j] > 0 and self.maps[i][j] < 9999:count += 1return countdef kruskal(self):res = []if self.nodenum <= 0 or self.edgenum < self.nodenum - 1:return resedge_list = []for i in range(self.nodenum):for j in range(i, self.nodenum):if self.maps[i][j] < 9999:edge_list.append([i, j, self.maps[i][j]])  # 按[begin, end, weight]形式加入edge_list.sort(key=lambda a: a[2])  # 已经排好序的边集合group = [[i] for i in range(self.nodenum)]for edge in edge_list:for i in range(len(group)):if edge[0] in group[i]:m = iif edge[1] in group[i]:n = iif m != n:res.append(edge)group[m] = group[m] + group[n]group[n] = []return resdef prim(self):res = []if self.nodenum <= 0 or self.edgenum < self.nodenum - 1:return resres = []seleted_node = [0]candidate_node = [i for i in range(1, self.nodenum)]while len(candidate_node) > 0:begin, end, minweight = 0, 0, 9999for i in seleted_node:for j in candidate_node:if self.maps[i][j] < minweight:minweight = self.maps[i][j]begin = iend = jres.append([begin, end, minweight])seleted_node.append(end)candidate_node.remove(end)return resmax_value = 9999
row0 = [0, 7, max_value, max_value, max_value, 5]
row1 = [7, 0, 9, max_value, 3, max_value]
row2 = [max_value, 9, 0, 6, max_value, max_value]
row3 = [max_value, max_value, 6, 0, 8, 10]
row4 = [max_value, 3, max_value, 8, 0, 4]
row5 = [5, max_value, max_value, 10, 4, 0]
maps = [row0, row1, row2, row3, row4, row5]
graph = Graph(maps)
print('邻接矩阵为\n%s' % graph.maps)
print('节点数据为%d,边数为%d\n' % (graph.nodenum, graph.edgenum))
print('------最小生成树kruskal算法------')
print(graph.kruskal())
print('------最小生成树prim算法')
print(graph.prim())

(五)图着色问题

(1) 问题分析:
图的m色优化问题:给定无向连通图G,为图G的各顶点着色, 使图中任2邻接点着不同颜色,问最少需要几种颜色。所需的最少颜色的数目m称为该图的色数。
若图G是可平面图,则它的色数不超过4色(4色定理).
4色定理的应用:在一个平面或球面上的任何地图能够只用4种
颜色来着色使得相邻的国家在地图上着有不同颜色。
(2) 算法实现:

#include<stdio.h>int color[100];
//int c[100][100];
bool ok(int k ,int c[][100])//判断顶点k的着色是否发生冲突
{int i,j;for(i=1;i<k;i++)if(c[k][i]==1&&color[i]==color[k])return false;return true;
}void graphcolor(int n,int m,int c[][100])
{int i,k;for(i=1;i<=n;i++)color[i]=0;//初始化k=1;while(k>=1){color[k]=color[k]+1;while(color[k]<=m)if (ok(k,c)) break;else color[k]=color[k]+1;//搜索下一个颜色if(color[k]<=m&&k==n)//求解完毕,输出解{for(i=1;i<=n;i++)printf("%d ",color[i]);printf("\n");//return;//return表示之求解其中一种解}else if(color[k]<=m&&k<n)k=k+1;    //处理下一个顶点else{color[k]=0;k=k-1;//回溯}}
}
void main()
{int i,j,n,m;int c[100][100];//存储n个顶点的无向图的数组printf("输入顶点数n和着色数m:\n");scanf("%d %d",&n,&m);printf("输入无向图的邻接矩阵:\n");for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&c[i][j]);printf("着色所有可能的解:\n");graphcolor(n,m,c);
}

五、实验结果及算法复杂度分析
(一)TSP问题
img
(二)哈夫曼编码问题
img
(三)动态服务问题

img
(四)最小生成树问题
img

(1)Prim算法
设连通网中有 n 个顶点,则第一个进行初始化的循环语句需要执行 n-1 次,第二个循环共执行 n-1 次,内嵌两个循环,其一是在长度为 n 的数组中求最小值,需要执行 n-1 次,其二是调整辅助数组,需要执行 n-1 次,所以,Prim 算法的时间复杂度为O(n2)。
(2) Kruskal算法
为了提高每次贪心选择时查找最短边的效率,可以先将图G中的边按代价从小到达排序,则这个操作的时间复杂度为O(elog2e),其中e为无向连通网中边的个数。对于两个顶点是否属于同一个连通分量,可以用并查集的操作将其时间性能提高到O(n),所以Kruskal算法的时间性能是O(elog2e)。

(五)图着色问题
img

实验小结(包括问题和解决方法、心得体会等)

通过本次实验,利用C/python语言编程实现解决了TSP问题,哈夫曼编码问题,顾客等待问题,最小生成树问题。对贪心算法有了更进一步的认识,对算法设计这门课程有了更多的认识。

本次实验增加了动手编码能力,对算法设计有了更进一步的认识,但是技术上的缺陷,编码能力上存在的短板,在今后的实验中还需要加大练习力度。

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

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

相关文章

Spring基于AspectJ实现验签切点

文章目录 引言I AspectJ 依赖II 验签切点2.1 匹配方法执行的连接点2.2 设置带有CustomAnnotation注解的方法为切点III 案例:验签2.1 用法2.2 定义注解2.3 定义切面和切点引言 需求:验签 实现:基于AspectJ实现验签切点 I AspectJ 依赖 AspectJ 是一个基于 Java 语言的 AOP …

go稀疏数组

稀疏数组 稀疏数组 稀疏数组 package testimport ("encoding/json""fmt""io/ioutil""log""reflect""testing" )type ValNode struct {Row int json:"row"Col int json:"col"Val int json:&qu…

Spring Cloud Kubernetes 实践 服务注册发现、服务动态配置

一、Spring Cloud Kubernetes 随着云计算和微服务架构的不断发展&#xff0c;k8s 和Spring Cloud成为了当今技术领域的两大热门话题。k8s作为一个开源的容器编排平台&#xff0c;已经在自动化部署、扩展和管理方面取得了巨大的成功&#xff0c;而Spring Cloud则以其丰富的生态…

MFC 列表控件修改实例(源码下载)

1、本程序基于前期我的博客文章《MFC下拉菜单打钩图标存取实例&#xff08;源码下载&#xff09;》 2、程序功能选中列表控件某一项&#xff0c;修改这一项的按钮由禁止变为可用&#xff0c;双击这个按钮弹出对话框可对这一项的记录数据进行修改&#xff0c;点击确定保存修改数…

基于Spring Boot的体质测试数据分析及可视化系统设计与实现

基于Spring Boot的体质测试数据分析及可视化系统的设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页界面图&#xff0c;体质测试…

Github查找代码项目高级语法(含科研项目查找案例)

基础搜索语法 1.搜索名字 in:name XXX 2.搜索描述 in:description XXX 3.搜索readme in:readme XXX 4.根据stars stars:>2000 5.根据fork fork:>3000 6.仓库大小搜索 size:>5000 [注意: 该处单位大小为 k] 7.根据更新时间 …

免费开源语音克隆-GPT-SoVITS-WebUI只需 5 秒的声音样本

语音克隆-GPT-SoVITS-WebUI 强大的少样本语音转换与语音合成Web用户界面。 功能&#xff1a; 零样本文本到语音&#xff08;TTS&#xff09;&#xff1a; 输入 5 秒的声音样本&#xff0c;即刻体验文本到语音转换。 少样本 TTS&#xff1a; 仅需 1 分钟的训练数据即可微调模型…

OpenCV如何在图像中寻找轮廓(60)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何模板匹配(59) 下一篇 :OpenCV检测凸包(61) 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 cv::findContours使用 OpenCV 函数 cv::d rawContours …

【Flask 系统教程 4】Jinjia2模版和语法

Jinjia2 模板 模板的介绍 Jinja2 是一种现代的、设计优雅的模板引擎&#xff0c;它是 Python 的一部分&#xff0c;由 Armin Ronacher 开发。Jinja2 允许你在 HTML 文档中嵌入 Python 代码&#xff0c;以及使用变量、控制结构和过滤器来动态生成内容。它的语法简洁清晰&#…

【Qt QML】用CMake管理Qt工程

CMake是一个开源、跨平台的工具系列&#xff0c;用于构建、测试和打包软件。CMake使用简单的独立配置文件来控制软件编译过程。与许多跨平台系统不同&#xff0c;CMake被设计为与本地构建环境结合使用。 下面我们在CMake项目中使用Qt的最基本方法。首先&#xff0c;创建一个基本…

系统架构设计师错题集

在实时操作系统中&#xff0c;两个任务并发执行&#xff0c;一个任务要等待另一个任务发来消息&#xff0c;或建立某个条件后再向前执行&#xff0c;这种制约性合作关系被称为任务的&#xff08;9&#xff09;。 (9)A.同步 B.互斥 C.调度 D.执行 【答案】A 【解析】本题考查…

Linux图形化界面怎么进入?CentOS 7图形界面切换

CentOS 7默认只安装命令行界面。要切换到图形界面&#xff0c;需要先检查系统是否安装图形界面&#xff0c;在终端输入以下命令&#xff1a; systemctl get-default若是返回结果是“multi-user.target”表示系统没有安装图形界面&#xff1b;若是返回结果是“graphical.target…

Linux migrate_type进一步探索

文章接着上回Linux migrate_type初步探索 1、物理页面添加到buddy系统 我们都知道物理内存一开始是由memblock进行分配管理&#xff0c;后面会切换到buddy系统管理。那么接下来我们看一下&#xff0c;memblock管理的物理页面是怎么添加到buddy系统中的。 start_kernel() -&g…

双指针(C++)

文章目录 1、移动零2、复写零3、快乐数4、盛最多水的容器5、有效三角形的个数6、和为s的两个数7、三数之和8、四数之和 需要理解的是&#xff0c;双指针并非只有指针&#xff0c;双指针的意思是两个位置。比如对于数组来说&#xff0c;两个下标也是双指针。当然&#xff0c;也可…

merge and rebase

文章目录 什么是merge什么是rebasemerge和rebase的区别操作执行git merge操作git rebase操作冲突解决解决冲突的步骤 Git Merge 和 Git Rebase 都是用于集成来自不同分支的修改的 Git 命令。 什么是merge Git Merge 是将一个分支的改动合并到另一个分支的方式。当你执行一个 m…

LabVIEW机械臂控制与图像处理示教平台

LabVIEW机械臂控制与图像处理示教平台 随着工业自动化技术的快速发展&#xff0c;工业机器人在制造业中的应用越来越广泛&#xff0c;它们在提高生产效率、降低人工成本以及保证产品质量方面发挥着重要作用。然而&#xff0c;传统的工业机器人编程和操作需要专业知识&#xff…

2024-5-3学习笔记 虚拟继承原理

目录 原理 总结 前面提到过&#xff0c;解决菱形继承产生的数据二义性问题和数据冗余&#xff0c;就需要用到虚拟继承&#xff0c;关于它是如何解决的&#xff0c;我们来一起研究。 class Person { public :string _name ; // 姓名 }; class Student : virtual public Perso…

某招聘网站搜索结果接口逆向之webpack扣取

逆向网址 aHR0cHM6Ly93ZS41MWpvYi5jb20v 逆向链接 aHR0cHM6Ly93ZS41MWpvYi5jb20vcGMvc2VhcmNoP2pvYkFyZWE9MDAwMDAwJmtleXdvcmQ9cGhwJnNlYXJjaFR5cGU9MiZrZXl3b3JkVHlwZT0 逆向接口 aHR0cHM6Ly93ZS41MWpvYi5jb20vYXBpL2pvYi9zZWFyY2gtcGM 逆向过程 请求方式 POST 逆向参数 …

华为Pura70发布,供应链公司进入静默保密期

保密措施&#xff1a;与华为Pura70发布相关的供应链公司在产品发布前后处于静默保密期。这可能是由于华为对于手机供应链的一些信息处于保密状态&#xff0c;尤其是关于麒麟芯片的代工厂商等敏感信息。这种保密措施有助于保持产品的神秘感&#xff0c;调动用户的好奇心&#xf…

微信小程序+esp8266温湿度读取

本文主要使用微信小程序显示ESP8266读取的温湿度并通过微信小程序控制LED灯。小程序界面如下图所示 原理讲解 esp8266 通过mqtt发布消息,微信小程序通过mqtt 订阅消息,小程序订阅后,就可以实时收到esp8266 传输来的消息。 个人可免费注册五个微信小程序账号,在微信小程序官…