Archive for the 'Problems' Category

Project Euler 43: Pandigital Numbers With Unusual Substring Divisibility Property

Anuvrat Singh Project Euler Badge

Anuvrat Singh Project Euler Badge

This was one of those questions that can be solved using a pen and paper. Problem 53 requires one to find all the 10-digit pandigital numbers which satisfy the following properties:

d_2d_3d_4 is divisible by 2

d_3d_4d_5 is divisible by 3

d_4d_5d_6 is divisible by 5

d_5d_6d_7 is divisible by 7

d_6d_7d_8 is divisible by 11

d_7d_8d_9 is divisible by 13

d_8d_9d_{10} is divisible by 17

Just to reiterate, a pandigital number is one in which each digit occurs only once. This is how we proceed:

  • d_4d_5d_6 is divisible by 5
    • This implies that d_6 is either 0 or 5.
  • d_6d_7d_8 is divisible by 11
    • This implies that d_6 + d_8 - d_7 is a  multiple of 11.
    • If d_6 were to be 0, it would mean that d_8 = d_7 or d_8 = d_7 + 11, neither of which can be true given that the number we seek is pandigital.
    • So we can assert that d_6 = 5.
    • We need all the pairs of d_7 and d_8 satisfying d_8 - d_7 = 6 or d_8 - d_7 = -5.
      • Possible (d_7, d_8) pairs are (6,1), (7,2), (8,3), (9,4), (1,7), (2,8), (3,9)
  • d_5d_6d_7 is divisible by 7
    • This essentially means that 10*d_5 + d_6 - 2*d_7 is a multiple of 7. We already have the value for d_6 and have narrowed down the possible values of d_7.
      • d_6, d_7 = 5, 6 \Rightarrow d_5 = 7
      • d_6, d_7 = 5, 7 \Rightarrow d_5 = 3
      • d_6, d_7 = 5, 8 \Rightarrow d_5 = 6
      • d_6, d_7 = 5, 9 \Rightarrow d_5 = 2
      • d_6, d_7 = 5, 1 \Rightarrow d_5 = 6
      • d_6, d_7 = 5, 2 \Rightarrow d_5 = 9
      • d_6, d_7 = 5, 3 \Rightarrow d_5 = NA
    • Possible (d5, d6, d7, d8) tuples are (7,5,6,1), (3,5,7,2), (6,5,8,3), (2,5,9,4), (6,5,1,7) or (9,5,2,8).
  • d_7d_8d_9 is divisible by 13
    • This implies that d_9 + 10*d_8 + 9*d_7 is a multiple of 13. We have already identified the possible d_7, d_8 pairs. We can get the possible d_9 values now.
      • d_7, d_8 = 6, 1 \Rightarrow d_9 = NA
      • d_7, d_8 = 7, 2 \Rightarrow d_9 = 8
      • d_7, d_8 = 8, 3 \Rightarrow d_9 = 2
      • d_7, d_8 = 9, 4 \Rightarrow d_9 = NA
      • d_7, d_8 = 1, 7 \Rightarrow d_9 = NA
      • d_7, d_8 = 2, 8 \Rightarrow d_9 = 6
    • Possible (d_5, d_6, d_7, d_8, d_9) tuples are (3,5,7,2,8), (6,5,8,3,2) or (9,5,2,8,6).
  • d_8d_9d_{10} is divisible by 17
    • This implies that 10*d_8 + d_9 - 5*d_{10} is a multiple of 17. Again using the possible values of the other two variables, we can narrow down the value of the third one.
      • d_8, d_9 = 2, 8 \Rightarrow d_{10} = 9
      • d_8, d_9 = 3, 2 \Rightarrow d_{10} = NA
      • d_8, d_9 = 8, 6 \Rightarrow d_{10} = 7

So we now have the following tuples for the values of (d_5, d_6, d_7, d_8, d_9, d_{10}): (3,5,7,2,8,9) or (9,5,2,8,6,7).

d_2d_3d_4 is divisible by 2 tells us that d_4 is even. So in the first case it can take the values 0,4,6 and in the second case the values 0,4.

