算法31:针对算法30货币问题进行拓展 + 时间复杂度 + 空间复杂度优化--------从左往右尝试模型

news2025/5/26 5:32:13

在算法30中,我们说过从左往右尝试模型,口诀就是针对固定集合,就是讨论要和不要的累加和

那么对于非固定集合,我们应该怎么做呢?

针对非固定集合,面值固定,张数不固定。口诀就是讨论要与不要,要的话逐步讨论要几张的累加和

题目:

* arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。

* 每个值都认为是一种面值,且认为张数是无限的。

* 返回组成aim的方法数

分析:

* 例如:arr = {1,2},aim = 4

* 方法如下:1+1+1+1、1+1+2、2+2 * 一共就3种方法,所以返回3

递归写法:

 //递归版本
    public static int ways(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        return process(arr, 0, aim);
    }

    public static int process (int[] arr, int index, int aim)
    {
        if (index == arr.length) { // 没钱了
            return aim == 0 ? 1 : 0;
        }

        //讨论不同货币组成aim的可能
        int ways = 0;
        //因为货币张数无限,所有需要讨论每一种情况。此次,就不是简单的要与不要的问题了
        for (int zhangshu = 0; zhangshu * arr[index] <= aim; zhangshu++) {
            /**
             * 此处,还是讨论要与不要的问题。
             * 0张代表不要
             * 要的话需要讨论要几张 :
             * 要1 张, 那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
             * 要2 张,那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
             *  依次类推
             */
            ways += process(arr, index + 1, aim - zhangshu * arr[index]);
        }
        return ways;
    }

动态规划

1. 套路还是老样子。以数组为横坐标,以aim为纵坐标。并且行与列都新增1个。

2. 根据递归

if (index == arr.length) { // 没钱了
    return aim == 0 ? 1 : 0;
}

可以推导出最后一行的第一列为1,其余全部为0.

aim 0aim 1aim 2aim 3aim 4
index 0 (1)
index 1 (2)
index 210000

3. 根据递归:

int ways = 0;
//因为货币张数无限,所有需要讨论每一种情况。此次,就不是简单的要与不要的问题了
for (int zhangshu = 0; zhangshu * arr[index] <= aim; zhangshu++) {
    /**
     * 此处,还是讨论要与不要的问题。
     * 0张代表不要
     * 要的话需要讨论要几张 :
     * 要1 张, 那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
     * 要2 张,那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
     *  依次类推
     */
    ways += process(arr, index + 1, aim - zhangshu * arr[index]);
}

可以得到:

1. zhangshu * arr[index] <= aim必须成立,否则不做推导;直接返回0

2. 如果zhangshu * arr[index] <= aim成立,那么当前行当前列的值  依赖于下一行的aim - zhangshu * arr[index]列。

aim= 0aim= 1aim= 2aim= 3aim= 4
index 0 (1)
index 1 (2)

aim - zhangshu * arr[index]  = 0 - 张数 *2;

讨论:

面值为2:张数为0,则 计算值为0

dp[1][0] = dp[2][0] = 1; 即只存在1种解法。

讨论:

张数为0

dp[1][1] = dp[2][1] = 0

 

张数为1

可得 1 - 2 < 0;取值0

0+0=0,最终取值0
 

讨论

张数0

dp[1][2] = dp[2][2] = 0;

张数为1,可得 2 - 2 =0;

可得dp[1][2] = dp[2][0] = 1;

张数为2,可得 2 - 2*2 <0;直接取0;

即1种解法,即0+1+0=1

讨论1:

张数为0

dp[1][3] ]= dp[2][3] = 0;

张数为1,可得 3 - 2 =1;

可得:

dp[1][3] = dp[2][1] = 0;

张数为2。

可得3 - 2*2 < 0取值0;

张数为3. 即3-3*2<0,直接取0

0+0+0+0=0,最终取值0

张数为0

dp[1][4] = dp[2][4] = 0;

张数为1,可得 4 - 2 =2;

dp[1][4] = dp[2][2] = 0;

张数为2.

可得4 - 2*2 = 0.

dp[1][4] = dp[2][0] = 1;

张数为3:

4-3*2 < 0;直接取0

张数为4,4-4*2 < 0;直接取0

