Add post on the Dyalog APL Competition

This commit is contained in:
Dimitri Lozeve 2020-07-14 16:06:35 +02:00
parent 92d759a9bf
commit 56d878eb0c

View file

@ -0,0 +1,311 @@
---
title: "Dyalog APL Problem Solving Competition 2020"
subtitle: "Annotated Solutions"
date: 2020-07-31
---
* Phase I
#+begin_src default
:Namespace Phase1
#+end_src
** 1. Let's Split!
#+begin_src default
split←(0>⊣)⌽((⊂↑),(⊂↓))
#+end_src
** 2. Character Building
#+begin_src default
characters←{(~⍵∊127+64)⊂⍵}
#+end_src
** 3. Excel-lent Columns
#+begin_src default
columns←26⊥⎕A∘
#+end_src
** 4. Take a Leap
#+begin_src default
leap←1 3∊⍨(0+.=400 100 4∘.|⊢)
#+end_src
** 5. Stepping in the Proper Direction
#+begin_src default
stepping←{(⊃⍵)+(-×-/⍵)×0,|-/⍵}
#+end_src
** 6. Please Move to the Front
#+begin_src default
movefront←{⍵[⍋⍺≠⍵]}
#+end_src
** 7. See You in a Bit
#+begin_src default
bits←{f←⍸∘⌽(2∘⊥⍣¯1)⋄∧/(f)∊f⍵}
#+end_src
** 8. Zigzag Numbers
#+begin_src default
zigzag←∧/2=∘|2-/×2-/(10∘⊥⍣¯1)
#+end_src
** 9. Rise and Fall
#+begin_src default
risefall←{∧/(⍳∘≢≡⍋)¨(⊂((⊢⍳⌈/)↑⊢),⍵),⊂⌽((⊢⍳⌈/)↓⊢),⍵}
#+end_src
** 10. Stacking It Up
#+begin_src default
stacking←{↑⊃,/↓¨⍕¨⍵}
#+end_src
#+begin_src default
:EndNamespace
#+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←subarraysdna
⍝ 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|-⍵+.×113 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←⍉(72)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⊥⍣¯12*⍵}
⍝ Keep only the subsets whose sum is exactly
⍝ (+/nums)÷2.
partitions←nums{((2÷⍨+/)=+.×⍵)/⍵}subsetsnums
: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