Finally, using the last condition that d_3d_4d_5 is divisible by 3, we conclude that d_3, d_4 in the first case can be either of 6,0 or 0,6 and in the second case is 0,9.

Thus we have the six possible numbers as: 1406357289, 1460357289, 4106357289, 4160357289, 1430952867 and 4130952867. And we are done! Sum of these 6 numbers is what the problem asked for.

Popularity: 5% [?]

Project Euler : Frequency Analysis For Message Decryption

Problem 59 of Project Euler brought back memories of Sherlock Holme’s Dancing Men. Holmes had to decrypt a series of messages – the characters of which were stick men figures in varying dancing positions. He computed the frequencies of each dancing figure. Then since ‘e’ is the most used letter in English, he replaced the most frequent dancing figure with ‘e’.

I did the same thing with a difference. The given text in the problem did not have any spaces. This meant that even the spaces had been encrypted. And the most frequent number occurred almost twice as much as the next one. So I replaced that with space. That’s it. The problem was solved.

The password used to encrypt the text is – ‘god’ and the following code decrypts the text and finds the answer in the count variable.


    private List<Distribution<Integer>> parseInputFile(String fileName) throws FileNotFoundException {
        List<Distribution<Integer>> frequencies = Lists.newArrayList(new Distribution<Integer>(), new Distribution<Integer>(), new Distribution<Integer>());

        Scanner scanner = new Scanner(new File(fileName)).useDelimiter(",");
        int count = 0;
        for(int i = 0;  scanner.hasNext(); i++) {
            int num = scanner.nextInt();
            frequencies.get(i % 3).add(num);

            if(i % 3 ==0) num ^= 103;
            else if(i%3 == 1) num ^= 111;
            else if(i%3 == 2) num ^= 100;
            count += num;
            System.out.printf("%c", num);
        }

        System.out.println("Sum = " + count);

        return frequencies;
    }

Popularity: 5% [?]

Project Euler : Expansion Of Continued Fraction For Square Root Of Two

Problem 57 of Project Euler is pretty simple forward. It is very easy to see the following relation:

Numerator = Numerator + 2 * Denominator

Denominator = Numerator + Denominator

Simple code and was done. Here’s that:

    public int getCount(int numberOfExpansions) {
        int count = 0;

        BigInteger numerator = BigInteger.valueOf(3);
        BigInteger denominator = BigInteger.valueOf(2);
        while(--numberOfExpansions > 0) {
            BigInteger a = denominator.multiply(NumberUtil.BIG_TWO).add(numerator);
            BigInteger b = numerator.add(denominator);
            numerator = a;
            denominator = b;
            if(NumberUtil.numberOfDigits(numerator) > NumberUtil.numberOfDigits(denominator)) count++;
        }

        return count;
    }

Popularity: 5% [?]

Project Euler : n Digit Integers That Are Also nth Power

Problem number 63 of Project Euler gives the expression a = b^c and imposes the condition that the number of digits in a is equal to c. An example would be 16807 = 7^5. ab and c are all positive integers. The problem asks one to find the number of such possibilities.

Solving this problem made me happy. So, I’ll go into the details.

Given the problem, the first thing that comes to mind is the special relationship between the log of a number and the number of digits. The hint is to use logarithms to simplify the problem.

a = b^c

\Rightarrow \log(a) = c \log(b)

But \log(a) = c - f, where f \in (0, 1]

\Rightarrow c - f = c \log(b)

\Rightarrow c \log(\frac{10}{b}) = f

\Rightarrow 0 < \log(\frac{10}{b}) \leq \frac{1}{c}, dividing by c \in Z^+

That’s it! Our problem has been solved. I hope you have already noted that b \in [1, 9]. This means that we can precompute the values of \log(\frac{10}{b}) and store the results in an array. Then for all c \geq 1, just count how many of the precomputed values are smaller than or equal to \frac{1}{c}. The iteration completes when the smallest precomputed value is greater than \frac{1}{c}.

