3
\$\begingroup\$

This C code is to print a triangle of stars.

enter image description here

#include <stdio.h>
int main(){
    for(int i=0;i<3;i++) {
        for(int j=0;j<i*2+1;j++){
            printf("*");
        }
        printf("\n");
    }
}

which works well though, I'd like to know if there is a better way to do the job, as it's said 2 layers of loop is inefficient, compared to vectorization.

\$\endgroup\$
0
3
\$\begingroup\$

I use a string to avoid the inner loop. The problem here is the printf() inside. A 3000-row triangle redirected to /dev/null takes 50ms, but now only 4ms.

I reformatted the output. And I left dots as fillers to see what is going on.

$ ./a.out 
...*...
..***..
.*****.
*******

It starts with one * in the so-called middle, and every row-iteration only sets two more bytes to * before it prints.

The complicated part is preparing the string i.e. array of chars. You can use calloc() to clear the right side, but it still takes a step to put blanks on the left side.

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

int trihi = 4;
char PIX = '*';

int main(void){

    int width = trihi * 2 - 1;
    char s[width + 1];
    memset(s, '.', width);
    s[width] = '\0';

    int mid = trihi - 1;
    s[mid] = PIX;
    printf("%s\n", s);

    for (int i = 1; i < trihi; i++) {

        s[mid - i] =
        s[mid + i] = PIX;
        printf("%s\n", s);
    }
}

This should add up. s[mid + i] runs up to 2*mid = 2*(trihi-1). The '\0' sits at width = 2*trihi - 1 which is one higher, and is the highest legal index for s.

It is very easy to make a small mistake and not have the \0 at the correct place.

It also works with a triangle height of 1:

$ ./a.out 
*

And by setting ... = --PIX; in the loop:

$ ./a.out 
.....*.....
....)*)....
...()*)(...
..'()*)('..
.&'()*)('&.
%&'()*)('&%

The So-Called Middle

This is how a triangle of height 4 is laid out. Size is 8. The middle is s[3]; there are 3 spaces to the left.

01234567  --- "offset", "index"
...*...0
|  |   |
|  |   +-- s[width]  (width = 2*trihi - 1)     
|  +------ s[mid]    (mid   = trihi - 1)   
+--------- s[0] 

You start with trihi=4, but what you need is mostly 3 and 7...the geometrical base width is also 7.


Just any Triangle

If this is enough (with original height = 3):

$ ./a.out 
*
**
***

then the code gets much simpler:

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

int trihi = 3;
char PIX = '*';

int main(void){

    char *s = calloc(trihi+1, 1);

    for (int i = 0; i < trihi; i++) {
        s[i] = PIX;
        printf("%s\n", s);
    }
}

I kicked everything out except the basic idea: re-using a prepared string/array. Here the calloc-zeroes are only after the PIXes. No need for a filler like space or dots via memset() anymore. But it is also not the same user experience...and the triangle is half the size.

Overiq.com has a double-loop version of a C Program to print Half Pyramid pattern using *. It takes much longer than the above (tri2.c below).

$ gcc -O2 over.c           
$ time ./a.out |wc -l
3001

real    0m0.029s
user    0m0.020s
sys     0m0.014s
$ gcc -O2 tri2.c 
$ time ./a.out |wc -l
3000

real    0m0.006s
user    0m0.000s
sys     0m0.011s
\$\endgroup\$
3
  • \$\begingroup\$ Your answer is almost a good one. Currently your observation about the performance of the program is implied, if you actually mention it in the answer it would be better. \$\endgroup\$ – pacmaninbw Nov 1 '20 at 16:44
  • \$\begingroup\$ With redir to a tmpfs file it is 54ms vs. 11ms. I don't know what you mean with "imply" or "mention". I tested and mentioned it. \$\endgroup\$ – user232636 Nov 1 '20 at 17:02
  • \$\begingroup\$ It is the OP who mentioned efficiency, and I think he is right to ask. His way would work well with curses, I guess, where you "refresh" when done modifying. \$\endgroup\$ – user232636 Nov 1 '20 at 17:20
2
\$\begingroup\$

Due to the size of the program, I have only a few things to suggest

  • Since you aren't returning anything from main(), use int main(void).
  • Use don't have to perform any arithmetic in the inner nested-loop, you only need to do j <= i
  • 3 in the outer-loop is a magic number or, an unnamed numeric constant. I suggest you assign it to const int rows, to make it clear
#include <stdio.h>

int main(void)
{
    const int rows = 3;

    for(int i = 0;i < rows; i++)
    {
        for(int j = 0; j <= i; j++)
            printf("*")

        printf("\n");
    }
}

I believe this will be the best, and easiest way to perform this task!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.