【算法基础】由股票收益问题再看分治算法和递归式

最大子数组问题

最近有一个比较火的话题,股票,那么这一篇就由此引入来进一步学习分治算法。在上一篇博客中已经对插入排序和分治算法做了初步的介绍,建议在看一篇前先看看:【算法基础】由插入排序看如何分析和设计算法

当然了,这篇博客主要用来介绍算法而非讲解股票,所以这里已经有了股票的价格,如下所示。

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
股票价格 50 57 65 75 67 60 54 51 48 44 47 43 56 64 71 65 61 73 70
价格波动 0 7 8 18 -8 -7 -6 -3 -3 -4 3 -4 13 10 7 -6 -4 12 -3

价格表已经有了问题是从哪一天买进、哪一天卖出会使得收益最高呢?你可以认为在价格最低的时候买入,在价格最高的时候卖出,这是对的,但不一定任何时候都适用。在这里的价格表中,股票价格最高的时候是第3天、价格最低的时候是第11天,怎么办?让时间反向行驶?

就像我以前参加学校里的程序设计竞赛时一样,也可以用多个for循环不断的进行比较。这里就是将每对可能的买进和卖出日期进行组合,只要卖出日期在买进日期之前就好,这样在18天中就有C218种日期组合,也可以写成(182)。因此对于n天,就有(n2)种组合,而(n2)=Θ(n2),另外处理每对日期所花费的时间至少也是常量,因此这种方法的运行时间为Ω(n2)。

然后,我们在学习算法,自然要以算法的角度来看这个问题。比起股票价格,我们更应该关注价格波动。如果将这个波动定义为数组A,那么问题就转换为寻找A的和最大的非空连续子数组。这种连续子数组就是标题中的最大子数组(maximum subarray)。将原表简化如下:

数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
A 7 8 18 -8 -7 -6 -3 -3 -4 3 -4 13 10 7 -6 -4 12 -3

在这个算法中,常常被说成是“一个最大子数组”而不是“最大子数组”,因为可能有多个子数组达到最大和。

只有当数组中包含负数时,最大子数组问题才有意义。如果所有数组元素都是非负的,最大子数组问题没有任何难度,因为整个数组的和肯定是最大的。

使用分治思想解决问题

我们将实际问题转换为算法问题,在这里也就是要寻找子数组A[low...high]的最大子数组。分治思想意味着将问题一分为二,这里就需要找到数组的中间位置mid,然后考虑求解2个子数组A[low…mid]和A[mid+1…high]。而A[low...high]的任何连续子数组A[i...j]所处的位置必然是以下情况之一:

1)完全位于子数组A[low…mid]中,因此low≤i≤j≤mid;

2)完全位于子数组A[mid+1…high]中,因此mid<i≤j≤high;

3)跨越中点mid,因此low≤i≤mid≤j≤high。

伪代码如下:

FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
1   left-sum = -10000
2   sum = 0
3   for i = mid downto low
4        sum = sum + A[i]
5        if sum > left-sum
6             left-sum = sum
7             max-left = i
8   right-sum = -10000
9   sum = 0
10  for j = mid + 1 to high
11       sum = sum + A[i]
12       if sum > right-sum
13            right-sum = sum
14            max-right = j
15   return (max-left, max-right, left-sum + right-sum)
#include <iostream>
#include <cstdio>

using namespace std;

int const n=18;
int A[n]={7,8,18,-8,-7,-6,-3,-3,-4,3,-4,13,10,7,-6,-4,12,-3};
int B[3];
int low,high,mid;
int max_left,max_right;
int sum;

void find_max_crossing_subarray(int A[],int low,int mid,int high);

int main()
{
    find_max_crossing_subarray(A,0,7,15);
    for(int i=0;i<3;i++)
    {
        printf("%d ",B[i]);
    }
    return 0;
}

void find_max_crossing_subarray(int A[],int low,int mid,int high)
{
   int left_sum=-10000;
   sum=0;
   for(int i=mid;i>=low;i--)
   {
       sum=sum+A[i];
       if(sum>left_sum)
       {
           left_sum=sum;
           max_left=i;
       }
   }
   int right_sum=-10000;
   sum=0;
   for(int j=mid+1;j<=high;j++)
   {
       sum=sum+A[j];
       if(sum>right_sum)
       {
           right_sum=sum;
           max_right=j;
       }
   }
   B[0]=max_left;
   B[1]=max_right;
   B[2]=left_sum+right_sum;
}

程序大概就是这个样子……也可以将数组A放到main函数中输入。

时间: 2024-12-15 01:32:46

【算法基础】由股票收益问题再看分治算法和递归式的相关文章

【算法】2 由股票收益问题再看分治算法和递归式