Below is the piece of code I wrote in Java to solve the task.

    public int getCount() {
        m_precomputedValues = new double[9];
        for(int i = 1; i < 10; i++) {
            m_precomputedValues[i-1] = Math.log10(10.0 / i);
        }

        int count = 0;

        int numberOfDigits = 1;
        double val = 1.0 / numberOfDigits;
        while (val > m_precomputedValues[8]) {
            for(int idx = 8; idx >= 0 &amp;amp;&amp;amp; m_precomputedValues[idx] <= val; idx--) count++;
            val = 1.0 / ++numberOfDigits;
        }

        return count;
    }

Popularity: 5% [?]

Project Euler : Count Of Lychrel Numbers Below 10000

Problem 55 of Project Euler explains what Lychrel numbers are and asks one to find the number of those below 10000. The only sticking point in the whole procedure is that after a few iterations the number become so large that BigInteger needs to be used. But otherwise, just follow the wording of the statement and the problem will be solved.

Here’s the outermost loop which does the counting:

    private static final int ARRAY_SIZE = 1000000;
    private static final BigInteger BIG_ARRAY_SIZE = BigInteger.valueOf(ARRAY_SIZE);

    public static int countOfLychrelNumbers(int upperLimit) {
        int count = 0;

        State[] lychrelNumbers = ArrayUtils.createArray(ARRAY_SIZE, State.NOT_VERIFIED, new State[0]);
        for (int i = 0; i <= upperLimit; i++) {
            if (lychrelNumbers[i] == State.NON_LYCHREL) continue;
            if (lychrelNumbers[i] == State.LYCHREL || testForLychrelProperty(lychrelNumbers, i)) {
                count++;
            }
        }

        return count;
    }

    private enum State {
        LYCHREL, NON_LYCHREL, NOT_VERIFIED
    }

It’s in the testForLychrelProperty method that all the caching logic comes in. It makes sense to cache the results. However, I have imposed a cache limit as well. Here’s the messier code with caching logic:

    private static boolean testForLychrelProperty(State[] lychrelNumbers, int number) {
        int iterCount = 1;
        State state = State.NOT_VERIFIED;
        BigInteger num = BigInteger.valueOf(number);
        List<BigInteger> found = new ArrayList<BigInteger>();
        found.add(num);
        while (iterCount < 50) {
            BigInteger reverse = NumberUtil.reverse(num);
            BigInteger sum = reverse.add(num);
            if (!num.remainder(BigInteger.TEN).equals(BigInteger.ZERO)) found.add(reverse);
            state = sum.compareTo(BIG_ARRAY_SIZE) < 0 ? lychrelNumbers[sum.intValue()] : State.NOT_VERIFIED;
            if (state == State.LYCHREL || state == State.NON_LYCHREL) {
                break;
            }
            if (PalindromeUtil.checkIfPalindrome(sum.toString())) {
                state = State.NON_LYCHREL;
                break;
            }
            num = sum;
            found.add(sum);
            iterCount++;
        }

        if (state == State.NOT_VERIFIED) state = State.LYCHREL;

        setState(lychrelNumbers, found, state);
        return state == State.LYCHREL;
    }

The setState method simply updates the cache with the latest result. Here is it’s implementation:

    private static void setState(State[] lychrelNumbers, List<BigInteger> numbers, State state) {
        for (BigInteger number : numbers) {
            if (number.compareTo(BIG_ARRAY_SIZE) > 0) return;
            lychrelNumbers[number.intValue()] = state;
        }
    }

All in all, the code runs in some 180ms on my laptop.

Popularity: 6% [?]

Project Euler : Largest Sum Of Digits In Exponentiation

The problem 56 of project euler requires one to sum the digits of the exponentiation a^b, where 1<= a,b <= 100, and find the largest sum.

Well, let’s just say I did it by proper brute force. Here’s my java code:

    public static int largestSumOfDigitsInExponentiationNew(int baseMax, int exponentMax) {
        int largest = 0;

        for (int i = 1; i <= baseMax; i++) {
            BigInteger num = BigInteger.valueOf(i);
            BigInteger prev = num;
            largest = Problem_56.checkLargestSum(prev, largest);
            for (int j = 2; j <= exponentMax; j++) {
                prev = prev.multiply(num);
                largest = Problem_56.checkLargestSum(prev, largest);
            }
        }

        return largest;
    }

    private static int checkLargestSum(BigInteger number, int curentLargest) {
        int sum = NumberUtil.getSumOfDigits(number);
        return curentLargest < sum ? sum : curentLargest;
    }

