【算法设计与分析】实验报告c++实现(矩阵链相乘问题、投资问题、背包问题、TSP问题、数字三角形)

一、实验目的

1.加深学生对动态规划算法设计方法的基本思想、基本步骤、基本方法的理解与掌握;

2.提高学生利用课堂所学知识解决实际问题的能力;

3.提高学生综合应用所学知识解决实际问题的能力。

二、实验任务

1、矩阵链相乘问题

2、投资问题

3、背包问题

4、TSP问题

旅行家要旅行n个城市,要求经历各个城市且仅经历一次,然后回到出发城市,并要求所走的路程最短。

5、数字三角形

问题描述:在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。

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

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

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

(一)、矩阵链相乘问题

(1) 算法分析:

输入P=< P0, P1, ..., Pn>,Ai...j 表示乘积 AiAi+1...Aj 的结果,其最后一次相乘是:

m[i,j] 表示得到Ai...j的最少的相乘次数。

递推方程:

为了确定加括号的次序,设计表s[i,j],记录求得最优时最一位置。

(2) 算法实现:

python 复制代码
import sys
import time

gk = lambda i, j: str(i)+','+str(j)
MAX = sys.maxsize  # 获取最大的整数


def memorized_matrix_chain(p):
    n = len(p)-1
    m = {}
    for i in range(1, n+1):
        for j in range(i, n+1):
            m[gk(i, j)] = MAX  # 先初始化为最大值

    return lookup_chain(m, p, 1, n)

def lookup_chain(m, p, i, j):
    if m[gk(i, j)] < MAX:
        return m[gk(i, j)]
    if i == j:
        m[gk(i, j)] = 0
    else:
        for k in range(i, j):
            # 依次求出每段的最小的值   p = [P0,P1,......,Pn] 矩阵链 第一个矩阵为P0*P1
            q = lookup_chain(m, p, i, k) + lookup_chain(m, p, k+1, j) + p[i-1]*p[k]*p[j]
            # 找出最优的解
            if q < m[gk(i, j)]:
                m[gk(i, j)] = q

    # 输出每一步的结果
    # print("______")
    # print(m)
    return m[gk(i, j)]

def main():
    p = [30, 35, 15, 5, 10, 20, 25, 5, 16, 34, 28, 19, 66, 34, 78, 55, 23]
    # p = [30, 15, 10, 20, 5]
    print(memorized_matrix_chain(p))

if __name__ == '__main__':
    b = time.time()
    main()
    print('total run time is:', time.time()-b)

(二)、投资问题

(1) 问题分析:

子问题界定:由参数k和x界定

k: 考虑对项目1,2,...,k的投资

x: 投资总数不超过x

矩阵链相乘里边的i,j代表的是矩阵的下标,是同样的参数,这里两个参数k,x是不同的参数。

原始问题输入:

k=n,x=m

子问题的计算顺序:k=1,2,3,...,n

对于给定的k,计算x=1,2,3,...,m

优化函数的递推方程:

Fk(x): x元钱投资给前k个项目的最大收益

多步判断:

如果知道p元钱(0≤p≤x)投给前k−1个项目的最大效益Fk−1§,就可以进行x元钱投给前k个项目的最优决策

递推方程和边界条件:

Fk(x)=max0≤xk≤x{fk(xk)+Fk−1(x−xk)},1<k≤n

F1(x)=f1(x)

第一个项目投资决策: 投资第一个项目的钱不超过x,x∈[0,m]的最大收益就是f1(x),x∈[0,m]不需要计算.

第二项目投资决策: 第二个投资F2(x),是当xk,xk∈[0,x]元钱投资给第二个项目得到的效益加上x−xk元钱投资给前一个项目的最优.然后再在这里面取最大值,就是最优决策.直到决策

第n个项目的投资: 第n个投资Fn(x)是当xk,xk∈[0,x]元钱投资给第n个项目得到的效益加上x−xk元钱投资给前n−1个项目的最优.然后再在这里面取最大值,就是最优决策.

