投资组合问题

2019-04-28  本文已影响0人  Mereder

动态规划合集:

1.矩阵链乘法
2.投资组合问题
3.完全背包问题
4.01背包问题
5.最长公共子序列

例题2——投资问题

描述

设 我们现在有m元钱,n项投资,函数f_i(x)表示将x元投入第i个项目所产生的效益,i=1,2...n,问如何分配这m元钱,使得总的投资效益最高?

假设钱数的分配都是非负整数,分配给第i个项目的钱数是x_i,那么该问题可以描述为:
目标函数:\max \left\{f_{1}\left(x_{1}\right)+f_{2}\left(x_{2}\right)+\cdots+f_{n}\left(x_{n}\right)\right\} \\ 约束条件:x_1+x_2+\cdots + x_n = m ,x_i \in N

实例

实例

子问题的界定和计算顺序

子问题界定:我们正常的想法K个项目,可以看看投前1个项目时候的收益,投前2个项目的....一直到前5个项目(全部项目)的最大收益,而对于前k个项目的收益,这里还需要对投入的钱再进行细分,前一个项目的时候,投入x元的最大收益(x=1,2...m)。这里也就出现了两个参数K和x。

在矩阵链中,我们的两个参数i和j都是同一个含义,就是矩阵的位置下标。这里我们的k和x是代表着不同的含义。

如果令F_k(x)表示x万元投入到前k个项目中,我们可以获得的最大收益。首先,我们从X万元中,分配x_k万元给第k个项目,那么剩下的x-x_k万元,就给前k-1个项目,而前k-1的最佳分配方案已经计算过,那么我们也就得到了递推方程:
\begin{array} {l}{F_{k}(x)=\max _{0\le x_{k}<x}\left\{f_{k}\left(x_{k}\right)+F_{k-1}\left(x-x_{k}\right)\right\}, \quad k=2,3, \cdots, n} \\ {F_{1}(x)=f_{1}(x), \quad F_{k}(0)=0, \quad k=1,2, \cdots, n} \end{array}

备忘录与标记函数

这个问题的初始化是F_1(x) = f_1(x)即得到了F_1(x)的第一列。

F_1(x)=11,F_2(x)=12,F_3(x)=13,F_4(x)=14,F_5(x)=15

根据递推公式:

F_2(x) = \max_{0 \le x_2 < x}\{f_2(x_2)+F_{2-1}(x-x_{2})\} \\ =\max_{0 \le x_2 < x}\{f_2(x_2)+F_{1}(x-x_{2})\}
F_2(1) = max\{f_2(0)+F_1(1),f_2(1)+F_1(0)\} = 11

F_2(2) = max\{f_2(0)+F_1(2),f_2(1)+F_1(1),f_2(2)+F_1(0)\} = 12

F_2(3) = max\{f_2(0)+F_1(3),f_2(1)+F_1(2),f_2(2)+F_1(1),f_2(3)+F_1(0)\} = 16

依次类推,可以得到F_2(4),F_2(5)

在计算F_3(x)的时候,只会考虑f_3(x_3)+F_2(x-x_3) 即与F_1(x)无关。依次类推计算F_4,F_5

代码实现

public class Investment {
    public static int[][] mem;
    public static int[][] tag;
    private static int compute_max_invest(int[][] invest,int n,int k) {
        // i 表示 前 i个 项目  i=0是第一个项目,已经初始化 所以从i=1开始
        for (int i = 1; i < k; i++) { // 1 2 3
            // j 来表示 分配j万元
            for (int j = 1; j < n; j++) { // 0 1 2 3 4 5
                // 当分配j万元时候 在各种组合中的求最大
                int max = Integer.MIN_VALUE;
                for (int l = 0; l <= j; l++) {
                    // F_k(x) = max{ f_k(x_k) + F_{k-1}(x-x_k) }
                    // temp = 第i个项目投入l万元 + 前i-1个项目投入j-l万元
                    int temp = invest[l][i]+mem[j-l][i-1];
                    if (temp >= max){
                        mem[j][i] = temp;
                        max = temp;
                        tag[j][i] = l;
                    }
                }
            }
        }
        return mem[n-1][k-1];
    }

    public static void trace_result(int res[],int k,int n){
        int total = n-1;
        // k 是项目数
        for (int i = k-1; i >= 0; i--) {
            int temp = tag[total][i];
            total -= temp;
            res[i] = temp;
        }
    }

    public static void main(String[] args) {
        // 输入是一个n*k的矩阵
        int invest[][] ={{0,0,0,0},{11,0,2,20},{12,5,10,21},{13,10,30,22},{14,15,32,23},{15,20,40,24}};
        int n = invest.length; // 行数  表示投入 n万元 n=6 实际上是 0 1 2 3 4 5
        int k = invest[0].length; // 列数  表示 共多少个项目
        mem = new int[n][k];
        tag = new int[n][k];
        System.out.println("输入数据为");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < k; j++) {
                System.out.printf("%d ",invest[i][j]);
            }
            System.out.println();
        }
        // 初始化 将 第一个项目的资金 放入 备忘录。
        // 标记函数
        for (int i = 0; i < n; i++) {
            mem[i][0] = invest[i][0];
            tag[i][0] = i;
        }

        int result[] = new int[k];
        int max_invest = compute_max_invest(invest,n,k);
        System.out.printf("max_investion:%d\n",max_invest);
        System.out.println("备忘录如下:");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < k; j++) {
                System.out.printf("%d ",mem[i][j]);
            }
            System.out.println();
        }
        System.out.println("追踪解如下:");
        trace_result(result,k,n);
        for (int i = 0; i < k; i++) {
            System.out.println(result[i]);
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读