Given some finite sequence $a_n$ for $n \in \{1..N\}$, is there a more efficient method to solve (P1) than the O($N^2$) brute force method?
($P1$) maximise $n_2-n_1$ such that $a_{n_1}>a_{n_2}$
Given some finite sequence $a_n$ for $n \in \{1..N\}$, is there a more efficient method to solve (P1) than the O($N^2$) brute force method?
($P1$) maximise $n_2-n_1$ such that $a_{n_1}>a_{n_2}$
This is $O(N)$.
If $a_n$ is greater than all the elements before it, call it a peak; and if $a_n$ is less than all elements after it, call it a trough.
Then if $(a_i,a_j)$ is a maximally separated pair satisfying $a_i>a_j$, we know that $a_i$ must be a peak (otherwise there would be an earlier element $a_k$ such that $a_k>a_j$); and similarly, $a_j$ must be a trough.
So here is the algorithm:
Note that we can start the scan in Step 4 from the value of $t$ in the previous iteration, so the total number of operations used by that step in the whole algorithm is $O(N)$.
Edited to add: In fact we can omit Step 1, and just scan for the next peak in Step 6. (We can't omit Step 2, because scanning for troughs has to be done backwards.)
I believe you can do this in $O(N \log N)$, but there may be a faster way. Here is a high level description of the algorithm:
[3, 1, 4, 2], you would store it as [(3,0), (1,1), (4,2), (2,3)]. This will be $O(N)$[(4, 2), (3, 0), (2, 3), (1, 1)]. This can be done in $O(N\log N)$Total complexity = $O(N \log N)$.
I think steps $1$ and $2$ are pretty self explanatory. If you are curious about how step $3$ can be done in $O(N)$, here is some code:
def max_diff_indexes(arr):
"""
@params:
arr - This is just an array of the SECOND parts of the tuple in
the algorithm described above. That is, arr contains only ints
representing the second values of the tuple (index field) after
the sorting in step 2.
@return:
max_pair - Returns a pair of indexes n_1 and n_2 that maximise
n_2 - n_1 such that a_{n_1} > a_{n_2}. The first property is
ensured by this algorithm while the second property in ensured
by the fact that the array was initally sorted in decresing order
of value
"""
if not arr: return None
min_val = arr[0]
max_diff = 0
for elem in arr:
max_diff = max(elem - min_val, max_diff)
min_val = min(elem, min_val)
max_val = max_diff - min_val
return min_val, max_val
Alright, I took a crack at it myself and came up with the following approach which seems to be $O\big( (N/M)^2 + N \log(N) \big)$, where $M$ is the optimal value of (P1).
Loop the following over $k$ from $1,2,\dots$:
Each $k$th iteration requires: $O\big( N+ 4^k\big)$ operations, so summing over all necessary $k$ gives the total complexity $O\big((N/M)^2 + N \log(N) \big)$, where the former term dominates for small $M$ and the latter terms dominates for $M \gtrsim \sqrt{N}$.