Saturday, November 30, 2013

Problem 69 Analytic - 100th post

So, problem 69 took a long time to brute force, but as is the case with most totient problems as I am starting to discover, its much easier to think about properties of the totient function than have to actually calculate it. Turns out, it is rather simple to find what the answer to problem 69 must be - it has to be primorial, the product of the first n primes for some n. Anyway, this post, which frees up J yet again, is my 100th on this blog. yay.

Problem 70 - Perl

I hadn't touched Perl except in a very, very brief rushed learning session at the end of the hackathon where I solved the first 20 problems. 50 problems later, I have finally returned to it. And though it is capable and all, boy is it a weird language. My solution to this problem is definitely smarter than a brute force (brute forcing the totient of all numbers below 10^7 would not be fun/fast), but still not as smart as some of the solutions on the Euler problem. My solution works using the fact that the solution must have very few prime factors, but I do an "exhaustive" search over numbers with "few" prime factors, instead of choosing a much nicer, more restrictive set of conditions to search that other people found. Anyway, the solution runs in about 30s, and this is essentially my birthday present to me, today is my birthday, and 70 problems are now done.
sub isPrime {
    my (@ops) = @_;
    my $x = $ops[0];
    if ($x < 2 || $x % 2 == 0) {
        return x == 2
    }
    for (my $i = 3; $i * $i <= $x; $i += 2) {
        if ($x % $i == 0) {
            return 0
        }
    }
    return 1
}

sub nextPrime {
    my (@ops) = @_;
    my $x = $ops[0];
    my $y = $x + 1;
    while (not(isPrime($y))) {
        $y += 1;
    }
    return $y;
}

sub lstprod {
    my (@ops) = @_;
    my $r = 1;
    for my $x (@ops) {
        $r *= $x;
    }
    return $r
}

sub totientIsPermut {
    my (@ops) = @_;
    my $n = lstprod(@ops);
    my $prev = 0;
    my $t = $n;
    #print "-$n, @ops\n";
    for my $p (@ops) {
        if ($p != $prev) {
            $t = $t / $p;
            $t = ($p - 1) * $t;
            $prev = $p
        }
    }
    my @tarr = sort split('', "$t");
    my @narr = sort split('', "$n");
    my $ts   = join('',@tarr);
    my $ns   = join('',@narr);
    if ($ts eq $ns) {
        $rat = $n / $t;
    }
    return ($ts eq $ns) 
}