Popularity: 6% [?]

Project Euler : Count Integral Shortest Paths In All Cuboid Smaller Than Certain Dimension

Problem 86 of Project Euler is the next problem I took up. Given a cuboid with integral dimensions, the shortest distance for an ant to crawl from one corner to diagonally opposite corner need not be an integral number. The problem statement required one to consider all the possible cuboid up to a maximum size of M x M x M and report the number of cuboid that had integral value for the shortest path.

The question has a huge hint hidden within its wording. It goes on to provide two examples – for M = 99 the count is 1975 and for M  = 100 the count is 2060! Solve the problem incrementally. This problem can be disintegrated into 3 smaller problems.

Firstly, we need to know the calculation of shortest path in a cuboid as an ant would crawl. To do this, explode the 3D figure into a 2D figure keeping track of the corners. It’s a pretty simple matter to calculate the distance now. There’s one thing to be remembered though – the explosion can be done in 3 different ways. So we have potentially 3 candidates for the shortest path.

Given the dimension as a X b X c, the squared distance is one of (a+b)^{2} + c^{2}(b+c)^{2} + a^{2} or (c+a)^{2} + b^{2}. We shall see later that in our case we do not need to perform 3 calculations as the choice becomes quite obvious.

Next  we tackle the problem of induction. Say we know that the count for the problem size  M x M x M is some p. How do we calculate the value for the size (M+1) x (M+1) x (M+1)? If you were to imagine two cuboid of dimensions M and (M+1) and enumerate all the cuboid not covered by the one of dimension M, you would find them to be of the form:

i X j X (M+1)

where i = 1 to M + 1

and j = i to M + 1

There lies our solution! We setup a nested for loop and calculate the shortest distances for each of these extra cuboid. It should now be pretty obvious to notice that for the computation of shortest distance we only need to consider the case (i+j)^{2} + (M+1)^{2}. Thus we can go from M to M+1.

I hope you have noticed some redundancy in the above loop setup. Every time we calculate (i+j). Consider the two cases:

i = 1, j = 3

i = 2, j = 2

In both these cases the value of (i+j) is the same. We are unnecessarily computing values already computed. Being a little cleverer we can improve the current O(n^{2}) to O(n).

Instead of using a nested loop, we use a single for-loop from the range of 2 to 2*(M+1) as those are the range for the sum of i and j. But this introduces another problem. Say for a particular sum we get that the shortest path is an integer. We need to know the number of ways in which the sum can be decomposed into i and j. I setup a single line function to calculate this value and I was done!

The code ran in 258ms on my Lenovo T410 laptop. Below is my function to move from M to M+1. To solve the Project Euler problem, I just needed to call this function in a loop, always updating the memoized value.

 

    /**
     * Count the number of integral shortest paths in all cuboids upto a size of size X size X size
     *
     * @param size the maximum dimensions of the cuboid
     * @param memoizedValue the value for a cuboid with max dimension size - 1
     * @return the value for a cuboid of max dimension size X size X size
     */
    public int countIntegralShortDistances(int size, int memoizedValue) {
        double sizeSquared = Math.pow(size, 2);
        for (int i = 2; i <= 2 * size; i++)
            if (NumberUtil.isInteger(Math.sqrt(Math.pow(i, 2) + sizeSquared))) memoizedValue += NumberUtil
                    .countSummations(i, size);
        return memoizedValue;
    }

Popularity: 14% [?]

Partition Function

The problem is to enumerate all the ways in which a given number can be written as sum of two or more positive integers. For example 5 can be written as [4, 1], [3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1], [2, 2, 1] and [3, 2].

I used a recursion to solve the problem. The recursion works like this. Write 5 as 4 + 1. Recurse for 4. 5 can also be written as 3 + 2. Recurse for 3. We are done!

Below is a piece of code written in Java to do the same. I use a map to store pre-computed values. Using the code, I have found the answer for 60. It takes a minute though.

