((λ (x) (x x)) (λ (x) (x x)))

Thoughts on Eternity, God, and lesser endeavors

Subscribe to RSS feed

Posts tagged with "languages"

APL Hacking: Project Euler Daily (#5)

, , , ...

I really do not like my solution here, but it was the simplest at the time. I think that there is definitely a neater way to do this one:

Problem #5:

R←PEFIVE

⍝ What is tthe smallest positive number that is evenly divisible
⍝ by all of the numbers from 1 to 20?

⍝ Prime factorizations

X←  2 0 0 0 0 0 0 0 0  0  0  0  0 ⍝ 2
X←X,0 0 0 0 3 0 0 0 0  0  0  0  0 ⍝ 3
X←X,2 2 0 0 0 0 0 0 0  0  0  0  0 ⍝ 4
X←X,0 0 0 0 0 0 0 5 0  0  0  0  0 ⍝ 5
X←X,2 0 0 0 3 0 0 0 0  0  0  0  0 ⍝ 6
X←X,0 0 0 0 0 0 0 0 7  0  0  0  0 ⍝ 7
X←X,2 2 2 0 0 0 0 0 0  0  0  0  0 ⍝ 8
X←X,0 0 0 0 3 3 3 0 0  0  0  0  0 ⍝ 9
X←X,2 0 0 0 0 0 0 5 0  0  0  0  0 ⍝ 10
X←X,0 0 0 0 0 0 0 0 0 11  0  0  0 ⍝ 11
X←X,2 2 0 0 3 0 0 0 0  0  0  0  0 ⍝ 12
X←X,0 0 0 0 0 0 0 0 0  0 13  0  0 ⍝ 13
X←X,2 0 0 0 0 0 0 0 7  0  0  0  0 ⍝ 14
X←X,0 0 0 0 3 0 0 5 0  0  0  0  0 ⍝ 15
X←X,2 2 2 2 0 0 0 0 0  0  0  0  0 ⍝ 16
X←X,0 0 0 0 0 0 0 0 0  0  0 17  0 ⍝ 17
X←X,2 0 0 0 3 3 0 0 0  0  0  0  0 ⍝ 18
X←X,0 0 0 0 0 0 0 0 0  0  0  0 19 ⍝ 19
X←X,2 2 0 0 0 0 0 5 0  0  0  0  0 ⍝ 20

R←×/⌈⌿20 13⍴X

APL Hacking: Project Euler Daily (#4)

, , , ...

When I first did this problem, I encoded the numbers, did a 1st axis rotate, and then decoded both vectors back into their elements. However, that solution ran in 330ms, whereas this solution runs in 278ms on my machine. In this case, I use a first axis reduction to avoid doing one of the decodings.

Problem #4:

∇R←PEFOUR;X;Y;⎕IO
⎕IO←1

⍝ Find the largest palindrome made from the product of two 3-digit numbers.
X←99↓⍳999 ⋄ X←(6⍴10)⊤∪,X∘.×X ⋄ Y←⊖X
R←⌈/(^⌿X=Y)/10⊥X
∇

APL Hacking: Project Euler Daily (#3)

, , , ...

For this problem, I had originally computed a very slow and annoying algorithm until I realized that computing the prime factors meant literally what I used to do in grade school. That simplified things down since I could grow a list of primes while at the same time shrinking the large value of N, which is too large to sit in 32-bit integers, and makes a Sieve too impractical to implement.

Problem #3:

∇R←PETHREE;N;P;X;⎕IO
⎕IO←1

⍝ What is the largest prime factor of 600851475143

N←600851475143

⍝ We will compute the prime factors by growing a list of primes.

⍝ P Primes
⍝ X Factor under consideration
⍝ N Number to factor

P←2 ⋄ X←3

LOOP:→(0∊P|X)/CONT ⋄ P←P,X ⋄ →(0≠X|N)/CONT
SHRINK:N←N÷X ⋄ →(0=X|N)/SHRINK
CONT:X←X+2 ⋄ →(X<N)/LOOP

R←N
∇


Extended explanation

Prime factors of a given number are the factors of a number that are prime. You did this in grade school when you factored numbers into their prime factors by first checking if it was divisible by two, then three, and so forth. Each time you found a factor, you would see how many of those you could use before moving on to the next prime. So, take 56 for example:

56 = 2 × 28
   = 2 × 2 × 14
   = 2 × 2 × 2 × 7


Now, at this point, 7 is a prime, and that is the last one.

The algorithm above does the exact same thing. It keeps track of the prime numbers in an array P that starts with 2. X start at 3 and increments by two. For each X, we check whether it is a prime factor, and if it is, we divide our current N by it, setting the quotient to be our new N. We repeat this until X no longer divides N, in which case we continue with X added to the list of primes and X containing the next number to test for primality.

The LOOP label line checks for prime factor, the SHRINK line is the line that shrinks or divides the N until it is not possible to do so, and the CONT line continues by incrementing the X value and checking whether we have reached the end. We know that we have reached the end when our X is greater than or equal to N.

The main trick here is the APL one armed conditional jump idiom.

→(0∊P∣X)/CONT


These types of forms are jumps where the → will jump to whatever label is to its right, unless there is nothing to its right, in which case it continues. In the above, we test whether X is divisible by any of our primes, thus making it non-prime, and allowing us to skip the rest of the computation and move to continue the loop. That (...) will be 0 or 1, where the 0 will cause the / to not select its single left argument, and the 1 will cause / to select the CONT.

CS Club Hackathon Result; or, Playing with APL

, , , ...

This was my submission to the CS Club hackathon. This was my first excuse to write what I would call a less than trivial APL program and overall, the experience was enlightening and fun. It took, in total, probably around an hour to write.

SAT Solver in APL

The following is a solution to the SAT problem that determines exhaustively all of the possible solutions to a given problem. This page displays APL characters. Make sure that you are using a font in your web browser that supports these characters.

Encoding the Problem Space

A SAT problem can be expressed in Conjuctive normal form, where we have a set of conjuctions of disjunctions:

(X ∨ ... ∨ X) ^ ... ^ (X ∨ ... ∨ X)


We can think of each clause in the conjuction as a set of variables, either negated or not. If we assume that we have D number of variables, then we can encode each clause as a vector of true and false values. This vector contains 2D elements. Each slot in this vector correponds to a possible value of a variable or the negation of a variable X. If that form appears directly in the clause, then the slot corresponding to that vector contains true. Otherwise, it contains false. For example, consider the following clause:

(X ∨ ~ Y)


Assuming that we have three variables X, Y, and Z, then the encoding of this into a vector is as follows:

Z Y X ~Z ~Y ~X
0 0 1  0  1  0


Notice that for each half of the vector corresponding to either the negative or positive values, we list the variables from last to first. You will see why later.

Given this, we can now encode all of the clauses as a N by 2D matrix, where N is the number of clauses, and D is the number of variables that we have. The rows of this matrix correspond to the clauses, and the columns to the variable terms (two per variable, positive and negative).

We will call this Clause matrix C.

Encoding the solution

Given D variables, we know that there are (2*D)-1 ways to value those variables (* here is used in its dyadic form to represent exponentiation, multiplication is ×). We can think of each possible assignment for a 3 variable problem as follows:

000
001
010
011
100
101
110
111


Notice that this is just the progression of numbers from 0 to ''(2*3)-1''. Let's assume for a moment that we zero index everything:

⎕IO ← 0


We can thus construct the possible solutions in binary using the following expression:

T←(D⍴2)⊤⍳(2*D)


This gives us a D by (2*D) matrix where each column corresponds to a possible solution. However, this does not give us the negative assignments, which are just the complement of the positive ones. We can do that with by concatenating the positive matrix with its complement along the first axis:

V←T⍪~T


Now T is our positive matrix and V is our total solution space with 2D rows.

Computing the solutions

Now we have an N by 2D clause matrix C and a 2D by (2*D) solution matrix V. Since the colunm count of C equals the row count of V, we can perform an inner product computation on these two matrices. For those unfamiliar with inner product, matrix multiplication is an inner product using multiplication and addition:

A+.×B ⍝ Matrix multiplication of A and B


However, instead of multiplying each element in the columns and rows of C and V, we want to compute their logical ^ value. We then replace addition with ∨ and this gives us a result like so:

C∨.^V


The result is a matrix where each element (x,y) indicates whether clause x was satisfied by potential solution y. Then we just need to compress the columns using ^ and we have a vector indicating what solutions satisfied the entire expression. We can then use this matrix to select the solution numbers from our original potential solution pool and encode those as binary. The final matrix has one solution for each column.

Solution Summary

The resulting function in APLX looks like this:

∇ R ← D SAT C;T
  T←(D⍴2)⊤⍳(2*D)
  (D⍴2)⊤(^⌿C∨.^T⍪~T)/⍳(2*D)
∇

Fortress for the Weary (or Lazy)

, , , ...

I have just finished watching an extended talk by Guy Steele about how we should be designing our programs for parallelism. The gist of his messages declares that we should not be thinking explicitly about the low level operations of dispatching our code to core, or the like, but should instead be focusing on structuring our programs using divide-and-conquer methods so that compilers can do the parallelizing for us.

Okay, obviously there are some things about this that do not quite work. Principally, humans are still better than machines at determining when to parallelize. Nonetheless, in those cases when I know that I want to parallelize something, then I very well do care about making that process as seemless and comprehensible as I can. The approach championed by Fortress, which is Steele’s research language, does have some appeal, but I do not want to use Fortress to get these benefits. I want to use Scheme.

So, my question then becomes, what is the appropriate general interface (read, what library ought to exist) to facilitate this sort of parallelism? Given a Scheme with appropriately capable multi-tasking primitives, such as pthreads or MPI access, what would a high-level interface look like? Maybe someone has done this already for languages like Haskell? Can anyone point me to what this should look like?

Observation on Programming Languages and IDEs

, , , ...

There are some programming languages that have very extensive IDEs. And there are some languages that do not. I don't think there is a correlation between quality of the language and quality of the IDEs. However, I do think that it is interesting how the features of IDEs in one language reflect the weaknesses in that language. At least, that's my hypothesis.

If you look at Java or C IDEs, there are massive amounts of extra support to overcome usability problems in the language, such as long names, complicated structures, and a lack of language based abstraction mechanisms. These work fairly well to overcome the language's problems, but I have a different solution, and in fact, this is the preferred solution of most programming language researchers. Indeed, I have found this to be a viable and especially useful feature in the business programming world if you are understaffed and have fewer programmers, but more "competence" than your competition.

Basically, I prefer to use my programming language to create my abstractions at the level where they are usable, so I only have to type in what I want, and I don't have to be overly verbose. It's not hard, in this way, to enter code, because you don't have waste. Design Patterns are, instead, just syntactic abstractions that are hidden away. In this way, I find that the IDE is overrated. As you can see if you look at some of the most productive Scheme programmers, very often, they do not use much of an IDE, though they have some tools to help them? Why? Well, if you look at their language, you'll see that they don't avoid the features that IDEs give them, but rather, they have them built into their language in a more elegant fashion.

I like this.

Basically, I don't use an IDE: my programming language is my IDE, and a good one at that.

This is too good to pass up

, , ,

I am sorry, I just couldn't pass this one up.



The Programming Matrix