Pergunta

There are algorithms for detecting the maximum subarray within an array (both contiguous and non-continguous). Most of them are based around having both negative and positive numbers, though. How is it done with positive numbers only?

I have an array of values of a stock over a consequtive range of time (let's say, the array contains values for all consecutive months).

[15.42, 16.42, 17.36, 16.22, 14.72, 13.95, 14.73, 13.76, 12.88, 13.51, 12.67, 11.11, 10.04, 10.38, 10.14, 7.72, 7.46, 9.41, 11.39, 9.7, 12.67, 18.42, 18.44, 18.03, 17.48, 19.6, 19.57, 18.48, 17.36, 18.03, 18.1, 19.07, 21.02, 20.77, 19.92, 18.71, 20.29, 22.36, 22.38, 22.39, 22.94, 23.5, 21.66, 22.06, 21.07, 19.86, 19.49, 18.79, 18.16, 17.24, 17.74, 18.41, 17.56, 17.24, 16.04, 16.05, 15.4, 15.77, 15.68, 16.29, 15.23, 14.51, 14.05, 13.28, 13.49, 13.12, 14.33, 13.67, 13.13, 12.45, 12.48, 11.58, 11.52, 11.2, 10.46, 12.24, 11.62, 11.43, 10.96, 10.63, 10.19, 10.03, 9.7, 9.64, 9.16, 8.96, 8.49, 8.16, 8.0, 7.86, 8.08, 8.02, 7.67, 8.07, 8.37, 8.35, 8.82, 8.58, 8.47, 8.42, 7.92, 7.77, 7.79, 7.6, 7.18, 7.44, 7.74, 7.47, 7.63, 7.21, 7.06, 6.9, 6.84, 6.96, 6.93, 6.49, 6.38, 6.69, 6.49, 6.76]

I need an algorithm to determine for each element the single time period where it had the biggest percentage gain. This could be a time period of 1 month, some span of several months, or the entire array (e.g., 120 months), depending on the stock. I then want to output the burst, in terms of percentage gain, as well as the return (change in price over the original price; so the peak price vs the starting price in the period).

I've combined the max subarray type algorithms, but realized that this problem is a bit different; the array has no negative numbers, so those algorithms just report the entire array as the period and the sum of all elements as the gain.

The algorithms I mentioned are located here and here, with the latter being based on the Master Theorem. Hope this helps.

I'm coding in Ruby but pseudocode would be welcome, too.

Foi útil?

Solução

I think you went the wrong way ...
I'm not familiar with ruby but let us build the algorithm in pseudocode using your own words :

I've got an array that contains the values of a stock over a range of time (let's say, for this example, each element is the value of the stock in a month; the array contains values for all consecutive months).

We'll name this array StockValues, its length is given by length(StockValues), assume it is 1 based (first item is retrieved with StockValues[1])

I need an algorithm to analyze the array, and determine for each element the single time period where it had the biggest percentage gain in price.

You want to know for a given index i at which index j with j>i we have a maximum gain in percent i.e. when gain=100*StockValues[j]/StockValues[i]-100 is maximum.

I then want to output the burst, in terms of percentage gain, as well as the return(change in price over the original price; so the peak price vs the starting price in the period).

You want to retrieve the two values burst=gain=100*StockValues[j]/StockValues[i]-100 and return=StockValues[j]-StickValues[i]
The first step will be to loop thru the array and for each element do a second loop to find when the gain is maximum, when we find a maximum we save the values you want in another array named Result (let us assume this array is initialized with invalid values, like burst=-1 which means no gain over any period can be found)

for i=1 to length(StockValues)-1 do
  max_gain=0
  for j=i+1 to length(StockValues) do
    gain=100*StockValues[j]/StockValues[i]-100
    if gain>max_gain then
      gain=max_gain
      Result[i].burst=gain
      Result[i].return=StockValues[j]-StockValues[i]
      Result[i].start=i
      Result[i].end=j
      Result[i].period_length=j-i+1
      Result[i].start_price=StockValues[i]
      Result[i].end_price=StockValues[j]
    end if
  end for
end for

Note that this algorithm gives the smallest period, if you replace gain>max_gain with gain>=max_gain you'll get the longest period in the case there are more than one period with the same gain value. Only positive or null gains are listed, if there is no gain at all, Result will contain the invalid value. Only period>1 are listed, if period of 1 are accepted then the worst gain possible would be 0%, and you would have to modify the loops i goes to length(StockValues) and j starts at i

Outras dicas

This doesn't really sound like several days of work :p unless I'm missing something.

# returns array of percentage gain per period
def percentage_gain(array)
  initial = array[0]
  after = 0
  percentage_gain = []
  1.upto(array.size-1).each do |i|
    after = array[i]
    percentage_gain << (after - initial)/initial*100
    initial = after
  end
  percentage_gain
end

# returns array of amount gain $ per period
def amount_gain(array)
  initial = array[0]
  after = 0
  amount_gain = []
  1.upto(array.size-1).each do |i|
    after = array[i]
    percentage_gain << (after - initial)
    initial = after
  end
  amount_gain
end

# returns the maximum amount gain found in the array
def max_amount_gain(array)
  amount_gain(array).max
end

# returns the maximum percentage gain found in the array
def max_percentage_gain(array)
  percentage_gain(array).max
end

# returns the maximum potential gain you could've made by shortselling constantly.
# i am basically adding up the amount gained when you would've hit profit.
# on days the stock loses value, i don't add them.
def max_potential_amount_gain(array)
  initial = array[0]
  after = 0
  max_potential_gain = 0
  1.upto(array.size-1).each do |i|
    after = array[i]
    if after - initial > 0
      max_potential_gain += after - initial
    end
    initial = after
  end
  amount_gain
end

array = [15.42, 16.42, 17.36, 16.22, 14.72, 13.95, 14.73, 13.76, 12.88, 13.51, 12.67, 11.11, 10.04, 10.38, 10.14, 7.72, 7.46, 9.41, 11.39, 9.7, 12.67, 18.42, 18.44, 18.03, 17.48, 19.6, 19.57, 18.48, 17.36, 18.03, 18.1, 19.07, 21.02, 20.77, 19.92, 18.71, 20.29, 22.36, 22.38, 22.39, 22.94, 23.5, 21.66, 22.06, 21.07, 19.86, 19.49, 18.79, 18.16, 17.24, 17.74, 18.41, 17.56, 17.24, 16.04, 16.05, 15.4, 15.77, 15.68, 16.29, 15.23, 14.51, 14.05, 13.28, 13.49, 13.12, 14.33, 13.67, 13.13, 12.45, 12.48, 11.58, 11.52, 11.2, 10.46, 12.24, 11.62, 11.43, 10.96, 10.63, 10.19, 10.03, 9.7, 9.64, 9.16, 8.96, 8.49, 8.16, 8.0, 7.86, 8.08, 8.02, 7.67, 8.07, 8.37, 8.35, 8.82, 8.58, 8.47, 8.42, 7.92, 7.77, 7.79, 7.6, 7.18, 7.44, 7.74, 7.47, 7.63, 7.21, 7.06, 6.9, 6.84, 6.96, 6.93, 6.49, 6.38, 6.69, 6.49, 6.76]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top