回顾分治算法 分治算法的英文名叫做"divide and conquer",它的意思是将一块领土分解为若干块小部分,然后一块块的占领征服,让它们彼此异化.这就是英国人的军事策略,但我们今天要看的是算法. 如前所述,分治算法有3步,在上一篇中已有介绍,它们对应的英文名分别是:divide.conquer.combine. 接下来我们通过多个小算法来深化对分治算法的理解. 二分查找算法 问题描述:在已排序的数组A中查找是否存在数字n. 1)分:取得数组A中的中间数,并将其与n比较 2)治:

再回首--分治算法

谈起分治算法,首先从字面意思理解:就是将一个问题划分成多个较小的问题的算法.其实正应题目的意思.其基本设计思想就是:将一个难以直接解决的大问题分解成一些规模较小的相同问题以便各个击破,分而治之. 设计步骤:1)分解:分解成若干子问题 2)求解:求解个子问题 3)合并:将子解合并成原问题的解. 在自考的时候,我们遇到的二路归并算法就属于一种分治法.当然,要学会算法,就要找到其核心,抓住其核心了,我们也就明白算法是怎么回事了.下面我们通过二路归并算法找到其核心. 例子:给出一列数:4,2,8,3.利

java 算法基础

1.算法概要 算法是用于计算.数据处理和自动推理使用的.算法主要是做精确计算和表示一个有限长列的有效方法.算法一般包含清晰定义的指令用于计算函数.基本上也属于一种思考最简洁的方式. 2.算法特征 算法主要包含五个特征 2.1.有穷性: 是指算法必须能在执行有限个步骤后终止: 2.2.确切性: 算法的每一个步骤必须有确切的定义: 2.3.输入项: 一个算法输入有0或多个输入,以刻画预算对象的初始情况,所谓0就是初始化条件: 2.4.输出项: 反馈对数据加工后的结果.没有输出的算法无意义. 2.5.

写给大家看的算法

第1章 什么是算法第2章 变量和数组第3章 数据结构第4章 学习算法基础第5章 排序算法第6章 搜索算法第7章 其他算法第8章 算法和计算机 第1章 什么是算法 1.1 算法其实就在身边 1.2 算法是人类智慧的结晶 1.3 了解算法对玩游戏有帮助吗? 1.4 算法有两个必要条件 1.5 要特别了解的重要算法 专题1 算法基础之结构化编程思想 第2章 变量和数组 2.1 所谓"数据",就是各种各样的信息 2.2 数据有不同的类型 2.3 最基础的数据是"值" 2.4

从分治算法到 MapReduce

从分治算法说起 要说 MapReduce 就不得不说分治算法,而分治算法其实说白了,就是四个字 分而治之 .其实就是将一个复杂的问题分解成多组相同或类似的子问题,对这些子问题再分,然后再分.直到最后的子问题可以简单得求解. 要具体介绍分治算法,那就不得不说一个很经典的排序算法 -- 归并排序.这里不说它的具体算法代码,只说明它的主要思想.而归并排序的思想正是分治思想. 归并排序采用递归的方式,每次都将一个数组分解成更小的两个数组,再对这两个数组进行排序,不断递归下去.直到分解成最简单形式的两个数

算法面试:精选微软等公司经典的算法面试100题 第1-40题

精选微软等公司,数据结构+算法,经典面试100题                            --------之前40题 -------------------------- 算法面试:精选微软等公司经典的算法面试100题 第1-40题如下: --------------- --------------1.把二元查找树转变成排序的双向链表 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向.      10  / \ 6 14 

Paxos算法细节详解(一)--通过现实世界描述算法

最近研究paxos算法,看了许多相关的文章,概念还是很模糊,觉得还是没有掌握paxos算法的精髓,所以花了3天时间分析了libpaxos3的所有代码,此代码可以从https://bitbucket.org/sciascid/libpaxos 下载.对paxos算法有初步了解之后,再看此文的效果会更好:如果你也想分析libpaxos3的话,此文应该会对你有不小帮助:关于paxos的历史这里不多做介绍,关于描述paxos算法写的最好的一篇文章应该就是维基百科了,地址戳这里:http://zh.wik

算法系列之常用算法之一----分治算法

一.基本概念 在计算机科学中,分治法是一种很重要的算法.分治算法,字面上的解释是"分而治之",分治算法主要是三点: 1.将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题----"分" 2.将最后子问题可以简单的直接求解----"治" 3.将所有子问题的解合并起来就是原问题打得解----"合" 这三点是分治算法的主要特点,只要是符合这三个特点的问题都可以使用分治算法进行解决(注意用词,是"

分治算法

分治算法即将一个问题划分成多个子问题求解,最后的结果就是几个子问题的合集,通常图形类的算法,尤其是2的几次方数组问题可以优先考虑. 汉诺塔和二分搜索都是分治算法的思想,个人觉得最好体现分治算法的demo是棋盘覆盖问题,代码如下: #include <stdio.h> #include <stdlib.h> #define SIZE 4 static int title = 1; //title表示L型骨牌的编号 static int board[SIZE][SIZE]; /** *