当前位置:网站首页>漫画:寻找股票买入卖出的最佳时机(整合版)
漫画:寻找股票买入卖出的最佳时机(整合版)
2020-11-08 13:02:00 【osc_z3aalppw】
前一段时间,小灰发布了上下两篇关于股票买卖的算法题讲解,激发了很多小伙伴的兴趣。
这一次,小灰把这两篇漫画整合在一起,并且修改了其中的一些细节错误,感谢小伙伴们的指正。
————— 第二天 —————
什么意思呢?让我们来举个例子,给定如下数组:
该数组对应的股票涨跌曲线如下:
显然,从第2天价格为1的时候买入,从第5天价格为8的时候卖出,可以获得最大收益:
此时的最大收益是 8-1=7。
在上面这个例子中,最大值9在最小值1的前面,我们又该怎么交易?总不能让时间倒流吧?
————————————
以下图为例,假如我们已经确定价格4的时候为卖出时间点,那么此时最佳的买入时间点是哪一个呢?
我们要选择价格4之前的区间,且必须是区间内最小值,显然,这个最佳的选择是价格2的时间点。
第1步,初始化操作,把数组的第1个元素当做临时的最小价格;最大收益的初始值是0:
第2步,遍历到第2个元素,由于2<9,所以当前的最小价格变成了2;此时没有必要计算差值的必要(因为前面的元素比它大),当前的最大收益仍然是0:
第3步,遍历到第3个元素,由于7>2,所以当前的最小价格仍然是2;如我们刚才所讲,假设价格7为卖出点,对应的最佳买入点是价格2,两者差值7-2=5,5>0,所以当前的最大收益变成了5:
第4步,遍历到第4个元素,由于4>2,所以当前的最小价格仍然是2;4-2=2,2<5,所以当前的最大收益仍然是5:
第5步,遍历到第5个元素,由于3>2,所以当前的最小价格仍然是2;3-2=1,1<5,所以当前的最大收益仍然是5:
以此类推,我们一直遍历到数组末尾,此时的最小价格是1;最大收益是8-1=7:
public class StockProfit {
public static int maxProfitFor1Time(int prices[]) {
if(prices==null || prices.length==0) {
return 0;
}
int minPrice = prices[0];
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] < minPrice) {
minPrice = prices[i];
} else if(prices[i] - minPrice > maxProfit){
maxProfit = prices[i] - minPrice;
}
}
return maxProfit;
}
public static void main(String[] args) {
int[] prices = {9,2,7,4,3,1,8,4};
System.out.println(maxProfitFor1Time(prices));
}
}
我们以下图这个数组为例,直观地看一下买入卖出的时机:
在图中,绿色的线段代表价格上涨的阶段。既然买卖次数不限,那么我们完全可以在每一次低点都进行买入,在每一次高点都进行卖出。
这样一来,所有绿色的部分都是我们的收益,最大总收益就是这些局部收益的加总:
(6-1)+(8-3)+(4-2)+(7-4) = 15
至于如何判断出这些绿色上涨阶段呢?很简单,我们遍历整个数组,但凡后一个元素大于前一个元素,就代表股价处于上涨阶段。
public int maxProfitForAnyTime(int[] prices) {
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i-1])
maxProfit += prices[i] - prices[i-1];
}
return maxProfit;
}
我们仍然以之前的数组为例:
首先,寻找到1次买卖限制下的最佳买入卖出点:
两次买卖的位置是不可能交叉的,所以我们找到第1次买卖位置后,把这一对买入卖出点以及它们中间的元素全部剔除掉:
接下来,我们按照同样的思路,再从剩下的元素中寻找第2次买卖的最佳买入卖出点:
这样一来,我们就找到了最多2次买卖情况下的最佳选择:
对于上图的这个数组,如果独立两次求解,得到的最佳买入卖出点分别是【1,9】和【6,7】,最大收益是 (9-1)+(7-6)=9:
但实际上,如果选择【1,8】和【3,9】,最大收益是(8-1)+(9-3)=13>9:
当第1次最佳买入卖出点确定下来,第2次买入卖出点的位置会有哪几种情况呢?
情况1:第2次最佳买入卖出点,在第1次买入卖出点左侧
情况2:第2次最佳买入卖出点,在第1次买入卖出点右侧
情况3:第1次买入卖出区间从中间截断,重新拆分成两次买卖
那么,如何判断给定的股价数组属于那种情况呢?很简单,在第1次最大买入卖出点确定后,我们可以比较一下哪种情况带来的收益增量最大:
寻找左侧和右侧的最大涨幅比较好理解,寻找中间的最大跌幅有什么用呢?
细想一下就能知道,当第1次买卖需要被拆分开来的时候,买卖区间内的最大跌幅,就等同于拆分后的收益增量(类似于炒股的抄底操作):
这样一来,我们可以总结出下面的公式:
最大总收益 = 第1次最大收益 + Max(左侧最大涨幅,中间最大跌幅,右侧最大涨幅)
所谓动态规划,就是把复杂的问题简化成规模较小的子问题,再从简单的子问题自底向上一步一步递推,最终得到复杂问题的最优解。
首先,让我们分析一下当前这个股票买卖问题,这个问题要求解的是一定天数范围内、一定交易次数限制下的最大收益。
既然限制了股票最多买卖2次,那么股票的交易可以划分为5个阶段:
没有买卖
第1次买入
第1次卖出
第2次买入
第2次卖出
我们把股票的交易阶段设为变量m(用从0到4的数值表示),把天数范围设为变量n。而我们求解的最大收益,受这两个变量影响,用函数表示如下:
最大收益 = F(n,m)(n>=1,0<=m<=4)
既然函数和变量已经确定,接下来我们就要确定动态规划的两大要素:
1.问题的初始状态
2.问题的状态转移方程式
问题的初始状态是什么呢?我们假定交易天数的范围只限于第1天,也就是n=1的情况:
1.如果没有买卖,也就是m=0时,最大收益显然是0,也就是 F(1,0)= 0
2.如果有1次买入,也就是m=1时,相当于凭空减去了第1天的股价,最大收益是负的当天股价,也就是 F(1,1)= -price[0]
3.如果有1次买出,也就是m=2时,买卖抵消(当然,这没有实际意义),最大收益是0,也就是 F(1,2)= 0
4.如果有2次买入,也就是m=3时,同m=1的情况,F(1,3)= -price[0]
5.如果有2次卖出,也就是m=4时,同m=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,m) = max(F(n-1,m-1)+ price[n-1],F(n-1,m))
由此我们可以得出,完整的状态转移方程式如下:
在表格中,不同的行代表不同天数限制下的最大收益,不同的列代表不同买卖阶段的最大收益。
我们仍然利用之前例子当中的数组,以此为基础来填充表格:
首先,我们为表格填充初始状态:
接下来,我们开始填充第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 || k<=0) {
return 0;
}
//表格的最大行数
int n = prices.length;
//表格的最大列数
int m = k*2+1;
//使用一维数组记录数据
int[] resultTable = new int[m];
//填充初始状态
for(int i=1;i<m;i+=2) {
resultTable[i] = -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];
}
—————END—————
喜欢本文的朋友,欢迎关注公众号 程序员小灰,收看更多精彩内容
点个[在看],是对小灰最大的支持!
版权声明
本文为[osc_z3aalppw]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4352922/blog/4708141
边栏推荐
- 入门级!教你小程序开发不求人(附网盘链接)
- Istio traffic management -- progress gateway
- 你的云服务器可以用来做什么?云服务器有什么用途?
- Top 5 Chinese cloud manufacturers in 2018: Alibaba cloud, Tencent cloud, AWS, telecom, Unicom
- 如何将 PyTorch Lightning 模型部署到生产中
- 2018中国云厂商TOP5:阿里云、腾讯云、AWS、电信、联通 ...
- Flink从入门到真香(3、从集合和文件中读取数据)
- How to write a resume and project
- Entry level! Teach you how to develop small programs without asking for help (with internet disk link)
- Top 5 Chinese cloud manufacturers in 2018: Alibaba cloud, Tencent cloud, AWS, telecom, Unicom
猜你喜欢
Get PMP certificate at 51CTO College
渤海银行百万级罚单不断:李伏安却称治理完善,增速呈下滑趋势
The young generation of winner's programming life, the starting point of changing the world is hidden around
Stm32uberide download and install - GPIO basic configuration operation - debug (based on CMSIS DAP debug)
2 days, using 4 hours after work to develop a test tool
Python basic syntax variables
2018中国云厂商TOP5:阿里云、腾讯云、AWS、电信、联通 ...
Android Basics - check box
Top 5 Chinese cloud manufacturers in 2018: Alibaba cloud, Tencent cloud, AWS, telecom, Unicom
Share the experience of passing the PMP examination
随机推荐
Don't look! Full interpretation of Alibaba cloud's original data lake system! (Internet disk link attached)
供货紧张!苹果被曝 iPhone 12 电源芯片产能不足
阿里教你深入浅出玩转物联网平台!(附网盘链接)
Eight ways to optimize if else code
[Python 1-6] Python tutorial 1 -- number
From a friend recently Ali, Tencent, meituan and other P7 Python development post interview questions
Powershell 使用.Net对象发送邮件
Top 5 Chinese cloud manufacturers in 2018: Alibaba cloud, Tencent cloud, AWS, telecom, Unicom
Adobe media encoder / me 2021 software installation package (with installation tutorial)
一文读懂机器学习“数据中毒”
金融领域首个开源中文BERT预训练模型,熵简科技推出FinBERT 1.0
Flink从入门到真香(3、从集合和文件中读取数据)
Top 5 Chinese cloud manufacturers in 2018: Alibaba cloud, Tencent cloud, AWS, telecom, Unicom
C语言I博客作业03
Service architecture and transformation optimization process of e-commerce trading platform in mogujie (including ppt)
How to solve the difference between NAT IP and port IP
Bohai bank million level fines continue: Li Volta said that the governance is perfect, the growth rate is declining
What is the database paradigm
It's worth seeing! EMR elastic low cost offline big data analysis best practice (with network disk link)
Tencent, which is good at to C, how to take advantage of Tencent's cloud market share in these industries?