简体   繁体   中英

Program to find the prime factorization

I wrote this code to find the prime factorization of a number. I just cannot figure out the last part. If x is entered as a double or float , the program should print an error message and terminate. How do I achieve this?

#include <stdio.h>

int main()
{
    int x, i;
    printf("Enter an integer:  ");
    scanf("%d", &x);
    
    if (x <= 1)
    {
        return 1;
    }
    printf("The prime factorization of %d is ", x);
    if (x > 1)
    {
        while (x % 2 == 0) 
        { 
            printf("2 "); 
            x = x / 2; 
        } 
        for (i = 3; i < 1009; i = i + 2)
        {
            while (x % i == 0) 
            { 
                printf("%d ", i); 
                x = x / i; 
            }
        }
    }
    return 0;
}

Your starting point should cover all desired and undesired cases so you should take float number from a user, not int . Then, you should check whether whole decimal part of the number is 0. That is, if all of them equals 0, the user want to provide an int number instead of float .

First step is to declare a float number:

float y;

After, take its value from the user:

scanf("%f", &y);

Second step is to check whether it is int or float . There are many ways for this step. For example, I find roundf() function useful. It takes a float number and computes the nearest integer to this number. So if the nearest integer is the number itself then the number has to be int . Right?

if(roundf(y)!=y)

If you are sure it is an int , you can move onto the third step: convert float type to int type. It is called type-casting . This step is required for the remaining part of your program because in your algorithm you manipulate the number with int type so just convert it to int :

x = (int)y;

After adding the line above, you can use the rest of code which you typed. I give the whole program:

#include <stdio.h>
#include <math.h>
int main()
{
    int x,i;
    float y;
    printf("Enter an integer:  ");
    scanf("%f", &y);

    if(roundf(y)!=y){
        printf("%f is not an integer!",y);
        return 1;
    }

    else{
        x = (int)y;
    }

    if (x <= 1)
    {
        printf("%d <= 1",x);
        return 1;
    }

    else
    {
        printf("The prime factorization of %d is ", x);
        while (x%2 == 0)
        {
            printf("2 ");
            x = x / 2;
        }
        for ( i = 3; i < 1009; i = i + 2)
        {
            while (x%i == 0)
            {
                printf("%d ",i);
                x = x / i;
            }
        }
    }
    return 0;
}

The use of scanf() is a bit tricky, I would avoid it to scan user generated input at almost all cost. But nevertheless here is a short overview for how to get the errors of scanf()

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