0+0+1+0+0=1

最终可得1种解法。

index 210000

    接着,我们推导出第一行的数据:

aim= 0aim= 1aim= 2aim= 3aim= 4
index 0 (1)

 面值为1
张数为0,dp[0][0] = dp[1][0] = 1;

  面值为1
张数为0,dp[0][1] = dp[1][1] = 0;

张数为1:

dp[0][1]=dp[1][0] = 1;

累计1种

 张数为0:

dp[0][2] = dp[1][2] = 1;

张数为1:

dp[0][2] = dp[1][1] = 0;

累计1种

张数为0:

dp[0][3] = dp[1][3] = 0;

张数为1:

dp[0][3] = dp[1][2] = 1;

张数为2:

dp[0][3] = dp[1][1] = 0;

累计1种

张数为0;

dp[0][4]=[1][4-0] = dp[1][4] = 1

张数为1:

dp[0][4] = dp[1][4-1*1] = dp[1][3] = 0;

张数为2:

dp[0][4] = dp[1][4-2*1] = dp[1][2] = 1;

张数为3:

dp[0][4] = dp[1][4-3*1] = dp[1][1] = 0;

张数为4:

dp[0][4] = dp[1][4-4*1] = dp[1][0] = 1;

累计1+1+1=3;

index 1 (2)

aim - zhangshu * arr[index]  = 0 - 张数 *2;

讨论:

面值为2:张数为0,则 计算值为0

dp[1][0] = dp[2][0] = 1; 即只存在1种解法。

讨论:

张数为0

dp[1][1] = dp[2][1] = 0

 

张数为1

可得 1 - 2 < 0;取值0

0+0=0,最终取值0
 

讨论

张数0

dp[1][2] = dp[2][2] = 0;

张数为1,可得 2 - 2 =0;

可得dp[1][2] = dp[2][0] = 1;

张数为2,可得 2 - 2*2 <0;直接取0;

即1种解法,即0+1+0=1

讨论1:

张数为0

dp[1][3] ]= dp[2][3] = 0;

张数为1,可得 3 - 2 =1;

可得:

dp[1][3] = dp[2][1] = 0;

张数为2。

可得3 - 2*2 < 0取值0;

张数为3. 即3-3*2<0,直接取0

0+0+0+0=0,最终取值0

 

张数为0,

dp[1][4] = dp[2][4] = 0;

张数为1,可得 4 - 2 =2;

dp[1][4] = dp[2][2] = 0;

张数为2.

可得4 - 2*2 = 0.

dp[1][4] = dp[2][0] = 1;

张数为3:

4-3*2 < 0;直接取0

张数为4,4-4*2 < 0;直接取0

0+0+1+0+0=1

最终可得1种解法。

index 210000

最终需要的是第一行最后一列数据:

1. aim为4,面值为1的一张都没有。可得dp[0][4]=[1][4-0] = dp[1][4] = 1

2. aim 为4, 面值为1的只有一张。可得 dp[0][4] = dp[1][4-1*1] = dp[1][3] = 0;

3. aim 为4, 面值为1的只有两张。可得 dp[0][4] = dp[1][4-2*1] = dp[1][2] = 1;

4. aim 为4, 面值为1的只有三张。可得 dp[0][4] = dp[1][4-3*1] = dp[1][1] = 0;

5. aim 为4, 面值为1的只有四张。可得 dp[0][4] = dp[1][4-4*1] = dp[1][0] = 1;

累计可得1+1+1 = 3, 即3种方法。

  //动态规划
    public static int ways2(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }

        //老样子
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        //最后一行的第一列为1,其余都为0。 根据递归 return aim == 0 ? 1 : 0;得到
        dp[N][0] = 1;

        //双层for循环,动态规划的老套路。一维数组arr作为横坐标,aim作为纵坐标
        for (int row = N -1 ; row >= 0; row--) {     //row代表递归中的 index
            for (int col = 0; col <= aim; col++) {   //col代表递归中的 aim

                //此处是比较复杂的,完全套用递归内部的代码。这一层的for循环就是对
                //要的张数进行讨论。
                int ways = 0;
                //此处zhangshu * arr[row] <= col是唯一变化。重点理解
                for (int zhangshu = 0; zhangshu * arr[row] <= col; zhangshu++) {
                    /**
                     * 此处,还是讨论要与不要的问题。
                     * 0张代表不要
                     * 要的话需要讨论要几张 :
                     * 要1 张, 那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
                     * 要2 张,那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
                     *  依次类推
                     */
                    //根据ways += process(arr, row + 1, aim - zhangshu * arr[row]);推导
                    //此处的aim - zhangshu * arr[index] 需要变成 col - zhangshu * arr[index]
                    ways += dp[row + 1][col - (zhangshu * arr[row])];
                }
                dp[row][col] = ways;
            }
        }
        return dp[0][aim];
    }

