Print a representation of the Pythagorean Triple i

2019-09-12 10:13发布

问题:

I am trying to create a program that prints the mapping data for found pythagorean triples in C. So far, I have coded the program to be able to find the triples.

#include <stdio.h>
#include <math.h>

int main (int argc, char * argv[]) {
    int a, b, c;
    int a2, b2, c2;
    int limit = 60;

    for (a = 1; a <= limit; a++) {
        a2 = a * a;
        for (b = 1; b <= limit; b++) {
            b2 = b * b;
            for (c = 0; c <= ((int)sqrt(a2+b2)); c++) {
                c2 = c * c;
                if (a < b && (c2 == (a2 + b2))) {
                    printf("triple: %d %d %d\n", a, b, c);
                }
            }
        }
    }
}

The intended output is expected in the format:

123456789012345678901234567890123456789012345678901234567890
1\
2 \
3  \
4  *\
5    \
6     \
7      \
8     * \
9        \
0         \
1          \
2    *   *  \

I am trying to write a loop that does this, but cannot think of how to print in this way. Any suggestions?

UPDATE: I managed to print the x and the y axis (x = a and y = b). The values are correct, now the mapping part is left.

    for (int x = 0; x < a; x++) { // x-axis = a
        printf("%d ", x);
    }

    printf("\n");

    for (int y = 0; y < b; y++) { // y-axis = b

        printf("%d\n", y);
    }

UPDATE: Modified the code, the output is printing, but having problems printing spaces. tried manually adding spaces to the " \ " and " * " but that just distorts the whole image.

#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool is_perfect_square(int num);

int main (int argc, char * argv[]) {

    int a, b, c;
    int a2, b2, c2;
    int limit = 60;
    bool flag = false;

    for (a = 1; a <= limit; a++) {
        a2 = a * a;
        for (b = 1; b <= limit; b++) {
            b2 = b * b;
            for (c = 0; c <= ((int)sqrt(a2+b2)); c++) {
                c2 = c * c;
                if (a < b && (c2 == (a2 + b2))) {
                    // printf("triple: %d %d %d\n", a, b, c);
                }
            }
        }
    }

    for (int x = 0; x < a; x++) {
        for (int y = 0; y < b; y++) {
            if (x == 0) {
                printf("%d ", ((y+1)% 10));
            } else if (y == 0) {
                printf("%d ", (x % 10));
            } else if (x == y) {
                printf("\\");
            } else if (is_perfect_square((x*x) + (y*y))) {
                printf("*");
            }
        }
        printf("\n");
    }
}

bool is_perfect_square (int num) {
    int root = (int)(sqrt(num));

    if (num == root * root) {
        return true;
    } else {
        return false;
    }
}

Still working on possible solution.

回答1:

Hint :

Have a nested loop with index i,j;

for i 0.. limit {
  for j 0 .. limit {
   /*Implement the below algorithm here!!*/
  }
    printf("\n")
}

Algorithm to be used inside the loop:

  • if i == 0 print x axis values by printing (j+1)% 10 [see note at end]
  • else if j == 0 print y axis values by printing i % 10
  • else if i == j print '\'
  • else if is_perfect_square((i*i) + (j*j)) returns 1, print '*'
  • else print a space.

Specifications for is_perfect_square function: A function that returns 1 if the input is a perfect square and 0 otherwise. For example:

  • is_perfect_square(25) should return 1
  • is_perfect_square(7) should return 0
  • is_perfect_square(49) should return 1

Note: i == 0 case should print j%10 to start the output with 0 to represent origin. But the output provided in question starts with 1. Hence using (j+1)%10

You might need to handle some corner cases, which should be straight forward once this algorithm is implemented in code.



回答2:

EASY WAY

If you manage to print all your stuff in the right order, you can easily avoid unnecessary if statements and optimize the math calc as a side effect.

To find the Pythagorean triples I've modified your first method, so I can avoid the calls to sqrt at every position.

#include <stdio.h>
#include <math.h>

// all the loops will test numbers from 1 to 60 included
#define LIMIT 61

int main ( int argc, char * argv[] ) {
    int i, j, k, k2, sum;

    // print the first line of reference numbers
    // start with a space to allign the axes
    putchar(' ');
    // print every digit... 
    for ( i = 1; i < LIMIT; ++i )         
        putchar('0' + i%10);
    // then newline
    putchar('\n');

    // now print every row
    for ( i = 1; i < LIMIT; ++i ) {
        // first print the number
        putchar('0' + i%10);
        // then test if the indeces (i,j) form a triple: i^2 + j^2 = k^2
        // I don't want to call sqrt() every time, so I'll use another approach
        k = i;
        k2 = k * k;
        for ( j = 1; j < i; ++j ) {
            // this  ^^^^^ condition let me find only unique triples and print
            // only the bottom left part of the picture

            // compilers should be (and they are) smart enough to optimize this
            sum = i * i  +  j * j;

            // find the next big enough k^2
            if ( sum > k2 ) {
                ++k;
                k2 = k * k;
            }

            // test if the triple i,j,k matches the Pythagorean equation
            if ( sum == k2 )
                // it's a match, so print a '*'
                putchar('*');
            else
                // otherwise print a space
                putchar(' ');
        }
        // the line is finished, print the diagonal (with a '\\') and newline
        printf("\\\n");
        // An alternative could be to put the reference numbers here instead:
        //      printf("%c\n",'0' + i%10);
    }

    return 0;
}