sub update {
    my (@ops) = @_;
    my $size = @ops;
    for (my $x = 0; $x < $size; $x += 1) {
        my $next = nextPrime($ops[$x]);
        my @slice = @ops[($x+1).. ($size-1)];
        my $nprod = $next * lstprod(@slice);
        if ($nprod < 10000000 && $nprod != 0) {
            @slice = @ops[($x) .. ($size-1)];
            $slice[$x] = $next;
            return @slice
        }
    }
    @slice = @ops[1 .. ($size-1)];
    return update(@slice)
}
my @nums = (2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
my $done = 0;
my $minrat = 2;
$rat = 2;
my $ans  = 0;
while (not($done)) {
    if (totientIsPermut(@nums)) {
        if ($rat < $minrat) {
            $minrat = $rat;
            $n = lstprod(@nums);
            if ($n > 10000000) {
                $done = 1
            } else {
                $ans = lstprod(@nums)
            }
        }
    }
    @nums = update(@nums)
}
print "$ans\n"


Tuesday, November 26, 2013

Problem 68 - Fantom

Fantom is yet another language that runs on the JVM, though the designers of Fantom don't stop there - it can also compile to code for the .NET framework or javascript. Anyway, its a fairly simple language with a common Scala/Java/Gosu/C/C#/Squirrel/Ceylon/How many curly-braced languages are there again? syntax. I decided to use it on problem 68 as the problem is not all that hard using a nice object oriented approach and ordering the different kinds of rings with lexicographic permutations (I think I have written that NthLP method in at least 4 different languages by now). Runs in about 1.8s on my machine.
class Ring {
    Int[] elems
    
    static Int fac(Int x) {
        if (x < 2) {
            return 1
        } else {
            return x * fac(x-1)
        }
    }

    Int[] NthLP(Int N, Int[] p, Int[] rem) {
        if (rem.size == 1) {
            p.addAll(rem)
            return p
        }
        Int k := fac(rem.size - 1)
        Int timesDivided := N / k
        p.add(rem[timesDivided])
        rem.removeAt(timesDivided)
        return NthLP(N % k, p, rem) 
    }   
    
    Bool isMagic() {
        Int target := elems[4] + elems[9] + elems[5]
        for (i := 0; i < 4; ++i) {
            if (elems[i] + elems[5+i] + elems[6+i] != target) {
                return false
            }
        }
        return true
    }
    Str description() {
        Int minIndex := 0
        Str desc := ""
        5.times |i| { if (elems[i] < elems[minIndex]) { 
                          minIndex = i} }
        5.times |i| { 
            Int j := (i + minIndex) % 5
            [elems[j], elems[5+j], 
        elems[(j == 4 ? 5 : 6 + j)]].each |d| { desc += "$d" }
        }
        return desc
    }
    new make(Int N) {
        //construct a Ring with elements
        //in the formation of the Nth Lexicographic permutation
        this.elems = NthLP(N, [,], [10,1,2,3,4,5,6,7,8,9])
    }
}

class E68 {

    static Void main() {
        Str maxstr := ""
        for (i := 0; i < Ring.fac(10); ++i) {
            Ring r := Ring(i)
            Str s := ""
            if (r.isMagic()) {
                s = r.description()
                if (s.size() > 16) {
                    break
                } else if (s.compare(maxstr) > 0) {
                    maxstr = s
                }
            }
        }
        echo(maxstr)
    }
}
        

Monday, November 25, 2013

Problem 19 redo - F#

F# is a language based on OCaml but which also does other stuff and such...I didn't have a very exciting experience with it for this problem, but in my delaying solving problem 68, I decided to free up some of my more capable language. Even though I have only used Perl for about 10 minutes in the past, it seemed like a language I might want to use again, so I ported my solution to problem 19 from Perl to F#. Solution runs in about 75ms on my machine:
let rec answer day sundays mo y =
    if y = 101  then sundays else
    if mo = 12  then (answer day sundays 0 (y + 1)) else
        let newsun = (if (day = 0) && not (y = 0) then 
                      sundays + 1 else sundays)
        if (mo = 0 || mo = 2 || mo = 4 || mo = 6 || mo = 7
         || mo = 9 || mo = 11) then
            (answer ((day + 3) % 7) newsun (mo + 1) y) else
        if (mo = 1) then
            (answer (if ((y % 4 = 0) && not (y = 0)) then 
            ((day + 1) % 7) else day) newsun (mo + 1) y)
        else
            (answer ((day + 2) % 7) newsun (mo + 1) y)

printfn "%d" (answer 1 0 0 0)

Sunday, November 24, 2013

Problem 69 - J

I decided to skip problem 68, at least for a little bit - it involves magic and polygons and all sorts of weird stuff. So, instead I decided to solve problem 69. This problem is rather simple in J, where a reasonably efficient implementation of Euler's totient function is part of the language: (5 p: y) is the totient of y. So, with that, writing a solution was not hard at all. Now, I am sure there are better solutions, as mine runs in about 20 seconds, but what works works.
f =: 3 : 'y % (5 p: y)'
lst =: f i.1000000
lst i.{.\:~lst

Problem 35 - ChucK

ChucK is a programming language that was never intended to be used for Project Euler. It is a"Strongly-timed, concurrent, and On-The-Fly Music Programming Language." Obviously, I did not have to make music to solve this problem, so I used few of ChucK's features. However, it is a programming language, so it is fair game for this challenge. My solution in ChucK to problem 35 has, unfortunately, 25 times as many lines as my previous solution, which was in J (I wanted to use J for problem 69, as will be clear in my next post). Nonetheless, ChucK was a reasonable enough language, even if the chuck operator => is rather odd. Solution runs in about 25s on my machine...not fast, but not Euler-rules-breakingly slow either.
fun int isPrime(int x) {
    if (x < 2) {
        return 0;
    } else if (x % 2 == 0) {
        return x == 2;
    }

    for (3 => int i; i * i <= x; 2+=>i) {
        if (x % i == 0) {
            return 0;
        }
    }
    return 1;
}

fun int mag(int x) {
    if (x < 10) {
        return 1;
    } else {
        return 10 * mag(x/10);
    }
}

fun int rotate(int x) {
    x % 10 => int d;
    return (d * mag(x)) + (x / 10);
}

fun int isCircPrime(int x) {
    if (!isPrime(x)) {
        return 0;
    }
    x => rotate => int r;
    while (x != r) {
        if (!isPrime(r)) {
            return 0;
        }
        r => rotate => r;
    }
    return 1;
}

//main
0 => int ans;
for (0 => int i; i < 1000000; ++i) {
    if (isCircPrime(i)) {
        ++ans;
    }
}
<<<ans>>>;

Sunday, November 17, 2013

Problem 67 - Prolog

Problem 67 is really identical to problem 18, other than that the data set is larger. Luckily, my solution in Prolog to problem 18 was plenty fast for it to scale well to this problem. My solution runs in about half a second, having to perform 34,211 inferences in Prolog. And yeah...maybe I should find a way to load the input from a text file, but for now, the entire data table is explicitly in the program...therefore, below is an abbreviated version, the full solution, with the 100 line triangle description, is linked to from the Progress table.
getRow(R, Row) :-
nth0(R,[[59],
[73,41],
[52,40,9],
[26,53,6,34],
...,59,63,35]], Row).

max(X, Y, Y) :- Y >= X.
max(X, Y, X) :- X >= Y.

prepend([], Z, [Z]).
prepend([X|Y], Z, [Z,X|Y]).

combineRows([X, Y], [Z], Row) :-
    XZ is X + Z,
    YZ is Y + Z,
    max(XZ, YZ, N),
    prepend([], N, Row).

combineRows([X,Y|T1], [Z|T2], Row) :-
    XZ is X + Z,
    YZ is Y + Z,
    max(XZ, YZ, N),
    combineRows([Y|T1], T2, Tail),
    prepend(Tail, N, Row).

ans(99, W) :- getRow(99, W).
ans(N, W)  :-
    N1 is N + 1,
    getRow(N, Top),
    ans(N1, Bottom),
    combineRows(Bottom, Top, W).

%% Command to Find the Answer:
e67(W) :- ans(0, List),
    nth0(0, List, W).

Problem 18 - WIND

The WIND is another fictional machine introduced in my CS237 class, with an Intel-like instruction set. I re-solved problem 18 in WIND, in order to re-use my original solution to problem 18 (in Prolog) for problem 67. While WIND instructions don't exactly run super quickly (the only interpreter is one written in Java, the fastest of languages), but luckily this program was fast enough that it still ran in about .5 seconds (taking about 14000 assembly instructions)
jmp main
.origin 85
.data 100,101,103,106,110,115,121,128,136,145,155,166,178,191,205
.data 75,95,64,17,47,82,18,35,87,10,20,4,82,47,65,19,1,23,75,3,34,
88,2,77,73,7,63,67,99,65,4,28,6,16,70,92,41,41,26,56,83,40,80,70,
33,41,48,72,33,47,32,37,16,94,29,53,71,44,65,25,43,91,52,97,51,14,
70,11,33,28,77,73,17,78,39,68,17,57,91,71,52,38,17,14,91,43,58,50,
27,29,48,63,66,4,68,89,53,67,30,73,16,69,87,40,31,4,62,98,27,23,
9,70,98,73,93,38,53,60,4,23

main:
    mov     $85,r3
    mov     $13,r1     
oloop:
    cmp     $0,r1
    jl      overoloop
    mov     $0,r2
    mov     r1,r0
    add     $1,r0
    mov     0(r3,r0),r4     ;; get ptr into row below
iloop:
    cmp     r2,r1
    jl      overiloop
    mov     0(r4,r2),r5     ;; elem below
    mov     r2,r0
    add     $1,r0
    mov     0(r4,r0),r15    ;; elem below and over
    cmp     r15,r5
    jl      r15big
    mov     r5,r15
r15big:
    mov     0(r3,r1),r14     ;; ptr into cur row
    add     r15,0(r14,r2)
    add     $1,r2
    jmp     iloop
overiloop:
    sub     $1,r1
    jmp     oloop
overoloop:
    mov     $100,r0
    mov     (r0),r0
    trap    $SysPutNum
    mov     $10,r0
    trap    $SysPutChar
    trap    $SysHalt

Saturday, November 16, 2013

Progress

This table records all of the languages I have used on this challenge, along with which problems have been solved in which languages. The statement of the problems can all be found on Project Euler, and my solutions are described in this blog, as well as being linked to from the table below. This table keeps growing, and keeps getting pushed lower and lower in the blog, so I keep reprinting it. The links all lead to postings of the code on github: so if you wanted all of the code, you could clone the whole repository (https://github.com/drsam94/PELC). A * denotes a solution that has been replaced by a solution in another language. If a language occurs more than once in the table, that is only because it would make the table too wide to list all solutions in one row.
BrainFuck 1*
Haskell 2*,37
J 3*,24*,35*,69*
Ruby 4*,30*,56
Whenever 2,5*
Ook! 6*
Racket 7*,26*,57
K 8*,30
Javascript 9*,31*,60
sML 10*,42
PHP 11
Go 12*,32
cLisp 13*,62
C 48*,50
Fortran95 3,15*
Python 16*,24*,39*
Python 65*,66
ELM 17*,48
Scala 18*,44
Perl 19*, 70
Java 20
FALSE 4
Squirrel 21*,52*,55
D 22
Ceylon 23
Postscript 7
Befunge 27
Boo 29
Frink 16
Forth 9
Shakespeare 12
Bash 33
Batch 34
Whitespace 36
LOLCODE 10
Lua 38
Erlang 24
Rust 40
Ada 41
Clojure 14
Coffeescript 43
Prolog 18*,67
R 45
Julia 46
x86-64 47
INTERCAL 17
OCaml 49
COBOL 21
Cobra 51
C++ 14*,53
APL 52
Chapel 54
C# 26*, 59
WARM 58
Kotlin 26
Pascal 61
ALGOL68 31
Smalltalk 13
Dart 63*,65
Tcl 64
Gosu 39
Rebol 63
WIND 18
ChucK 35
F# 19
Fantom 68
Analytic 1,5,6,8
Analytic 15,25,28,69

Problem 66 - Python

So, problem 66...It involves solving Diophantine equations, which is arguably the scariest math word to appear in a Project Euler problem up to this point (yes, it just means we are looking for solutions over the integers, but Diophantine...big word). Trying to answer this problem by brute force gets one nowhere...many of the minimal solutions are absolutely enormous, and would take forever to find by brute force (especially as one can't exactly binary search for a minimal solution...). Instead, one must find out by some means (my source: Wikipedia), that the equation in question is Pell's equation, which has nice solutions in terms of the convergents of the continued fraction expansion of the square root of D. Now, Euler did a good job of preparing most users for this problem: Problem 64 was about the continued fraction expansion of square roots, and problem 65 was about finding convergents of continued fractions. So if you are most people, by the time you reached problem 66, you should have had most of the code written already. However, as I must solve all my problems in different languages, this was not the case for me. Now, I decided to actually reuse my code for problem 65, but that meant resolving problem 65, as explained in the posts below. I did however have to port my problem 64 solution in Tcl into python, which was not too difficult. All in all, the combination of code reuse and wikipedia reading made this problem not terribly hard, but it would certainly be hard in an arbitrary language. My solution takes advantage of Python's lists, dictionaries, classes, arbitrary precision integer arithmetic, and potential for functions to have multiple return values. Resolving this problem in another language would certainly not be an easy task, and is not one I plan on doing any time too soon - this problem makes for the 5th Python solution, so its probably not a bad thing, either that I will probably not recover Python for a while. Anyway, now that this problem is done, which required writing three solutions, onwards to problem 67: which is essentially identical to problem 18, explaining why 5x as many people have solved that problem as had solved 66. Below is my solution, which runs in about 1.3s on my machine.
import math

def gcd(a, b):
    return a if b == 0 else gcd(b, a%b)

class rational:
    def __add__(self, other):
        return rational(self.num*other.denom+other.num*self.denom,
                        self.denom*other.denom)

    def inverse(self):
        return rational(self.denom, self.num)

    def __init__(self, n, d):
        g = gcd(n, d)
        self.num   = n / g
        self.denom = d / g

coefDict = {}

def coeff(n,D):
    if n == 1:
        return int(math.sqrt(D))
    else:
        lst = coefDict[D]
        return lst[(n - 2)%len(lst)]

def nextDepth(n,N,D):
    if n == N:
        return rational(coeff(n,D),1)
    else:
        return rational(coeff(n,D),1) + nextDepth(n+1,N,D).inverse()

def nthConv(n,D):
    return nextDepth(1,n,D)

def mag(x):
    return 100 if x < 10 else 10 * mag(x / 10)

def nextTerm(N, m, b):
    d  = (N - b ** 2) / m
    k  = int(math.ceil(2.0 * b / d))
    bp = k*d - b
    if bp ** 2 > N:
        k -= 1
        bp = k*d - b
    return k,d,bp

def computeExpansion(D):
    b = int(math.sqrt(D))
    coefDict[D] = []
    m = 1
    computations = []
    while True:
        a, m, b = nextTerm(D, m, b)
        c = mag(b)*m + b
        if c not in computations:
            coefDict[D].append(a)
            computations.append(c)
        else:
            break

def minx(D):
    computeExpansion(D)
    n = 1
    while True:
        test = nthConv(n,D)
        if test.num ** 2 - D * (test.denom ** 2) == 1:
            return test.num
        n += 1

ans = 0
maxsol = 0
for D in range(2,1000):
    if int(math.sqrt(D)) ** 2 != D:
        x = minx(D)
        if x > maxsol:
            ans = D
            maxsol = x
print ans


65 redone in Dart

After I made a hole for problem 65 by using Python in 66, I was in a bit of a pickle thinking about what language to use, as this problem is fairly hard in a language that doesn't either have support for arbitrary-integer precision rational numbers or can be made to have such capabilities. Thus, I used Dart, which has arbitrarily large integers and nice classes and such. Though I liked Dart enough the last time I used it, on problem 63, using it on problem 65 showed me why people think it would be a better replacement for Javascript...classes and objects in Javascript can be a real pain to deal with, but in Dart they are very reasonable to work with, it was not hard at all to make a class, do operator overloading, and all that sort of stuff. (also, I discovered that Dart has a succinct syntax for single line functions, which is always nice...and a ternary operator, because every good language needs one). Solution below runs in about 80ms on my machine:
class Rational {
    int n, d;
    static int gcd(a, b) =>
        b == 0 ? a : gcd(b, a%b);

    Rational(int top, int bottom) :
        n = top ~/ gcd(top, bottom), d = bottom ~/ gcd(top, bottom) {}

    operator +(Rational other) => 
        new Rational(this.n*other.d + other.n*this.d, other.d*this.d);

    Rational inverse() => new Rational(this.d, this.n);
}

int digisum(x) => x < 10 ? x : x%10 + digisum(x~/10);

int coeff(n) => n == 1 ? 2 : (n % 3 == 0 ? 2*(n~/3) : 1);

Rational nextDepth(n,N) {
    if (n == N) {
        return new Rational(1,coeff(n));
    } else {
        return new Rational(coeff(n),1) + nextDepth(n+1,N).inverse();
    }
}

Rational nthConv(n) => nextDepth(1,n); 

main() {
    print(digisum(nthConv(100).n));
}

Problem 63 - Rebol (a long sequence of redos)

This and the next few posts are actually going to be in the reverse order that I solved a set of problems, in order to keep with the technical rules of this challenge. Problem 66 involves solving Pell's Equation, which as some quick Wikipedia ready is enough to find out, is solved relatively easily using the convergent's of continued fractions.  So, I solved problem 66 in Python, reusing some of the code from Problem 65, then resolved problem 65 using Dart, as my solution was most portable to a nice language like dart with arbitrary precision integer arithmetic and classes, and then to fill the last hole, I solved problem 63 in Rebol.

Rebol is an interesting language...it's advertising scheme revolves around the fact that it is a very fast, very small (the compiler is <1 MB), and yet very powerful language...and though I didn't need to use too much of the arguable 'power' (most of which involves its capabilities for large-scale programs, including GUI stuff), it seems like a nice enough language, once you get past some of the odd syntax (everything is in square brackets, "either" replaces if-else, functions can be declared in multiple ways, though I still only understand the 'func' identifier (which is in some ways different from 'does')). Anyway, here is a solution, runs in about 20ms on my machine:
log-10: func [x] [return (log-e x) / (log-e 10)]

ans: 0
j: 1
b: 1
while [b < 10] [
    i: b
    while [i < 10] [
        digs: 1 + (to integer! (j * log-10 (i)))
        either digs = j [
            ans: ans + 1
        ] [
            b: i + 1
        ]
        i: i + 1
    ]
    j: j + 1
]
print ans

Thursday, November 14, 2013

Problem 65 - Python, again

After using Gosu this morning, Python was free again, so I decided to use it. This might have been a little brash, as problem 65 wasn't too too hard, but it was especially easy given arbitrary precision integer arithmetic and classes. This is the fourth problem that I have solved in Python (yes, I kind of like Python), and there may even be more to come in the future.
def gcd(a, b):
    return a if b == 0 else gcd(b, a%b)

class rational:
    def __add__(self, other):
        return rational(self.num*other.denom+other.num*self.denom,
                        self.denom*other.denom)

    def inverse(self):
        return rational(self.denom, self.num)

    def __init__(self, n, d):
        g = gcd(n, d)
        self.num   = n / g
        self.denom = d / g

def digisum(x):
    if x < 10:
        return x
    else:
        return x % 10 + digisum(x / 10)

def coeff(n):
    if n == 1:
        return 2
    elif n % 3 == 0:
        return 2 * (n / 3)
    else:
        return 1

def nextDepth(n,N):
    if n == N:
        return rational(1,coeff(n))
    else:
        return rational(coeff(n),1) + nextDepth(n+1,N).inverse()

def nthConv(n):
    return nextDepth(1,n)

print digisum(nthConv(100).num)

Problem 39 - Redo in Gosu

Gosu is a fairly fun language based that runs on the JVM. As it runs on the JVM, it has access to Java classes and stuff, so it has access to collections like maps, and all that fun stuff. As such, I decided to use Gosu in order to resolve problem 39, which I had previously solved in python by leveraging the Python dicts in order to make things simple: in Gosu, I just used Java HashMaps (though with a bit less of the clunky Java syntax). The hardest part of this solution was finding how to use Math.sqrt in gosu...oh well. Solution runs in about 2.5 seconds on my machine.
uses java.lang.*
    
var sols = { 0 -> 0 }

for (a in 2..500) {
    for (b in a..500) {
        var c2 = a*a + b*b
        var c  = Math.sqrt(c2) as int
        if (c2 == c * c) {
            var p = a + b + c
            if (p <= 1000) {
                if (!sols.containsKey(p)) {
                    sols.put( p, 1)
                } else {
                    sols.put( p, sols.get(p) + 1)
                }
            }
        }
    }
}

var ans = 0
var maxsols = 0
for (p in sols.keySet()) {
    if (maxsols < sols.get(p)) {
        ans = p
        maxsols = sols.get(p)
    }
}
print(ans)
     

Wednesday, November 13, 2013

Problem 64 - Tcl

Tcl is a weird little language. It's not all that bad once you get used to it, but things like how everything is actually a list of strings, and calculations need to be wrapped in an expr lead to some weird stuff. But, after plenty of difficulty, I managed to solve problem 64 using Tcl. Part of the added difficulty was my lack of prior exposure to continued fractions, which resulted in me having to derive the method entirely from the single given example, which caused me to state the method incorrectly more than once. Also, I messed up my strange method of storing a pair of numbers in a list a couple times (to check for repeats, I checked for repeats of what I call in the code m and b, by storing m * (10^(1 + the order of magnitude of b) + b in a list...its an effective method, despite any oddness. (and me forgetting the +1 at first, causing some false positives). The solution is a tad slow, taking about 3 seconds, but I feel like that is probably mostly Tcl's fault...as I said, everything is a list of strings apparently, which gives me no confidence in the speed of code written in the language.
proc nextTerm {N m b} {
    set d [expr {$N - ($b ** 2)}]
    set d [expr {int($d/ $m)}]
    set k [expr {ceil((2 * $b) / $d)}]
    set bp [expr {($k * $d) - $b}]
    if [expr {$bp ** 2 > $N}] {
        set k [expr {$k-1}]
        set bp [expr {($k * $d) - $b}]
    }
    return [list $d $bp]
}

proc mag {x} {
    if [expr {$x < 10}] {
        return 100
    } else {
        set b [expr {$x / 10}]
        set g [mag $b]
        return [expr {10 * $g}]
    }
}

set ans 0
for {set N 2} {$N <= 10000 } {incr N} {
    set a0 [expr {int(sqrt($N))}]
    if [expr {!($a0 * $a0 == $N)}] {
        set done -1
        set b $a0
        set m 1
        set coeffs {}
        while {$done == -1} {
            set data [nextTerm $N $m $b]
            set m    [lindex $data 0]
            set b    [lindex $data 1]
            set mg   [mag $b]
            set v    [expr {($m * $mg) + $b}]
            set done [lsearch $coeffs $v]
            if [expr {$done == -1 }] {
                lappend coeffs $v
            }
        }
        set l [llength $coeffs]
        if [expr {$l % 2 == 1}] {
            incr ans
        }
    }
}
puts $ans

Tuesday, November 12, 2013

Problem 63 - Dart

Dart is a programming language that plans on replacing Javascript...maybe it will be able to that, it has a lot of engineers from Google trying to make that happen. But either way, it makes a nice language to solve a Project Euler problem in. I discovered the language through http://helloworldquiz.com/, which a friend showed me, and which had surprisingly many languages which I had yet to consider for this challenge. Solution runs in about 44ms on my machine.

import "dart:math";

main() {
    num ans = 0;
    num j = 1;
    num b = 1;
    while (b < 10) {
        for (num i = b; i <= 9; ++i) {
            num pdigs = (log(pow(i,j)) / LN10 + 1).floor();
            if (pdigs == j) {
                ++ans;
            } else {
                b = (i + 1);
            }
        }
        ++j;
    }
    print(ans);
}

Problem 62 - Lisp

Lisp is a nice language, I personally prefer the Scheme dialect to Common Lisp, but I have no major qualms with it. This problem wasn't too bad...however, my solution right now is pretty slow (~2-3 min). The last time I had this issue (the other day of Prob. 60), I found a way to make the solution much faster the next day...so I will take the same approach here, and consider myself done, but I will look tomorrow to see if I can tweak it or find a better solution.
(defun digits (x)
  (if (< x 10) (cons x '()) 
      (cons (mod x 10) (digits (floor (/ x 10))))))

(defun cubes (size)
  (defun help (elem acc)
    (if (< (log (expt elem 3) 10) (- size 1)) acc 
        (help (- elem 1) (cons (expt elem 3) acc))))
  (help (floor (expt 10 (/ size 3))) '()))

(defun lsum (lst)
  (defun help (l tot)
    (if (equal l '()) tot (help (cdr l) (+ tot (car l)))))
  (help lst 0))

(defun ans (lst)
  (if (equal lst '()) 0
    (let ((digs (sort (digits (car lst)) #'>)))
    (let ((a (lsum (map 'list #'(lambda (x) 
        (if (equal digs (sort (digits x) #'>)) 1 0)) (cdr lst)))))
        (if (= a 4) (car lst) (ans (cdr lst)))))))

(defun e62 (size)
  (let ((a (ans (cubes size)))) (if (= a 0) (e62 (+ size 1)) a)))

(e62 5)

Monday, November 11, 2013

Problem 13 re-do - Smalltalk cop-out

I have never been a big fan of Smalltalk - I was first introduced to it in Principles of Programming Languages in the context of Squeak, a very strange IDE that only helped to exaggerate the strangeness of the message-sending object-oriented language. However, it was just about the only language left out there which I had actually used at some point in my life, yet I had not used in this challenge yet. Luckily, turns out Smalltalk has arbitrary precision integer arithmetic, which allowed me to use it to provide a very simple solution to problem 13, freeing up Common Lisp for the first time since the initial 24-hour hackathon during which I solved the first 20 problems. My wonderfully unexciting program is below.
((37107287533902102798797998220837590246510135740250 + 
46376937677490009712648124896970078050417018260538 + 
74324986199524741059474233309513058123726617309629 + 
91942213363574161572522430563301811072406154908250 + 
23067588207539346171171980310421047513778063246676 + 
89261670696623633820136378418383684178734361726757 + 
28112879812849979408065481931592621691275889832738 + 
44274228917432520321923589422876796487670272189318 + 
47451445736001306439091167216856844588711603153276 + 
70386486105843025439939619828917593665686757934951 + 
62176457141856560629502157223196586755079324193331 + 
64906352462741904929101432445813822663347944758178 + 
92575867718337217661963751590579239728245598838407 + 
58203565325359399008402633568948830189458628227828 + 
80181199384826282014278194139940567587151170094390 + 
35398664372827112653829987240784473053190104293586 + 
86515506006295864861532075273371959191420517255829 + 
71693888707715466499115593487603532921714970056938 + 
54370070576826684624621495650076471787294438377604 + 
53282654108756828443191190634694037855217779295145 + 
36123272525000296071075082563815656710885258350721 + 
45876576172410976447339110607218265236877223636045 + 
17423706905851860660448207621209813287860733969412 + 
81142660418086830619328460811191061556940512689692 + 
51934325451728388641918047049293215058642563049483 + 
62467221648435076201727918039944693004732956340691 + 
15732444386908125794514089057706229429197107928209 + 
55037687525678773091862540744969844508330393682126 + 
18336384825330154686196124348767681297534375946515 + 
80386287592878490201521685554828717201219257766954 + 
78182833757993103614740356856449095527097864797581 + 
16726320100436897842553539920931837441497806860984 + 
48403098129077791799088218795327364475675590848030 + 
87086987551392711854517078544161852424320693150332 + 
59959406895756536782107074926966537676326235447210 + 
69793950679652694742597709739166693763042633987085 + 
41052684708299085211399427365734116182760315001271 + 
65378607361501080857009149939512557028198746004375 + 
35829035317434717326932123578154982629742552737307 + 
94953759765105305946966067683156574377167401875275 + 
88902802571733229619176668713819931811048770190271 + 
25267680276078003013678680992525463401061632866526 + 
36270218540497705585629946580636237993140746255962 + 
24074486908231174977792365466257246923322810917141 + 
91430288197103288597806669760892938638285025333403 + 
34413065578016127815921815005561868836468420090470 + 
23053081172816430487623791969842487255036638784583 + 
11487696932154902810424020138335124462181441773470 + 
63783299490636259666498587618221225225512486764533 + 
67720186971698544312419572409913959008952310058822 + 
95548255300263520781532296796249481641953868218774 + 
76085327132285723110424803456124867697064507995236 + 
37774242535411291684276865538926205024910326572967 + 
23701913275725675285653248258265463092207058596522 + 
29798860272258331913126375147341994889534765745501 + 
18495701454879288984856827726077713721403798879715 + 
38298203783031473527721580348144513491373226651381 + 
34829543829199918180278916522431027392251122869539 + 
40957953066405232632538044100059654939159879593635 + 
29746152185502371307642255121183693803580388584903 + 
41698116222072977186158236678424689157993532961922 + 
62467957194401269043877107275048102390895523597457 + 
23189706772547915061505504953922979530901129967519 + 
86188088225875314529584099251203829009407770775672 + 
11306739708304724483816533873502340845647058077308 + 
82959174767140363198008187129011875491310547126581 + 
97623331044818386269515456334926366572897563400500 + 
42846280183517070527831839425882145521227251250327 + 
55121603546981200581762165212827652751691296897789 + 
32238195734329339946437501907836945765883352399886 + 
75506164965184775180738168837861091527357929701337 + 
62177842752192623401942399639168044983993173312731 + 
32924185707147349566916674687634660915035914677504 + 
99518671430235219628894890102423325116913619626622 + 
73267460800591547471830798392868535206946944540724 + 
76841822524674417161514036427982273348055556214818 + 
97142617910342598647204516893989422179826088076852 + 
87783646182799346313767754307809363333018982642090 + 
10848802521674670883215120185883543223812876952786 + 
71329612474782464538636993009049310363619763878039 + 
62184073572399794223406235393808339651327408011116 + 
66627891981488087797941876876144230030984490851411 + 
60661826293682836764744779239180335110989069790714 + 
85786944089552990653640447425576083659976645795096 + 
66024396409905389607120198219976047599490197230297 + 
64913982680032973156037120041377903785566085089252 + 
16730939319872750275468906903707539413042652315011 + 
94809377245048795150954100921645863754710598436791 + 
78639167021187492431995700641917969777599028300699 + 
15368713711936614952811305876380278410754449733078 + 
40789923115535562561142322423255033685442488917353 + 
44889911501440648020369068063960672322193204149535 + 
41503128880339536053299340368006977710650566631954 + 
81234880673210146739058568557934581403627822703280 + 
82616570773948327592232845941706525094512325230608 + 
22918802058777319719839450180888072429661980811197 + 
77158542502016545090413245809786882778948721859617 + 
72107838435069186155435662884062257473692284509516 + 
20849603980134001723930671666823555245252804609722 + 
53503534226472524250874054075591789781264330331690 ) 
asString copyFrom: 1 to: 10) printNl

Sunday, November 10, 2013

Problem 60 - Javascript

Problem 60 was a bit annoying - its a problem that I had never solved in my past life of doing Euler problems without language restrictions, but everything turned out O.K. My first solution was excessively slow (2-5min), but I noticed something really dumb I was doing that caused me to search way more combinations than necessary. The code below runs in <1s. And now I am formally up in another multiple of 10 - the 60's are upon us.
function isPrime(x) {
    if (x < 2) {
        return false
    } else if (x % 2 == 0) {
        return x == 2
    }

    for (var i = 3; i * i <= x; ++i) {
        if (x % i == 0) {
            return false;
        }
    }
    return true;
}
function mag(x) {
    return x < 10 ? 10 : 10 * mag(x / 10)
}
function concat(x, y) {
    return x * mag(y) + y
}

function getPrimes(N) {
    p = new Array()
    for (var i = 2; i <= N; ++i) {
        if (isPrime(i)) {
            p.push(i)
        }
    }
    return p
}

function satisfies(p,s) {
    for (var i = 0; i < s.length; ++i) {
        if (!(isPrime(concat(s[i],p)) && isPrime(concat(p,s[i])))) {
            return false
        }
    }
    return true
}
function setSum(set) {
    sum = 0;
    for (var i = 0; i < set.length; ++i) {
        sum += set[i]
    }
    return sum 
}
function scopy(set) {
    return set.slice(0)
}

primes = getPrimes(10000)
function finishSet(set, target, start) {
    if (set.length == 5) {
        test = setSum(set)
        return test < target ? test : target
    } else {
        for (var i = start; i < primes.length; ++i) {
            p = primes[i]
            if (satisfies(p,set)) {
                cset = scopy(set)
                cset.push(p)
                test = finishSet(cset,target,i)
                if (test < target) {
                    target = test
                    break
                }
            }
        }
    }
    return target
}
/** this is 'main' */
function euler60() {
    ans = finishSet(new Array(),10000000,0)
    console.log(ans)
    alert(ans)
}

Progress

This table records all of the languages I have used on this challenge, along with which problems have been solved in which languages. The statement of the problems can all be found on Project Euler, and my solutions are described in this blog, as well as being linked to from the table below. This table keeps growing, and keeps getting pushed lower and lower in the blog, so I keep reprinting it. The links all lead to postings of the code on github: so if you wanted all of the code, you could clone the whole repository (https://github.com/drsam94/PELC). A * denotes a solution that has been replaced by a solution in another language.
BrainFuck 1*
Haskell 2*,37
J 3*,24*,35
Ruby 4*,30*,56
Whenever 2,5*
Ook! 6*
Racket 7*,26*,57
K 8*,30
Javascript 9*,31*,60
sML 10*,42
PHP 11
Go 12*,32
cLisp 13*,62
C 48*,50
Fortran95 3,15*
Python 16*,24*,39*, 65
ELM 17*,48
Scala 18*,44
Perl 19
Java 20
FALSE 4
Squirrel 21*,52*,55
D 22
Ceylon 23
Postscript 7
Befunge 27
Boo 29
Frink 16
Forth 9
Shakespeare 12
Bash 33
Batch 34
Whitespace 36
LOLCODE 10
Lua 38
Erlang 24
Rust 40
Ada 41
Clojure 14
Coffeescript 43
Prolog 18
R 45
Julia 46
x86-64 47
INTERCAL 17
OCaml 49
COBOL 21
Cobra 51
C++ 14*,53
APL 52
Chapel 54
C# 26*, 59
WARM 58
Kotlin 26
Pascal 61
ALGOL68 31
Smalltalk 13
Dart 63
Tcl 64
Gosu 39
Analytic 1,5,6,8
Analytic 15,25,28

ALGOL 68 to re-do problem 31

Continuing with the trend of using more historical languages after using Pascal yesterday, I decided to use ALGOL 68 today, in order to re-solve problem 31 (so that I have a language I know as well as Javascript in order to solve problem 60, which seems a little annoying / I haven't ever solved it even in my prior Euler experience, so yeah, Javascript will be nice). Anyway, ALGOL 68 wasn't that bad to actually write code in...and a nice paper by Tannenbaum basically gave all the description I needed (http://maben.homeip.net/static/S100/research%20machines/software/algol/ACMSurveys0676-Tanenbaum.pdf), but I ran into a lot of issues not related to the actual writing of code...rather, I ran into issues with compilers. After taking a little while to find a compiler at all, I install ALGOL 68 Genie, which purports to be one of the better/more full compilers of ALGOL. Turns out it actually is a reasonably good compiler, but there was one large difference between the compiler and the Tannenbaum paper: Tanenbaum uses all lowercase words, and a68g requires all reserved word to be written in all caps. Once I discovered this, it wasn't much of an issue...but it took a long time to discover this. It wasn't documented particularly well anywhere, and the compiler errors related to using lower case words were utterly unhelpful (most looked like missing-semicolon errors)...but anyway, after getting past all of that, things were not too bad. Below is my solution which takes 27s to run...I imagine that is more due to the compiler not being the most efficient than because my code is actually slow.
BEGIN

[1:7] INT a := (1,2,5,10,20,50,100);
INT ways := 1;

PROC helper = (INT amountindex, inheritedamount) VOID:
BEGIN
    INT current;
    BOOL done := FALSE;   
    FOR i FROM 0 BY 1 TO 200 WHILE NOT done
    DO
        current := inheritedamount + (i * a[amountindex]);
        IF current > 200
            THEN done := TRUE
            ELIF current = 200
            THEN ways +:= 1;
                 done := TRUE
            ELIF amountindex < 7
            THEN helper(amountindex + 1, current)
        FI
    OD
END;

helper(1,0);
write((ways, new line))          
END

Saturday, November 9, 2013

Problem 61 - Pascal

Pascal is one of those languages that comes up all the time as an older language that used to be hip and cool, but nobody uses anymore. So, that makes it a perfect target for use in this blog. My experience seemed to validate Pascal's current status: its a fine language and all, but its a bit stiff and has a lot of weird features that have been smoothed out in later languages (variable declarations need to go in a certain place, function parameters are grouped by type, semicolons are necessary in weird places, and must be omitted in even weirder places [defining weird as unnatural to my sensibilities, though the rules do themselves make some sense]). Anyway, most of my difficulty with this problem came not in difficulties with Pascal, but rather difficulties with a corner case of the problem itself: it is necessary to find a cycle of numbers where one is triangular, one square, one pentagonal, one hexagonal, one heptagonal, and one octagonal. I originally decided to make the beginning of my cycle an octagonal number, as there are the fewest of them, but I realized this to be foolhardy due to an issue involving all hexagonal numbers also being triangular. This was solved by beginning cycles with triangular numbers. Anyway, my Pascal solution is below, and it runs fairly quickly (~ 20ms on my machine). Also, if anyone noticed, yes, I skipped problem 60. I'll get to it soon enough.
program Euler61;

var
    temp : integer;
    ans  : integer;
    t    : integer;
    i    : integer;

function nthTri(x : integer) : integer;
begin
    nthTri := (x * (x + 1)) div  2
end;
function isTri(x : integer) : Boolean;
begin
    temp  := round(sqrt(2*x + 1/4) + 1/2);
    isTri := temp * (temp + 1) / 2 = x
end;

function isSquare(x : integer) : Boolean;
begin
    temp     := round(sqrt(x));
    isSquare := temp * temp = x
end;

function isPent(x : integer) : Boolean;
begin
    temp   := round(sqrt(2/3 * x + 1 / 36) + 1/6);
    isPent := temp * (3 * temp - 1) / 2 = x
end;

function isHex(x : integer) : Boolean;
begin
    temp  := round(sqrt(x / 2 + 1 / 16) + 1/4);
    isHex := temp * (2 * temp - 1) = x
end;

function isHept(x : integer) : Boolean;
begin
    temp   := round(sqrt(2 / 5 * x + 9 / 100) + 3 / 10);
    isHept := temp * (5 * temp - 3) / 2 = x
end;

function isOct(x : integer) : Boolean;
begin
    temp  := round(sqrt(x / 3 + 1/9) + 1/3);
    isOct := temp * (3 * temp - 2) = x
end;

function shape(x,found : integer) : integer;
begin
    if isHept(x) then
        shape := 5
    else
        if isHex(x) then
            shape := 4
        else
            if isPent(x) then
                shape := 3
            else
                if isSquare(x) then
                    shape := 2
                else
                    if isOct(x) then 
                        shape := 1
                    else
                        shape := 0;
end;

function Next(x, found, sum, origin : integer) : integer;
var
    base : integer;
    s : integer;
    r : integer;
    test : integer;
    tfound : integer;
    tsum : integer;
begin
    r := 10;
    base := (x mod 100) * 100;
    while r <= 99 do
    begin
        s := 1 shl shape(base + r, found);
        if (found and s) = 0 then
        begin
            tfound := found + s;
            tsum   := sum + (base + r);
            if tfound = 63 then
            begin
                if r = origin div 100 then
                    Next := tsum
                else
                    Next := 0;
                exit;
            end

            else
            begin
                test := Next(base + r, tfound, tsum, origin);
                if test > 0 then
                begin
                    Next := test;
                    exit
                end;
            end;
        end;
        r := r + 1;
    end;
    Next := 0
end;

begin
    ans := 0;
    i := 45;
    while ans = 0 do
    begin
        t   := nthTri(i);
        if t mod 100 > 10 then
            ans := Next(t,1,t,t);
        i   := i + 1;
        if t > 9999 then
            exit;
    end;
    writeln(ans)
end.

Friday, November 8, 2013

Problem 59 - C# again

Despite the fact that this is the second C# solution in about 5 solutions, I really am not a huge fan of the language - It is a nice language with C-like syntax though which I had yet to use for a hard enough problem that it was attractive to continue uprooting it. This problem was mildly annoying though, require things like file i/o that can sometimes be frustrating or near-impossible with more esoteric languages. This problem involves XOR encryption of a file, and you must brute-force find a key for the message. Though I remembered the message and key from the last time I solved this problem, I didn't take any advantage of that knowledge in my solution, as indicated by the fact that my test for correctness is simply containing " the " somewhere in the message. Anyway, this should put C# to rest for a while...solution runs in 2 seconds, but I could probably speed it up if I wanted to by not re-reading the file on every key. Also, for anyone checking my solutions, I reformatted the input file to have one number per line.
using System.IO;
public class Euler59 {
    
    public static char[] decode(char[] key) {
        StreamReader reader = new StreamReader("cipher1.txt");
        char[] message = new char[1201];
        int i = 0;
        do {
            char c = (char) System.Convert.ToInt16(reader.ReadLine());
            message[i] = (char)(((int)c) ^ ((int)key[i%3]));
            i++;
        } while (reader.Peek() != -1);
        return message;
    }


    public static int Main(string[] args) {
        for (char a = 'a'; a <= 'z'; ++a) {
            for (char b = 'a'; b <= 'z'; ++b) {
                for (char c = 'b'; c <= 'z'; ++c) {
                    char[] key = new char[3] {a,b,c};
                    char[] asciis = decode(key);
                    string message = new string(asciis);
                    if (message.Contains(" the ")) {
                        int ans = 0;
                        for (int i = 0; i < asciis.Length; ++i) {
                            ans += (int) asciis[i];
                        }
                        System.Console.WriteLine(ans);
                        return 0;
                    }
                }
            }
        }
        return 0;
    }
}

Kotlin on problem 26

Kotlin is a language that compiles to both the JVM and javascript. It seems like a fairly reasonable language, using some features that seem similar to Scala, but overall retaining a very Java-like syntax. I had some difficulty installing Kotlin (aka my hard drive does not have enough free space for it), so I only used the free online in-browser version...as such, Kotlin would not have worked very well for the problem I am up to, problem 59, which requires file I/O. So instead, I used Kotlin to rewrite my solution to problem 26 that was in C#, and C# will be used for a solution to 59.
import java.util.Collections.*
fun biggestCycle(d: Int): Int {
  val rems: Array< int > = Array< int >(1000){i -> 0}
  var r: Int = 1
  var i: Int = 0
  while (r != 0) {
    r = (r * 10) % d
    for (j in 0..i) {
      if (rems[j] == r) {
        return i
      }
    }
    rems[i++] = r
  }
  return i
}
  
  
fun main(args : Array< string >) {
  var ans = 0
  for (d in 1..1000) {
    if (ans < biggestCycle(d)) {
      ans = d
    } 
  }
  println(ans)
}

Thursday, November 7, 2013

After a long hiatus, WARM for Problem 58

I have not gotten around to solving too many project Euler problems as of late, as unfortunately I have had ample supplies of classwork to do. However, after finishing a midterm earlier today, I decided that I finally had enough time to solve a problem. I couldn't entirely leave my studies though, so I decided to solve this problem in WARM, a Risc assembly language based on ARM created for Williams CS237 classes. The solution is rather slow due to the lack of a direct implementation of WARM, it has to run in a virtual machine. But nonetheless, it produces the correct answer, bringing me up to 58 problems solved.
    b       main
isPrime:
    cmp     r0,#2
    blt     finif
    beq     finit
    tst     r0,#1
    beq     finif
    mov     r1,#3
loop:
    mul     r2,r1,r1
    cmp     r2,r0
    bgt     finit
    div     r2,r0,r1
    mul     r2,r2,r1
    cmp     r2,r0
    beq     finif
    add     r1,r1,#2
    b       loop
finif:
    mov     r0,#0
    mov     pc, lr
finit:
    mov     r1,#0
    mov     pc, lr

main:
    mov     v1,#1  ;; level
    mov     v3,#0  ;; primes
loop2:
    mov     v2,#1  ;; index in level
loop3:
    cmp     v2,#4
    bge     overloop3
    mul     r0,v1,#2
    sub     r0,r0,#1
    mul     r0,r0,r0
    mul     v4,v1,v2, lsl #1
    add     r0,r0,v4 ;; finished computation
    bl      isPrime
    cmp     r0,#0
    addne   v3,v3,#1
    add     v2,v2,#1
    b       loop3
overloop3:
    mul     v4,v1,v2
    add     v4,v4,#1
    div     r0,v4,v3
    cmp     r0,#10
    bge     overloop2
    add     v1,v1,#1
    b       loop2
overloop2:
    mov     r0,v1, lsl #1 
    add     r0,r0,#1
    swi     #SysPutNum
    mov     r0,#10
    swi     #SysPutChar
    swi     #SysHalt