(2) 算法实现:

cpp 复制代码
#define MAX_N 105 // 最大投资项目数目
#define MAX_M 105 // 最大投资钱数

int n,m; // n个项目,投资m万元
int f[MAX_N][MAX_M]; // 第i项投资j万元的收益 1 <= i <= n, 1 <= j <= m
int F[MAX_N][MAX_M]; // 投资前i项项目不超过j元钱的最大收益 1 <= i <= n, 1 <= j <= m
int x[MAX_N][MAX_M]; // 标记函数

// 打印计算结果
void printAns(){
    int i,j;
    for(i = 0; i <= n; i++){
        for(j = 0; j <= m; j++){
            printf("(%d,%d)\t", F[i][j], x[i][j]);
        }
        printf("\n");
    }
    printf("%d\n", F[n][m]);
}

// 动态规划算法实现, 时间复杂度O(nm^2)
void solve(){
    int i,j,k,tmp;
    for(i = 2; i <= n; i++) memset(F[i], -1, sizeof(int)*(m+1)); // 设置最大收益值的最小数目是-1
    for(i = 0; i <= m; i++) F[1][i] = f[1][i]; // 投资第一个项目
    for(i = 2; i <= n; i++){ // 投资前i个项目
        F[i][0] = 0; // 投资前i个项目不超过0元的收益是0
        x[i][0] = 0; // 标记
        for(j = 1; j <= m; j++){ // 投资钱数不超过j
            for(k = 0; k <= j; k++){ // 投资当前项目的钱数
                tmp = f[i][k]+F[i-1][j-k];
                if(tmp > F[i][j]){
                    F[i][j] = tmp; // 更新当前的最有解
                    x[i][j] = k; // 更新标记函数
                }
            }
        }
    }
    printAns(); // 打印结果
}

int main(){
    //freopen("in.txt", "r", stdin);
    int i,j;
    while(~scanf("%d%d", &n, &m) && n && m){ // 输入项目数,投资钱数
        for(i = 1; i <= n; i++){ // 输入效益函数
            f[i][0] = 0; // 投资0元的投资收益是0
            for(j = 1; j <= m; j++) scanf("%d", &f[i][j]);
        }
        solve();
    }
    return 0;
}

(三)、背包问题

(1) 问题分析:

a) 把背包问题抽象化(X1,X2,...,Xn,其中Xi取0或1,表示第i个物品选或不选),Vi表示第i个物品的价值,Wi表示第i个物品的体积(重量);

b)建立模型,即求max(V1X1+V2X2+...+VnXn);

c)约束条件,W1X1+W2X2+...+WnXn<capacity;

d)定义V(i,j):当前背包容量j,前i个物品最佳组合对应的价值;

e)最优性原理是动态规划的基础,最优性原理是指"多阶段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略"。

(2) 算法实现:

python 复制代码
import numpy as np
# 行李数n,不超过的重量W,重量列表w和价值列表p
def fun(n, W, w, p):
    a = np.array([[0] * (W + 1)] * (n + 1))
    # 依次计算前i个行李的最大价值,n+1在n的基础上进行
    for i in range(1, n + 1):
        for j in range(1, W + 1):
            if w[i - 1] > j:
                a[i, j] = a[i - 1, j]
            else:
                a[i, j] = max(a[i - 1, j], p[i - 1] + a[i - 1, j - w[i - 1]])  # 2种情况取最大值
    # print(a)
    print('max value is:' + str(a[n, W]))
    findDetail(p, n, a[n, W])


