HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Truncating an integer from left to right and right to left

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
lefttruncatingandfromintegerright

Problem

Project Euler problem 37 says:


The number 3797 has an interesting property. Being prime itself, it is possible to continuously remove digits from left to right, and remain prime at each stage: 3797, 797, 97, and 7. Similarly we can work from right to left: 3797, 379, 37, and 3.

Going right to left is trivial enough as you just keep dividing by 10:

IEnumerable RTL (long x)
{
    while(x > 10) {
        x /= 10;
        yield return x;
    }
}


Going left to right was a little trickier. Just wondering if there's a more optimal way to do this.

IEnumerable LTR (long x)
{
    while(x > 10) 
    {
        long y = x;        //take a copy of current value of x;
        int powerten = 0;  //variable to count what the 10^n value is

        while(y > 10){     //calculate powerten by repeated dividing y / 10
            y /= 10;       // for 3797 this would be '3' i.e. 3.797 * 10^3
            powerten++;
        }

        long p = (long)Math.Pow(10, powerten); // 10 ^ 3 = 1000
        long z = (x/p)*p;                      // 3797/1000*1000 = 3000
        x = (x - z);                           // subtract from x
        yield return x;
    }
}


It runs pretty fast but I'm just wondering is there a more optimal way to remove the leftmost significant digit of a number on each iteration without all the "gymnastics".

Solution

I would suggest returning the truncations in order of increasing lengths for truncation from the left (checking the shorter tails for primality is faster, so if the number is not a left-truncatable prime, that should be detected faster on average when the tails are yielded in that order),

IEnumerable LTR (long x)
{
    long powerOfTen = 10;
    while(powerOfTen <= x)
    {
        long tail = x % powerOfTen;
        powerOfTen *= 10;
        yield return tail;
    }
}


is nice and short if you know that the argument x is never so large that the next larger power of 10 exceeds long range. I f x may be that large, you need to guard against overflow, slightly longer:

IEnumerable LTR(long x)
{
    // if (x < 10) return ??;
    long bound = x/10, lastPowerOfTen = 1;
    while(lastPowerOfTen <= bound)
        lastPowerOfTen *= 10;      // doesn't overflow
    }
    long powerOfTen = 1;
    do
    {
        powerOfTen *= 10;
        yield return x % powerOfTen;
    }while(powerOfTen < lastPowerOfTen);
}


If you want to return the truncations in order of decreasing lengths, you can easily modify the last:

IEnumerable LTR(long x)
{
    // if (x  1)
    {
        x %= powerOfTen;
        powerOfTen /= 10;
        yield return x;
    }
}


Note: If the decimal representation of x contains at least one zero other than the last digit, these would all return the same value more than once. Since no (right or left) truncatable prime can have a zero digit in its decimal expansion, I did not care about that. If you do, in the last (decreasing length) version, you could easily avoid that by replacing

powerOfTen /= 10;


with a loop

do {
    powerOfTen /= 10;
}while(powerOfTen > 1 && powerOfTen > x);


In the increasing-length versions, that would not be as simple.

Further note:

long p = (long)Math.Pow(10, powerten);


Don't use floating point functions for integer arithmetic. In this case, if the called Math.Pow function operates on doubles [I'm not sure how that is in C#], it will not lead to wrong results, but it could produce wrong results if it operates on floats, and also in general (for other bases) even if double is used.

Code Snippets

IEnumerable<long> LTR (long x)
{
    long powerOfTen = 10;
    while(powerOfTen <= x)
    {
        long tail = x % powerOfTen;
        powerOfTen *= 10;
        yield return tail;
    }
}
IEnumerable<long> LTR(long x)
{
    // if (x < 10) return ??;
    long bound = x/10, lastPowerOfTen = 1;
    while(lastPowerOfTen <= bound)
        lastPowerOfTen *= 10;      // doesn't overflow
    }
    long powerOfTen = 1;
    do
    {
        powerOfTen *= 10;
        yield return x % powerOfTen;
    }while(powerOfTen < lastPowerOfTen);
}
IEnumerable<long> LTR(long x)
{
    // if (x < 10) return ??;
    long bound = x/10, powerOfTen = 1;
    while(powerOfTen <= bound)
        powerOfTen *= 10;      // doesn't overflow
    }
    while(powerOfTen > 1)
    {
        x %= powerOfTen;
        powerOfTen /= 10;
        yield return x;
    }
}
powerOfTen /= 10;
do {
    powerOfTen /= 10;
}while(powerOfTen > 1 && powerOfTen > x);

Context

StackExchange Code Review Q#26788, answer score: 3

Revisions (0)

No revisions yet.