动态规划解决股票交易问题的三种经典变种
2026/9/11 10:37:35 网站建设 项目流程

1. 股票交易算法专题解析

最近在算法训练中遇到了几个经典的股票交易问题,这类问题在面试和实际量化交易中都非常常见。今天我想重点分享三个变种问题的解法:允许最多K次交易的买卖股票问题、包含冷冻期的交易问题以及含手续费的交易问题。这三个问题层层递进,覆盖了动态规划在金融算法中的典型应用场景。

股票交易类算法题的核心在于状态转移的设计。与常规动态规划不同,这类问题需要同时考虑持有股票和未持有股票两种状态,以及交易次数、冷冻期等额外约束条件。我们先从最基础的框架开始,逐步扩展到更复杂的场景。

2. 188. 买卖股票的最佳时机IV(最多K次交易)

2.1 问题分析与状态定义

这是买卖股票系列中最通用的一个变种,允许最多完成K笔交易(买入和卖出算一次完整交易)。相比无限次交易的简单情况,这里需要额外跟踪交易次数的限制。

关键点在于定义状态数组:

  • dp[i][j][0]表示第i天结束时,已经完成j次交易,当前不持有股票的最大利润
  • dp[i][j][1]表示第i天结束时,已经完成j次交易,当前持有股票的最大利润

状态转移方程需要考虑:

  1. 第i天不持有股票:可能是前一天就不持有,或者前一天持有今天卖出(完成一次交易)
  2. 第i天持有股票:可能是前一天就持有,或者前一天不持有今天买入(注意交易次数只在卖出时增加)

2.2 代码实现与优化

def maxProfit(k, prices): if not prices: return 0 n = len(prices) if k >= n // 2: # 相当于无限次交易 return sum(max(prices[i+1]-prices[i],0) for i in range(n-1)) dp = [[[0]*2 for _ in range(k+1)] for __ in range(n)] for j in range(k+1): dp[0][j][1] = -prices[0] for i in range(1, n): for j in range(1, k+1): dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]) dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]) return dp[-1][k][0]

注意:当k很大时(k ≥ n/2),问题退化为无限次交易的情况,可以直接用贪心算法求解,避免不必要的空间消耗。

2.3 空间优化技巧

原始三维DP可以优化为二维数组,因为每天的状态只依赖前一天的状态:

def maxProfit(k, prices): if not prices: return 0 n = len(prices) if k >= n // 2: return sum(max(prices[i+1]-prices[i],0) for i in range(n-1)) dp = [[0]*2 for _ in range(k+1)] for j in range(k+1): dp[j][1] = -prices[0] for i in range(1, n): for j in range(k, 0, -1): # 反向遍历避免覆盖 dp[j][0] = max(dp[j][0], dp[j][1] + prices[i]) dp[j][1] = max(dp[j][1], dp[j-1][0] - prices[i]) return dp[k][0]

3. 309. 最佳买卖股票时机含冷冻期

3.1 冷冻期问题的特殊性

冷冻期意味着卖出股票后,无法在第二天立即买入,需要至少休息一天。这改变了状态转移的规则,我们需要更细致地区分不同状态:

  1. 持有股票:可能是前一天就持有,或者前两天卖出后今天买入(跳过冷冻期)
  2. 不持有股票且处于冷冻期:只能是今天卖出股票
  3. 不持有股票且不处于冷冻期:可能是前一天就不持有且不处于冷冻期

3.2 状态机解法

def maxProfit(prices): if not prices: return 0 n = len(prices) hold = -prices[0] # 持有股票 cool = 0 # 处于冷冻期 free = 0 # 自由状态 for i in range(1, n): new_hold = max(hold, free - prices[i]) new_cool = hold + prices[i] new_free = max(free, cool) hold, cool, free = new_hold, new_cool, new_free return max(cool, free)

这种状态机表示更加直观,三种状态清晰地反映了问题的约束条件。在实际量化交易系统中,这种模型更容易扩展和修改。

4. 714. 买卖股票的最佳时机含手续费

4.1 手续费处理方式

手续费可以在买入或卖出时扣除,但通常选择在卖出时扣除更为方便(避免买入时资金不足的情况)。这只需要在状态转移方程中减去手续费即可。

4.2 无限次交易带手续费解法

def maxProfit(prices, fee): if not prices: return 0 n = len(prices) empty = 0 # 不持有股票 hold = -prices[0] # 持有股票 for i in range(1, n): empty = max(empty, hold + prices[i] - fee) hold = max(hold, empty - prices[i]) return empty

实操技巧:手续费可以看作增加了卖出成本,因此在卖出时减去fee。如果选择在买入时扣除手续费,则需要确保账户资金充足。

4.3 贪心算法替代方案

对于含手续费的无限次交易问题,还可以使用贪心算法:

def maxProfit(prices, fee): if not prices: return 0 profit = 0 min_price = prices[0] for price in prices[1:]: if price < min_price: min_price = price elif price > min_price + fee: profit += price - min_price - fee min_price = price - fee # 关键步骤,防止重复扣除手续费 return profit

这种解法在价格连续上涨时更为高效,避免了动态规划的空间开销。

5. 综合比较与实战技巧

5.1 三种问题的对比分析

问题类型状态维度关键约束时间复杂度空间复杂度
最多K次交易天数×交易次数×持仓状态交易次数限制O(nk)O(nk)可优化到O(k)
含冷冻期天数×3种状态卖出后必须休息一天O(n)O(1)
含手续费天数×持仓状态每次交易扣除固定费用O(n)O(1)

5.2 常见错误与调试技巧

  1. 边界条件处理:空价格列表、单日价格、k=0等情况需要特殊处理
  2. 状态初始化:持有股票的初始状态应为-prices[0]
  3. 交易次数计数:只在卖出时增加交易次数,买入时不增加
  4. 冷冻期实现:确保卖出后至少休息一天才能买入
  5. 手续费扣除时机:统一在买入或卖出时扣除,不要重复扣除

调试时可以打印每天的DP表格,验证状态转移是否符合预期。

5.3 性能优化建议

  1. 对于大k值(k ≥ n/2),先检查并转为无限次交易问题
  2. 使用滚动数组优化空间复杂度
  3. 在含手续费问题中,贪心算法可能更高效
  4. 在实际应用中,可以结合价格波动特征提前终止循环

6. 扩展与应用场景

6.1 实际量化交易中的应用

这些算法不仅存在于面试题中,在真实量化交易系统中也有广泛应用:

  1. 高频交易策略评估:限制交易次数防止过度交易
  2. 风险管理:通过冷冻期控制交易频率
  3. 成本控制:将手续费纳入收益计算
  4. 组合优化:作为更复杂策略的基础组件

6.2 与其他算法的结合

  1. 与均值回归策略结合:在价格偏离均值时触发交易
  2. 与技术指标结合:使用MACD、RSI等指标作为买卖信号
  3. 与机器学习结合:用预测模型替代固定交易规则

6.3 变种问题挑战

  1. 多资产组合交易:同时考虑多只股票的相关性
  2. 非线性手续费:按交易金额比例收取
  3. 市场冲击成本:大额交易影响市场价格
  4. 限制卖空:只能先买后卖

在实际开发量化交易系统时,我通常会先实现这些基础版本,然后根据具体需求逐步添加更复杂的约束条件。动态规划框架的灵活性使其能够适应各种变种问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询