php股票交易程序,PHP求解股票买卖问题

动态规划比较经典的题目

给定一支股票一段日期内的的每日价格price

题目1: 求这段时间内买卖一次获得的最大收益

思路:即找到一组i和j,使得 price[i] - price[j] 最大,且i

状态转移方程式为:profit = max(profit, price[i] - minPrice);

而minPrice = min(minPrice, price[i]);

function solution1($price) {

if(!$price)

return 0;

$profit = 0;

$min = $price[0];

for ($i = 1; $i < count($price); $i++) {

$min = min($min, $price[$i]);

$profit = max($profit, $price[$i] - $min);

}

return $profit;

}

题目2: 求这段时间内无限次买卖可获得的最大收益

思路一、动态规划法:对某一时间点i,有两种状态,持有或不持有,那么同理在i-1天也有这两种状态,用hold表示在i天持有的状态下的最大收益,free表示在i天不持有的最大收益,状态转移方程式就可以写为

hold[i] = max(hold[i - 1], free[i - 1] - price[i]);//持有状态有两种情况:前一天就持有的收益和前一天空仓当天买入的收益做比较

free[i] = max(free[i - 1], hold[i - 1] + price[i]);//不持有有两种情况:前一天就不持有的收益和前一天持有当天卖出的收益做比较

状态初始条件为:

hold[0] = -price[0];

free[0] = 0;

状态转移方程式可以优化空间效率,因为当前收益总是单纯依赖前一天的收益,因此可写为:

hold = max(hold, free - price[i]);

free = max(free, hold + price[i]);

function solution2($price) {

if(!$price)

return 0;

$hold = -$price[0];

$free = 0;

for ($i = 1; $i < count($price); $i++) {

$pre_hold = $hold;

$hold = max($hold, $free - $price[$i]);

$free = max($free, $pre_hold + $price[$i]);

}

return $free;

}

思路二、贪心法:

只要后一天比前一天贵,就卖出,比较容易理解

题目3: 带冷却时间的购买,即卖出后必须等待一天,才能再次购买

思路一:

这里有两种解题思路,先看比较简单的一种,与题目2基本一致,只是第i天的持有状态要从第i-2天转移来而不是i-1天,状态转移公式改为:

hold[i] = max(hold[i - 1], free[i - 2] - price[i]);

free[i] = max(free[i - 1], hold[i - 1] + price[i]);

状态初始条件要改为:

hold[0] = -price[0];

hold[1] = max(hold[0], -price[1]);

free[0] = 0;

free[1] = max(free[0], hold[0]+price[1]);

做一下空间效率优化

hold = max(hold, pre_free - price[i]);//pre_free不是前一天,而是前两天,在状态转移方程式等号右边中的free本身就是前一天的状态

free = max(free, hold + price[i]);

状态初始条件:

hold = max(-price[0], -price[1]);

pre_free = 0;

free = max(pre_free, hold + price[1]);

function solution4($price) {

if(!$price)

return 0;

$hold = max(-$price[0], -$price[1]);

$pre_free = 0;

$free = max($pre_free, $hold+$price[1]);

for ($i = 2; $i < count($price); $i++) {

$pre_hold = $hold;

$hold = max($hold, $pre_free - $price[$i]);

$pre_free = $free;

$free = max($free, $pre_hold + $price[$i]);

}

return $free;

}

思路二:

第二种思路引入了rest这种状态,即冷却日期,好在rest的前一状态必为持有状态hold,状态机如图

0818b9ca8b590ca3270a3433284dd417.png

因此状态转移方程式即为(这里直接写空间优化后的):

free = max(free, rest);//stay at s0, or rest from s2

hold = max(hold, free - price[i]); //stay at s1, or buy from s0

rest = hold + price[i]; // only one way from s1

状态初始条件为:

hold = -price[0];

free = 0;

rest = 0;

function solution3($price) {

if(!$price)

return 0;

$free = 0;

$hold = -$price[0];

$rest = 0;

for ($i = 1; $i < count($price); $i++) {

$pre_hold = $hold;

$pre_free = $free;

$free = max($free, $rest);

$hold = max($hold, $pre_free - $price[$i]);

$rest = $pre_hold + $price[$i];

}

return max($free, $rest);

}

