股票收益——动态规划系列

"""
股票收益问题是一种典型的动态规划问题,其状态主要由:1)股票交易的时间;2)股票可交易的次数;3)股票当前的状态【买入、卖出及不变】组成, 例如:
dp[3][2][1]的含义就是:今天是第三天,我现在⼿上持有着股票,⾄今最多进⾏ 2 次交易。再⽐如 dp[2][3][0] 的含义:今天是第⼆天,
我现在⼿上没有持有股票,⾄今最多进⾏ 3 次交易。
在解题过程中,通过题意,其涉及的状态都是由上面三种进行组合。 【dp的定义】
其中状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) #max( 选择 rest , 选择 sell )
解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有;
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) #max( 选择 rest , 选择 buy )
解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest,所以我今天还持有着股票;
要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。
基本的base case为:
dp[-1][k][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0。
dp[-1][k][1] = -infinity
解释:还没开始的时候,是不可能持有股票的,⽤负⽆穷表⽰这种不可能。
dp[i][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0。
dp[i][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的,⽤负⽆穷表⽰这种不可能。
"""

"""
问题1. k=1的类似问题
解题思路:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) # 由于dp[i][0][0] = 0
k相同,所以简化为:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], - prices[i])
"""
def max_profile_on_k_1(list_num):
n = len(list_num)
dp = [[0 for _ in range(2)] for _ in range(n)] #dp[i][0]/dp[i][1]
for i in range(n):
if i == 0:
dp[i][0] = 0 #base case
dp[i][1] = -list_num[i] #base case
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+list_num[i]) #状态转移方程
dp[i][1] = max(dp[i-1][1], -list_num[i]) #状态转移方程
return dp[n-1][0]

#状态压缩
def max_profile_on_k_1_v2(list_num):
n = len(list_num)
dp_i_0 = 0
dp_i_1 = -float('inf')
for i in range(n):
dp_i_0 = max(dp_i_0, dp_i_1+list_num[i]) #状态转移方程
dp_i_1 = max(dp_i_1, -list_num[i]) #状态转移方程
return dp_i_0

"""
问题2. k=indentity的类似问题 k无穷大,可以交易无数次
解题思路:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
如果k为正⽆穷,那么就可以认为 k 和 k - 1 是⼀样所以简化为:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]- prices[i])
"""

def max_profile_on_k_indentity(list_num):
n = len(list_num)
dp = [[0 for _ in range(2)] for i in range(n)]
for i in range(n):
if i == 0:
dp[i][0] = 0
dp[i][1] = -list_num[i]
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + list_num[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-list_num[i])
return dp[n-1][0]

def max_profile_on_k_indentity_v2(list_num):
n = len(list_num)
dp_i_0 = 0
dp_i_1 = -float('inf')
for i in range(n):
temp = dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1 + list_num[i])
dp_i_1 = max(dp_i_1, temp - list_num[i])
return dp_i_0

"""
问题3. k = +infinity with cooldown 每次sell之后要等⼀天才能继续交易。
解题思路:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
如果k为正⽆穷,那么就可以认为 k 和 k - 1 是⼀样所以简化为:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][0]- prices[i]) #第i天选择buy的时候,要从 i-2 的状态转移,⽽不是 i-1
"""
def maxProfit_with_cool(prices):
n = len(prices)
dp_i_0 = 0
dp_i_1 = -float('inf')
dp_pre_0 = 0
for i in range(n):
temp = dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1 + prices[i])
dp_i_1 = max(dp_i_1, dp_pre_0 - prices[i])
dp_pre_0 = temp
return dp_i_0

"""
问题4. k = +infinity with fee 每次交易要⽀付⼿续费,只要把⼿续费从利润中减去即可
解题思路:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
如果k为正⽆穷,那么就可以认为 k 和 k - 1 是⼀样所以简化为:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]- prices[i] - fee) #把⼿续费从利润中减去即可
"""
def max_profit_with_fee(list_num, fee):
n = len(list_num)
dp_i_0 = 0
dp_i_1 = -float('inf')
for i in range(n):
temp = dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1 + list_num[i])
dp_i_1 = max(dp_i_1, temp - list_num[i] - fee) #把⼿续费从利润中减去即可
return dp_i_0

"""
问题5 k=2
"""
def max_profit_k_2(prices):
dp_i10 = 0
dp_i11 = -float('inf')
dp_i20 = 0
dp_i21 = -float('inf')
for price in prices:
dp_i20 = max(dp_i20, dp_i21 + price)
dp_i21 = max(dp_i21, dp_i10 - price)
dp_i10 = max(dp_i10, dp_i11 + price)
dp_i11 = max(dp_i11, -price)

return dp_i20

"""
问题6: k=6
"""
def max_profit_k_any(max_k, prices):
n = len(prices)
if max_k > n / 2:
"""
⼀次交易由买⼊和卖出构成,⾄少需要两天。所以说有效的限制 k 应该不超
过 n/2,如果超过,就没有约束作⽤了,相当于 k = +infinity。
"""
return max_profile_on_k_indentity_v2(prices)
dp = [[[[0 for _ in range(2)] for _ in range(max_k + 1)] for _ in range(n)]]
for i in range(n):
for k in range(max_k, 0, -1):
if i == 0:
pass
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])
return dp[n - 1][max_k][0]

posted on 2021-12-15 09:49  random_boy  阅读(133)  评论(0)    收藏  举报

导航