4

TL;DR I would like to know how to clean up the first if statement. I tried looking online and found nothing.

I am writing a program to test whether a number typed by the user has repeated digits. I have managed to create a 10-element boolean array (a[10]) such that if a[i] equals 0, this means that the digit 'i' is present in the typed number at most once. If a[i] equals 1, then the digit 'i' is present in the typed number at least twice (thus is repeated). Note 0<=i<=9.

Now I am trying to analyse the values in this array such that if all values equal zero then we type "Repeated digit". And if not we say which numbers are repeated.

if(a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 && a[5] == 0 && a[6] == 0 && a[7] == 0 && a[8] == 0 && a[9] == 0)  
       printf("No repeated digits");
  
else  
  printf("Repeated digits: "); 
  for(i = 0; i < 10; i++) {
        if(a[i] == 1)
        printf("%d ", i); 
    }   


I can't find a way of using a for loop combined with an if loop to clean up the first if statement. I have tried looking online but can't find a solution.

Guthrie
  • 75
  • 6
  • 2
    "quick" as in runtime? Or least code? – Eugene Sh. Apr 12 '21 at 14:40
  • quick as in not having to explicitly type out that each array element equals zero. Trying to equivalently write out the first if statement using some looping mechanism. Imagine if this problem were in base 99 instead of base 10! (! for effect, not factorial) – Guthrie Apr 12 '21 at 14:42
  • 1
    If you stored the "array" as a bit-vector in a single unsigned integer, you could check that all bits are zero with a single test: `a == 0`. – Ian Abbott Apr 12 '21 at 14:57
  • As you create the array in the first place, set a flag if any element is not zero. – Eric Postpischil Apr 12 '21 at 15:01
  • I need to check out this flag thing, first I've heard of it, sounds useful. – Guthrie Apr 12 '21 at 15:02
  • Does this answer your question? [Faster approach to checking for an all-zero buffer in C?](https://stackoverflow.com/questions/1493936/faster-approach-to-checking-for-an-all-zero-buffer-in-c) – thetaprime Apr 16 '21 at 03:26

7 Answers7

9

There is a trick to check if an array has all elements equal to N:

if (a[0]==N && memcmp(a, a+1, (array_length-1)*sizeof(a[0]) ) == 0)
    printf("All equal to N\n");

In you case you can do:

if (a[0]==0 && memcmp(a, a+1, 9*sizeof(a[0]) ) == 0)
    printf("All zeros\n");

This code is explicitly checking the first element to be zero, and then the memcmp is doing the following checks for you:

a[0] == a[1] && a[1] == a[2] &&....

This requires no extra allocated and initialized zero array as the other memcmp -based answers do.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
4

Or use:

for (i=0; i<10; i++)
    if (a[i])
        break;

if (i==10) printf("No repeated digits");
else {
    //...
}
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
1

You can use a flag to indicate if an non-zero element is found.

int nonzero_found = 0;

for(i = 0; i < 10; i++) {
    if (a[i] != 0) {
        nonzero_found = 1;
        break;
    }
}

if (nonzero_found) {
    printf("Repeated digits: "); 
    for(i = 0; i < 10; i++) {
        if(a[i] == 1)
            printf("%d ", i); 
    }
} else {
    printf("No repeated digits");
}

Or if you really want to print Repeated digits: even if there are no repeated digits (like your original code):

int nonzero_found = 0;

for(i = 0; i < 10; i++) {
    if (a[i] != 0) {
        nonzero_found = 1;
        break;
    }
}

if (!nonzero_found) {
    printf("No repeated digits");
}

printf("Repeated digits: "); 
for(i = 0; i < 10; i++) {
    if(a[i] == 1)
        printf("%d ", i); 
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
0

You can use memcmp in combination with a compound literal of the same type set to all elements zero.

Assuming the elements of a are of type int:

if (memcmp(a, (int[10]){0}, sizeof a) == 0) {
  printf("all zero\n");
} else {
  printf("not all zero\n");
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Its a 10-element boolean array. Might the boolean padding be non-zero? – chux - Reinstate Monica Apr 12 '21 at 15:24
  • Assuming the zero array should have its size set dynamically rather than fixed to `10`, then using a compound literal as VLA might not be the best idea, since that might force slow stack allocation. I'm note entirely convinced that every compiler will be smart enough to recognize a local, all-zeroes VLA as something that it doesn't actually need to allocate. A `static const` array with size "max" might be faster in case the code needs to be generic. It would have to be disassembled in any case. – Lundin Apr 12 '21 at 15:26
  • @tstanisl There isn't one, until you want to turn the code generic instead of using hard-coded sizes. – Lundin Apr 13 '21 at 06:25
0

My suggestion for the loop:

char const* prefix = "No repeated digits";

for (i = 0; i < 10; ++i) {
    if (a[i] == 1 ) {
        // check if the prefix has been printed yet
        if (strcmp( prefix, "") != 0) {
            printf("Repeated digits: ");
            
            // and set the prefix to nothing so it won't be printed again
            prefix = "";
        }

        printf("%d ", i);
    }
}

printf( "%s\n", prefix);

The prefix variable gets used both as a flag of sorts and as the prefix for the line of text that gets printed.

One small benefit of this approach is that the array only gets walked once. Not a big deal (the "Big O" complexity isn't changed), but it's not nothing.

Note that this code assumes that the a[] elements are only 0 or 1 which was implied by the code in the original post but not explicitly stated.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
0

You could make a function for the task. The logic is kind of the same as the flag approach, with the advantage that you can easily check and recheck different arrays in different places of your program.

The function would be something like

int allZeros( int *a, int len ) {

    int i = 0;
    
    while( i++ < len && !*a++ );
    
    return i == len+1;
}

And can be used in this way:

if( allZeros(a,10) ) puts("No repeated digits");

else {
    printf("Repeated digits: ");
    etc...
}
anotherOne
  • 1,513
  • 11
  • 20
0

For starters you could write a function that counts how many times a digit is encountered in a number.

If the function accepts signed integer numbers then you need to process such numbers correctly.

Here is a demonstrative program.

#include <stdio.h>

#define N   10

struct Digits
{
    char a[N];
};

struct Digits split_to_digits( long long int n )
{
    const long long int Base = N;
    
    struct Digits digits = { 0 };
    
    do 
    {
        ++digits.a[n < 0 ? -( n % Base ) : n % Base];
    } while ( n /= Base );
    
    return digits;
}

int main(void) 
{
    while ( 1 )
    {
        printf( "Enter an integer number (0 - exit): " );
        
        long long int n;
        
        if ( scanf( "%lld", &n ) != 1 || n == 0 ) break;
        
        struct Digits digits = split_to_digits( n );
        
        int unique = 1;
        
        for ( size_t i = 0; unique && i < N; i++ )
        {
            unique = digits.a[i] < 2;
        }
        
        if ( unique )
        {
            puts( "No repeated digits." );
        }
        else
        {
            printf( "Repeated digits: " );
            
            for ( size_t i = 0; i < N; i++ )
            {
                if ( !( digits.a[i] < 2 ) )
                {
                    printf( "%zu ", i );
                }
            }
            
            putchar( '\n' );
        }
    }

    return 0;
}

Its output might look like

Enter an integer number (0 - exit): 12345
No repeated digits.
Enter an integer number (0 - exit): -12345
No repeated digits.
Enter an integer number (0 - exit): 12233445
Repeated digits: 2 3 4 
Enter an integer number (0 - exit): -12233445
Repeated digits: 2 3 4 
Enter an integer number (0 - exit): 0
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335