Friday, May 10, 2013

Flooring numbers with your hands tied

Interesting question on ##csharp@freenode tonight. One of the users was using a system with limited scripting capabilities and needed to write his own floor() function using the very limited set primitives. Basically, if-branching, +, -, *, /, a few math functions (ln, pow, abs) and oddly enough, a distance calculation function for GPS coordinates. The initial reaction of the room, including myself, was along the lines of "sorry, you're screwed." But for some reason the question excited me and I put some more thought into it... hmm, without looping or recursion we've obviously lost Turing-completeness and the problem was probably unsolvable for all inputs, but on the other hand on a logarithmic scale, practical numbers aren't particularly large. What if we tried chopping off decimal digits to get the fractional part? As long as the numbers are bounded, you can throw away the whole part of a number in ~ 9*(log10 bound) operations. Something along the lines of the following C# code:
double GetFractionalPart(double num) {

    // assume num < 1000

    var frac = num;
    if(frac >= 100) {
        frac -= 100;
        if(frac >= 100) {
            if(frac >= 100) {
                frac -= 100;
                    ... // 9 times total
            }
        }
    }

    if(frac >= 10) {
        frac -= 10;
        if(frac >= 10) {
            ...
        }
    }

    if(frac >= 1) {
        frac -= 1;
        if(frac >= 1) {
            ...
        }
    }
}
This method could easily be generated with a script for any desired maximum number of digits. Wait, we can do this for every base... what about binary? 2*(log2 bound) = 2*log2 10*(log10 bound), a clear win in conciseness and ease of generation, not to mention using convenient binary arithmetic:
double GetFractionalPart(double num) {

    var frac = num;
    // ...
    if(frac >= 32) {
        frac -= 32;
    }
    if(frac >= 16) {
        frac -= 16;
    }
    if(frac >= 8) {
        frac -= 8;
    }
    if(frac >= 4) {
        frac -= 4;
    }
    if(frac >= 2) {
        frac -= 2;
    }
    if(frac >= 1) {
        frac -= 1;
    }
    
    return frac;
    
}
This was enough to solve the asker's problem adequately. Hmmm... what if the language in question supported recursion?
double GetFractionalPart(double num) {
    return GetFractionalPart(num, Math.Pow(2,96));
}

double GetFractionalPart(double num, double max) {
    if(num >= max) {
        num -= max;
    }
    if(max > 0) {
        return GetFractionalPart(num, max /= 2);
    }
    else {
        return num;
    }
}
This could easily be written in completely statement-less style:
double GetFractionalPart(double num, double max) {
    max > 0 ? return GetFractionalPart(num >= max ? num - max : num, max /= 2)
            : num;
}
Having extracted the fractional part, getting the job done is trivial:
double Floor(double num) {

    var frac = GetFractionalPart(Math.Abs(num));
    if(num > 0) {
        num -= frac;
    }
    else {
        num += frac;
    }
    return num;
    
}
The person in question wasn't particularly grateful, but it was an interesting challenge nonetheless.

Thursday, May 9, 2013

Code drill 1: Factorial (Part 1)

The idea of this exercise is to come up with as many possible original solutions of a problem. Note that most of the solutions are intentionally terse, and therefore leave something to be desired when it comes to style. Statement: Write a program that calculates N! = N*(N-1)*...*2*1, for non-negative integer N.


Solution 1: straightforward
static int Fact(int n) {
    var ret = 1;
    for(var ii = 2; ii <= n; ++ii) {
        ret *= ii;
    }
    return ret;
}
Solution 2: oneliner
static int Fact(int n) {
    return Enumerable.Range(1,n).Aggregate(1,(x,y) => x*y);
}
Solution 3: recursion
static int Fact(int n) {
    return n == 0 ? 1 : n*Fact(n-1);
}
Solution 4: hidden multiplication
static int Fact(int n) 
{

    var sb = new StringBuilder("*");
    for(var ii = 1; ii < n; ++ii) {
        var str = sb.ToString();
        for(var jj = 0; jj < ii; ++jj) {
            sb.Append(str);
        }
    }
    return sb.Length;
}
Solution 5: query comprehension abuse #1
static public int Fact(int n) 
{
    if (n == 0)
        return 1;
       
    return from n1 in n 
           where n1 - 1
           select n1*n;
}

static public int Where(this int n, Func f) 
{
   return f(n);
}

static public int Select(this int n1, Func f)
{
    return f(Fact(n1));
}
Solution 6: brute-force
static public int Included(int n, int k) {
    if (k<=1) return 0;
    if (n < k) return 0;
    return (n / k) + Included(n/k, k);
}
static public int Fact(int n) 
{
    if(n == 0) {
        return 1;
    }

    for(int ii = n, jj; ; ++ii) {
        for(jj = 2; jj <= n; ++jj) {
            for(int kk = 0, temp = ii; kk < Included(n,jj); ++kk) {
                if(temp != jj*(temp /= jj)) {
                    jj = n+1;
                    break;
                
                }
            }
        }
        if(jj == n+1) {
            return ii;
        }
    }
}