用C++代码生成一面五星红旗

原始问题的背景和描述

这个问题的来源另外一篇博客里面已经提到过,它的来源是StackExchange上的原始问题及答案。

比赛基于下面的基础代码 ,如果原始链接无法访问,可以备用我的另一CSDN博客中引用给出的代码。已提到,感兴趣的是基于这个代码如何生成一面五星红旗。

红旗的设计图如下,也在我的上一篇博客搜索给出过。
用C++代码生成一面五星红旗_第1张图片

计算和代码

首先,五星红旗颜色简单,只有红色黄色两种。从加法色(RGB)1的角度看,黄色就是红色加绿色。所以,背景是红色很单一,关键就是如何填充五角星区域内的绿色,即改写unsigned char GR(int i,int j)函数。

考虑到问题不是那么简单直接,先不考虑字符数仅为140个(一twitter)的限制,先做出来再说。

我打算用Mathematica完成逻辑部分。代码如下:

ClearAll["Global`*"];
sol = Solve[{(x - x0)^2 + (y - y0)^2 == 
      r^2, (y - y0) (x1 - x0) == (x - x0) (y1 - y0)}, {x, y}] // 
   FullSimplify;
p1 = {x, y} /. sol[[2]] // Simplify;
a = {x0, y0};

p2 = RotationTransform[2 Pi/5, a][p1] // Simplify;
p3 = RotationTransform[2 Pi/5, a][p2] // Simplify;
p4 = RotationTransform[2 Pi/5, a][p3] // Simplify;
p5 = RotationTransform[2 Pi/5, a][p4] // Simplify;

q1 = {x, y} /. sol[[1]] // Simplify;
q2 = RotationTransform[2 Pi/5, a][q1] // Simplify;
q3 = RotationTransform[2 Pi/5, a][q2] // Simplify;
q4 = RotationTransform[2 Pi/5, a][q3] // Simplify;
q5 = RotationTransform[2 Pi/5, a][q4] // Simplify;

