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

news2024/10/6 20:31:03

一、实验目的

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 = 1
    Detemp = 10000000
    while True:
        l = 0
        flag = 0
        if k in s:
            flag = 1
        if (flag == 0) and (dist[k][s[i - 1]] < Detemp):
            j = k;
            Detemp = dist[k][s[i - 1]];
        k += 1
        if k >= n:
            break;
    s.append(j)
    i += 1;
    sumpath += Detemp
    if 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 = None
        self.right = None
        self.father = None
        self.freq = freq

    def 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_left
        node_father.right = node_right
        node_left.father = node_father
        node_right.father = node_father

        queue.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.father
    return 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 + 1
            freq_store[char_store.index(input_string[index])] = temp
        else:
            char_store.append(input_string[index])
            freq_store.append(1)
    # 返回字符列表和频数列表
    return char_store, freq_store

# 获取字符、频数的列表
def get_char_frequency(char_store=[], freq_store=[]):
    # 用于存放char_frequency
    char_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 decode

input_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 = maps
        self.nodenum = self.get_nodenum()
        self.edgenum = self.get_edgenum()

    def get_nodenum(self):
        return len(self.maps)

    def get_edgenum(self):
        count = 0
        for i in range(self.nodenum):
            for j in range(i):
                if self.maps[i][j] > 0 and self.maps[i][j] < 9999:
                    count += 1
        return count

    def kruskal(self):
        res = []
        if self.nodenum <= 0 or self.edgenum < self.nodenum - 1:
            return res
        edge_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 = i
                if edge[1] in group[i]:
                    n = i
            if m != n:
                res.append(edge)
                group[m] = group[m] + group[n]
                group[n] = []
        return res

    def prim(self):
        res = []
        if self.nodenum <= 0 or self.edgenum < self.nodenum - 1:
            return res
        res = []
        seleted_node = [0]
        candidate_node = [i for i in range(1, self.nodenum)]

        while len(candidate_node) > 0:
            begin, end, minweight = 0, 0, 9999
            for i in seleted_node:
                for j in candidate_node:
                    if self.maps[i][j] < minweight:
                        minweight = self.maps[i][j]
                        begin = i
                        end = j
            res.append([begin, end, minweight])
            seleted_node.append(end)
            candidate_node.remove(end)
        return res


max_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.coloradmin.cn/o/1640156.html

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

相关文章

基于Springboot的web后端开发三层架构上手实操

引言 我们拿到了xml文件 我们要将将xml文件中的数据加载并解析 完成数据的处理 并且返回给前端页面(result格式) 1.将xml文件放在resources目录下 xml是我们需要解析的文件 查看xml文件 2.在springboot中引入dom4j依赖 解析xml需要在springboot中引入dom4j依赖 这边我们…

iOS 创建依赖其他开源库的开源库

参考文章&#xff08;感激各位大神前路的明灯&#xff09; 参考文章一 参考项目 整体流程 流程简介 1&#xff09;使用pod命令行创建本地项目和git仓库并回答终端里的四个问题 2&#xff09;编辑podspec文件 3&#xff09;将需要开源的代码添加到Development Pods文件夹中&am…

2024第84届中国(云南昆明)教育装备展示会

关于第84届中国教育装备展示会将在云南昆明举办&#xff01; 谨此奉邀! 第84届中国教育装备展示会组委会 一、展会时间、地点 布展时间&#xff1a;2024年10月22日-24日 开展时间&#xff1a;2024年10月25日-27日 撤展时间&#xff1a;2024年10月27日 16:00-24:00 地点&a…

2024年5月前端面试记录:裸辞、旅游、面试

在职期间面试 富途 有效括号匹配 判断b是否是a的子集&#xff08;a和b有重复元素&#xff0c;要求b的同个元素出现次数<a的同个元素出现次数&#xff09; 离职原因 最有挑战的项目 技术分享做过哪些 如何发现用户痛点并解决 vue和react的相同点和不同点 单项数据流…

《有限元分析及应用》《有限元分析基础教程》-曾攀-清华大学|pdf电子书+有限元分析及应用视频教程(全85讲) 曾攀、雷丽萍 ​​​+课件PPT

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

Java -- (part20)

一.Map集合 1.概述 双列集合的顶级接口 2.实现类 HashMap 特点: a.key唯一,value可重复->如果key重复了,会发生value覆盖 b.无序 c.无索引 d.线程不安全 e.可以存null键null值 数据结构: 哈希表 方法: LinkedHashMap 特点: a.key唯一,value可重复->如果ke…

基于 Spring Boot 博客系统开发(五)

基于 Spring Boot 博客系统开发&#xff08;五&#xff09; 本系统是简易的个人博客系统开发&#xff0c;为了更加熟练地掌握 SprIng Boot 框架及相关技术的使用。&#x1f33f;&#x1f33f;&#x1f33f; 基于 Spring Boot 博客系统开发&#xff08;四&#xff09;&#x1f…

vue3 ——笔记 (表单输入,监听器)