那么如何进行时间复杂度优化呢?继续观察二维数组:

aim= 0aim= 1aim= 2aim= 3aim= 4
index 0 (1)

 面值为1
张数为0,dp[0][0] = dp[1][0] = 1;

  面值为1
张数为0,dp[0][1] = dp[1][1] = 0;

张数为1:

dp[0][1]=dp[1][0] = 1;

累计1种

 张数为0:

dp[0][2] = dp[1][2] = 1;

张数为1:

dp[0][2] = dp[1][1] = 0;

累计1种

张数为0:

dp[0][3] = dp[1][3] = 0;

张数为1:

dp[0][3] = dp[1][2] = 1;

张数为2:

dp[0][3] = dp[1][1] = 0;

累计1种

张数为0;

dp[0][4]=[1][4-0] = dp[1][4] = 1

张数为1:

dp[0][4] = dp[1][4-1*1] = dp[1][3] = 0;

张数为2:

dp[0][4] = dp[1][4-2*1] = dp[1][2] = 1;

张数为3:

dp[0][4] = dp[1][4-3*1] = dp[1][1] = 0;

张数为4:

dp[0][4] = dp[1][4-4*1] = dp[1][0] = 1;

累计1+1+1=3;

index 1 (2)

aim - zhangshu * arr[index]  = 0 - 张数 *2;

讨论:

面值为2:张数为0,则 计算值为0

dp[1][0] = dp[2][0] = 1; 即只存在1种解法。

讨论:

张数为0

dp[1][1] = dp[2][1] = 0

 

张数为1

可得 1 - 2 < 0;取值0

0+0=0,最终取值0
 

讨论

张数0

dp[1][2] = dp[2][2] = 0;

张数为1,可得 2 - 2 =0;

可得dp[1][2] = dp[2][0] = 1;

张数为2,可得 2 - 2*2 <0;直接取0;

即1种解法,即0+1+0=1

讨论1:

张数为0

dp[1][3] ]= dp[2][3] = 0;

张数为1,可得 3 - 2 =1;

可得:

dp[1][3] = dp[2][1] = 0;

张数为2。

可得3 - 2*2 < 0取值0;

张数为3. 即3-3*2<0,直接取0

0+0+0+0=0,最终取值0

 

张数为0,

dp[1][4] = dp[2][4] = 0;

张数为1,可得 4 - 2 =2;

dp[1][4] = dp[2][2] = 0;

张数为2.

可得4 - 2*2 = 0.

dp[1][4] = dp[2][0] = 1;

张数为3:

4-3*2 < 0;直接取0

张数为4,4-4*2 < 0;直接取0

0+0+1+0+0=1

最终可得1种解法。

index 210000

1. 任何一行的每一列,当张数为0的时候,都是直接依赖下一行的当前列的值。也就是说这个值一定是依赖下一行的当前列的值。

2.  col是aim的值,当 zhangshu * arr[row] <= col 的时候,我们是依赖 dp[row + 1][col - (zhangshu * arr[row])]的值的。这一点是非常非常重要的。由此我们可以推导出;

0张,我们依赖y1处的值

1张,依赖y2处的值。

2张,依赖y3处的值

3张,依赖y4处的值

4张,依赖y5处的值

5张,依赖y6处的值

那 x 不就是 y1 + y2 + y3 + y4 + y5 + y6 吗?

x1 不就是 y2 + y3 + y4 + y5 +y6 吗?

状态转移可得。 x = x1 + y1.

根据以上推导,我们是不是可以直接转化成完整的表格,即