There is this problem on project euler – Problem 76 – which requires one to find the number of ways 100 can be partitioned. Using the above code, I get a heap space error for 100. I suppose instead of enumerating all the ways, to solve the project euler problem, I should try to just count all the possibilities. This might me much quicker.

Here is the java code for the above recursion.

 

    /**
     * Find all the ways of writing a given number as sum of positive integers
     *
     * @param n the number whose partitions are to be found
     * @return the set of all the partitions of the number
     */
    public static List<List<Integer>> partitionFunction(int n) {
        return NumberRepresentations.partitionFunction(n, 1, new HashMap<Integer, Map<Integer, List<List<Integer>>>>());
    }

    /**
     * Find all the ways of writing a given number as sum of positive integers
     *
     * @param number the number whose partitions are to be found
     * @param minValue the minimum value of any partition number
     * @return the set of all the partitions of the number
     */
    private static List<List<Integer>> partitionFunction(int number, int minValue,
            Map<Integer, Map<Integer, List<List<Integer>>>> preComputedValues) {
        if (preComputedValues.containsKey(number)) {
            List<List<Integer>> prePartitions = preComputedValues.get(number).get(minValue);
            if (prePartitions != null) return prePartitions;
        }

        List<List<Integer>> partitions = new ArrayList<List<Integer>>();
        partitions.add(Arrays.asList(number));
        if (number == 1) return partitions;

        int rightVal;
        for (int leftVal = number - 1; leftVal >= (rightVal = number - leftVal); leftVal--) {
            if (rightVal < minValue) continue;
            for (List<Integer> leftNumbers : NumberRepresentations.partitionFunction(leftVal, rightVal,
                    preComputedValues)) {
                List<Integer> tempList = new ArrayList<Integer>(leftNumbers);
                tempList.add(number - leftVal);
                partitions.add(tempList);
            }
        }

        Map<Integer, List<List<Integer>>> prePartitions = preComputedValues.get(number);
        if (prePartitions == null) preComputedValues.put(number,
                prePartitions = new HashMap<Integer, List<List<Integer>>>());
        prePartitions.put(minValue, partitions);

        return partitions;
    }

Popularity: 8% [?]

Project Euler : Finding Minimal Sum Path In A Matrix

The problem 81 of Project Euler gives a square matrix and requires one to find the minimal sum path. Specifically, it only required the sum of the numbers on the path. The question immediately reminded me of problem 67 and I set out solving with the same logic.

The logic has been explained in the following post of mine: Project Euler : Maximum Sum Traversing Top To Bottom In A Triangle.

The following lines of code did the work for me:

    public static int getMinimalSumPath(int[][] numbers) {
        int dimension = numbers.length - 1;
        for (int x = dimension; x >= 0; x--)
            for (int y = dimension; y >= 0; y--) {
                if (x == dimension &amp;amp;&amp;amp; y == dimension) continue;
                int val1 = x < dimension ? numbers[x + 1][y] : Integer.MAX_VALUE;
                int val2 = y < dimension ? numbers[x][y + 1] : Integer.MAX_VALUE;
                numbers[x][y] += val1 < val2 ? val1 : val2;
            }

        return numbers[0][0];
    }

Popularity: 14% [?]

Project Euler : Counting The Number Of Right Triangles In A Grid

The problem 91 of Project Euler was a bit tricky. It requires one to find the number of right angled triangles in a n X n grid, with one point (0,0) fixed. For example, as shown on the problem page, a 3 X 3 grid contains 14 triangles. The problem required the answer for a 51 X 51 grid.

Well, at first I started with a pencil-paper approach. I listed down all the cases I could think of and checked my calculation for the 3 X 3 grid. Got the answer right. But applied the same to the larger 51 X 51 grid I realized that there were cases I had not enumerated all the cases. I am still not sure if the problem can be solved using just a pencil and a paper.

I did not want to do a brute force way of picking up two arbitrary points and checking if they form a right angled triangle. In my solution I have never tried to determine the actual triangles. As you shall see, all I do is to find the number of possible triangles by using a parameter and bounding it within an interval. The range of the interval is my solution for that particular point.

