Add post on the Dyalog APL Competition
This commit is contained in:
parent
92d759a9bf
commit
56d878eb0c
1 changed files with 311 additions and 0 deletions
311
posts/dyalog-apl-competition-2020.org
Normal file
311
posts/dyalog-apl-competition-2020.org
Normal 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←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
|
Loading…
Add table
Add a link
Reference in a new issue