既然我们知道

x = x1 + y1; 假设x处的列下标col为15. 那么x1处的小标不就是 col - value.  此处的value不就是一维数组中的arr[index]的值,即为3。 那么x1处的下标不就是 15 - 3 = 12吗?

 for (int zhangshu = 0; zhangshu * arr[row] <= col; zhangshu++);循环是每次枚举下一行的值的。既然下一行的值已经存储在 x1 x2 x3 .......处,那我们还要这个for循环干嘛呢?

时间复杂度优化代码如下:

 //动态规划 针对时间复杂度的优化版
    public static int ways3(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }

        //老样子
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        //最后一行的第一列为1,其余都为0。 根据递归 return aim == 0 ? 1 : 0;得到
        dp[N][0] = 1;

        //双层for循环,动态规划的老套路。一维数组arr作为横坐标,aim作为纵坐标
        for (int row = N -1 ; row >= 0; row--) {     //row代表递归中的 index
            for (int col = 0; col <= aim; col++) {   //col代表递归中的 aim

                // 每一列都存在aim为0张的情况,
                // 下一行的当前列
                dp[row][col] = dp[row + 1][col];

                //当前下标col, 那么前一处的下标不就是 col - arr[row] 吗 ?
                if (col - arr[row] >= 0) {
                    /**
                     * for循环中的 下一行的 dp[row + 1][col - (zhangshu * arr[row])]列
                     *
                     * 直接转化为当前行的 前 col - arr[row] 列。
                     */
                    dp[row][col] += dp[row][col - arr[row]];
                }
            }
        }
        return dp[0][aim];
    }

 以上代码已经对时间复杂度进行了优化,下面按照空间压缩的思维,对空间复杂度再进行优化。

在算法28 算法28:力扣64题,最小路径和------------样本模型-CSDN博客 和 算法29 算法29:不同路径问题(力扣62和63题)--针对算法28进行扩展-CSDN博客中,我已经详细的分析过空间压缩的技巧以及思维。下面直接上代码

空间压缩代码

//动态规划 针对空间复杂度的优化版
    public static int ways4(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }

        //老样子
        int N = arr.length;
        int[] dp = new int[aim + 1];
        //最后一行的第一列为1,其余都为0。 根据递归 return aim == 0 ? 1 : 0;得到
        dp[0] = 1;

        //双层for循环,动态规划的老套路。一维数组arr作为横坐标,aim作为纵坐标
        for (int row = N -1 ; row >= 0; row--) {     //row代表递归中的 index
            for (int col = 0; col <= aim; col++) {   //col代表递归中的 aim
                if (col - arr[row] >= 0) {
                    dp[col] += dp[col - arr[row]];
                }
            }
        }
        return dp[aim];
    }

从递归到动态规划,再对动态规划代码进行时间复杂度空间复杂度进行双重优化。本文重点介绍了从左往右模型的技巧、动态规划的推理、时间复杂度的优化。至于空间复杂度,由于前面2篇博客已经重点分析了,所有没有过多累赘叙述。

从左往右模型总结:

1、 针对固定集合,就是讨论要和不要的累加和

2.    针对非固定集合,面值固定,张数不固定。口诀就是讨论要与不要,要的话逐步讨论要几张的累加和

下面贴出完整代码,并加入对数器进行海量数据测试:

package code03.动态规划_07.lesson4;

/**
 * arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。
 * 每个值都认为是一种面值,且认为张数是无限的。
 * 返回组成aim的方法数
 * 例如:arr = {1,2},aim = 4
 * 方法如下:1+1+1+1、1+1+2、2+2
 * 一共就3种方法,所以返回3
 */
public class ContainWaysNotLimitPaper_05 {

    //递归版本
    public static int ways(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        return process(arr, 0, aim);
    }

    public static int process (int[] arr, int index, int aim)
    {
        if (index == arr.length) { // 没钱了
            return aim == 0 ? 1 : 0;
        }

        //讨论不同货币组成aim的可能
        int ways = 0;
        //因为货币张数无限,所有需要讨论每一种情况。此次,就不是简单的要与不要的问题了
        for (int zhangshu = 0; zhangshu * arr[index] <= aim; zhangshu++) {
            /**
             * 此处,还是讨论要与不要的问题。
             * 0张代表不要
             * 要的话需要讨论要几张 :
             * 要1 张, 那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
             * 要2 张,那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
             *  依次类推
             */
            ways += process(arr, index + 1, aim - zhangshu * arr[index]);
        }
        return ways;
    }