The output of this program is:

 123456789012345678901234567890123456789012345678901234567890
1\
2 \
3  \
4  *\
5    \
6     \
7      \
8     * \
9        \
0         \
1          \
2    *   *  \
3            \
4             \
5       *      \
6           *   \
7                \
8                 \
9                  \
0              *    \
1                   *\
2                     \
3                      \
4      *  *       *     \
5                        \
6                         \
7                          \
8                    *      \
9                            \
0               *             \
1                              \
2                       *       \
3                                \
4                                 \
5           *                      \
6              *           *        \
7                                    \
8                                     \
9                                      \
0        *                    *         \
1                                        \
2                                       * \
3                                          \
4                                *          \
5                       *   *                \
6                                             \
7                                              \
8             *     *               *           \
9                                                \
0                                                 \
1                                                  \
2                                      *            \
3                                                    \
4                                                     \
5                                               *      \
6                                *        *             \
7                                                        \
8                                                         \
9                                                          \
0          *             *      *            *              \


LESS EASY WAY

I'll show you another method to print out what you want.

Consider using an array of strings as a drawing space, stored in a struct. It seems a complication, but you can simplify and maybe generalize the output procedures:

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

typedef struct {
    char **img;
    int rows;
    int cols;
} Image;

Image *init_image( int r, int c, char s ) {
    int i;
    char *tmp = NULL;

    Image *pi = malloc(sizeof(Image));
    if ( !pi ) {
        perror("Error");
        exit(-1);
    }
    pi->rows = r;  
    pi->cols = c;
    pi->img = malloc(r * sizeof(char*));
    if ( !pi->img ) {
        perror("Error");
        exit(-1);
    }
    for ( i = 0; i < r; ++i ) {
        tmp = malloc(c + 1);
        if ( !tmp ) {
            perror("Error");
            exit(-1);
        }
        // fill with initial value (spaces) and add the terminating NULL
        memset(tmp,s,c);
        tmp[c] = '\0';
        pi->img[i] = tmp;
    }
    return pi;
}

void free_image( Image *pi ) {
    int i;
    if ( !pi || !pi->img ) return;
    for ( i = 0; i < pi->rows; ++i ) {
        free(pi->img[i]);
    }
    free(pi->img);
    free(pi);
}

void draw_axes( Image *pi ) {
    int i;
    if ( !pi ) return;
    // I use to loops because cols can be != rows, but if it's always a square...
    for ( i = 1; i < pi->cols; ++i ) {
        pi->img[0][i] = '0' + i%10;
    }
    for ( i = 1; i < pi->rows; ++i ) {
        pi->img[i][0] = '0' + i%10;
    }
}

void draw_diagonal ( Image *pi, char ch ) {
    int i, m;

    if ( !pi ) return;
    m = pi->rows < pi->cols ? pi->rows : pi->cols;
    for ( i = 1; i < m; ++i ) {
        pi->img[i][i] = ch;
    }
}

void print_image( Image *pi ) {
    int i;

    if ( !pi ) return;
    for ( i = 0; i < pi->rows; ++i ) {
        printf("%s\n",pi->img[i]);
    }
}

void draw_triples( Image *pi, char ch ) {
    int i, j, k, k2, sum;

    for ( i = 1; i < pi->rows; ++i ) {
        k = i;
        k2 = k * k;
        // print only the left bottom part
        for ( j = 1; j < i && j < pi->cols; ++j ) {
            sum = i * i  +  j * j;
            if ( sum > k2 ) {
                ++k;
                k2 = k * k;
            }
            if ( sum == k2 ) {
                pi->img[i][j] = ch;
                // printf("%d %d %d\n",i,j,k);
            }
        }
    }
}

int main(int argc, char *argv[]) {
    // Initialize the image buffer to contain 61 strings of 61 spaces
    Image *img = init_image(61,61,' ');

    // draw the reference numbers at row 0 and column 0
    draw_axes(img);
    // draw a diagonal with character '\'
    draw_diagonal(img,'\\');
    // put a '*' if a couple of coordinates forms a Pythagorean triple
    draw_triples(img,'*');
    // print out the image to stdout
    print_image(img);

    free_image(img);
    return 0;
}

The output of this code is the same as the previous snippet, but, believe it or not, this is faster (at least on my system) due to the less numbers of function calls that print to stdout.


