问题
I am trying to create a Sierpinski carpet using the C language. I successfully created the first iteration. When the second iteration begins, the function correctly generates squares just around the first square. One more problem is that my code generates the middle square too (normally it should be 8 squares, not 9).
Below is a picture and the code. I'm just out of ideas and I can't find where I went wrong.
#include <stdio.h>
#include<stdlib.h>
void draw(FILE *file, int size, int x, int y);
void sierpenski(FILE *file,int iterations,int size, int x, int y);
int main( int argc , char *argv[] ) {
if( argc == 1)
{
printf("Not enough input arguments..\n");\
exit(EXIT_FAILURE);
}
FILE *file;
if(!(file = fopen("my.svg","w") ) )
{
printf("File has not been opened\n");
exit(EXIT_FAILURE);
}
fprintf(file,"<svg height=\"1000\" width=\"1000\" fill=\"blue\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n");
fprintf(file,"<rect x=\"400\" y=\"400\" height=\"200\" width=\"200\" />\n");
int iterations = atoi(argv[1]);
sierpenski(file,iterations,200,400,400);
fprintf(file,"</svg>\n");
return 0;
}
void draw(FILE *file, int size , int x , int y)
{ int aux_size = size;
size /= 3;
x /= 3;
y /= 3;
for( int i = 0; i < 9; i ++) {
printf("%i\n",i);
if( i == 1){
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n", x, y, size, size);
}
else if( i == 2)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n", x+2*aux_size-(aux_size/3), y, size, size);
}
else if( i == 3)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n", x+4*aux_size-2*(aux_size/3), y, size, size);
}
else if( i == 4)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n", x+4*aux_size-2*(aux_size/3), y+2*aux_size-(aux_size/3), size, size);
}
else if( i == 5)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n", x+4*aux_size-2*(aux_size/3), y+4*aux_size-2*(aux_size/3), size, size);
}
else if( i == 6)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n",x + 2 * aux_size - (aux_size / 3), y + 4* aux_size - 2*(aux_size / 3), size, size);
}
else if( i == 7)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n",x, y+4*aux_size-2*(aux_size/3), size, size);
}
else if( i == 8)
{
fprintf(file, "<rect x=\"%i\" y=\"%i\" height=\"%i\" width=\"%i\" />\n",x, y+2*aux_size-(aux_size/3), size, size);
}
}
}
void sierpenski( FILE *file,int iterations,int size, int x, int y) {
if (iterations == 0)
return;
int aux_size = size;
draw(file, size, x, y);
for( int i = 1 ; i < 9 ; i++) {
printf("%iiteratii sie\n",i);
if( i == 1)
{
sierpenski(file, iterations - 1, size / 3, x / 3, y / 3);
}
else if( i == 2)
{
sierpenski(file, iterations - 1, size / 3, x+4*aux_size-(aux_size/3), y / 3);
}
else if( i == 3)
{
sierpenski(file, iterations - 1, size / 3, x+9*aux_size-aux_size/3, y / 3);
}
else if( i == 4)
{
sierpenski(file, iterations - 1, size / 3, x+9*aux_size-aux_size/3, y + 4*aux_size-(aux_size/3));
}
else if( i == 5)
{
sierpenski(file, iterations - 1, size / 3, x+9*aux_size-aux_size/3, y+9*aux_size-aux_size/3);
}
else if( i == 6)
{
sierpenski(file, iterations - 1, size / 3, x+4*aux_size-aux_size/3, y+9*aux_size-aux_size/3);
}
else if( i == 7)
{
sierpenski(file, iterations - 1, size / 3, x/3, y+9*aux_size-aux_size/3);
}
else if( i == 8 )
{
sierpenski(file, iterations - 1, size / 3, x/3, y+4*aux_size-aux_size/3);
}
}
}
回答1:
Good attempt; you're pretty close. Nonetheless, I recommend taking a step back and re-approaching the problem a bit.
For starters, the loops and conditionals take up a lot of space but aren't actually doing anything. Since one and only one of the "counting" if
statements can be true per iteration, it winds up doing exactly what the code would do if executed as straight-line code without any blocks.
Beyond that admittedly superficial adjustment, the shrinking logic is unclear and seems dispersed between draw
and sierpinski
. Instead of drawing 9 boxes per iteration, I'd only render a single box per call (draw it right in the center of the field defined by the size
parameter), then let the recursive calls do the work to draw the smaller surrounding boxes.
The primary computation per call frame is size / 3
; everything else is relative to this in positioning the next iteration's parameters.
The initial call is sierpenski(file, iterations, size, 0, 0);
, which draws the largest box right in the center. It spawns 8 recursive calls with x/y coordinates which can be hand-computed one by one:
x , y // top left
x + third , y // top center
x + third * 2, y // top right
x , y + third // middle left
x + third , y + third // middle center, not actually needed
x + third * 2, y + third // middle right
x , y + third * 2 // bottom left
x + third , y + third * 2 // bottom middle
x + third * 2, y + third * 2 // bottom right
This is a lot of repetition. Do you notice a pattern here? It's a nested for
loop counting from 0-2 inclusive for rows and columns with a skip on i == 1 && j == 1
, which is the middle block.
When you look at the output image, there is some misalginment from using integer division. Switching the variables to floats solves the problem but makes the image less crisp. I'll leave fiddling with this as an exercise to the reader.
Let's put it all together:
#include <stdio.h>
#include <stdlib.h>
void sierpenski(FILE *file, int iterations, float size, float x, float y) {
if (!iterations) return;
float third = size / 3;
fprintf(file, "<rect x=\"%f\" y=\"%f\" height=\"%f\" "
"width=\"%f\" />\n", x + third, y + third, third, third);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i != 1 || j != 1) {
sierpenski(file, iterations - 1, third,
x + third * i, y + third * j);
}
}
}
}
int main(int argc, char **argv) {
int iterations = argc > 1 ? atoi(argv[1]) : 5;
float size = argc > 2 ? atoi(argv[2]) : 300;
FILE *file = fopen(argc > 3 ? argv[3] : "carpet.svg", "w");
if (!file) {
fprintf(stderr, "Could not open SVG file\n");
exit(EXIT_FAILURE);
}
fprintf(file, "<svg height=\"%f\" width=\"%f\" fill=\"blue\" "
"xmlns=\"http://www.w3.org/2000/svg\" "
"version=\"1.1\">\n", size, size);
sierpenski(file, iterations, size, 0, 0);
fprintf(file,"</svg>\n");
return 0;
}
来源:https://stackoverflow.com/questions/61738012/sierpinski-carpet-with-recursion-in-c-using-svg