    //动态规划
    public static int ways2(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }

        //老样子
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        //最后一行的第一列为1,其余都为0。 根据递归 return aim == 0 ? 1 : 0;得到
        dp[N][0] = 1;

        //双层for循环,动态规划的老套路。一维数组arr作为横坐标,aim作为纵坐标
        for (int row = N -1 ; row >= 0; row--) {     //row代表递归中的 index
            for (int col = 0; col <= aim; col++) {   //col代表递归中的 aim

                //此处是比较复杂的,完全套用递归内部的代码。这一层的for循环就是对
                //要的张数进行讨论。
                int ways = 0;
                //此处zhangshu * arr[row] <= col是唯一变化。重点理解
                for (int zhangshu = 0; zhangshu * arr[row] <= col; zhangshu++) {
                    /**
                     * 此处,还是讨论要与不要的问题。
                     * 0张代表不要
                     * 要的话需要讨论要几张 :
                     * 要1 张, 那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
                     * 要2 张,那么后面的数组需要进行递归继续讨论要还是不要,要的话要几张
                     *  依次类推
                     */
                    //根据ways += process(arr, row + 1, aim - zhangshu * arr[row]);推导
                    //此处的aim - zhangshu * arr[index] 需要变成 col - zhangshu * arr[index]
                    ways += dp[row + 1][col - (zhangshu * arr[row])];
                }
                dp[row][col] = ways;
            }
        }
        return dp[0][aim];
    }

    //动态规划 针对时间复杂度的优化版
    public static int ways3(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }

        //老样子
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        //最后一行的第一列为1,其余都为0。 根据递归 return aim == 0 ? 1 : 0;得到
        dp[N][0] = 1;

        //双层for循环,动态规划的老套路。一维数组arr作为横坐标,aim作为纵坐标
        for (int row = N -1 ; row >= 0; row--) {     //row代表递归中的 index
            for (int col = 0; col <= aim; col++) {   //col代表递归中的 aim

                // 每一列都存在aim为0张的情况,
                // 下一行的当前列
                dp[row][col] = dp[row + 1][col];

                //当前下标col, 那么前一处的下标不就是 col - arr[row] 吗 ?
                if (col - arr[row] >= 0) {
                    /**
                     * for循环中的 下一行的 dp[row + 1][col - (zhangshu * arr[row])]列
                     *
                     * 直接转化为当前行的 前 col - arr[row] 列。
                     */
                    dp[row][col] += dp[row][col - arr[row]];
                }
            }
        }
        return dp[0][aim];
    }

    //动态规划 针对空间复杂度的优化版
    public static int ways4(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }

        //老样子
        int N = arr.length;
        int[] dp = new int[aim + 1];
        //最后一行的第一列为1,其余都为0。 根据递归 return aim == 0 ? 1 : 0;得到
        dp[0] = 1;

        //双层for循环,动态规划的老套路。一维数组arr作为横坐标,aim作为纵坐标
        for (int row = N -1 ; row >= 0; row--) {     //row代表递归中的 index
            for (int col = 0; col <= aim; col++) {   //col代表递归中的 aim
                if (col - arr[row] >= 0) {
                    dp[col] += dp[col - arr[row]];
                }
            }
        }
        return dp[aim];
    }

    // 为了测试
    public static int[] randomArray(int maxLen, int maxValue) {
        int N = (int) (Math.random() * maxLen);
        int[] arr = new int[N];
        boolean[] has = new boolean[maxValue + 1];
        for (int i = 0; i < N; i++) {
            do {
                arr[i] = (int) (Math.random() * maxValue) + 1;
            } while (has[arr[i]]);
            has[arr[i]] = true;
        }
        return arr;
    }

    // 为了测试
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
     /*   int[] arr = {1,2,3};
        int aim = 13;
        System.out.println(ways(arr, aim));
        System.out.println(ways2(arr, aim));
        System.out.println(ways3(arr, aim));
        System.out.println(ways4(arr, aim));*/

        // 为了测试

            int maxLen = 10;
            int maxValue = 30;
            int testTime = 1000000;
            System.out.println("测试开始");
            for (int i = 0; i < testTime; i++) {
                int[] arr = randomArray(maxLen, maxValue);
                int aim = (int) (Math.random() * maxValue);
                int ans1 = ways(arr, aim);
                int ans2 = ways2(arr, aim);
                int ans3 = ways3(arr, aim);
                int ans4 = ways4(arr, aim);
                if (ans1 != ans2 || ans1 != ans3 || ans1 != ans4) {
                    System.out.println("Oops!");
                    printArray(arr);
                    System.out.println(aim);
                    System.out.println(ans1);
                    System.out.println(ans2);
                    System.out.println(ans3);
                    break;
                }
            }
            System.out.println("测试结束");

    }
}

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

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