(P1 = Limit[
       Limit[{p1, p2, p3, p4, p5} /. {x1 -> 100, y1 -> 200, r -> 60}, 
        x0 -> 100], y0 -> 100] // Simplify // N // Round);
(P2 = Limit[
       Limit[{q1, q2, q3, q4, q5} /. {x1 -> 100, y1 -> 100, r -> 20}, 
        x0 -> 200], y0 -> 180] // Simplify // N // Round);
(P3 = Limit[
       Limit[{q1, q2, q3, q4, q5} /. {x1 -> 100, y1 -> 100, r -> 20}, 
        x0 -> 200], y0 -> 40] // Simplify // N // Round);
(P4 = Limit[
       Limit[{q1, q2, q3, q4, q5} /. {x1 -> 100, y1 -> 100, r -> 20}, 
        x0 -> 240], y0 -> 140] // Simplify // N // Round);
(P5 = Limit[
       Limit[{q1, q2, q3, q4, q5} /. {x1 -> 100, y1 -> 100, r -> 20}, 
        x0 -> 240], y0 -> 80] // Simplify // N // Round);

line[P_] := (P[[1, 1]] - P[[2, 1]]) y + P[[2, 1]] P[[1, 2]] - 
  P[[1, 1]] P[[2, 2]] + x (P[[2, 2]] - P[[1, 2]])
lines[P_] := {line@P[[{1, 3}]], line@P[[{3, 5}]], line@P[[{5, 2}]], 
  line@P[[{2, 4}]], line@P[[{4, 1}]]}

pc01 = lines[P1];
pc02 = lines[P2];
pc03 = lines[P3];
pc04 = lines[P4];
pc05 = lines[P5];

innerQ[rp_, pc_] := 
 Not[(pc[[1]] /. rp) >= 0 && (pc[[3]] /. rp) >= 0] && 
  Not[(pc[[1]] /. rp) >= 0 && (pc[[4]] /. rp) >= 0] && 
  Not[(pc[[2]] /. rp) >= 0 && (pc[[4]] /. rp) >= 0] && 
  Not[(pc[[2]] /. rp) >= 0 && (pc[[5]] /. rp) >= 0] && 
  Not[(pc[[3]] /. rp) >= 0 && (pc[[5]] /. rp) >= 0]

testQ[pc01_] := 
 Not[(pc01[[1]]) >= 0 && (pc01[[3]]) >= 0] && 
  Not[(pc01[[1]]) >= 0 && (pc01[[4]]) >= 0] && 
  Not[(pc01[[2]]) >= 0 && (pc01[[4]]) >= 0] && 
  Not[(pc01[[2]]) >= 0 && (pc01[[5]]) >= 0] && 
  Not[(pc01[[3]]) >= 0 && (pc01[[5]]) >= 0]

Print[testQ@pc01 // CForm]
Print[""]
Print[testQ@pc02 // CForm]
Print[""]
Print[testQ@pc03 // CForm]
Print[""]
Print[testQ@pc04 // CForm]
Print[""]
Print[testQ@pc05 // CForm]

上面这段代码直接输出五个五角星区域的逻辑判断为C/C++代码形式,几乎不用什么修改。唯一的缺点是太长了。把它们逻辑并(OR,|| )起来就是GR函数中需要的部分2。现在回头再看上面这些Mathematica代码,感觉不但冗余,而且拙劣;不过也能实现功能

修改好的完整的C++代码是这样的:

// NOTE: compile with g++ filename.cpp -std=c++11

#include <iostream>
#include <cmath>
#include <cstdlib>
#define DIM1 600
#define DIM2 400
#define DM1 (DIM1-1)
#define DM2 (DIM2-1)
#define _sq(x) ((x)*(x)) // square
#define _cb(x) abs((x)*(x)*(x)) // absolute value of cube
#define _cr(x) (unsigned char)(pow((x),1.0/3.0)) // cube root

unsigned char GR(int,int);
unsigned char BL(int,int);

unsigned char RD(int i,int j){
// YOUR CODE HERE
    return 255;
}
unsigned char GR(int i,int j){
    int x=i,y=j;
// YOUR CODE HERE
return (!(-9500+109*x-35*y>=0&&9234-114*y>=0)&&!(-9500+109*x-35*y>=0&&-18128+68*x+92*y>=0)&&!(-4528-68*x+92*y>=0&&-18128+68*x+92*y>=0)&&!(-4528-68*x+92*y>=0&&12300-109*x-35*y>=0)&&!(9234-114*y>=0&&12300-109*x-35*y>=0))||(!(3656+13*x-36*y>=0&&10074-30*x-24*y>=0)&&!(3656+13*x-36*y>=0&&-8188+38*x+2*y>=0)&&!(-8897+10*x+37*y>=0&&-8188+38*x+2*y>=0)&&!(-8897+10*x+37*y>=0&&2176-31*x+21*y>=0)&&!(10074-30*x-24*y>=0&&2176-31*x+21*y>=0))||(!(6374-28*x-25*y>=0&&5407-32*x+19*y>=0)&&!(6374-28*x-25*y>=0&&-1830+15*x-35*y>=0)&&!(-7784+37*x+4*y>=0&&-1830+15*x-35*y>=0)&&!(-7784+37*x+4*y>=0&&-3314+8*x+37*y>=0)&&!(5407-32*x+19*y>=0&&-3314+8*x+37*y>=0))||(!(5572-2*x-38*y>=0&&10043-37*x-10*y>=0)&&!(5572-2*x-38*y>=0&&-7044+36*x-13*y>=0)&&!(-10206+24*x+30*y>=0&&-7044+36*x-13*y>=0)&&!(-10206+24*x+30*y>=0&&456-21*x+31*y>=0)&&!(10043-37*x-10*y>=0&&456-21*x+31*y>=0))||(!(6562-17*x-34*y>=0&&8406-38*x+6*y>=0)&&!(6562-17*x-34*y>=0&&-4563+27*x-27*y>=0)&&!(-9758+34*x+17*y>=0&&-4563+27*x-27*y>=0)&&!(-9758+34*x+17*y>=0&&-1834-6*x+38*y>=0)&&!(8406-38*x+6*y>=0&&-1834-6*x+38*y>=0))?255:0;
}
unsigned char BL(int i,int j){
// YOUR CODE HERE
    return 0;
}

void pixel_write(int,int);
FILE *fp;
int main(){
fp = fopen("MathPic.ppm","wb");
fprintf(fp, "P6\n%d %d\n255\n", DIM1, DIM2);
for(int j=0;j<DIM2;j++)
for(int i=0;i<DIM1;i++)
pixel_write(i,j);
fclose(fp);
return 0;
}
void pixel_write(int i, int j){
static unsigned char color[3];
color[0] = RD(i,j)&255;
color[1] = GR(i,j)&255;
color[2] = BL(i,j)&255;
fwrite(color, 1, 3, fp);
}

它的输出已经是一面五星红旗了。用XnViewConvert.ppm.png格式如下:
用C++代码生成一面五星红旗_第2张图片

遗憾的是,我所修改的GR函数中的逻辑判断太长,远超一个twitter和140字符的要求。单纯把一些长的表达式用宏定义的方式来压缩,可行性很差:

unsigned char GR(int i,int j){
// YOUR CODE HERE
#define A 6562-17*i-34*j>=0
#define B 8406-38*i+6*j>=0
#define C -4563+27*i-27*j>=0
#define D -9758+34*i+17*j>=0
#define E -1834-6*i+38*j>=0
#define F -10206+24*i+30*j>=0
#define G 456-21*i+31*j>=0
#define H -10206+24*i+30*j>=0
#define I -7044+36*i-13*j>=0
#define J 10043-37*i-10*j>=0
#define K 5572-2*i-38*j>=0
#define L -9500+109*i-35*j>=0
#define M 9234-114*j>=0
#define N -18128+68*i+92*j>=0
#define O -4528-68*i+92*j>=0
#define P 12300-109*i-35*j>=0
#define Q 3656+13*i-36*j>=0
#define R 10074-30*i-24*j>=0
#define S -8188+38*i+2*j>=0
#define T -8897+10*i+37*j>=0
#define U 2176-31*i+21*j>=0
#define V 6374-28*i-25*j>=0
#define W 5407-32*i+19*j>=0
#define X -1830+15*i-35*j>=0
#define Y -7784+37*i+4*j>=0
#define Z -3314+8*i+37*j>=0
return (!(L&&M)&&!(L&&N)&&!(O&&N)&&!(O&&P)&&!(M&&P))||(!(Q&&R)&&!(Q&&S)&&!(T&&S)&&!(T&&U)&&!(R&&U))||(!(V&&W)&&!(V&&X)&&!(Y&&X)&&!(Y&&Z)&&!(W&&Z))||(!(K&&J)&&!(K&&I)&&!(F&&I)&&!(H&&G)&&!(J&&G))||(!(A&&B)&&!(A&&C)&&!(D&&C)&&!(D&&E)&&!(B&&E))?255:0;
}

需要一些特殊技巧来压缩,目前我还做不到。或者能证明无法实现?

本来我还指望对五角星的边界作一些近似和优化或者能够把逻辑表达式简化,从而实现”140”字符以内表达,后来做了些功课,发现非凸多边形的情况下,进行逻辑判断本来就是一个比较难的问题. 所以, 除非有超出当前这些限制的极其冷门的技巧,否则只能望推兴叹了.

现在我想到的基于凹多边形卷绕数的算法可以把字符进一步压缩,但仍大于140*3,而且不容易刚好填入三个函数内

// NOTE: compile with g++ filename.cpp -std=c++11
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <vector>
//#include <functional>
#define DIM1 600
#define DIM2 400
#define DM1 (DIM1-1)
#define DM2 (DIM2-1)
#define _sq(x) ((x)*(x)) // square
#define _cb(x) abs((x)*(x)*(x)) // absolute value of cube
#define _cr(x) (unsigned char)(pow((x),1.0/3.0)) // cube root

#define pi 3.14159
#define eps 2.02e-16
double arct(double x,double y){
    return abs(x)>eps?(x>eps?atan(y/x):(y>0?atan(y/x)+pi:atan(y/x)-pi)):(y>0?pi/2:-pi/2);
}

bool inQ(int i,int j,int* px){
    double art[5],aro[5];
    for(int k=0;k<5;k++){
        art[k]=arct((double)(px[k]-i),(double)(px[k+5]-j));//
    }
    double total=0.;
    for(int k=0;k<5;k++){
    aro[k]=art[(k+1)%5]-art[k];
        while(aro[k]<-pi) aro[k]+=pi;
        while(aro[k]>pi) aro[k]-=pi;
    total+=aro[k];
    }
    return abs(total)>0.5;
    };

unsigned char GR(int,int);
unsigned char BL(int,int);

unsigned char RD(int i,int j){
// YOUR CODE HERE
    return 255;
}
unsigned char GR(int i,int j){
// YOUR CODE HERE
            int p1[10]={100, 135, 43, 157, 65, 40, 149, 81, 81, 149},
        p2[10]={184, 220, 183, 207, 205, 168, 181, 191, 161, 199},
        p3[10]={183, 208, 204, 185, 220, 50, 22, 59, 27, 42},
        p4[10]={221, 259, 229, 239, 252, 135, 133, 157, 120, 156},
        p5[10]={220, 254, 237, 231, 258,83, 66, 100, 62, 89};
    return inQ(i,j,p1)||inQ(i,j,p2)||inQ(i,j,p3)||inQ(i,j,p4)||inQ(i,j,p5)?255:0;
}
unsigned char BL(int i,int j){
// YOUR CODE HERE
    return 0;
}

void pixel_write(int,int);
FILE *fp;
int main(){
fp = fopen("MathPic.ppm","wb");
fprintf(fp, "P6\n%d %d\n255\n", DIM1, DIM2);
for(int j=0;j<DIM2;j++)
for(int i=0;i<DIM1;i++)
pixel_write(i,j);
fclose(fp);
system("pause");
return 0;
}
void pixel_write(int i, int j){
static unsigned char color[3];
color[0] = RD(i,j)&255;
color[1] = GR(i,j)&255;
color[2] = BL(i,j)&255;
fwrite(color, 1, 3, fp);
}

所以,我是来征集一个代码压缩技巧的。

……
问谁又能做到?
……
——Beyond

  1. 【如果对“加法色”和“减法色”不了解也没啥,直接查不同颜色对应的RGB值即可,黄色就是0xFFFF00;我写这篇博客犯的两个错误之一就是把加减法搞错,不过这个不影响结果】 ↩
  2. 【我写这段博客犯的两个错误之二是,因为图像的坐标 y 轴方向与五星红旗通常的坐标方向相反,进行坐标变换时不小心,导致四个小五星都有一个角正背对着大五星!这是一个严重的错误,惊出了我一身冷汗。】来见识下我不小心弄出来的这种罕见的红旗,不仔细看还真发现不了问题,惭愧用C++代码生成一面五星红旗_第3张图片

你可能感兴趣的:(C++,c,CSDN博客,Mathematica,设计图)