给出一段股票的趋势图,找出某区间最大收益的方案。
$data = array(); for($i = 0; $i <= 50; $i++){ if(0 == $i){ $data[] = mt_rand(1,50); }else{ $data[] = mt_rand($data[$i-1] - 1, $data[$i-1] +1); } }
function time_chart($data){ $flip_data = array(); $max = max($data); $length = count($data); foreach($data as $k => $v){ if(isset($flip_data[$v])){ if(!is_array($flip_data[$v])){ $flip_data[$v] = array($flip_data[$v]); } $flip_data[$v] = array_merge($flip_data[$v], array($k)); }else{ $flip_data[$v] = array($k); } } for($i = $max; $i >= 0; $i--){ $data = isset($flip_data[$i]) ? $flip_data[$i] : array(); echo "\t$i"; for($j = 0; $j < $length; $j++){ if(in_array($j, $data)){ echo '.'; }else{ echo ' '; } } echo "\n"; } }
如果最小值出现在最大值前面,那么结果就是它了。
function max_profit($data){ $origin_data = $data; arsort($data); reset($data); $max_key = key($data); end($data); $min_key = key($data); if($min_key < $max_key){ $max_profit = $data[$max_key] - $data[$min_key]; echo "lucky\n"; echo "Buy date: $min_key\t Sell date: $max_key\tMax profit: $max_profit\n"; } }
最小值出现在最大值后。
将每一个值与后面所有的数值做差,保存最大的差值直到最后。这个很直观,效率低下,可以用来验证其它算法的结果。
时间复杂度是 1 到 n 的等差数列和,也就是 $O(n^2)$。
function not_lucky_force($data){ $max_profit = 0; $max_key = 0; $min_key = 0; $length = count($data); foreach($data as $k => $v){ for($i = $k+1; $i < $length; $i++){ if($data[$i] - $data[$k] > $max_profit){ $max_profit = $data[$i] - $data[$k]; $max_key = $i; $min_key = $k; } } } return array($min_key, $max_key, $max_profit); }
按最小值和最大值划分 3 个区间。在第一个区间里找出最小值,求出此最小值与最大值的差值。在最后一个区间找出最大值,求此值与最小值的差值。中间区间的最小值如果出现在最大值前返回差值,比较三个差值,取最大的。如果中间区间的最小值出现在最大值前,重复上一过程。
不考虑每次内部的排序1和递归的消耗的话,时间复杂度为 $O(\log_3 n)$
function not_lucky_optimised($data, &$step){ $step++; $sorted_data = $data; arsort($sorted_data); reset($sorted_data); $max_key = key($sorted_data); end($sorted_data); $min_key = key($sorted_data); if($min_key < $max_key){ return $data[$max_key] - $data[$min_key]; } reset($data); $first_key = key($data); $first = array_slice($data, 0, $max_key + 1 - $first_key, TRUE); $middle = array_slice($data, $max_key + 1 - $first_key, $min_key - $max_key - 1, TRUE); if(count($middle) <= 2){ $first = array_shift($middle); $second = array_shift($middle); $middle_max_profit = max(0, $second - $first); }else{ $middle_max_profit = not_lucky_optimised($middle, $step); } $last = array_slice($data, $min_key - $first_key, null, TRUE); if(count($first) <= 1){ $first_max_profit = 0; }else{ $first_max_profit = max($first) - min($first); } if(count($last) <= 1){ $last_max_profit = 0; }else{ $last_max_profit = max($last) - min($last); } return max( $first_max_profit, $last_max_profit, $middle_max_profit ); }
https://gist.github.com/sdpfoue/7808352
函数中取最大和最小值的位置没有必要进行一次全排序 ↩