题目4: 求这段时间内最多交易两次的最大收益

思路:由于题目限制在买入之前必须未持有,因此不会产生交叉交易的情况,我们可以找到一个中间点,分成两段分别计算最大收益,转化为两个与题目1一样的子问题。

假设总共有n天preProfit[i]表示从0到i天的最大收益,postProfit[i] 表示从i天到n天的最大收益。有状态转移方程式:

preProfit[i] = max(preProfit[i-1], price[i] - minPrice);

postProfit[i] = max(postProfit[i+1], maxPrice - price[i]);

最后再同时遍历两数组,max(preProfit[i]+postProfit[i]);即为所求

function solution5($price) {

if(!$price)

return 0;

$pre_profit = array(0);

$post_profit = array();

$post_profit[count($price) - 1] = 0;

$min = $price[0];

for ($i = 1; $i < count($price); $i++) {

$min = min($min, $price[$i]);

$pre_profit[$i] = max($pre_profit[$i - 1], $price[$i] - $min);

}

$max = $price[count($price) - 1];

for ($i = count($price) - 2; $i >= 0; $i--){

$max = max($max, $price[$i]);

$post_profit[$i] = max($post_profit[$i + 1], $max - $price[$i]);

}

$max_profit = 0;

for ($i = 0; $i < count($price); $i++) {

$max_profit = max($max_profit, $pre_profit[$i] + $post_profit[$i]);

}

return $max_profit;

}

题目5: 求这段时间内最多交易k次的最大收益

思路一:参照Code Ganker 的思路,维护两个变量,原文为local和global,local[i][j]表示第i天一定有交易发生,globlal即为第i天最多进行k次交易的最大收益,其实我觉得用mustSell描述local更容易理解些,状态转移方程式为:

mustSell[i][j] = max(global[i – 1][j – 1]+max(0, profit) , mustSell[i – 1][j] +profit);

//profit = prices[i] – prices[i – 1];

global[i][j] = max(global[i – 1][j], mustSell[i][j]);

mustSell 的值在 前一天的最大收益global[i – 1][j – 1]+max(0, 当天卖出的收益) 和 前一天必须卖出+当天的收益(注意mustSell[i – 1][j]这里并不是j-1,即表示合并前一天的买卖为一次)中取大值

global很好理解,就是第i天卖与不卖之间取最大值

function solution6($price, $k){

if(!$price)

return 0;

if($k > count($price)/2)

return solution2($price);

$mustSell = array();

$global = array();

for ($i = 1; $i < count($price); $i++) {

$profit = $price[$i] - $price[$i - 1];

for ($j = 1; $j <= $k; $j++) {

$mustSell[$i][$j] = max($global[$i - 1][$j - 1]+max(0, $profit), $mustSell[$i - 1][$j] + $profit);

$global[$i][$j] = max($global[$i - 1][$j], $mustSell[$i][$j]);

}

}

return $global[count($price) - 1][$k];

}

当k > I/2时,问题退化为题目2

思路二、滚动扫描:

我们并不需要知道每个时间点买卖收益的全部信息,只需要知道每个时间点的最大收益信息就可以了。我们用free[k]表示在第k次交易卖出的最大收益,hold[k]表示在第k次交易买入的最大交易,状态转移方程式:

free[k] = max(free[k], hold[k] + price[k]);

hold[k] = max(hold[k], free[k-1] - price[k]);

function solution7($price, $k) {

if(!$price)

return 0;

if($k > count($price)/2)

return solution2($price);

$free = array(0);

for ($i = 0; $i <= $k; $i++) {

$hold[$i] = -INF;

}

for ($i = 0; $i < count($price); $i++) {

for ($j = 1; $j <= $k; $j++) {

$free[$j] = max($free[$j], $hold[$j] + $price[$i]);

$hold[$j] = max($hold[$j], $free[$j-1] - $price[$i]);

}

}

return $free[$k];

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值