2

I need to design an algorithm in C to calculate unique combinations of digits for 0 to 1,000,000. For example, when 13 appears, 31 would not be included in this sequence. Can anyone help me find an algorithm to describe this? The first few numbers in the series would be:

 1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,22,23,24,25,26,27,28,29,33,etc

Thanks!

edit - Sorry, forgot to mention that zero isn't included

Bucknall
  • 25
  • 1
  • 4
  • The first few number in this series would be, 0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,22,23,24,etc. – Bucknall Feb 23 '15 at 21:09
  • Do you want the actual numbers or just the count of them? It's completely different question – amit Feb 23 '15 at 21:17
  • 3
    You should probably explain why 10 doesn't appear in the list. – user3386109 Feb 23 '15 at 21:21
  • When you arrive at 100, can u give us an example? 100 doesn't appear, maybe 101? – CCebrian Feb 23 '15 at 21:22
  • 1
    `10` is not in the series either. Is it assumed to be the same as `01`? A combination of 6 digits with implicit zeroes at the front should only appear once in the list? – chqrlie Feb 23 '15 at 21:23
  • 101 should not appear as it is a permutation of 11 – chqrlie Feb 23 '15 at 21:23
  • 6
    I guess a number is in the list if all its digits are in increasing order. – chqrlie Feb 23 '15 at 21:25
  • 1
    @DanielKleinstein 132 shouldn't be in the list as 123 is already in it – Bucknall Feb 23 '15 at 21:30
  • 0 should be in the list: it is the unique combination of 0 digits – chqrlie Feb 23 '15 at 21:33
  • Actually the problem is easier to specify if you only take into account digits from 1 to 9. `0` then is not in the list. – chqrlie Feb 23 '15 at 21:34
  • @chqrlie it's easier to solve if `0` is included as a digit (i.e. `1000` is not in the list because `0001` is) – M.M Feb 23 '15 at 22:25
  • Not really. including `0` as a digit requires extra wording in the problem definition to specify that `01` is the same as `1`. It is not so obvious, as the first few comments showed. It is much simpler to specify that we want to enumerate combinations of the digits 1 to 9. – chqrlie Feb 23 '15 at 22:35

2 Answers2

6
#include <stdio.h>
int main(void) {
    int i, n;
    for (n = 1; n < 1000000; n++) {
        for (i = n;;) {
            if (i / 10 % 10 > i % 10) break;
            if ((i /= 10) == 0) { printf("%d\n", n); break; }
        }
    }
}

5004 numbers in the series from 0 to 1000000

A much quicker version:

#include <stdio.h>
#include <stdlib.h>

static long long enumerate(char *p, int i, int n, int digit, int silent) {
    long long count = 0;
    if (i >= n) {
        if (!silent) printf("%s\n", p);
        return 1;
    }
    for (p[i] = digit; p[i] <= '9'; p[i]++)
        count += enumerate(p, i + 1, n, p[i], silent);
    return count;
}

int main(int argc, char **argv) {
    char array[256];
    int i, n;
    int max = (argc > 1) ? strtol(argv[1], NULL, 0) : 6;
    int silent = 0;
    long long count = 0;
    if (max < 0) {
        max = -max;
        silent = 1;
    }
    array[sizeof(array)-1] = '\0';
    for (n = 1; n <= max; n++) {
        count += enumerate(array + sizeof(array) - 1 - n, 0, n, '1', silent);
        if (silent)
            printf("%lld combinations between 0 and 1E%d\n", count, n);
    }
}

invoke with a positive number to enumerate combinations and a negative number to just count them.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Only `24309` combinations upto 100 million and `48619` below 1 billion. The series length seems to grow like log(N). Finding an analytical formula for N=10^n is your new assignment ! – chqrlie Feb 23 '15 at 21:44
  • I know I should have done your homework, I just couldn't resist. I test every number between 1 and the upper bound. I check that the last digit is at least as large as the previous one, and do it again for the previous pair by dividing the number by 10 until it is 0. If the test reaches 0, the number is OK and gets printed. – chqrlie Feb 23 '15 at 21:47
  • This method is quick and dirty. It is fast enough for 1000000 but becomes quite slow for much higher bounds. Find a quicker method with an array of chars that finds all the combinations in linear time. – chqrlie Feb 23 '15 at 21:51
  • How fast does it run for you? It's been running for a few minutes now for me... are you sure you shouldn't have a stop condition in your inner loop? You should keep the `i > 0` I think. – IVlad Feb 23 '15 at 21:55
  • My bad! the test `i > 0` was redundant, but I forgot the `break;`. – chqrlie Feb 23 '15 at 22:07
  • @chqrlie: I just realized, I wrote code to do exactly that last week. Lemme find it. Aha! It's on my SO profile labeled "next_increasing" – Mooing Duck Feb 23 '15 at 22:12
  • How fast? 40ms upto 1000000 but 40 seconds upto 1 billion, linear time. I have a faster version that computes 350343564 combinations upto 10^32 in 16 seconds, but that is not enough to reach 1 googol in decent time. – chqrlie Feb 23 '15 at 22:15
  • @chqrlie: http://coliru.stacked-crooked.com/a/859c659106f9042e, it works on strings, so not sure how it would compare to yours. – Mooing Duck Feb 23 '15 at 22:20
  • I tried compiling your C++ sample and clang failed with an error on `mather plus={}`. I know this is a pointless troll, but what are they trying to achieve? What proportion of self proclaimed C++ programmers can actually make sense of this and explain how it works? – chqrlie Feb 23 '15 at 22:48
  • Thanks! This helped me out, I wrote my own solution that iterates through an array that is size dependent on the base of the integer, being check for 'uniqueness'. – Bucknall Feb 25 '15 at 10:40
2

The function next updates the array a to the next number, returning the value of the bottom digit. The main function iterates through the sequence, stopping once the top digit is 10 (since once the array has been used up, next just keeps incrementing the most significant digit).

The algorithm, in words and ignoring bounds checking, could be described as "to find the next number, add one to the bottom digit, and if it overflows find the next number ignoring the bottom digit, and then duplicate the new bottom digit."

#include <stdio.h>

int next(int *a, size_t len) {
    if (*a == 9 && len > 1) {
        *a = next(a-1, len-1);
    } else {
        *a += 1;
    }
    return *a;
}

#define N 6

int main(int argc, char *argv[]) {
    int a[N] = {0};
    while (next(a+N-1, N) != 10) {
        for (int i = 0; i < N; i++) {
            if (a[i] != 0) printf("%d", a[i]);
        }
        printf("\n");
    }
    return 0;
}

You can count the solutions in O(N) time (where N is the number of digits). If K(n, d) is the number of solutions with exactly n digits, and whose top digit is 9-d, then K(0, d) = 1, and K(n+1, d) = K(n, 0) + K(n, 1) + ... + K(n, d). The number of solutions with n or fewer digits is then K(1, 8) + K(2, 8) + ... + K(n, 8). These observations yield this dynamic programming solution:

int count(int n) {
    int r[9] = {1};
    int t = 0;
    for (int i = 0; i < n+1; i++) {
        for (int j = 1; j < 9; j++) {
            r[j] += r[j-1];
        }
        t += r[8];
    }
    return t - 1;
}

int main(int argc, char* argv[]) {
    printf("there are %d numbers.\n", count(6));
    return 0;
}

Gives:

there are 5004 numbers.
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
  • quite elegant! `4263421511270` combinations between 1 and 1 googol. I had to use `unsigned long long` in `count` but it is very quick. You rule! – chqrlie Feb 24 '15 at 19:47