# 找到价值列表中的一个子集,使得其和等于前面求出的最大价值,即为选择方案
def findDetail(p, n, v):
    a = np.array([[True] * (v + 1)] * (n + 1))
    for i in range(0, n + 1):
        a[i][0] = True
    for i in range(1, v + 1):
        a[0][i] = False
    for i in range(1, n + 1):
        for j in range(1, v + 1):
            if p[i - 1] > j:
                a[i, j] = a[i - 1, j]
            else:
                a[i, j] = a[i - 1, j] or a[i - 1, j - p[i - 1]]
    if a[n, v]:
        i = n
        result = []
        while i >= 0:
            if a[i, v] and not a[i - 1, v]:
                result.append(p[i - 1])
                v -= p[i - 1]
            if v == 0:
                break
            i -= 1
        print(result)
    else:
        print('error')


# 初始化权重和价格
weights = [1, 2, 5, 6, 7, 9]
price = [1, 6, 18, 22, 28, 36]
#  设置背包的最大重量为13
fun(len(weights), 13, weights, price)

(四)TSP问题

(1) 问题分析:

假定从城市1出发,经过了一些地方,并到达了城市j。毋庸置疑,我们需要记录的信息有当前的城市j。同时我们还需要记录已经走过的城市的集合。同理,使用S记录未走过的城市的集合也可以的,且运算方便。

于是可以得出状态转移方程 go(S,init)=min{go(S−i,i)+dp[i][init]}∀s∈S

go(s,init)表示从init点开始,要经过s集合中的所有点的距离

因为是NP问题,所以时间复杂度通常比较大。使用dis[s][init]用来去重,初始化为-1,如果已经计算过init--->S(递归形式),则直接返回即可。

(2) 算法实现:

python 复制代码
import pandas as pd
import numpy as np
import math
import time

# 读取城市的数据
dataframe = pd.read_csv("./data/TSP10cities.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))

"""
N:城市数
s:二进制表示,遍历过得城市对应位为1,未遍历为0
dp:动态规划的距离数组
dist:城市间距离矩阵
sumpath:目前的最小路径总长度
Dtemp:当前最小距离
path:记录下一个应该到达的城市
"""

N=train_v.shape[0]
path = np.ones((2**(N+1),N))
dp = np.ones((2**(train_v.shape[0]+1),train_d.shape[0]))*-1


def TSP(s, init, num):
    if dp[s][init] != -1:
        return dp[s][init]
    if s == (1 << (N)):
        return dist[0][init]
    sumpath=1000000000
    for i in range(N):
        if s &(1<<i):
            m = TSP(s&(~(1<<i)),i,num+1)+dist[i][init]
            if m < sumpath:
                sumpath = m
                path[s][init]=i
    dp[s][init] = sumpath
    return dp[s][init]


if __name__ == "__main__":
    init_point=0
    s=0
    for i in range(1,N+1):
        s=s|(1<<i)
    start = time.clock()
    distance=TSP(s,init_point,0)
    end = time.clock()
    s=0b11111111110
    init=0
    num=0
    print(distance)
    while True:
        print(path[s][init])
        init=int(path[s][init])
        s=s&(~(1<<init))
        num+=1
        if num>9:
            break
    print("程序的运行时间是:%s"%(end-start))

五、实验结果及算法复杂度分析

(一) 矩阵链相乘问题

1 实验结果

(二)投资最少问题

1 实验结果

时间复杂度 O ( n m 2 ) O(nm^2) O(nm2)

(三)背包问题

1 实验结果

(四)TSP问题

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

通过本次实验,利用C/C++、python语言编程实现解决了矩阵链相乘问题,投资问题,背包问题以及TSP问题。对于投资问题,python的实现没有实现出来,存在问题,使用了C语言成功实现。通过本次实验,也进一步加深的对动态规划算法的认识。

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

相关推荐
机器视觉知识推荐、就业指导12 分钟前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Swift社区1 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman2 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
IT 青年2 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Yang.992 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王2 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_2 小时前
C++自己写类 和 运算符重载函数
c++
Dong雨2 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
六月的翅膀2 小时前
C++:实例访问静态成员函数和类访问静态成员函数有什么区别
开发语言·c++