打印分形图形
分形 ,每一部分都(至少近似地)是整体缩小后的形状。
分形,具有以非整数维形式充填空间的形态特征???
通常被定义为“一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状”,即具有自相似的性质
正是由于每一部分都(至少近似地)是整体缩小后的形状这个性质,可以通过递归来打印分形
对于n阶图形,由若干n-1阶分形构成,为了打印n阶图形,先找到 n阶图形整体中的一点,到各个分形中对应点 之间的距离、方位,从而递归打印出各个分形。
以下几题,所谓的 “从左上角开始扩散递归” 和 “从中心开始扩散递归” ,就是找到整体的点和各分形中对应位置的点
譬如 确定了n阶图形的点在左上角,接着就找各分形中左上角的点 的方位
由于打印图形靠的是输出矩阵,因此确定图形的点只要在 该图形所处的矩阵之内即可,该点不一定要有图案
【1】1104: 分形图
http://oj.ecustacm.cn/problem.php?id=1104
从左上角扩散递归
由于从左上角扩散递归,左上角图形和右上、中间、左下、右下都是上一阶图形,
且左上、中间 图形之间间距是上一阶图形的尺寸
左上、左下/右上 图形之间间距是(2*上一阶图形的尺寸)
注意对应矩阵的坐标系,行递增的方向(竖直向下)对应x轴
每一阶的图形尺寸都是 方形,看图片有点看不出来,但题干已经明确指出了
#include <iostream>
/*run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
ll power(int base,int expon){
ll res=1;
while(expon){
if(expon&1)res*=base;
base*=base;
expon>>=1;
}
return res;
}
char map[800][800]={};//power(3,6)=729
void draw(int n,int x,int y){
if(n==1){
map[x][y]='X';
return ;//递归要有结束的临界条件
}
else{
int interval=power(3,n-2);//上一阶图形的尺寸
//由于从左上角扩散递归,左上角图形和右上、中间、左下、右下都是
// 上一阶图形,且左上、中间 图形之间间距是上一阶图形的尺寸
// 左上、左下/右上 图形之间间距是(2*上一阶图形的尺寸)
//注意对应矩阵的坐标系,行递增的方向(竖直向下)对应x轴
draw(n-1,x,y);//左上角是上一阶的图形
draw(n-1,x,y+2*interval);//右上
draw(n-1,x+interval,y+interval);//中
draw(n-1,x+2*interval,y);// 左下
draw(n-1,x+2*interval,y+2*interval);//右下
}
}
int main(int argc, char** argv) {
int n;
cin>>n;
int size=power(3,n-1);
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++){
map[i][j]=' ';
}
}//n对应的尺寸是size这个方框,因此这个方框中的空格也要输出
draw(n,1,1);//从左上角开始扩散递归
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++){
cout<<map[i][j];
}
cout<<endl;
}
return 0;
}
从中心扩散递归
#include <iostream>
/*run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
ll power(int base,int expon){
ll res=1;
while(expon){
if(expon&1)res*=base;
base*=base;
expon>>=1;
}
return res;
}
char map[800][800]={};//power(3,6)=729
void draw(int n,int x,int y){
if(n==1){
map[x][y]='X';
return ;//递归要有结束的临界条件
}
else{
int interval=power(3,n-2);//上一阶图形的尺寸
//中心 到 四个角的中心 的距离都是 上一阶图形的尺寸
draw(n-1,x,y);//中心是上一阶的图形
draw(n-1,x-interval,y+interval);//右上
draw(n-1,x-interval,y-interval);//左上
draw(n-1,x+interval,y-interval);// 左下
draw(n-1,x+interval,y+interval);//右下
}
}
int main(int argc, char** argv) {
int n;
cin>>n;
int size=power(3,n-1);
draw(n,400,400);
//从最中心开始扩散递归,输出时根据尺寸确定图形所在的边界方框
for(int i=400-size/2;i<=400+size/2;i++){
for(int j=400-size/2;j<=400+size/2;j++){
if(map[i][j]=='X')cout<<map[i][j];
else cout<<" ";
}
cout<<endl;
}
return 0;
}
【2】 打印图形(2018年第9届蓝桥杯省赛试题)。(原题是填空题)
编写一个程序,在控制台绘制分形图(就是整体与局部自相似的图形)。
当n=1,2,3的时候,输出如下:
(1)从左上角扩散递归,左上角不用打印,递归到有图形的各个位置就好
度数为n的图形,其大小是3n*3n,可以用字符数组来存储图形中各个字符。
度数为n的图形可以有以下递归式子表示:
B(n - 1)
B(n - 1) B(n - 1) B(n - 1)
B(n - 1)
设递归函数void draw(int n,int x,int y)表示在(x,y)
位置开始设置度数为n的图形,它由5个度数为n-1的图形组成,
其起始位置分别为:(x+s,y)、(x,y+s)、(x+s,y+s)
、(x+2*s,y+s)和(x+s,y+2*s),其中s=3^(n-1)。
#include <iostream>
using namespace std;
#define N 729
void draw(char a[][N], int n, int row, int col)
{
if(n==0){
a[row][col] = 'O';
return;
}
int w = 1;
int i;
for(i=1; i<=n-1; i++) w *= 3;
draw(a,n-1, row+w, col);
draw(a,n-1, row, col+w);
draw(a,n-1, row+w,col+w);
draw(a,n-1, row+2*w, col+w);
draw(a,n-1, row+w,col+2*w);
}
int main()
{
char a[N][N];
int n,w,i,j;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
a[i][j] = ' ';
cin>>n;
w=1;
for(i=1; i<=n; i++) w *= 3;
draw(a,n,0,0);
for(i=0; i<w; i++)
{
for(j=0; j<w; j++)
cout<<a[i][j];
cout<<endl;
}
return 0;
}
(2)从中心扩散递归
要注意尺寸噢,上一题 n阶的尺寸时3^n-1
这题 n阶的尺寸时3^n
到左边、右边和到左上、左下距离不一样,但每个距离单位都是上一阶的尺寸(上一题 n阶的尺寸时3 ^ n-2
这题 n阶的尺寸时3 ^ n-1
由于还是上下左右中心 围绕中心,可以从中心出发
#include <iostream>
/*run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
ll power(int base,int expon){
ll res=1;
while(expon){
if(expon&1)res*=base;
base*=base;
expon>>=1;
}
return res;
}
char map[800][800]={};//power(3,6)=729
void draw(int n,int x,int y){
if(n==0){
map[x][y]='O';
return ;//递归要有结束的临界条件
}
else{
int interval=power(3,n-1);//上一阶图形的尺寸
//中心 到 四个角的中心 的距离都是 上一阶图形的尺寸
draw(n-1,x,y);//中心是上一阶的图形
draw(n-1,x,y-interval);//左
draw(n-1,x,y+interval);//右
draw(n-1,x-interval,y);//上
draw(n-1,x+interval,y);//下
}
}
//还是上下左右中的结构,每一阶的图形尺寸依旧是 方形
int main(int argc, char** argv) {
int n;
cin>>n;
int size=power(3,n);
draw(n,400,400);
//从最中心开始扩散递归,输出时根据尺寸确定图形所在的边界方框
for(int i=400-size/2;i<=400+size/2;i++){
for(int j=400-size/2;j<=400+size/2;j++){
if(map[i][j]=='O')cout<<map[i][j];
else cout<<" ";
}
cout<<endl;
}
return 0;
}
蓝桥杯填空题给出的源代码就是从中心扩散递归的思路
数组表现形式不同罢了,把数组和尺寸都当参数传进递归函数
(3)蓝桥杯填空题给出的源程序。(请自己阅读体会)
#include <stdio.h>
#include <stdlib.h>
void show(char* buf, int w)
{
int i,j;
for(i=0; i<w; i++) {
for(j=0; j<w; j++) {
printf("%c", buf[i*w+j]==0? ' ' : 'o');
}
printf(" ");
}
}
void draw(char* buf, int w, int x, int y, int size){
if(size==1){
buf[y*w+x] = 1;
return;
}
int n = **size/3** ; // 填空
draw(buf, w, x, y, n);
draw(buf, w, x-n, y ,n);
draw(buf, w, x+n, y ,n);
draw(buf, w, x, y-n ,n);
draw(buf, w, x, y+n ,n);
}
int main()
{
int N = 3;
int t = 1;
int i;
for(i=0; i<N; i++) t *= 3;
char* buf = (char*)malloc(t*t);
for(i=0; i<t*t; i++) buf[i] = 0;
draw(buf, t, t/2, t/2, t);
show(buf, t);
free(buf);
return 0;
}
【3】打印图形(2014年第5届蓝桥杯省赛试题)
编写一个程序,实现该图形的打印。
(1)编程思路。(来自链接)
度数n为4的图案在二维数组中的存储情况如图3所示。由图3可知,度数为4的图案可以由3个度数为3的图案(图3中分别用绿色、浅绿色和黄色蓝色底纹所示)。
因此,度数为n的图形G(n)可以由以下递归式子表示:
G(n - 1)
G(n - 1) G(n - 1)
设递归函数void draw(int n,int x,int y)表示在(x,y)
位置开始设置度数为n的图形,它由3个度数为n-1的图形组成,
其起始位置分别为:(x,y+2n-2)、(x+2n-2,y)和
(x+2n-2,y+2n-1)。
该递归函数的结束条件是:当n=1时(即度数为1的图形),
只需在(x,y)位置设置一个字符'*'即可。
#include <iostream>
/*run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
const int N=100;//这题要求打印4度的不过16
ll power(int base,int expon){
ll res=1;
while(expon){
if(expon&1)res*=base;
base*=base;
expon>>=1;
}
return res;
}
char map[100][100]={};//power(2,6)=64
void draw(int n,int x,int y){
if(n==1){
map[x][y]='*';
return ;//递归要有结束的临界条件
}
else{
int interval=power(2,n-2);//上一阶图形的尺寸
//中心 到 四个角的中心 的距离都是 上一阶图形的尺寸
// draw(n-1,x,y);//找的左上角这点是不绘制图案的
draw(n-1,x,y+interval);//上
draw(n-1,x+interval,y);//左下
draw(n-1,x+interval,y+2*interval);//右下
}
}
//上、左下、右下的结构,每一阶的图形尺寸 宽高不同
int main(int argc, char** argv) {
int n;
cin>>n;
int size=power(2,n-1);
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
map[i][j] = ' ';
draw(n,1,1);//从(0,0)还是(1,1)没关系,反正都是加上间距
//从左上角的点开始扩散递归,输出时根据尺寸确定图形所在的边界方框
// 高尺寸是size,宽尺寸 size*2-1
for(int i=1;i<=size;i++){
for(int j=1;j<=size*2;j++){
cout<<map[i][j];
}
cout<<endl;
}
return 0;
}
【4】谢尔宾斯基地毯(有点难,更能体现递归意义,尽量观察分形简单到只有一个字符的,更易观察出规律)
题目来源
Problem Description
谢尔宾斯基地毯是一种分形图案,它的定义如下:
令F(n)表示嵌套n层的谢尔宾斯基地毯,那么(下面的“空”均表示空格,仅为示意,实际输出时应仍为空格)一般地,如果F(n-1)表示嵌套n-1层的谢尔宾斯基地毯,则F(n)的递归定义如下:
F(n-1)F(n-1)F(n-1)F(n-1)X(n-1)F(n-1)F(n-1)F(n-1)F(n-1)
其中X(n)表示边长为n的正方形,其内部被字符X完全填充。 现在输入一个正整数n,请画出嵌套n层的谢尔宾斯基地毯F(n)。
中心扩散递归
因为图形是中心对称的,外面的图形是分形,若干个分形包裹着一个正方形9*9,是奇数,有中心。(不要被图片迷惑,自己数一数正方形的行和列)
这里找的固定点就是 每个图形中心正方形的中心,到分形正方形中心的距离、方位,至于中心正方形的打印
#include <iostream>
/*run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
const int N=800;
ll power(int base,int expon){
ll res=1;
while(expon){
if(expon&1)res*=base;
base*=base;
expon>>=1;
}
return res;
}
char map[800][800]={};
int dx[9]={-1,1,0,0,-1,1,-1,1,0};
int dy[9]={0,0,-1,1,-1,-1,1,1,0};
void draw(int n,int x,int y){
if(n==1)return ;
else if(n==2){
map[x][y]='X';
return ;//递归要有结束的临界条件
}
else{
int dis1=power(3,n-2);//上一阶图形的尺寸
//中心 到 四周八个分形的中心 的横竖距离都是 上一阶图形的尺寸
//中心是有图案的,那么对中心也要递归
for(int i=0;i<9;i++){
int cx=x+dx[i]*dis1;
int cy=y+dy[i]*dis1;
draw(n-1,cx,cy);
}
int dis2=power(3,n-3);
//打印中间的正方形,观察n=3情形,每个分形都只是*,中间正方形其实是
//中心点八方距离为dis2 的8个点,也就是8个距离为dis2的分形,和距离为
//dis1的分形意义是一样的,并不能视作正方形意义上来打印
//随着n增大,分形增大,只是由于dis2距离的限制,中心点距离dis2的8个分形
//会有重叠,恰好控制在一个正方形内
for(int i=0;i<9;i++){
int cx=x+dx[i]*dis2;
int cy=y+dy[i]*dis2;
draw(n-1,cx,cy);
}
}
}
int main(int argc, char** argv) {
int n;
cin>>n;
int size=power(3,n-1);
draw(n,400,400);
for(int i=400-size/2-1;i<=400+size/2+1;i++){
for(int j=400-size/2-1;j<=400+size/2+1;j++){
if(i==400-size/2-1||i==400+size/2+1||j==400-size/2-1||j==400+size/2+1)cout<<'+';
else if(map[i][j]=='X')cout<<'X';
else cout<<" ";
}
cout<<endl;
}
return 0;
}
【5】递归三角形图案(简单)。
输入一个正整数n(n<=7),按图1的示例输出相应的由星号
组成的三角形图案。
#include <iostream>
/*run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;
const int N=100;//这题要求打印7度的不过64
ll power(int base,int expon){
ll res=1;
while(expon){
if(expon&1)res*=base;
base*=base;
expon>>=1;
}
return res;
}
char map[100][100]={};//power(2,6)=64
void draw(int n,int x,int y){
if(n==1){
map[x][y]='*';
return ;//递归要有结束的临界条件
}
else{
int interval=power(2,n-2);//上一阶图形的尺寸
//中心 到 四个角的中心 的距离都是 上一阶图形的尺寸
draw(n-1,x,y);//左上
draw(n-1,x,y+interval);//右
draw(n-1,x+interval,y);//左下
}
}
int main(int argc, char** argv) {
int n;
cin>>n;
int size=power(2,n-1);
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
map[i][j] = ' ';
draw(n,1,1);//从(0,0)还是(1,1)没关系,反正都是加上间距
//从左上角的点开始扩散递归,输出时根据尺寸确定图形所在的边界方框
// 高尺寸是size,宽尺寸 size
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++){
cout<<map[i][j]<<" ";
}
cout<<endl;
}
return 0;
}