相关文章

URLConnection()和openStream()两个方法产生SSRF的原理和修复方法

今年是自主研发的第三个年份&#xff0c;也是重视安全的年份。 转一篇小文章&#xff1a; 0x00 前言 SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定 URL 地址获取网页文本内容&#xff0c;加载指定地址的图…

《算法导论》复习——CHP1、CHP2 算法基础

基本定义&#xff1a; 算法是一组有穷的规则&#xff0c;规定了解决某一特定类型问题的一系列运算。 关心算法的正确性和效率。 算法的五个重要特性&#xff1a;确定性、能行性、输入、输出、有穷性。 基础方法&#xff1a; 伪代码&#xff08;Pseudocode&#xff09;&#xff…

C# .Net学习笔记—— 异步和多线程(Task)

一、概念 Task是DotNet3.0之后所推出的一种新的使用多线程的方式&#xff0c;它是基于ThreadPool线程进行封装的。 二、使用多线程的时机 任务能够并发运行的时候&#xff0c;提升速度&#xff1b;优化体验 三、基本使用方法 private void button5_Click(object sender, Ev…

Spark内核解析-数据存储5(六)

1、Spark的数据存储 Spark计算速度远胜于Hadoop的原因之一就在于中间结果是缓存在内存而不是直接写入到disk&#xff0c;本文尝试分析Spark中存储子系统的构成&#xff0c;并以数据写入和数据读取为例&#xff0c;讲述清楚存储子系统中各部件的交互关系。 1.1存储子系统概览 …

RT-Thread 14. GD32F330RBT6 Keil4移植RT-Thread

1.增加rt-thread-v4.1.0源码 rt-thread-v4.1.0\bsp\gd32350r-eval复制重命名为gd32f330_v1 2.文件组织结构 Usr&#xff1a;存放App任务应用&#xff0c;属于应用层&#xff0c;完全脱离硬件 CMSIS&#xff1a;硬件层&#xff0c;启动文件、系统文件 Driver&#xff1a;硬件外…

Spring实现IoC:依赖注入/构造注入

● 控制反转&#xff0c;反转的是什么&#xff1f; ○ 将对象的创建权利交出去&#xff0c;交给第三方容器负责。 ○ 将对象和对象之间关系的维护权交出去&#xff0c;交给第三方容器负责。 ● 控制反转这种思想如何实现呢&#xff1f; ○ DI&#xff08;Dependency Injection&…

通过IP地址如何进行网络安全防护

IP地址在网络安全防护中起着至关重要的作用&#xff0c;可以用于监控、过滤和控制网络流量&#xff0c;识别潜在威胁并加强网络安全。以下是通过IP地址进行网络安全防护的一些建议&#xff1a; 1. 建立IP地址白名单和黑名单&#xff1a; 白名单&#xff1a;确保只有授权的IP地…

UMLChina书籍大全(2024)软件方法人月神话人件企业应用架构模式UML参考手册彩色UML建模领域驱动设计对象设计……

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 以下列出有UMLChina标记的书。 首先是写了十几年还没有写完的软件方法 其他的是译作&#xff1a; 人月神话 2002年&#xff0c;UMLChina和清华大学出版社合作&#xff0c;翻译了《人…

老生常谈:Web 与低代码开发