int main(void)
{
   int x, i, scanf_return;

   printf("Enter an integer:  ");

   /* Reset "errno". Not necessary here, just in case. */
   errno = 0;
   /* scanf() returns a value in case of an error */
   scanf_return = scanf("%d", &x);
   /*
    * scanf() returns "EOF" if it didn't find all what you wanted or
    * and error happened.
    * It sets "errno" to the value of the actual error. See manpage
    * for all of the details.
    */
   if (scanf_return == EOF) {
      /*
       * The error is connected to the stream, so we can differ between
       * an error within scanf() and and error with the input stream
       * (here: stdin)
       */
      if (ferror(stdin)) {
         fprintf(stderr, "Something went wrong while reading stdin: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
      } else {
         /* e.g. a conversion error, a float instead of an integer, letters
            instead of a decimal number */
         fprintf(stderr, "Something went wrong within scanf()\n");
         exit(EXIT_FAILURE);
      }
   }
   /*
    * If no error occurred, the return holds the number of objects
    * scanf() was able to read. We only need one, but it would throw an
    * error if cannot find any objects, so the check is here for
    * pedagogical reasons only.
    */
   if (scanf_return != 1) {
      fprintf(stderr, "Something went wrong within scanf(): wrong number of objects read.\n");
      exit(EXIT_FAILURE);
   }

   if (x <= 1) {
      fprintf(stderr, "Input must be larger than 1!\n");
      exit(EXIT_FAILURE);
   }
   printf("The prime factorization of %d is ", x);
   /* No need for that test, x is already larger than one at this point. */
   /* if (x > 1) { */
   while (x%2 == 0) {
      printf("2 ");
      x = x / 2;
   }
   for (i = 3; i < 1009; i = i + 2) {
      while (x%i == 0) {
         printf("%d ",i);
         x = x / i;
      }
   }
   /* } */
   /* Make it pretty. */
   putchar('\n');
   exit(EXIT_SUCCESS);
}

Does it work?

$ ./stackoverflow_003 
Enter an integer:  1234
The prime factorization of 1234 is 2 617 
$ factor 1234
1234: 2 617
$ ./stackoverflow_003 
Enter an integer:  asd
Something went wrong within scanf(): wrong number of objects read.
$ ./stackoverflow_003 
Enter an integer:  123.123
The prime factorization of 123 is 3 41

No, it does not work. Why not? If you ask scanf() to scan an integer it grabs all consecutive decimal digits (0-9) until there is no one left. The little qualifier "consecutive" is most likely the source of your problem: a floating point number with a fractional part has a decimal point and that is the point where scanf() assumes that the integer you wanted ended. Check:

 $ ./stackoverflow_003 
 Enter an integer:  .123
 Something went wrong within scanf(): wrong number of objects read

How do you find out? @weather-vane gave one of many ways to do so: check if the next character after the integer is a period (or another decimal separator of your choice):

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

int main(void)
{
   int x, i, scanf_return;
   char c = -1;

   printf("Enter an integer:  ");

   /* Reset "errno". Not necessary here, just in case. */
   errno = 0;
   /* scanf() returns a value in case of an error */
   scanf_return = scanf("%d%c", &x, &c);
   /*
    * scanf() returns "EOF" if it didn't find all what you wanted or
    * and error happened.
    * It sets "errno" to the value of the actual error. See manpage
    * for all of the details.
    */
   if (scanf_return == EOF) {
      /*
       * The error is connected to the stream, so we can differ between
       * an error within scanf() and and error with the input stream
       * (here: stdin)
       */
      if (ferror(stdin)) {
         fprintf(stderr, "Something went wrong while reading stdin: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
      } else {
         /* e.g. a conversion error, a float instead of an integer, letters
            instead of a decimal number */
         fprintf(stderr, "Something went wrong within scanf()\n");
         exit(EXIT_FAILURE);
      }
   }
   /*
    * If no error occurred, the return holds the number of objects
    * scanf() was able to read. We can use this information now.
    * If there is a period (actually any character) after the integer
    * it returns 2 (assuming no error happened, of course)
    */

   /* If no integer given, the following character ("%c") gets ignored. */
   if (scanf_return == 0) {
      fprintf(stderr, "Something went wrong within scanf(): no objects read.\n");
      exit(EXIT_FAILURE);
   }
   /* Found two objects, check second one which is the character. */
   if (scanf_return == 2) {
      if (c == '.') {
         fprintf(stderr, "Floating point numbers are not allowed.\n");
         exit(EXIT_FAILURE);
      }
   }

   if (x <= 1) {
      fprintf(stderr, "Input must be larger than 1!\n");
      exit(EXIT_FAILURE);
   }
   printf("The prime factorization of %d is ", x);
   /* No need for that test, x is already larger than one at this point. */
   /* if (x > 1) { */
   while (x%2 == 0) {
      printf("2 ");
      x = x / 2;
   }
   for (i = 3; i < 1009; i = i + 2) {
      while (x%i == 0) {
         printf("%d ",i);
         x = x / i;
      }
   }
   /* } */
   /* Make it pretty. */
   putchar('\n');
   exit(EXIT_SUCCESS);
}

Check:

$ ./stackoverflow_003 
Enter an integer:  123
The prime factorization of 123 is 3 41 
$ ./stackoverflow_003 
Enter an integer:  123.123
Floating point numbers are not allowed.
$ ./stackoverflow_003 
Enter an integer:  .123
Something went wrong within scanf(): no objects read.

Looks good enough for me. With one little bug:

$ ./stackoverflow_003 
Enter an integer:  123.
Floating point numbers are not allowed

But I think I can leave that as an exercise for the dear reader.

You can try this simple C99 implementation of Pollard Rho algorithm:

// Integer factorization in C language.
// Decompose a composite number into a product of smaller integers.

unsigned long long pollard_rho(const unsigned long long N) {
    // Require : N is a composite number, not a square.
    // Ensure : you already performed trial division up to 23.
    // Option : change the timeout, change the rand function.
    static const int timeout = 18;
    static unsigned long long rand_val = 2994439072U;
    rand_val = (rand_val * 1025416097U + 286824428U) % 4294967291LLU;
    unsigned long long gcd = 1, a, b, c, i = 0, j = 1, x = 1, y = 1 + rand_val % (N - 1);
    for (; gcd == 1; ++i) {
        if (i == j) {
            if (j >> timeout)
                break;
            j <<= 1;
            x = y; // "x" takes the previous value of "y" when "i" is a power of 2.
        }
        a = y, b = y; // computes y = f(y)
        for (y = 0; a; a & 1 ? b >= N - y ? y -= N : 0, y += b : 0, a >>= 1, (c = b) >= N - b ? c -= N : 0, b += c);
        y = (1 + y) % N; // function f performed f(y) = (y * y + 1) % N
        for (a = y > x ? y - x : x - y, b = N; (a %= b) && (b %= a););
        gcd = a | b; // the GCD(abs(y - x), N) was computed
        // it continues until "gcd" is a non-trivial factor of N.
    }
    return gcd;
}
  • Usually you performed some trial division before calling the algorithm
  • The algorithm isn't designed to receive a prime number as input
  • Two consecutive calls may not result in the same answer
  • Alternately, there is a pure C quadratic sieve which factors numbers from 0 to 300-bit.

If in doubt about the primality of N you can use a C99 primality checker:

typedef unsigned long long int ulong;

ulong mul_mod(ulong a, ulong b, const ulong mod) {
    ulong res = 0, c; // return (a * b) % mod, avoiding overflow errors while doing modular multiplication.
    for (b %= mod; a; a & 1 ? b >= mod - res ? res -= mod : 0, res += b : 0, a >>= 1, (c = b) >= mod - b ? c -= mod : 0, b += c);
    return res % mod;
}

ulong pow_mod(ulong n, ulong exp, const ulong mod) {
    ulong res = 1; // return (n ^ exp) % mod
    for (n %= mod; exp; exp & 1 ? res = mul_mod(res, n, mod) : 0, n = mul_mod(n, n, mod), exp >>= 1);
    return res;
}

int is_prime(ulong N) {
    // Perform a Miller-Rabin test, it should be a deterministic version.
    const ulong n_primes = 9, primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
    for (ulong i = 0; i < n_primes; ++i)
        if (N % primes[i] == 0) return N == primes[i];
    if (N < primes[n_primes - 1]) return 0;
    int primality = 1, a = 0;
    ulong b;
    for (b = N - 1; ~b & 1; b >>= 1, ++a);
    for (ulong i = 0; i < n_primes && primality; ++i) {
        ulong c = pow_mod(primes[i], b, N);
        if (c != 1) {
            for (int j = a; j-- && (primality = c + 1 != N);)
                c = mul_mod(c, c, N);
            primality = !primality;
        }
    }
    return primality;
}

To try it there is a factor function:

// return the number that was multiplied by itself to reach N.
ulong square_root(const ulong num) {
    ulong res = 0, rem = num, a, b;
    for (a = 1LLU << 62 ; a; a >>= 2) {
        b = res + a;
        res >>= 1;
        if (rem >= b)
            rem -= b, res += a;
    }
    return res;
}

ulong factor(ulong num){
    const ulong root = square_root(num);
    if (root * root == num) return root ;
    const ulong n_primes = 9, primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
    for (ulong i = 0; i < n_primes && primes[i] <= root; ++i)
        if (num % primes[i] == 0) return primes[i];
    if (is_prime(num))
        return 1 ;
    return pollard_rho(num);
}

Which is completed by the main function:

#include <assert.h>
int main(void){
    for(ulong i = 2; i < 63; ++i){
        ulong f = factor(i);
        assert(f <= 1 || f >= i ? is_prime(i) : i % f == 0);
        ulong j = (1LLU << i) - 1 ;
        f = factor(j);
        assert(f <= 1 || f >= j ? is_prime(j) : j % f == 0);
        j = 1 | pow_mod((ulong) &main, i, -5);
        f = factor(j);
        assert(f <= 1 || f >= j ? is_prime(j) : j % f == 0);
    }
}

There are some problems in your code:

  • you do not check the return value of scanf , so you cannot detect invalid or missing input and will have undefined behavior in those cases.

  • you only test divisors up to 1009 , so composite numbers with larger prime factors do not produce any output.

  • prime numbers larger than 1009 do not produce any output.

  • you should probably output a newline after the factors.

Testing and reporting invalid input such as floating point numbers can be done more easily by reading the input as a full line and parsing it with strtol() .

Here is a modified version:

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

int main() {
    char input[120];
    char ch;
    char *p;    
    long x, i;
    int last_errno;
    printf("Enter an integer: ");
    if (!fgets(input, sizeof input, stdin)) {
        fprintf(stderr, "missing input\n");
        return 1;
    }
    errno = 0;
    x = strtol(input, &p, 0);
    last_errno = errno;
    if (p == input || sscanf(p, " %c", &ch) == 1) {
        fprintf(stderr, "invalid input: %s", input);
        return 1;
    }
    if (last_errno == ERANGE) {
        fprintf(stderr, "number too large: %s", input);
        return 1;
    }
    if (x < 0) {
        fprintf(stderr, "number is negative: %ld\n", x);
        return 1;
    }
    if (x <= 1) {
        return 1;
    }
    printf("The prime factorization of %ld is", x);
    while (x % 2 == 0) { 
        printf(" 2"); 
        x = x / 2; 
    } 
    for (i = 3; x / i >= i;) {
        if (x % i == 0) { 
            printf(" %ld", i); 
            x = x / i; 
        } else {
            i = i + 2;
        }
    }
    if (x > 1) {
        printf(" %ld", x);
    }
    printf("\n");
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM