漫画:程序教你寻找股票买入卖出的最佳时机(动态规划)
作者 | 小灰
来源 | 程序员小灰(ID:chengxuyuanxiaohui)
前一段时间,我们介绍了一个经典算法题目:寻找股票买入卖出的最佳时机。这个题目看似简单,却有着许多种变化。
在上一篇中,我们讲解了最多1次买卖和无限次买卖的解法,那么,如果只允许最多2次股票买卖,如何寻找最佳时机呢?
我们仍然以之前的数组为例:
首先,寻找到1次买卖限制下的最佳买入卖出点:
两次买卖的位置是不可能交叉的,所以我们找到第1次买卖位置后,把这一对买入卖出点以及它们中间的元素全部剔除掉:
接下来,我们按照同样的思路,再从剩下的元素中寻找第2次买卖的最佳买入卖出点:
这样一来,我们就找到了最多2次买卖情况下的最佳选择:
对于上图的这个数组,如果独立两次求解,得到的最佳买入卖出点分别是【1,9】和【6,7】,最大收益是 (9-1)+(7-6)=9:
但实际上,如果选择【1,8】和【3,9】,最大收益是(8-1)+(9-3)=13>9:
所谓动态规划,就是把复杂的问题简化成规模较小的子问题,再从简单的子问题自底向上一步一步递推,最终得到复杂问题的最优解。
首先,让我们分析一下当前这个股票买卖问题,这个问题要求解的是一定天数范围内、一定交易次数限制下的最大收益。
既然限制了股票最多买卖2次,那么股票的交易可以划分为5个阶段:
没有买卖
第1次买入
第1次卖出
第2次买入
第2次卖出
我们把股票的交易阶段设为变量k(用从0到4的数值表示),把天数范围设为变量n。而我们求解的最大收益,受这两个变量影响,用函数表示如下:
最大收益 = F(n,k)(0<=k<=4,n>=1)
既然函数和变量已经确定,接下来我们就要确定动态规划的两大要素:
1.问题的初始状态
2.问题的状态转移方程式
问题的初始状态是什么呢?我们假定交易天数的范围只限于第1天,也就是n=1的情况:
1.如果没有买卖,也就是k=0时,最大收益显然是0,也就是 F(1,0)= 0
2.如果有1次买入,也就是k=1时,相当于凭空减去了第1天的股价,最大收益是负的当天股价,也就是 F(1,1)= -price[0]
3.如果有1次买出,也就是k=2时,买卖抵消(当然,这没有实际意义),最大收益是0,也就是 F(1,2)= 0
4.如果有2次买入,也就是k=3时,同k=1的情况,F(1,3)= 0
5.如果有2次卖出,也就是k=4时,同k=2的情况,F(1,4)= 0
确定了初始状态,我们再来看一看状态转移。假如天数范围限制从n-1天增加到n天,那么最大收益会有怎样的变化呢?
这取决于现在处于什么阶段(是第几次买入卖出),以及对第n天股价的操作(买入、卖出或观望)。让我们对各个阶段情况进行分析:
1.假如之前没有任何买卖,而第n天仍然观望,那么最大收益仍然是0,即 F(n,0) = 0
2.假如之前没有任何买卖,而第n天进行了买入,那么最大收益是负的当天股价,即 F(n,1)= -price[n-1]
3.假如之前有1次买入,而第n天选择观望,那么最大收益和之前一样,即 F(n,1)= F(n-1,1)
4.假如之前有1次买入,而第n天进行了卖出,那么最大收益是第1次买入的负收益加上当天股价,即那么 F(n,2)= F(n-1,1)+ price[n-1]
5.假如之前有1次卖出,而第n天选择观望,那么最大收益和之前一样,即 F(n,2)= F(n-1,2)
6.假如之前有1次卖出,而第n天进行2次买入,那么最大收益是第1次卖出收益减去当天股价,即F(n,3)= F(n-1,2) - price[n-1]
7.假如之前有2次买入,而第n天选择观望,那么最大收益和之前一样,即 F(n,3)= F(n-1,3)
8.假如之前有2次买入,而第n天进行了卖出,那么最大收益是第2次买入收益减去当天股价,即F(n,4)= F(n-1,3) + price[n-1]
9.假如之前有2次卖出,而第n天选择观望(也只能观望了),那么最大收益和之前一样,即 F(n,4)= F(n-1,4)
最后,我们把情况【2,3】,【4,5】,【6、7】,【8,9】合并,可以总结成下面的5个方程式:
F(n,0) = 0
F(n,1)= max(-price[n-1],F(n-1,1))
F(n,2)= max(F(n-1,1)+ price[n-1],F(n-1,2))
F(n,3)= max(F(n-1,2)- price[n-1],F(n-1,3))
F(n,4)= max(F(n-1,3)+ price[n-1],F(n-1,4))
从后面4个方程式中,可以总结出每一个阶段最大收益和上一个阶段的关系
F(n,k) = max(F(n-1,k-1)+ price[n-1],F(n-1,k))
由此我们可以得出,完整的状态转移方程式如下:
在表格中,不同的行代表不同天数限制下的最大收益,不同的列代表不同买卖阶段的最大收益。
我们仍然利用之前例子当中的数组,以此为基础来填充表格:
首先,我们为表格填充初始状态:
接下来,我们开始填充第2行数据。
没有买卖时,最大收益一定为0,因此F(2,0)的结果是0:
根据之前的状态转移方程式,F(2,1)= max(F(1,0)-2,F(1,1))= max(-2,-1)= -1,所以第2行第2列的结果是-1:
F(2,2)= max(F(1,1)+2,F(1,2))= max(1,0)= 1,所以第2行第3列的结果是1:
F(2,3)= max(F(1,2)-2,F(1,3))= max(-2,-1)= -1,所以第2行第4列的结果是-1:
F(2,4)= max(F(1,3)+2,F(1,4))= max(1,0)= 1,所以第2行第5列的结果是1:
接下来我们继续根据状态转移方程式,填充第3行的数据:
接下来填充第4行:
以此类推,我们一直填充完整个表格:
如图所示,表格中最后一个数据13,就是全局的最大收益。
//最大买卖次数private static int MAX_DEAL_TIMES = 2;public static int maxProfitFor2Time(int[] prices) {if(prices==null || prices.length==0) {return 0;}//表格的最大行数int n = prices.length;//表格的最大列数int m = MAX_DEAL_TIMES*2+1;//使用二维数组记录数据int[][] resultTable = new int[n][m];//填充初始状态resultTable[0][1] = -prices[0];resultTable[0][3] = -prices[0];//自底向上,填充数据for(int i=1;i<n;++i) {for(int j=1;j<m;j++){if((j&1) == 1){resultTable[i][j] = Math.max(resultTable[i-1][j], resultTable[i-1][j-1]-prices[i]);}else {resultTable[i][j] = Math.max(resultTable[i-1][j], resultTable[i-1][j-1]+prices[i]);}}}//返回最终结果return resultTable[n-1][m-1];}
//最大买卖次数 private static int MAX_DEAL_TIMES = 2;public static int maxProfitFor2TimeV2(int[] prices) { if(prices==null || prices.length==0) { return 0; } //表格的最大行数 int n = prices.length; //表格的最大列数 int m = MAX_DEAL_TIMES*2+1; //使用一维数组记录数据 int[] resultTable = new int[m]; //填充初始状态 resultTable[1] = -prices[0]; resultTable[3] = -prices[0]; //自底向上,填充数据 for(int i=1;i<n;++i) { for(int j=1;j<m;j++){ if((j&1) == 1){ resultTable[j] = Math.max(resultTable[j], resultTable[j-1]-prices[i]); }else { resultTable[j] = Math.max(resultTable[j], resultTable[j-1]+prices[i]); } } } //返回最终结果 return resultTable[m-1]; }
在这段代码中,resultTable从二维数组简化成了一维数组。由于最大买卖次数是常量,所以算法的时间复杂度也从O(n)降低到了O(1)。
public static int maxProfitForKTime(int[] prices, int k) {if(prices==null || prices.length==0) {return 0;}//表格的最大行数int n = prices.length;//表格的最大列数int m = k*2+1;//使用一维数组记录数据int[] resultTable = new int[m];//填充初始状态resultTable[1] = -prices[0];resultTable[3] = -prices[0];//自底向上,填充数据for(int i=1;i<n;++i) {for(int j=1;j<m;j++){if((j&1) == 1){resultTable[j] = Math.max(resultTable[j], resultTable[j-1]-prices[i]);}else {resultTable[j] = Math.max(resultTable[j], resultTable[j-1]+prices[i]);}}}//返回最终结果return resultTable[m-1];}
更多精彩推荐
☞程序员必备!CSDN 公众号新功能上线!现在体验有惊喜!
☞任正非:明年至少招聘 8000 名应届生,华为人才将分为三类
☞在吗?我要讲件大事了,你绝对不知道CSDN公众号还有这个功能!错过后悔!
☞隐私数据在隐私AI框架中的安全流动☞荷兰政府用大数据预测天气预防自然灾害,他们是怎么做的?
☞以太坊开发者的常见误解
点分享点点赞点在看
漫画:程序教你寻找股票买入卖出的最佳时机(动态规划)相关推荐
- 漫画:寻找股票买入卖出的最佳时机(动态规划)
前一段时间,我们介绍了一个经典算法题目:寻找股票买入卖出的最佳时机.这个题目看似简单,却有着许多种变化. 在上一篇中,我们讲解了最多1次买卖和无限次买卖的解法,那么,如果只允许最多2次股票买卖,如何寻 ...
- 漫画:寻找股票买入卖出的最佳时机
----- 第二天 ----- 什么意思呢?让我们来举个例子,给定如下数组: 该数组对应的股票涨跌曲线如下: 显然,从第2天价格为1的时候买入,从第5天价格为8的时候卖出,可以获得最大收益: 此时 ...
- 股票买入卖出 LeetCode 变形题 度小满
度小满,股票买入卖出变种题. 最大化收益,最小化买卖次数. def stock():profit = 0length = len(trade)count = 0for i in range(lengt ...
- 股票买入卖出问题 leetcode123(最多k次交易)
leetcode123 问题链接 https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ 给定一些天数的股票价格list, ...
- 网易游戏面试--两次股票买入卖出的最大收益
第一问: 对于只有一次买入卖出的最大收益可以参照http://blog.csdn.net/calmreason/article/details/7904062,已经将的非常详细了 第二问: 当时做完这 ...
- LeetCode 121 Best Time to Buy and Sell Stock(股票买入卖出的最佳时间)
翻译 话说你有一个数组,其中第i个元素表示在第i天的股票价格.如果你被只被允许最多一次交易(例如,买入然后卖出一个股票),设计一个算法并找出最大利润. 原文 Say you have an array ...
- LeetCode 122 Best Time to Buy and Sell Stock II(股票买入卖出的最佳时间 II)
翻译 话说你有一个数组,其中第i个元素表示第i天的股票价格.设计一个算法以找到最大利润.你可以尽可能多的进行交易(例如,多次买入卖出股票).然而,你不能在同一时间来多次交易.(例如,你必须在下一次买入 ...
- 动态规划--股票(一次买入卖出和两次买入卖出)
动态规划–股票(一次买入卖出和两次买入卖出)(c++) ##一次买入卖出 如果用一个数组代表股票每天的价格,可以选择从某一天买入,然后之后的一天卖出,求能够获得的最大收益. 例如,一只股票在某些时间节 ...
- 基于北向资金的择时买入卖出策略复现
点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 位卑未敢忘忧国,事定犹须待阖棺. ...
最新文章
- 几步教你轻松搭建一个Java Web项目
- 老李分享:系统可用性评估
- 解决socket粘包的两种low版模式 os.popen()和struct模块
- [Swift]LeetCode326. 3的幂 | Power of Three
- elasticsearch_script_01
- 《零基础看得懂的C语言入门教程 》——(十二)原来结构体是这么回事
- LeetCode 418. 屏幕可显示句子的数量(DP)*
- 【Docker】Error: No such image: gotok8s/kube-proxy:v1.16.5
- 服务器中修改项目端口,c#-在Visual Studio 2013中更改项目端口号
- 5复数与复变函数(五)
- 构建大数据平台的必要性
- 计算机打字过程中,关于电脑打字过程中的疑问?
- android jni调试打印char阵列
- 前端实习日记(6月前两周)
- 【Unity优化篇】| Unity3D场景 常用优化策略,遮挡剔除、层消隐距离技术 和 LOD多层次细节
- python学什么内容_老男孩Python都需要学什么内容?老男孩教育
- MySql 练习- 留存率计算
- windows无法连接到打印机
- [Unity插件]物体轮廓特效HighlightPlus
- Yocto系列讲解[技巧篇]81 - 如何打patch修复打补丁出错的recipe