表单输入 在Vue 3中&#xff0c;v-model指令的用法稍有不同于Vue 2。在Vue 3中&#xff0c;v-model指令实际上是一个语法糖&#xff0c;它会自动将value属性和input事件绑定到组件或元素上&#xff0c;以实现双向数据绑定。 在自定义组件中使用v-model时&#xff0c;需要在组…

【docker】maven 打包docker的插件学习

docker-maven-plugin GitHub地址&#xff1a;https://github.com/spotify/docker-maven-plugin 您可以使用此插件创建一个 Docker 映像&#xff0c;其中包含从 Maven 项目构建的工件。例如&#xff0c;Java 服务的构建过程可以输出运行该服务的 Docker 映像。 该插件是 Spot…

Nginx深度解析:核心特性、应用场景与全局、events、http等全面配置指南

Nginx是一款高性能的Web服务器与反向代理服务器软件&#xff0c;以其高并发处理能力、低内存消耗和反向代理负载均衡功能闻名。它通过事件驱动、异步非阻塞I/O模型&#xff0c;实现了极高的效率和稳定性&#xff0c;广泛应用于网站部署、API代理、静态资源服务及微服务架构中&a…

C#简单创建DLL文件并调用

DLL是Dynamic Link Library的缩写&#xff0c;意为动态链接库。动态链接库其实是由编译器将一系列相关的类型编译、链接并封装成一个独立的文件&#xff0c;与对其进行调用的程序分开。这样一个独立的文件相当于程序的一个模块&#xff0c;如果需要对程序进行更新&#xff0c;只…

【C++】1.贪心算法:零钱兑换的奇妙之旅

欢迎来CILMY23的博客 本篇主题为 贪心算法&#xff1a;零钱兑换的奇妙之旅 个人主页&#xff1a;CILMY23-CSDN博客 个人专栏&#xff1a; Python | C | C语言 | 数据结构与算法 上一篇C博客&#xff1a;掌握C函数重载和引用开启代码优化的新篇章 感谢观看&#xff0c;支…

nginx--自定义日志跳转长连接文件缓存状态页

自定义日志服务 [rootlocalhost ~]# cat /apps/nginx/conf/conf.d/pc.conf server {listen 80;server_name www.fxq.com;error_log /data/nginx/logs/fxq-error.log info;access_log /data/nginx/logs/fxq-access.log main;location / {root /data/nginx/html/pc;index index…

每日OJ题_贪心算法二③_力扣1005. K 次取反后最大化的数组和

目录 力扣1005. K 次取反后最大化的数组和 解析代码 力扣1005. K 次取反后最大化的数组和 1005. K 次取反后最大化的数组和 难度 简单 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。…

智能优化算法及 MATLAB 实现(书籍推荐)

智能优化算法及 MATLAB 实现&#xff08;书籍推荐&#xff09; 介绍前言目录第1章 粒子群优化算法原理及其MATLAB实现第2章 哈里斯鹰优化算法原理及其MATLAB实现第3章 沙丘猫群优化算法原理及其MATLAB实现第4章 鲸鱼优化算法原理及其MATLAB实现第5章 大猩猩部队优化算法原理及其…

大模型常用的预训练数据集

文章目录 通用网页数据中文网页数据书籍维基百科代码混合型数据集 与早期的预训练语言模型相比&#xff0c;大语言模型需要更多的训练数据&#xff0c;这些数据需要涵盖广泛的内容范围。多领域、多源化的训练数据可以帮助大模型更加全面地学习真实世界的语言与知识&#xff0c;…

基于缓存注解的时间戳令牌防重复提交设计

文章目录 一&#xff0c;概述二&#xff0c;实现过程1、引入pom依赖2、定义缓存管理3、时间戳服务类4、模拟测试接口 三&#xff0c;测试过程1&#xff0c; 模拟批量获取2&#xff0c; 消费令牌 四&#xff0c;源码放送五&#xff0c;优化方向 一&#xff0c;概述 API接口由于…

力扣面试150 简化路径 栈 模拟

Problem: 71. 简化路径 思路 &#x1f469;‍&#x1f3eb; 三叶题解 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public String simplifyPath(String path){ArrayDeque<String> d new ArrayDeque<>();…

SpringBoot-@Transactional注解失效

Transactional注解失效 Transactional失效场景 以下是一些常见导致Transactional注解失效的场景&#xff0c;配合相应的Java代码演示&#xff1a; 1、方法修饰符非公开&#xff08;非public&#xff09; Transactional注解失效的原因在于Spring事务管理器如何实现对事务的代…

【竞技宝】意甲:退出齐尔克泽争夺战!国米免签伊朗神锋!

博洛尼亚中锋齐尔克泽成为了意甲当红炸子鸡,不少豪门球队都希望可以签下他,目前对球员有意向的俱乐部包括AC米兰、尤文图斯、阿森纳、国际米兰和曼联,看到自家球员如此有市场,博洛尼亚方面咬死5000万欧元的价格不松口,想要得到他必须要拿出真金白银。不过意甲霸主国际米兰率先退…