Paul Waring

Freelance C Developer based in Manchester, UK

Project Euler problem 8

Largest Product in a Series

Find the largest product of any 13 digit number within a larger number.

Solution

The first thing we need to decide is how to read in the input number.

Read the whole input. This has the advantage of allowing us to create a 13 digit moving window. However, we don't know how large the input will be (assuming we're not hardcoding the length of the puzzle), so allocating sufficient memory would be difficult. We could make a guess and gradually reallocate a larger block when it is exceeded, but that seems like a lot of work for a relatively straightforward problem.

Read one digit at a time. This is possibly less efficient from an I/O point of view, but it does mean that we only need to hold a fixed number of digits (13) in memory at any one time. Since we know this at compile time, we can create an array of the current digits. This has the bonus of being allocated on the stack automatically, so no memory management is required.

Our first step is to read the first 13 digits, to populate our buffer. getchar does most of the work for us, reading from stdin. We need to check the following:

We also need to convert the return value of getchar from an int representation to the decimal value of the digit. The conventional way to do this is to subtract the value of the character literal '0' (which is represented as int, not char). This assumes that the characters 0-9 are sequential, which is the case for all encodings in widespread use, and also required by the C standard:

In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.

If you want a historical example of an encoding which does not meet the standard, take a look at the Baudot code.

All of this is possible with a while loop:

#define DIGIT_COUNT 13

int digits[DIGIT_COUNT];
int digits_read = 0;
int c = 0;

while (digits_read < DIGIT_COUNT && (c = getchar()) != EOF) {
    if (isdigit(c)) {
        digits[digits_read] = c - '0';
        digits_read++;
    }
}

The next step is to calculate the product of the digits in the buffer, update the largest product, and get the next character. For safety we also check first whether the buffer contains 13 digits - if not then the input is too small and we can skip over this section.

We use a do...while loop as we want to check the buffer at least once, and our termination test comes at the end.

int64_t largest_product = 0;
int64_t current_product = 1;

if (digits_read == DIGIT_COUNT) {
    do {
        // Calculate current product
        current_product = 1;

        for (int d = 0; d < DIGIT_COUNT; d++) {
            current_product *= digits[d];
        }

        if (current_product > largest_product) {
            largest_product = current_product;
        }

        c = getchar();

        if (c != EOF && isdigit(c)) {
            // Move all digits down one space, and
            // add the latest one at the end
            for (int d = 0; d < DIGIT_COUNT; d++) {
                digits[d] = digits[d+1];
            }

            digits[DIGIT_COUNT - 1] = c - '0';
        }
    } while (c != EOF);
}

Note that we use the int64_t type for the product, as this guarantees a 64 bit integer, whereas int does not - even on a 64 bit platform. The reason for this is that the largest possible product of 13 digits is if every digit was 9, i.e. 9^13. This exceeds the maximum size of a 32 bit integer, causing an overflow which would give an incorrect result (e.g. a negative number). However, we still use a signed integer for consistency with the rest of the code, otherwise we would have to cast the return value of getchar.

The full solution is:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#include <inttypes.h>

#define DIGIT_COUNT 13

int main() {
    int digits[DIGIT_COUNT];
    int digits_read = 0;
    int c = 0;
    int64_t largest_product = 0;
    int64_t current_product = 1;

    while (digits_read < DIGIT_COUNT && (c = getchar()) != EOF) {
        if (isdigit(c)) {
            digits[digits_read] = c - '0';
            digits_read++;
        }
    }

    if (digits_read == DIGIT_COUNT) {
        do {
            // Calculate current product
            current_product = 1;

            for (int d = 0; d < DIGIT_COUNT; d++) {
                current_product *= digits[d];
            }

            if (current_product > largest_product) {
                largest_product = current_product;
            }

            c = getchar();

            if (c != EOF && isdigit(c)) {
                // Move all digits down one space, and
                // add the latest one at the end
                for (int d = 0; d < DIGIT_COUNT; d++) {
                    digits[d] = digits[d+1];
                }

                digits[DIGIT_COUNT - 1] = c - '0';
            }
        } while (c != EOF);
    }

    printf("%" PRIi64 "\n", largest_product);

    return EXIT_SUCCESS;
}