Split into two different posts
This commit is contained in:
parent
44f662be42
commit
5be4981b16
2 changed files with 249 additions and 248 deletions
|
@ -1,17 +1,15 @@
|
||||||
---
|
---
|
||||||
title: "Dyalog APL Problem Solving Competition 2020"
|
title: "Dyalog APL Problem Solving Competition 2020 — Phase I"
|
||||||
subtitle: "Annotated Solutions"
|
subtitle: "Annotated Solutions"
|
||||||
date: 2020-07-31
|
date: 2020-07-31
|
||||||
toc: true
|
toc: true
|
||||||
---
|
---
|
||||||
|
|
||||||
* Phase I
|
|
||||||
|
|
||||||
#+begin_src default
|
#+begin_src default
|
||||||
:Namespace Phase1
|
:Namespace Phase1
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** 1. Let's Split!
|
* 1. Let's Split!
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
Write a function that, given a right argument ~Y~ which is a scalar or
|
Write a function that, given a right argument ~Y~ which is a scalar or
|
||||||
|
@ -43,7 +41,7 @@ The second train ~(0>⊣)~ will return 1 if its left argument is
|
||||||
positive. From this, we can use [[https://help.dyalog.com/18.0/index.htm#Language/Primitive%20Functions/Rotate.htm][Rotate]] (~⌽~) to correctly order the
|
positive. From this, we can use [[https://help.dyalog.com/18.0/index.htm#Language/Primitive%20Functions/Rotate.htm][Rotate]] (~⌽~) to correctly order the
|
||||||
nested array, in the last train.
|
nested array, in the last train.
|
||||||
|
|
||||||
** 2. Character Building
|
* 2. Character Building
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
UTF-8 encodes Unicode characters using 1-4 integers for each
|
UTF-8 encodes Unicode characters using 1-4 integers for each
|
||||||
|
@ -85,7 +83,7 @@ continuation character as 0, and all the others as 1. Next, we can use
|
||||||
this binary array with [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Partitioned%20Enclose.htm][Partitioned Enclose]] (~⊂~) to return the correct
|
this binary array with [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Partitioned%20Enclose.htm][Partitioned Enclose]] (~⊂~) to return the correct
|
||||||
output.
|
output.
|
||||||
|
|
||||||
** 3. Excel-lent Columns
|
* 3. Excel-lent Columns
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
A Microsoft Excel spreadsheet numbers its rows counting up
|
A Microsoft Excel spreadsheet numbers its rows counting up
|
||||||
|
@ -106,7 +104,7 @@ the alphabet of every character. As a train, this can be done by
|
||||||
letter from 1 to 26. The [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Decode.htm][Decode]] (~⊥~) function can then turn this
|
letter from 1 to 26. The [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Decode.htm][Decode]] (~⊥~) function can then turn this
|
||||||
base-26 number into the expected result.
|
base-26 number into the expected result.
|
||||||
|
|
||||||
** 4. Take a Leap
|
* 4. Take a Leap
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
Write a function that, given a right argument which is an integer
|
Write a function that, given a right argument which is an integer
|
||||||
|
@ -130,7 +128,7 @@ divisible by one of these numbers. If the count is 1 or 3, it is a
|
||||||
leap year. Note that we use [[https://help.dyalog.com/latest/#Language/Primitive%20Operators/Commute.htm][Commute]] (~⍨~) to keep the dfn as a train,
|
leap year. Note that we use [[https://help.dyalog.com/latest/#Language/Primitive%20Operators/Commute.htm][Commute]] (~⍨~) to keep the dfn as a train,
|
||||||
and to preserve the natural right-to-left reading of the algorithm.
|
and to preserve the natural right-to-left reading of the algorithm.
|
||||||
|
|
||||||
** 5. Stepping in the Proper Direction
|
* 5. Stepping in the Proper Direction
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
Write a function that, given a right argument of 2 integers, returns a
|
Write a function that, given a right argument of 2 integers, returns a
|
||||||
|
@ -151,7 +149,7 @@ decreasing if needed, so we multiply it by the opposite of the sign of
|
||||||
~-/⍵~. Finally, we just have to start the sequence at the first
|
~-/⍵~. Finally, we just have to start the sequence at the first
|
||||||
element of ~⍵~.
|
element of ~⍵~.
|
||||||
|
|
||||||
** 6. Please Move to the Front
|
* 6. Please Move to the Front
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
Write a function that, given a right argument which is an integer
|
Write a function that, given a right argument which is an integer
|
||||||
|
@ -166,7 +164,7 @@ while all other elements keep their order.
|
||||||
the left argument. Using this index to sort in the usual way with
|
the left argument. Using this index to sort in the usual way with
|
||||||
[[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Grade%20Up%20Monadic.htm][Grade Up]] will return the expected result.
|
[[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Grade%20Up%20Monadic.htm][Grade Up]] will return the expected result.
|
||||||
|
|
||||||
** 7. See You in a Bit
|
* 7. See You in a Bit
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
A common technique for encoding a set of on/off states is to use a
|
A common technique for encoding a set of on/off states is to use a
|
||||||
|
@ -198,7 +196,7 @@ case). That is what the function ~f~ does.
|
||||||
Next, we just need to check that all elements of ~f⍺~ are also in
|
Next, we just need to check that all elements of ~f⍺~ are also in
|
||||||
~f⍵~.
|
~f⍵~.
|
||||||
|
|
||||||
** 8. Zigzag Numbers
|
* 8. Zigzag Numbers
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
A zigzag number is an integer in which the difference in magnitude of
|
A zigzag number is an integer in which the difference in magnitude of
|
||||||
|
@ -217,7 +215,7 @@ First, we decompose a number into an array of digits, using
|
||||||
compute the difference between each pair of digits, take the sign, and
|
compute the difference between each pair of digits, take the sign, and
|
||||||
ensure that the signs are indeed alternating.
|
ensure that the signs are indeed alternating.
|
||||||
|
|
||||||
** 9. Rise and Fall
|
* 9. Rise and Fall
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
Write a function that, given a right argument which is an integer
|
Write a function that, given a right argument which is an integer
|
||||||
|
@ -256,7 +254,7 @@ Next, ~(⍳∘≢≡⍋)~ on each of the two vectors will test if they are
|
||||||
non-decreasing (i.e. if the ranks of all the elements correspond to a
|
non-decreasing (i.e. if the ranks of all the elements correspond to a
|
||||||
simple range from 1 to the size of the vector).
|
simple range from 1 to the size of the vector).
|
||||||
|
|
||||||
** 10. Stacking It Up
|
* 10. Stacking It Up
|
||||||
|
|
||||||
#+begin_quote
|
#+begin_quote
|
||||||
Write a function that takes as its right argument a vector of simple
|
Write a function that takes as its right argument a vector of simple
|
||||||
|
@ -317,238 +315,3 @@ final result.
|
||||||
:EndNamespace
|
:EndNamespace
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
* Phase II
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
:Namespace Contest2020
|
|
||||||
|
|
||||||
:Namespace Problems
|
|
||||||
(⎕IO ⎕ML ⎕WX)←1 1 3
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 1 -- Take a Dive
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ score←dd DiveScore scores
|
|
||||||
:If 7=≢scores
|
|
||||||
scores←scores[¯2↓2↓⍋scores]
|
|
||||||
:ElseIf 5=≢scores
|
|
||||||
scores←scores[¯1↓1↓⍋scores]
|
|
||||||
:Else
|
|
||||||
scores←scores
|
|
||||||
:EndIf
|
|
||||||
score←2(⍎⍕)dd×+/scores
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 2 -- Another Step in the Proper Direction
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ steps←{p}Steps fromTo;segments;width
|
|
||||||
width←|-/fromTo
|
|
||||||
:If 0=⎕NC'p' ⍝ No left argument: same as Problem 5 of Phase I
|
|
||||||
segments←0,⍳width
|
|
||||||
:ElseIf p<0 ⍝ -⌊p is the number of equally-sized steps to take
|
|
||||||
segments←(-⌊p){0,⍵×⍺÷⍨⍳⍺}width
|
|
||||||
:ElseIf p>0 ⍝ p is the step size
|
|
||||||
segments←p{⍵⌊⍺×0,⍳⌈⍵÷⍺}width
|
|
||||||
:ElseIf p=0 ⍝ As if we took zero step
|
|
||||||
segments←0
|
|
||||||
:EndIf
|
|
||||||
⍝ Take into account the start point and the direction.
|
|
||||||
steps←fromTo{(⊃⍺)+(-×-/⍺)×⍵}segments
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
|
|
||||||
** Problem 3 -- Past Tasks Blast
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ urls←PastTasks url;r;paths
|
|
||||||
r←HttpCommand.Get url
|
|
||||||
paths←('[a-zA-Z0-9_/]+\.pdf'⎕S'&')r.Data
|
|
||||||
urls←('https://www.dyalog.com/'∘,)¨paths
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 4 -- Bioinformatics
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
⍝ Test if a DNA string is a reverse palindrome.
|
|
||||||
isrevp←{⍵≡⌽'TAGC'['ATCG'⍳⍵]}
|
|
||||||
|
|
||||||
⍝ Generate all subarrays (position, length) pairs, for
|
|
||||||
⍝ 4 ≤ length ≤ 12.
|
|
||||||
subarrays←{⊃,/(⍳⍵),¨¨3↓¨⍳¨12⌊1+⍵-⍳⍵}
|
|
||||||
|
|
||||||
∇ r←revp dna;positions
|
|
||||||
positions←subarrays⍴dna
|
|
||||||
⍝ Filter subarrays which are reverse palindromes.
|
|
||||||
r←↑({isrevp dna[¯1+⍵[1]+⍳⍵[2]]}¨positions)/positions
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
sset←{((1E6|2∘×)⍣⍵)1}
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 5 -- Future and Present Value
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
⍝ First solution: ((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. However,
|
|
||||||
⍝ this has quadratic complexity.
|
|
||||||
⍝ rr←(,\⊣)((1+⊢)⊥⊣)¨(,\⊢)
|
|
||||||
|
|
||||||
⍝ Second solution: We want to be able to use the
|
|
||||||
⍝ recurrence relation (recur) and scan through the
|
|
||||||
⍝ vectors of amounts and rates, accumulating the total
|
|
||||||
⍝ value at every time step. However, APL evaluation is
|
|
||||||
⍝ right-associative, so a simple Scan
|
|
||||||
⍝ (recur\amounts,¨values) would not give the correct
|
|
||||||
⍝ result, since recur is not associative and we need
|
|
||||||
⍝ 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
|
|
||||||
⍝ dfns.ascan, although heavily simplified.)
|
|
||||||
rr←{recur←{⍵[1]+⍺×1+⍵[2]} ⋄ 1↓⌽⊃{(⊂(⊃⍵)recur⍺),⍵}/⌽⍺,¨⍵}
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
⍝ Simply apply the formula for cashflow calculations.
|
|
||||||
pv←{+/⍺÷×\1+⍵}
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 6 -- Merge
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ val←ns getval var
|
|
||||||
:If ''≡var ⍝ literal '@'
|
|
||||||
val←'@'
|
|
||||||
:ElseIf (⊂var)∊ns.⎕NL ¯2
|
|
||||||
val←⍕ns⍎var
|
|
||||||
:Else
|
|
||||||
val←'???'
|
|
||||||
:EndIf
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ text←templateFile Merge jsonFile;template;ns
|
|
||||||
template←⊃⎕NGET templateFile 1
|
|
||||||
ns←⎕JSON⊃⎕NGET jsonFile
|
|
||||||
⍝ We use a simple regex search and replace on the
|
|
||||||
⍝ template.
|
|
||||||
text←↑('@[a-zA-Z]*@'⎕R{ns getval ¯1↓1↓⍵.Match})template
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 7 -- UPC
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
CheckDigit←{10|-⍵+.×11⍴3 1}
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
⍝ Left and right representations of digits. Decoding
|
|
||||||
⍝ the binary representation from decimal is more
|
|
||||||
⍝ compact than writing everything explicitly.
|
|
||||||
lrepr←⍉(7⍴2)⊤13 25 19 61 35 49 47 59 55 11
|
|
||||||
rrepr←~¨lrepr
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ bits←WriteUPC digits;left;right
|
|
||||||
:If (11=≢digits)∧∧/digits∊0,⍳9
|
|
||||||
left←,lrepr[1+6↑digits;]
|
|
||||||
right←,rrepr[1+6↓digits,CheckDigit digits;]
|
|
||||||
bits←1 0 1,left,0 1 0 1 0,right,1 0 1
|
|
||||||
:Else
|
|
||||||
bits←¯1
|
|
||||||
:EndIf
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ digits←ReadUPC bits
|
|
||||||
:If 95≠⍴bits ⍝ incorrect number of bits
|
|
||||||
digits←¯1
|
|
||||||
:Else
|
|
||||||
⍝ Test if the barcode was scanned right-to-left.
|
|
||||||
:If 0=2|+/bits[3+⍳7]
|
|
||||||
bits←⌽bits
|
|
||||||
:EndIf
|
|
||||||
digits←({¯1+lrepr⍳⍵}¨(7/⍳6)⊆42↑3↓bits),{¯1+rrepr⍳⍵}¨(7/⍳6)⊆¯42↑¯3↓bits
|
|
||||||
:If ~∧/digits∊0,⍳9 ⍝ incorrect parity
|
|
||||||
digits←¯1
|
|
||||||
:ElseIf (⊃⌽digits)≠CheckDigit ¯1↓digits ⍝ incorrect check digit
|
|
||||||
digits←¯1
|
|
||||||
:EndIf
|
|
||||||
:EndIf
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 8 -- Balancing the Scales
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ parts←Balance nums;subsets;partitions
|
|
||||||
⍝ This is a brute force solution, running in
|
|
||||||
⍝ exponential time. We generate all the possible
|
|
||||||
⍝ partitions, filter out those which are not
|
|
||||||
⍝ balanced, and return the first matching one. There
|
|
||||||
⍝ are more advanced approach running in
|
|
||||||
⍝ pseudo-polynomial time (based on dynamic
|
|
||||||
⍝ programming, see the "Partition problem" Wikipedia
|
|
||||||
⍝ page), but they are not warranted here, as the
|
|
||||||
⍝ input size remains fairly small.
|
|
||||||
|
|
||||||
⍝ Generate all partitions of a vector of a given
|
|
||||||
⍝ size, as binary mask vectors.
|
|
||||||
subsets←{1↓2⊥⍣¯1⍳2*⍵}
|
|
||||||
⍝ Keep only the subsets whose sum is exactly
|
|
||||||
⍝ (+/nums)÷2.
|
|
||||||
partitions←nums{((2÷⍨+/⍺)=⍺+.×⍵)/⍵}subsets⍴nums
|
|
||||||
:If 0=≢,partitions
|
|
||||||
⍝ If no partition satisfy the above
|
|
||||||
⍝ criterion, we return ⍬.
|
|
||||||
parts←⍬
|
|
||||||
:Else
|
|
||||||
⍝ Otherwise, we return the first possible
|
|
||||||
⍝ partition.
|
|
||||||
parts←nums{((⊂,(⊂~))⊃↓⍉⍵)/¨2⍴⊂⍺}partitions
|
|
||||||
:EndIf
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Problem 9 -- Upwardly Mobile
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
∇ weights←Weights filename;mobile;branches;mat
|
|
||||||
⍝ Put your code and comments below here
|
|
||||||
|
|
||||||
⍝ Parse the mobile input file.
|
|
||||||
mobile←↑⊃⎕NGET filename 1
|
|
||||||
branches←⍸mobile∊'┌┴┐'
|
|
||||||
⍝ TODO: Build the matrix of coefficients mat.
|
|
||||||
|
|
||||||
⍝ Solve the system of equations (arbitrarily setting
|
|
||||||
⍝ the first variable at 1 because the system is
|
|
||||||
⍝ overdetermined), then multiply the coefficients by
|
|
||||||
⍝ their least common multiple to get the smallest
|
|
||||||
⍝ integer weights.
|
|
||||||
weights←((1∘,)×(∧/÷))mat[;1]⌹1↓[2]mat
|
|
||||||
∇
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src default
|
|
||||||
:EndNamespace
|
|
||||||
:EndNamespace
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
* General Remarks
|
|
238
posts/dyalog-apl-competition-2020-phase-2.org
Normal file
238
posts/dyalog-apl-competition-2020-phase-2.org
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
---
|
||||||
|
title: "Dyalog APL Problem Solving Competition 2020 — Phase II"
|
||||||
|
subtitle: "Annotated Solutions"
|
||||||
|
date: 2020-07-31
|
||||||
|
toc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
:Namespace Contest2020
|
||||||
|
|
||||||
|
:Namespace Problems
|
||||||
|
(⎕IO ⎕ML ⎕WX)←1 1 3
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 1 -- Take a Dive
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ score←dd DiveScore scores
|
||||||
|
:If 7=≢scores
|
||||||
|
scores←scores[¯2↓2↓⍋scores]
|
||||||
|
:ElseIf 5=≢scores
|
||||||
|
scores←scores[¯1↓1↓⍋scores]
|
||||||
|
:Else
|
||||||
|
scores←scores
|
||||||
|
:EndIf
|
||||||
|
score←2(⍎⍕)dd×+/scores
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 2 -- Another Step in the Proper Direction
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ steps←{p}Steps fromTo;segments;width
|
||||||
|
width←|-/fromTo
|
||||||
|
:If 0=⎕NC'p' ⍝ No left argument: same as Problem 5 of Phase I
|
||||||
|
segments←0,⍳width
|
||||||
|
:ElseIf p<0 ⍝ -⌊p is the number of equally-sized steps to take
|
||||||
|
segments←(-⌊p){0,⍵×⍺÷⍨⍳⍺}width
|
||||||
|
:ElseIf p>0 ⍝ p is the step size
|
||||||
|
segments←p{⍵⌊⍺×0,⍳⌈⍵÷⍺}width
|
||||||
|
:ElseIf p=0 ⍝ As if we took zero step
|
||||||
|
segments←0
|
||||||
|
:EndIf
|
||||||
|
⍝ Take into account the start point and the direction.
|
||||||
|
steps←fromTo{(⊃⍺)+(-×-/⍺)×⍵}segments
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
|
||||||
|
* Problem 3 -- Past Tasks Blast
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ urls←PastTasks url;r;paths
|
||||||
|
r←HttpCommand.Get url
|
||||||
|
paths←('[a-zA-Z0-9_/]+\.pdf'⎕S'&')r.Data
|
||||||
|
urls←('https://www.dyalog.com/'∘,)¨paths
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 4 -- Bioinformatics
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
⍝ Test if a DNA string is a reverse palindrome.
|
||||||
|
isrevp←{⍵≡⌽'TAGC'['ATCG'⍳⍵]}
|
||||||
|
|
||||||
|
⍝ Generate all subarrays (position, length) pairs, for
|
||||||
|
⍝ 4 ≤ length ≤ 12.
|
||||||
|
subarrays←{⊃,/(⍳⍵),¨¨3↓¨⍳¨12⌊1+⍵-⍳⍵}
|
||||||
|
|
||||||
|
∇ r←revp dna;positions
|
||||||
|
positions←subarrays⍴dna
|
||||||
|
⍝ Filter subarrays which are reverse palindromes.
|
||||||
|
r←↑({isrevp dna[¯1+⍵[1]+⍳⍵[2]]}¨positions)/positions
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
sset←{((1E6|2∘×)⍣⍵)1}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 5 -- Future and Present Value
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
⍝ First solution: ((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. However,
|
||||||
|
⍝ this has quadratic complexity.
|
||||||
|
⍝ rr←(,\⊣)((1+⊢)⊥⊣)¨(,\⊢)
|
||||||
|
|
||||||
|
⍝ Second solution: We want to be able to use the
|
||||||
|
⍝ recurrence relation (recur) and scan through the
|
||||||
|
⍝ vectors of amounts and rates, accumulating the total
|
||||||
|
⍝ value at every time step. However, APL evaluation is
|
||||||
|
⍝ right-associative, so a simple Scan
|
||||||
|
⍝ (recur\amounts,¨values) would not give the correct
|
||||||
|
⍝ result, since recur is not associative and we need
|
||||||
|
⍝ 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
|
||||||
|
⍝ dfns.ascan, although heavily simplified.)
|
||||||
|
rr←{recur←{⍵[1]+⍺×1+⍵[2]} ⋄ 1↓⌽⊃{(⊂(⊃⍵)recur⍺),⍵}/⌽⍺,¨⍵}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
⍝ Simply apply the formula for cashflow calculations.
|
||||||
|
pv←{+/⍺÷×\1+⍵}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 6 -- Merge
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ val←ns getval var
|
||||||
|
:If ''≡var ⍝ literal '@'
|
||||||
|
val←'@'
|
||||||
|
:ElseIf (⊂var)∊ns.⎕NL ¯2
|
||||||
|
val←⍕ns⍎var
|
||||||
|
:Else
|
||||||
|
val←'???'
|
||||||
|
:EndIf
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ text←templateFile Merge jsonFile;template;ns
|
||||||
|
template←⊃⎕NGET templateFile 1
|
||||||
|
ns←⎕JSON⊃⎕NGET jsonFile
|
||||||
|
⍝ We use a simple regex search and replace on the
|
||||||
|
⍝ template.
|
||||||
|
text←↑('@[a-zA-Z]*@'⎕R{ns getval ¯1↓1↓⍵.Match})template
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 7 -- UPC
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
CheckDigit←{10|-⍵+.×11⍴3 1}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
⍝ Left and right representations of digits. Decoding
|
||||||
|
⍝ the binary representation from decimal is more
|
||||||
|
⍝ compact than writing everything explicitly.
|
||||||
|
lrepr←⍉(7⍴2)⊤13 25 19 61 35 49 47 59 55 11
|
||||||
|
rrepr←~¨lrepr
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ bits←WriteUPC digits;left;right
|
||||||
|
:If (11=≢digits)∧∧/digits∊0,⍳9
|
||||||
|
left←,lrepr[1+6↑digits;]
|
||||||
|
right←,rrepr[1+6↓digits,CheckDigit digits;]
|
||||||
|
bits←1 0 1,left,0 1 0 1 0,right,1 0 1
|
||||||
|
:Else
|
||||||
|
bits←¯1
|
||||||
|
:EndIf
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ digits←ReadUPC bits
|
||||||
|
:If 95≠⍴bits ⍝ incorrect number of bits
|
||||||
|
digits←¯1
|
||||||
|
:Else
|
||||||
|
⍝ Test if the barcode was scanned right-to-left.
|
||||||
|
:If 0=2|+/bits[3+⍳7]
|
||||||
|
bits←⌽bits
|
||||||
|
:EndIf
|
||||||
|
digits←({¯1+lrepr⍳⍵}¨(7/⍳6)⊆42↑3↓bits),{¯1+rrepr⍳⍵}¨(7/⍳6)⊆¯42↑¯3↓bits
|
||||||
|
:If ~∧/digits∊0,⍳9 ⍝ incorrect parity
|
||||||
|
digits←¯1
|
||||||
|
:ElseIf (⊃⌽digits)≠CheckDigit ¯1↓digits ⍝ incorrect check digit
|
||||||
|
digits←¯1
|
||||||
|
:EndIf
|
||||||
|
:EndIf
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 8 -- Balancing the Scales
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ parts←Balance nums;subsets;partitions
|
||||||
|
⍝ This is a brute force solution, running in
|
||||||
|
⍝ exponential time. We generate all the possible
|
||||||
|
⍝ partitions, filter out those which are not
|
||||||
|
⍝ balanced, and return the first matching one. There
|
||||||
|
⍝ are more advanced approach running in
|
||||||
|
⍝ pseudo-polynomial time (based on dynamic
|
||||||
|
⍝ programming, see the "Partition problem" Wikipedia
|
||||||
|
⍝ page), but they are not warranted here, as the
|
||||||
|
⍝ input size remains fairly small.
|
||||||
|
|
||||||
|
⍝ Generate all partitions of a vector of a given
|
||||||
|
⍝ size, as binary mask vectors.
|
||||||
|
subsets←{1↓2⊥⍣¯1⍳2*⍵}
|
||||||
|
⍝ Keep only the subsets whose sum is exactly
|
||||||
|
⍝ (+/nums)÷2.
|
||||||
|
partitions←nums{((2÷⍨+/⍺)=⍺+.×⍵)/⍵}subsets⍴nums
|
||||||
|
:If 0=≢,partitions
|
||||||
|
⍝ If no partition satisfy the above
|
||||||
|
⍝ criterion, we return ⍬.
|
||||||
|
parts←⍬
|
||||||
|
:Else
|
||||||
|
⍝ Otherwise, we return the first possible
|
||||||
|
⍝ partition.
|
||||||
|
parts←nums{((⊂,(⊂~))⊃↓⍉⍵)/¨2⍴⊂⍺}partitions
|
||||||
|
:EndIf
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Problem 9 -- Upwardly Mobile
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
∇ weights←Weights filename;mobile;branches;mat
|
||||||
|
⍝ Put your code and comments below here
|
||||||
|
|
||||||
|
⍝ Parse the mobile input file.
|
||||||
|
mobile←↑⊃⎕NGET filename 1
|
||||||
|
branches←⍸mobile∊'┌┴┐'
|
||||||
|
⍝ TODO: Build the matrix of coefficients mat.
|
||||||
|
|
||||||
|
⍝ Solve the system of equations (arbitrarily setting
|
||||||
|
⍝ the first variable at 1 because the system is
|
||||||
|
⍝ overdetermined), then multiply the coefficients by
|
||||||
|
⍝ their least common multiple to get the smallest
|
||||||
|
⍝ integer weights.
|
||||||
|
weights←((1∘,)×(∧/÷))mat[;1]⌹1↓[2]mat
|
||||||
|
∇
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src default
|
||||||
|
:EndNamespace
|
||||||
|
:EndNamespace
|
||||||
|
#+end_src
|
Loading…
Add table
Add a link
Reference in a new issue