13 KiB
— title: "Dyalog APL Problem Solving Competition 2020" subtitle: "Annotated Solutions" date: 2020-07-31 —
Phase I
:Namespace Phase1
1. Let's Split!
Write a function that, given a right argument
Y
which is a scalar or a non-empty vector and a left argumentX
which is a single non-zero integer so that its absolute value is less or equal to≢Y
, splitsY
into a vector of two vectors according toX
, as follows:
- If
X>0
, the first vector contains the firstX
elements ofY
and the second vector contains the remaining elements.- If
X<0
, the second vector contains the last|X
elements ofY
and the first vector contains the remaining elements.
split←(0>⊣)⌽((⊂↑),(⊂↓))
2. Character Building
UTF-8 encodes Unicode characters using 1-4 integers for each character. Dyalog APL includes a system function,
⎕UCS
, that can convert characters into integers and integers into characters. The expression'UTF-8'∘⎕UCS
converts between characters and UTF-8.Consider the following:
'UTF-8'∘⎕UCS 'D¥⍺⌊○9' 68 194 165 226 141 186 226 140 138 226 151 139 57 'UTF-8'∘⎕UCS 68 194 165 226 141 186 226 140 138 226 151 139 57 D¥⍺⌊○9
How many integers does each character use?
'UTF-8'∘⎕UCS¨ 'D¥⍺⌊○9' ⍝ using ]Boxing on ┌──┬───────┬───────────┬───────────┬───────────┬──┐ │68│194 165│226 141 186│226 140 138│226 151 139│57│ └──┴───────┴───────────┴───────────┴───────────┴──┘
The rule is that an integer in the range 128 to 191 (inclusive) continues the character of the previous integer (which may itself be a continuation). With that in mind, write a function that, given a right argument which is a simple integer vector representing valid UTF-8 text, encloses each sequence of integers that represent a single character, like the result of
'UTF-8'∘⎕UCS¨'UTF-8'∘⎕UCS
but does not use any system functions (names beginning with⎕
)
characters←{(~⍵∊127+⍳64)⊂⍵}
3. Excel-lent Columns
A Microsoft Excel spreadsheet numbers its rows counting up from 1. However Excel's columns are labelled alphabetically — beginning with A–Z, then AA–AZ, BA–BZ, up to ZA–ZZ, then AAA–AAZ and so on.
Write a function that, given a right argument which is a character scalar or non-empty vector representing a valid character Excel column identifier between A and XFD, returns the corresponding column number
columns←26⊥⎕A∘⍳
4. Take a Leap
Write a function that, given a right argument which is an integer array of year numbers greater than or equal to 1752 and less than 4000, returns a result of the same shape as the right argument where 1 indicates that the corresponding year is a leap year (0 otherwise).
A leap year algorithm can be found here.
leap←1 3∊⍨(0+.=400 100 4∘.|⊢)
5. Stepping in the Proper Direction
Write a function that, given a right argument of 2 integers, returns a vector of the integers from the first element of the right argument to the second, inclusively.
stepping←{(⊃⍵)+(-×-/⍵)×0,⍳|-/⍵}
6. Please Move to the Front
Write a function that, given a right argument which is an integer vector and a left argument which is an integer scalar, reorders the right argument so any elements equal to the left argument come first while all other elements keep their order.
movefront←{⍵[⍋⍺≠⍵]}
7. See You in a Bit
A common technique for encoding a set of on/off states is to use a value of $2^n$ for the state in position $n$ (origin 0), 1 if the state is "on" or 0 for "off" and then add the values. Dyalog APL's component file permission codes are an example of this. For example, if you wanted to grant permissions for read (access code 1), append (access code 8) and rename (access code 128) then the resulting code would be 137 because that's 1 + 8 + 128.
Write a function that, given a non-negative right argument which is an integer scalar representing the encoded state and a left argument which is an integer scalar representing the encoded state settings that you want to query, returns 1 if all of the codes in the left argument are found in the right argument (0 otherwise).
bits←{f←⍸∘⌽(2∘⊥⍣¯1)⋄∧/(f⍺)∊f⍵}
8. Zigzag Numbers
A zigzag number is an integer in which the difference in magnitude of each pair of consecutive digits alternates from positive to negative or negative to positive.
Write a function that takes a single integer greater than or equal to 100 and less than 1015 as its right argument and returns a 1 if the integer is a zigzag number, 0 otherwise.
zigzag←∧/2=∘|2-/∘×2-/(10∘⊥⍣¯1)
9. Rise and Fall
Write a function that, given a right argument which is an integer scalar or vector, returns a 1 if the values of the right argument conform to the following pattern (0 otherwise):
- The elements increase or stay the same until the "apex" (highest value) is reached
- After the apex, any remaining values decrease or remain the same
risefall←{∧/(⍳∘≢≡⍋)¨(⊂((⊢⍳⌈/)↑⊢),⍵),⊂⌽((⊢⍳⌈/)↓⊢),⍵}
10. Stacking It Up
Write a function that takes as its right argument a vector of simple arrays of rank 2 or less (scalar, vector, or matrix). Each simple array will consist of either non-negative integers or printable ASCII characters. The function must return a simple character array that displays identically to what
{⎕←⍵}¨
displays when applied to the right argument.
stacking←{↑⊃,/↓¨⍕¨⍵}
:EndNamespace
Phase II
:Namespace Contest2020
:Namespace Problems
(⎕IO ⎕ML ⎕WX)←1 1 3
Problem 1 – Take a Dive
∇ 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
∇
Problem 2 – Another Step in the Proper Direction
∇ 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
∇
Problem 3 – Past Tasks Blast
∇ urls←PastTasks url;r;paths
r←HttpCommand.Get url
paths←('[a-zA-Z0-9_/]+\.pdf'⎕S'&')r.Data
urls←('https://www.dyalog.com/'∘,)¨paths
∇
Problem 4 – Bioinformatics
⍝ 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
∇
sset←{((1E6|2∘×)⍣⍵)1}
Problem 5 – Future and Present Value
⍝ 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⍺),⍵}/⌽⍺,¨⍵}
⍝ Simply apply the formula for cashflow calculations.
pv←{+/⍺÷×\1+⍵}
Problem 6 – Merge
∇ val←ns getval var
:If ''≡var ⍝ literal '@'
val←'@'
:ElseIf (⊂var)∊ns.⎕NL ¯2
val←⍕ns⍎var
:Else
val←'???'
:EndIf
∇
∇ 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
∇
Problem 7 – UPC
CheckDigit←{10|-⍵+.×11⍴3 1}
⍝ 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
∇ 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
∇
∇ 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
∇
Problem 8 – Balancing the Scales
∇ 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
∇
Problem 9 – Upwardly Mobile
∇ 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
∇
:EndNamespace
:EndNamespace