ADDENDUM

It's way off topic, but I had fun adapting the previous code to actually output an image file (as a grayscaled 512x512 PGM binary formatted file) representing all the triples with side lengths up to 8192.

Every pixel correspond to a 16x16 square block, black colored if there is no match or lighter depending on how many triples the alghorithm found in the block.

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

typedef struct {
    char *img;      // store data in a 1D array this time
    int dim;
    int samples;
} Image;

Image *init_image( int d, int z );
void free_image( Image *pi );
void add_sample( Image *pi, int r, int c );
void draw_triples_sampled( Image *pi );
void save_image( char *file_name, Image *pi );

int main(int argc, char *argv[]) {
    // store the results in a 512x512 image, with each pixel corresponding
    // to a 16x16 square, so the test area is 8192x8192 wide
    Image *img = init_image(512,16);

    draw_triples_sampled(img);
    save_image("triples.pgm",img);
    free_image(img);

    return 0;
}

Image *init_image( int d, int z ) {

    Image *pi = malloc(sizeof(Image));
    if ( !pi ) {
        perror("Error");
        exit(-1);
    }
    pi->dim = d;
    pi->samples = z;
    pi->img = calloc(d*d,1);
    if ( !pi->img ) {
        perror("Error");
        exit(-1);
    }
    return pi;
}

void free_image( Image *pi ) {
    if ( !pi ) free(pi->img);
    free(pi);
}

void add_sample( Image *pi, int r, int c ) {
    // each pixel represent a square block of samples
    int i = r / pi->samples;                
    int j = c / pi->samples;
    // convert 2D indeces to 1D array index
    char *pix = pi->img + (i * pi->dim + j);
    ++(*pix);
}

void draw_triples_sampled( Image *pi ) {
    int i, j, k, k2, sum;
    int dim;
    char *val;

    if ( !pi ) return;
    dim = pi->dim * pi->samples + 1;
    for ( i = 1; i < dim; ++i ) {
        k = i;
        k2 = k * k;
        // test only bottom left side for simmetry...
        for ( j = 1; j < i; ++j ) {
            sum = i * i + j * j;
            if ( sum > k2 ) {
                ++k;
                k2 = k * k;
            }
            if ( sum == k2 ) {              
                add_sample(pi,i-1,j-1);
                // draw both points, thanks to simmetry
                add_sample(pi,j-1,i-1);
            }
        }
    }
}

void save_image( char *file_name, Image *pi ) {
    FILE *pf = NULL;
    char v;
    char *i = NULL, *end = NULL;

    if ( !pi ) {
        printf("Error saving image, no image data\n");
        return;
    }
    if ( !file_name ) {
        printf("Error saving image, no file name specified\n");
        return;
    }
    pf = fopen(file_name,"wb");
    if ( !pf ) {
        printf("Error saving image in file %s\n",file_name);
        perror("");
        return;
    }
    // save the image as a grayscale PGM format file
    // black background, pixels from gray to white   
    fprintf(pf,"P5 %d %d %d ",pi->dim,pi->dim,255);
    end = pi->img + pi->dim * pi->dim;
    for ( i = pi->img; i != end; ++i ) {
        if ( *i == 0 )
            v = 0;
        else if ( *i < 10 )
            v = 105 + *i * 15;
        else
            v = 255; 
        fprintf(pf,"%c",v);
    }
    close(pf);
}

The output picture is (once converted to PNG to post here) this. Note the emerging patterns:



回答3:

#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool is_perfect_square(int num);

int main (int argc, char * argv[]) {

    int a, b, c;
    int a2, b2, c2;
    int limit = 60;
    bool flag = false;

    for (a = 1; a <= limit; a++) {
        a2 = a * a;
        for (b = 1; b <= limit; b++) {
            b2 = b * b;
            for (c = 0; c <= ((int)sqrt(a2+b2)); c++) {
                c2 = c * c;
                if (a < b && (c2 == (a2 + b2))) {
                    // printf("triple: %d %d %d\n", a, b, c);
                }
            }
        }
    }

    printf(" ");
    for (int x = 0; x < a; x++) {
        for (int y = 0; y < b; y++) {
            if (x == 0) {
                printf("%d", ((y+1) % 10));
            } else if (y == 0) {
                printf("%d", (x % 10));
            } else if (x == y) {
                printf("\\");
            } else if (is_perfect_square((x*x) + (y*y))) {
                printf("*");
            } else {
                printf(" ");
            }
        }
        printf("\n");
    }
}

bool is_perfect_square (int num) {
    int root = (int)(sqrt(num));

    if (num == root * root) {
        return true;
    } else {
        return false;
    }
}

FINAL UPDATE: finally managed to come up with this answer. a big thanks to Mr. Ravichandra. This is for anyone who might need something like this later. The code is not perfect and can be improved upon. The output is satisfactory and prints out a pattern almost similar to the desired output pattern.