Solve problem 5 task 1 in linear time

This commit is contained in:
Dimitri Lozeve 2020-05-10 21:12:34 +02:00
parent fb28ee869f
commit 324122088b

View file

@ -60,28 +60,31 @@
⍝ 2020 APL Problem Solving Competition Phase II ⍝ 2020 APL Problem Solving Competition Phase II
⍝ Problem 5, Task 1 - rr ⍝ Problem 5, Task 1 - rr
⍝ ((1+⊢)⊥⊣) computes the total return for a vector of
⍝ amounts and a vector of rates ⍵. It is applied to
⍝ every prefix subarray of amounts and rates to get
⍝ all intermediate values. Quadratic complexity.
rr←(,\⊣)((1+⊢)⊥⊣)¨(,\⊢)
∇ r←amounts rr2 rates;recur ⍝ First solution: ((1+⊢)⊥⊣) computes the total return
⍝ Second solution ⍝ for a vector of amounts and a vector of rates
⍝ The recurrence relation ( is the previous value, ⍝ ⍵. It is applied to every prefix subarray of amounts
⍝ ⍵[1] is the current amount, and ⍵[2] is the ⍝ and rates to get all intermediate values. However,
⍝ current rate). ⍝ this has quadratic complexity.
recur←{⍵[1]+×1+⍵[2]} ⍝ rr←(,\⊣)((1+⊢)⊥⊣)¨(,\⊢)
⍝ Because APL evaluates from right to left, we can't
⍝ use Scan directly, as recur is not associative. We ⍝ Second solution: We want to be able to use the
⍝ need something like Haskell's scanl, that will ⍝ recurrence relation (recur) and scan through the
⍝ accumulate left-to-right and accumulate ⍝ vectors of amounts and rates, accumulating the total
⍝ left-to-right. Scan accumulates left-to-right but ⍝ value at every time step. However, APL evaluation is
⍝ evaluates right-to-left. This solution therefore ⍝ right-associative, so a simple Scan
⍝ folds for all subarrays, but it is not good in ⍝ (recur\amounts,¨values) would not give the correct
⍝ terms of performance. ⍝ result, since recur is not associative and we need
r←{⊃⊃f⍨/(⌽⍵↑amounts,¨rates),⊂0 0}¨amounts ⍝ to evaluate it left-to-right. (In any case, in this
⍝ case, Scan would have quadratic complexity, so would
⍝ not bring any benefit over the previous solution.)
⍝ What we need is something akin to Haskell's scanl
⍝ function, which would evaluate left to right in O(n)
⍝ time. This is what we do here, accumulating values
⍝ from left to right. (This is inspired from
⍝ https://dfns.dyalog.com/c_ascan.htm, although
⍝ heavily simplified.)
rr←{recur←{⍵[1]+×1+⍵[2]} ⋄ 1↓⌽⊃{(⊂(⊃⍵)recur),⍵}/⌽⍺,¨⍵}
∇ r←cashFlow pv rates ∇ r←cashFlow pv rates
⍝ 2020 APL Problem Solving Competition Phase II ⍝ 2020 APL Problem Solving Competition Phase II