Web技术和低代码平台是当前技术领域中的两个热门话题。它们在应用开发领域中扮演着重要的角色&#xff0c;不断被提及和讨论。本文将讨论为什么“Web与低代码”这个话题成为了“老生常谈”&#xff0c;探讨其背后的原因以及这两个概念的关系。 在当今技术飞速发展的时代&#x…

imgaug库指南(二):从入门到精通的【图像增强】之旅

文章目录 引言前期回顾代码示例小结结尾 引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应…

静态S5在项目管理中的应用与案例分享

静态S5作为一种强大的数据分析工具&#xff0c;不仅在数据处理和可视化方面表现出色&#xff0c;还在项目管理中发挥着重要作用。本篇将通过实际案例分享&#xff0c;探讨静态S5在项目管理中的应用与优势。 一、静态S5在项目管理中的应用 项目进度管理&#xff1a;静态S5通过…

每日一题——LeetCode1046.最后一块石头的重量

方法一 暴力排序 保证数组从小到大排序&#xff0c;所以最后两个就是最大的石头&#xff0c;每次取最后两个元素进行比较&#xff0c;一样重就直接下一次循环&#xff0c;不一样重就把两个石头重量差push进数组&#xff0c;把数组再次排序 循序嵌套sort排序时间复杂度较高&am…

基于SSM的汽车出租管理系统

目录 前言 开发环境以及工具 项目功能介绍 管理系统 详细设计 登录页面 客户信息管理 汽车信息管理 系统日志记录 汽车租赁功能 出租单入库检查功能 源码获取 前言 本项目是一个基于IDEA和Java语言开发基于SSM的汽车出租管理系统。应用包含管理系统&#xff1b; 汽…

LeetCode(33) 搜索旋转排序数组

整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], ..., nums[n-1], nums[0], nums[1], ..…

AD自动求导算法

Jacobian matrix 雅可比矩阵 在后面的前向反向积累会用到。AD需要用到中间变量的导数&#xff0c;所以Jacobian matrix就是来计算这些的。 Hessian矩阵 在牛顿法的优化中有应用。 牛顿法 Newton-type methods 自动求导 Automatic Differentiation, AD 数值微分 import n…

构建企业级AI中台,实现业务场景价值闭环

从数据资产到模型资产&#xff0c;大模型的发展&#xff0c;正在加速企业构建AI中台&#xff0c;旨在让更多 AI 的能力能够被构建出来、能够被管理起来、能够面向业务开放起来&#xff0c;打通从数据到模型到最后决策的全链路。 本文将从为什么建AI中台、如何建AI中台、企业AI…

盲盒、一番赏小程序搭建,打开线上盲盒市场

近几年&#xff0c;我国潮玩市场发展非常迅速&#xff0c;在互联网的影响下&#xff0c;盲盒更是迅速走红网络&#xff0c;深受年轻人的喜欢&#xff0c;各大社交平台上关于盲盒的讨论度也是层出不穷。 一番赏与盲盒的机制都是差不多的&#xff0c;盲盒是在包装一样的盒子中放…

ubuntu系统如何安装man命令的中文文档

ubuntu系统如何安装man命令的中文文档 背景 在Linux系统上需要使用一些命令的时候&#xff0c;往往会通过man命令去查询命令的使用方法和参数的说明&#xff0c;但是这些文档说明都是英文的&#xff0c;怎么样才能变成中文的文档&#xff0c;看上去更加清晰呢&#xff1f; 解…

到底谁还在用企业公示系统,聪明的人已经...

随着互联网和数字经济的发展&#xff0c;企业背调变得越来越重要了。毕竟现在企业数量不断增加&#xff0c;据调查数据显示&#xff1a;截止到23年8月11日全国市场总量已经达到3.4亿户&#xff0c;今年新增2049万户。 其中&#xff0c;企业总量9922万户&#xff0c;今年新增621…

线性代数-第五课,第六课,第七课,第八课

第五课 判断某向量是否可由某向量组线性表示 把向量组组成一个行列式&#xff0c;计算行列式的秩 把所有向量放在一起构成一个行列式&#xff0c;计算行列式的秩 如果两个行列式的秩相等&#xff0c;表示可以线性表示&#xff0c;写答案的格式如下 线性表示&#xff1a;bk…