diff --git a/_site/atom.xml b/_site/atom.xml index 033962d..d2b11d9 100644 --- a/_site/atom.xml +++ b/_site/atom.xml @@ -103,50 +103,33 @@
Since we cannot just compute \(2^n\) directly and take the remainder, we use modular arithmetic to stay mod 1,000,000 during the whole computation. The dfn (1E6|2∘×)
doubles its argument mod 1,000,000. So we just apply this function \(n\) times using the Power operator (⍣
), with an initial value of 1.
⍝ 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⍺),⍵}/⌽⍺,¨⍵}
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.
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)\) timeThere is an interesting StackOverflow answer explaining the behaviour of Scan, and compares it to Haskell’s scanl
function.
+
+. This is what we do here, accumulating values from left to right. (This is inspired from dfns.ascan
, although heavily simplified.)
For the second task, there is an explicit formula for cashflow calculations, so we can just apply it.
+∇ 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
-∇
∇ 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
+∇
We first read the template and the JSON values from their files. The ⎕NGET
function read simple text files, and ⎕JSON
extracts the key-value pairs as a namespace.
Assuming all variable names contain only letters, we match the regex @[a-zA-Z]*@
to match variable names enclosed between @
symbols. The function getval
then returns the appropriate value, and we can replace the variable name in the template.
∇ val←ns getval var
- :If ''≡var ⍝ literal '@'
- val←'@'
- :ElseIf (⊂var)∊ns.⎕NL ¯2
- val←⍕ns⍎var
- :Else
- val←'???'
- :EndIf
-∇
∇ val←ns getval var
+ :If ''≡var ⍝ literal '@'
+ val←'@'
+ :ElseIf (⊂var)∊ns.⎕NL ¯2
+ val←⍕ns⍎var
+ :Else
+ val←'???'
+ :EndIf
+∇
This function takes the namespace matching the variable names to their respective values, and the name of the variable.
@@
, which corresponds to a literal @
.???
.The check digit satisfies the equation \[ 3 x_{1}+x_{2}+3 x_{3}+x_{4}+3 x_{5}+x_{6}+3 x_{7}+x_{8}+3 x_{9}+x_{10}+3 x_{11}+x_{12} \equiv 0 \bmod 10, \] therefore, \[ x_{12} \equiv -(3 x_{1}+x_{2}+3 x_{3}+x_{4}+3 x_{5}+x_{6}+3 x_{7}+x_{8}+3 x_{9}+x_{10}+3 x_{11}) \bmod 10. \]
Translated to APL, we just take the dot product between the first 11 digits of the barcode with 11⍴3 1
, negate it, and take the remainder by 10.
⍝ 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
⍝ 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
For the second task, the first thing we need to do is save the representation of digits. To save space, I did not encode the binary representation explicitly, instead using a decimal representation that I then decode in base 2. The right representation is just the bitwise negation.
-∇ 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
-∇
∇ 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
+∇
First of all, if the vector digits
does not have exactly 11 elements, all between 0 and 9, it is an error and we return ¯1
.
Then, we take the first 6 digits and encode them with lrepr
, and the last 5 digits plus the check digit encoded with rrepr
. In each case, adding 1 is necessary because ⎕IO←1
. We return the final bit array with the required beginning, middle, and end guard patterns.
∇ 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
-∇
∇ 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
+∇
¯1
.lrepr
and rrepr
vectors), and for the check digit.∇ 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
-∇
∇ 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
+∇
∇ 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
-∇
This is the only problem that I didn’t complete. It required parsing the files containing the graphical representations of the trees, which was needlessly complex and, quite frankly, hard and boring with a language like APL.
+However, the next part is interesting: once we have a matrix of coefficients representing the relationships between the weights, we can solve the system of equations. Matrix Divide (⌹
) will find one solution to the system. Since the system is overdetermined, we fix A=1
to find one possible solution. Since we want integer weights, the solution we find is smaller than the one we want, and may contain fractional weights. So we multiply everything by the Lowest Common Multiple (∧
) to get the smallest integer weights.
∇ 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
+∇
Since we cannot just compute \(2^n\) directly and take the remainder, we use modular arithmetic to stay mod 1,000,000 during the whole computation. The dfn (1E6|2∘×)
doubles its argument mod 1,000,000. So we just apply this function \(n\) times using the Power operator (⍣
), with an initial value of 1.
⍝ 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⍺),⍵}/⌽⍺,¨⍵}
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.
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)\) timeThere is an interesting StackOverflow answer explaining the behaviour of Scan, and compares it to Haskell’s scanl
function.
+
+. This is what we do here, accumulating values from left to right. (This is inspired from dfns.ascan
, although heavily simplified.)
For the second task, there is an explicit formula for cashflow calculations, so we can just apply it.
+∇ 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
-∇
∇ 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
+∇
We first read the template and the JSON values from their files. The ⎕NGET
function read simple text files, and ⎕JSON
extracts the key-value pairs as a namespace.
Assuming all variable names contain only letters, we match the regex @[a-zA-Z]*@
to match variable names enclosed between @
symbols. The function getval
then returns the appropriate value, and we can replace the variable name in the template.
∇ val←ns getval var
- :If ''≡var ⍝ literal '@'
- val←'@'
- :ElseIf (⊂var)∊ns.⎕NL ¯2
- val←⍕ns⍎var
- :Else
- val←'???'
- :EndIf
-∇
∇ val←ns getval var
+ :If ''≡var ⍝ literal '@'
+ val←'@'
+ :ElseIf (⊂var)∊ns.⎕NL ¯2
+ val←⍕ns⍎var
+ :Else
+ val←'???'
+ :EndIf
+∇
This function takes the namespace matching the variable names to their respective values, and the name of the variable.
@@
, which corresponds to a literal @
.???
.The check digit satisfies the equation \[ 3 x_{1}+x_{2}+3 x_{3}+x_{4}+3 x_{5}+x_{6}+3 x_{7}+x_{8}+3 x_{9}+x_{10}+3 x_{11}+x_{12} \equiv 0 \bmod 10, \] therefore, \[ x_{12} \equiv -(3 x_{1}+x_{2}+3 x_{3}+x_{4}+3 x_{5}+x_{6}+3 x_{7}+x_{8}+3 x_{9}+x_{10}+3 x_{11}) \bmod 10. \]
Translated to APL, we just take the dot product between the first 11 digits of the barcode with 11⍴3 1
, negate it, and take the remainder by 10.
⍝ 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
⍝ 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
For the second task, the first thing we need to do is save the representation of digits. To save space, I did not encode the binary representation explicitly, instead using a decimal representation that I then decode in base 2. The right representation is just the bitwise negation.
-∇ 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
-∇
∇ 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
+∇
First of all, if the vector digits
does not have exactly 11 elements, all between 0 and 9, it is an error and we return ¯1
.
Then, we take the first 6 digits and encode them with lrepr
, and the last 5 digits plus the check digit encoded with rrepr
. In each case, adding 1 is necessary because ⎕IO←1
. We return the final bit array with the required beginning, middle, and end guard patterns.
∇ 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
-∇
∇ 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
+∇
¯1
.lrepr
and rrepr
vectors), and for the check digit.∇ 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
-∇
∇ 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
+∇
∇ 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
-∇
This is the only problem that I didn’t complete. It required parsing the files containing the graphical representations of the trees, which was needlessly complex and, quite frankly, hard and boring with a language like APL.
+However, the next part is interesting: once we have a matrix of coefficients representing the relationships between the weights, we can solve the system of equations. Matrix Divide (⌹
) will find one solution to the system. Since the system is overdetermined, we fix A=1
to find one possible solution. Since we want integer weights, the solution we find is smaller than the one we want, and may contain fractional weights. So we multiply everything by the Lowest Common Multiple (∧
) to get the smallest integer weights.
∇ 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
+∇
Since we cannot just compute \(2^n\) directly and take the remainder, we use modular arithmetic to stay mod 1,000,000 during the whole computation. The dfn (1E6|2∘×)
doubles its argument mod 1,000,000. So we just apply this function \(n\) times using the Power operator (⍣
), with an initial value of 1.
⍝ 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⍺),⍵}/⌽⍺,¨⍵}
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.
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)\) timeThere is an interesting StackOverflow answer explaining the behaviour of Scan, and compares it to Haskell’s scanl
function.
+
+. This is what we do here, accumulating values from left to right. (This is inspired from dfns.ascan
, although heavily simplified.)
For the second task, there is an explicit formula for cashflow calculations, so we can just apply it.
+∇ 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
-∇
∇ 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
+∇
We first read the template and the JSON values from their files. The ⎕NGET
function read simple text files, and ⎕JSON
extracts the key-value pairs as a namespace.
Assuming all variable names contain only letters, we match the regex @[a-zA-Z]*@
to match variable names enclosed between @
symbols. The function getval
then returns the appropriate value, and we can replace the variable name in the template.
∇ val←ns getval var
- :If ''≡var ⍝ literal '@'
- val←'@'
- :ElseIf (⊂var)∊ns.⎕NL ¯2
- val←⍕ns⍎var
- :Else
- val←'???'
- :EndIf
-∇
∇ val←ns getval var
+ :If ''≡var ⍝ literal '@'
+ val←'@'
+ :ElseIf (⊂var)∊ns.⎕NL ¯2
+ val←⍕ns⍎var
+ :Else
+ val←'???'
+ :EndIf
+∇
This function takes the namespace matching the variable names to their respective values, and the name of the variable.
@@
, which corresponds to a literal @
.???
.The check digit satisfies the equation \[ 3 x_{1}+x_{2}+3 x_{3}+x_{4}+3 x_{5}+x_{6}+3 x_{7}+x_{8}+3 x_{9}+x_{10}+3 x_{11}+x_{12} \equiv 0 \bmod 10, \] therefore, \[ x_{12} \equiv -(3 x_{1}+x_{2}+3 x_{3}+x_{4}+3 x_{5}+x_{6}+3 x_{7}+x_{8}+3 x_{9}+x_{10}+3 x_{11}) \bmod 10. \]
Translated to APL, we just take the dot product between the first 11 digits of the barcode with 11⍴3 1
, negate it, and take the remainder by 10.
⍝ 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
⍝ 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
For the second task, the first thing we need to do is save the representation of digits. To save space, I did not encode the binary representation explicitly, instead using a decimal representation that I then decode in base 2. The right representation is just the bitwise negation.
-∇ 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
-∇
∇ 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
+∇
First of all, if the vector digits
does not have exactly 11 elements, all between 0 and 9, it is an error and we return ¯1
.
Then, we take the first 6 digits and encode them with lrepr
, and the last 5 digits plus the check digit encoded with rrepr
. In each case, adding 1 is necessary because ⎕IO←1
. We return the final bit array with the required beginning, middle, and end guard patterns.
∇ 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
-∇
∇ 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
+∇
¯1
.lrepr
and rrepr
vectors), and for the check digit.∇ 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
-∇
∇ 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
+∇
∇ 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
-∇
This is the only problem that I didn’t complete. It required parsing the files containing the graphical representations of the trees, which was needlessly complex and, quite frankly, hard and boring with a language like APL.
+However, the next part is interesting: once we have a matrix of coefficients representing the relationships between the weights, we can solve the system of equations. Matrix Divide (⌹
) will find one solution to the system. Since the system is overdetermined, we fix A=1
to find one possible solution. Since we want integer weights, the solution we find is smaller than the one we want, and may contain fractional weights. So we multiply everything by the Lowest Common Multiple (∧
) to get the smallest integer weights.
∇ 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
+∇