I went the mathematics way. One of the points is fixed at (0,0). Select one point as (a,b). The slope of the line joining these two points is \frac{b}{a}. So the equation of a line perpendicular to this one and passing through the point (a,b) would be :

y = -\frac{a}{b}(x - a) + b

Now points which lie on the above line will all form right angled triangles with the points (0,0) and (a,b). The following conditions need to be satisfied in our particular problem:

  • The solutions to the above equation are integers.
  • The points lie within a grid of n X n. So 0 \leq x \leq n and 0 \leq y \leq n.

There are two cases we need to consider. One where a is a multiple of b and the other where it is not.

Let us first consider the case where a is not a multiple of b. We rewrite the equation as follows:

y = -\frac{c}{d}(x - fc) + fd

where f is the gcd of a and b. Now since y is an integer, we can impose the following condition on x :

x = fc + kd

where k is some parameter, in which case the value of y becomes:

y = fd - kc

And now imposing the second criteria, we can limit the values of the parameter k as thus:

0 \leq x \leq n

\Rightarrow -\frac{fc}{d} \leq k \leq \frac{n - fc}{d}

Similarly, imposing the same conditions for y, we get

\frac{fd - n}{c} \leq k \leq \frac{fd}{c}

The intersection of the two intervals gives us the eligible values for the parameter k as :

max ( -\frac{fc}{d}, \frac{fd - n}{c} ) \leq k \leq min ( \frac{n - fc}{d}, \frac{fd}{c} )

For k = 0 we get the point (a,b). For all other values of k we get the points which complete the right angled triangle. So the number of right angled triangles in this case will be the range of k – 1.

Now the other case. Let a = pb. The equation then becomes :

y = -p (x - a) + b

Let q = x - a. Then we have

y = -pq + b

Once again the applying the boundary conditions we get:

\frac{b - n}{p} \leq q \leq \frac{b}{p}

-a \leq q \leq n - a

Once again taking the intersection of the intervals gives us:

max ( \frac{b - n}{p}, -a ) \leq q \leq min ( \frac{b}{p}, n - a )

For k = 0 we get the point (a,b). For all other values of k we get the points which complete the right angled triangle. So the number of right angled triangles in this case will be the range of k – 1.

Add to this the trivial case of 3n^{2} right angled triangles formed when one of the points lie on the axis. It is easy to see these if you sit with a pencil and a paper.

I went ahead to code the above conditions. The code is given below. On my laptop I was able to run this code for a grid of 4000 X 4000 in 1 second.

In the code given below, I have used (xIdx, yIdx) as the point (a,b). f and p are as described above.

    public int getCount(int n) {
        int count = 0;

        for (int xIdx = 1; xIdx <= n; xIdx++)
            for (int yIdx = xIdx; yIdx <= n; yIdx++) {
                int tempCount = xIdx % yIdx == 0 ? countForXMultipleOfY(xIdx, yIdx, n) : countForXNotMultipleOfY(xIdx,
                        yIdx, n);
                count += xIdx != yIdx ? 2 * tempCount : tempCount;
            }

        return (int) (count + 3 * Math.pow(n, 2));
    }

    private int countForXMultipleOfY(int xIdx, int yIdx, int n) {
        int p = xIdx / yIdx;

        int lowerBoundary = NumberUtil.larger((yIdx - n) / p, xIdx * -1);
        int upperBoundary = NumberUtil.smaller(yIdx / p, n - xIdx);

        return upperBoundary > lowerBoundary ? upperBoundary - lowerBoundary : 0;
    }

    private int countForXNotMultipleOfY(int xIdx, int yIdx, int n) {
        int f = MathUtils.gcd(yIdx, xIdx);

        int lowerLimit = NumberUtil.larger(-1 * xIdx / (yIdx / f), (yIdx - n) / (xIdx / f));
        int upperLimit = NumberUtil.smaller(yIdx / (xIdx / f), (n - xIdx) / (yIdx / f));

        return upperLimit > lowerLimit ? upperLimit - lowerLimit : 0;
    }

Popularity: 16% [?]