使用C++#define进行循环与递归展开
前言
在知乎上看到了一篇神奇的文章:
哪段代码最能代表程序员的暴力美学
深感 C++
宏替换功能前途不可小觑,觉得是时候搞一波事情了(手动滑稽)。
编译器内存超限
最开始的时候,我想写一个解八皇后的程序,过程中先写了一个枚举八个元素全排列的程序:
/// 利用编译器的计算能力来进行常数优化 #include <iostream>
using namespace std;int arr[9], vis[9], cnt = 0; /// 用于枚举 全排列 #define put8(x) if(!vis[x]){vis[x]=1;arr[8]=x;cnt++;vis[x]=0;}
#define set8 put8(1);put8(2);put8(3);put8(4);put8(5);put8(6);put8(7);put8(8);#define put7(x) if(!vis[x]){vis[x]=1;arr[7]=x;set8;vis[x]=0;}
#define set7 put7(1);put7(2);put7(3);put7(4);put7(5);put7(6);put7(7);put7(8);#define put6(x) if(!vis[x]){vis[x]=1;arr[6]=x;set7;vis[x]=0;}
#define set6 put6(1);put6(2);put6(3);put6(4);put6(5);put6(6);put6(7);put6(8);#define put5(x) if(!vis[x]){vis[x]=1;arr[5]=x;set6;vis[x]=0;}
#define set5 put5(1);put5(2);put5(3);put5(4);put5(5);put5(6);put5(7);put5(8);#define put4(x) if(!vis[x]){vis[x]=1;arr[4]=x;set5;vis[x]=0;}
#define set4 put4(1);put4(2);put4(3);put4(4);put4(5);put4(6);put4(7);put4(8);#define put3(x) if(!vis[x]){vis[x]=1;arr[3]=x;set4;vis[x]=0;}
#define set3 put3(1);put3(2);put3(3);put3(4);put3(5);put3(6);put3(7);put3(8);#define put2(x) if(!vis[x]){vis[x]=1;arr[2]=x;set3;vis[x]=0;}
#define set2 put2(1);put2(2);put2(3);put2(4);put2(5);put2(6);put2(7);put2(8); /// 尝试所有可能 #define put1(x) if(!vis[x]){vis[x]=1;arr[1]=x;set2;vis[x]=0;}
#define set1 put1(1);put1(2);put1(3);put1(4);put1(5);put1(6);put1(7);put1(8);int main() {set1;cout << cnt << endl;return 0;
}
我用 g++ define.cpp -o define
命令,将这个文件在 ubuntu 20.04
系统下进行了编译,经过了长达十几分钟的艰苦奋战(电脑内存占用几乎时时刻刻都是 95%
以上),编译器终于缴械投降了。
define.cpp: In function ‘int main()’:
define.cpp:8:26: internal compiler error: Segmentation fault8 | #define put8(x) if(!vis[x]){vis[x]=1;arr[8]=x;cnt++;vis[x]=0;}| ^
define.cpp:9:33: note: in expansion of macro ‘put8’9 | #define set8 put8(1);put8(2);put8(3);put8(4);put8(5);put8(6);put8(7);put8(8);| ^~~~
define.cpp:11:47: note: in expansion of macro ‘set8’11 | #define put7(x) if(!vis[x]){vis[x]=1;arr[7]=x;set8;vis[x]=0;}| ^~~~
define.cpp:12:25: note: in expansion of macro ‘put7’12 | #define set7 put7(1);put7(2);put7(3);put7(4);put7(5);put7(6);put7(7);put7(8);| ^~~~
define.cpp:14:47: note: in expansion of macro ‘set7’14 | #define put6(x) if(!vis[x]){vis[x]=1;arr[6]=x;set7;vis[x]=0;}| ^~~~
define.cpp:15:25: note: in expansion of macro ‘put6’15 | #define set6 put6(1);put6(2);put6(3);put6(4);put6(5);put6(6);put6(7);put6(8);| ^~~~
define.cpp:17:47: note: in expansion of macro ‘set6’17 | #define put5(x) if(!vis[x]){vis[x]=1;arr[5]=x;set6;vis[x]=0;}| ^~~~
define.cpp:18:49: note: in expansion of macro ‘put5’18 | #define set5 put5(1);put5(2);put5(3);put5(4);put5(5);put5(6);put5(7);put5(8);| ^~~~
define.cpp:20:47: note: in expansion of macro ‘set5’20 | #define put4(x) if(!vis[x]){vis[x]=1;arr[4]=x;set5;vis[x]=0;}| ^~~~
define.cpp:21:17: note: in expansion of macro ‘put4’21 | #define set4 put4(1);put4(2);put4(3);put4(4);put4(5);put4(6);put4(7);put4(8);| ^~~~
define.cpp:23:47: note: in expansion of macro ‘set4’23 | #define put3(x) if(!vis[x]){vis[x]=1;arr[3]=x;set4;vis[x]=0;}| ^~~~
define.cpp:24:41: note: in expansion of macro ‘put3’24 | #define set3 put3(1);put3(2);put3(3);put3(4);put3(5);put3(6);put3(7);put3(8);| ^~~~
define.cpp:26:47: note: in expansion of macro ‘set3’26 | #define put2(x) if(!vis[x]){vis[x]=1;arr[2]=x;set3;vis[x]=0;}| ^~~~
define.cpp:27:41: note: in expansion of macro ‘put2’27 | #define set2 put2(1);put2(2);put2(3);put2(4);put2(5);put2(6);put2(7);put2(8); /// �������п���| ^~~~
define.cpp:29:47: note: in expansion of macro ‘set2’29 | #define put1(x) if(!vis[x]){vis[x]=1;arr[1]=x;set2;vis[x]=0;}| ^~~~
define.cpp:30:25: note: in expansion of macro ‘put1’30 | #define set1 put1(1);put1(2);put1(3);put1(4);put1(5);put1(6);put1(7);put1(8);| ^~~~
define.cpp:33:2: note: in expansion of macro ‘set1’33 | set1;| ^~~~
mmap: Cannot allocate memory
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-9/README.Bugs> for instructions.
场面一度十分尴尬。后来我试了试 n<8n < 8n<8 的情况,最后终于成功的计算出了 n=6n = 6n=6 的情况下的全排列。
/// 利用编译器的计算能力来进行常数优化 #include <iostream>
using namespace std;int arr[9], vis[9], cnt = 0; /// 用于枚举 全排列 #define put6(x) if(!vis[x]){vis[x]=1;arr[6]=x;cnt++;vis[x]=0;}
#define set6 put6(1);put6(2);put6(3);put6(4);put6(5);put6(6);#define put5(x) if(!vis[x]){vis[x]=1;arr[5]=x;set6;vis[x]=0;}
#define set5 put5(1);put5(2);put5(3);put5(4);put5(5);put5(6);#define put4(x) if(!vis[x]){vis[x]=1;arr[4]=x;set5;vis[x]=0;}
#define set4 put4(1);put4(2);put4(3);put4(4);put4(5);put4(6);#define put3(x) if(!vis[x]){vis[x]=1;arr[3]=x;set4;vis[x]=0;}
#define set3 put3(1);put3(2);put3(3);put3(4);put3(5);put3(6);#define put2(x) if(!vis[x]){vis[x]=1;arr[2]=x;set3;vis[x]=0;}
#define set2 put2(1);put2(2);put2(3);put2(4);put2(5);put2(6); /// 尝试所有可能 #define put1(x) if(!vis[x]){vis[x]=1;arr[1]=x;set2;vis[x]=0;}
#define set1 put1(1);put1(2);put1(3);put1(4);put1(5);put1(6);int main() {set1;cout << cnt << endl;return 0;
}
总之还是编译了十多秒。
改名
标识符的名字短点的话是否能让编辑过程消耗的内存少一些呢?
/// 利用编译器的计算能力来进行常数优化 #include <iostream>
using namespace std;#define vis v
#define arr aint arr[9], vis[9], cnt = 0; /// 用于枚举 全排列
inline void D(int x) {vis[x] = 0;}
inline void V(int x) {vis[x] = 1;}
inline void C() {cnt ++;}
inline void A(int id, int x) {arr[id] = x;}#define P8(x) if(!vis[x]){V(x);A(8,x);C();D(x);}
#define S8 P8(1);P8(2);P8(3);P8(4);P8(5);P8(6);P8(7);P8(8);#define P7(x) if(!vis[x]){V(x);A(7,x);S8;D(x);}
#define S7 P7(1);P7(2);P7(3);P7(4);P7(5);P7(6);P7(7);P7(8);#define P6(x) if(!vis[x]){V(x);A(6,x);S7;D(x);}
#define S6 P6(1);P6(2);P6(3);P6(4);P6(5);P6(6);P6(7);P6(8);#define P5(x) if(!vis[x]){V(x);A(5,x);S6;D(x);}
#define S5 P5(1);P5(2);P5(3);P5(4);P5(5);P5(6);P5(7);P5(8);#define P4(x) if(!vis[x]){V(x);A(4,x);S5;D(x);}
#define S4 P4(1);P4(2);P4(3);P4(4);P4(5);P4(6);P4(7);P4(8);#define P3(x) if(!vis[x]){V(x);A(3,x);S4;D(x);}
#define S3 P3(1);P3(2);P3(3);P3(4);P3(5);P3(6);P3(7);P3(8);#define P2(x) if(!vis[x]){V(x);A(2,x);S3;D(x);}
#define S2 P2(1);P2(2);P2(3);P2(4);P2(5);P2(6);P2(7);P2(8); /// 尝试所有可能 #define P1(x) if(!vis[x]){V(x);A(1,x);S2;D(x);}
#define S1 P1(1);P1(2);P1(3);P1(4);P1(5);P1(6);P1(7);P1(8);int main() {S1;cout << cnt << endl;return 0;
}
很不幸的是,编译最终还是因为内存不足而失败了。
测试最大展开
#include <cstdio>int cnt = 0;
inline void P(int i) {cnt ++;
}#define D1(i) P(i);
#define D2(i) D1(i);D1(i+1);
#define D4(i) D2(i);D2(i+2);
#define D8(i) D4(i);D4(i+4);
#define DA(i) D8(i);D8(i+8); /// DA: D16
#define DB(i) DA(i);DA(i+16); /// DB: D32
#define DC(i) DB(i);DB(i+32); /// DC: D64
#define DD(i) DC(i);DC(i+64); /// DD: D128
#define DE(i) DD(i);DD(i+128); /// DE: D256
#define DF(i) DE(i);DE(i+256); /// DF: D512
#define DG(i) DF(i);DF(i+512); /// DG: D1024
#define DH(i) DG(i);DG(i+1024); /// DH: D2048
#define DI(i) DH(i);DH(i+2048); /// DI: D4096
#define DJ(i) DI(i);DI(i+4096); /// DJ: D8192
#define DK(i) DJ(i);DJ(i+8192); /// DK: D16384
#define DL(i) DK(i);DK(i+16384); /// DL: D32768
#define DM(i) DL(i);DL(i+32768); /// DM: D65536
#define DN(i) DM(i);DM(i+65536); /// DN: D131072
#define DO(i) DN(i);DN(i+131072); /// DO: D262144int main() {DN(0); ///DO(0); 会导致编译器崩溃 printf("%d\n", cnt);return 0;
}
在我的电脑上,上述程序能够成功展开至 131072131072131072,但是当展开至 262144262144262144 时,编译因内存不足失败。
因此可以借助这个程序进行 1∼n(n≤131072)1\sim n\;(n\leq 131072)1∼n(n≤131072) 的循环展开。
#include <cstdio>int cnt = 0;
inline void P(int i) {cnt ++;
}#define D1(i) P(i);
#define D2(i) D1(i);if(i+1<=n){D1(i+1);}
#define D4(i) D2(i);if(i+2<=n){D2(i+2);}
#define D8(i) D4(i);if(i+4<=n){D4(i+4);}
#define DA(i) D8(i);if(i+8<=n){D8(i+8);} /// DA: D16
#define DB(i) DA(i);if(i+16<=n){DA(i+16);} /// DB: D32
#define DC(i) DB(i);if(i+32<=n){DB(i+32);} /// DC: D64
#define DD(i) DC(i);if(i+64<=n){DC(i+64);} /// DD: D128
#define DE(i) DD(i);if(i+128<=n){DD(i+128);} /// DE: D256
#define DF(i) DE(i);if(i+256<=n){DE(i+256);} /// DF: D512
#define DG(i) DF(i);if(i+512<=n){DF(i+512);} /// DG: D1024
#define DH(i) DG(i);if(i+1024<=n){DG(i+1024);} /// DH: D2048
#define DI(i) DH(i);if(i+2048<=n){DH(i+2048);} /// DI: D4096
#define DJ(i) DI(i);if(i+4096<=n){DI(i+4096);} /// DJ: D8192
#define DK(i) DJ(i);if(i+8192<=n){DJ(i+8192);} /// DK: D16384
#define DL(i) DK(i);if(i+16384<=n){DK(i+16384);} /// DL: D32768
#define DM(i) DL(i);if(i+32768<=n){DL(i+32768);} /// DM: D65536
#define DN(i) DM(i);if(i+65536<=n){DM(i+65536);} /// DN: D131072
#define DO(i) DN(i);if(i+131072<=n){DN(i+131072);} /// DO: D262144int main() {int n = 1000;DN(0); ///DO(0); 会导致编译器崩溃 printf("%d\n", cnt);return 0;
}
经过了 202020 多秒的漫长编译,上述程序得出了正确的计算结果 100110011001。
循环展开实践
输入 nnn 个数,输出这 nnn 个数的和。
#include <cstdio>int cnt = 0, A[10001];
inline void input(int i) {scanf("%d", &A[i]);
}
inline void sum(int i) {cnt += A[i];
}#define D1(i) P(i);
#define D2(i) D1(i);if(i+1<=n){D1(i+1);}
#define D4(i) D2(i);if(i+2<=n){D2(i+2);}
#define D8(i) D4(i);if(i+4<=n){D4(i+4);}
#define DA(i) D8(i);if(i+8<=n){D8(i+8);} /// DA: D16
#define DB(i) DA(i);if(i+16<=n){DA(i+16);} /// DB: D32
#define DC(i) DB(i);if(i+32<=n){DB(i+32);} /// DC: D64
#define DD(i) DC(i);if(i+64<=n){DC(i+64);} /// DD: D128
#define DE(i) DD(i);if(i+128<=n){DD(i+128);} /// DE: D256
#define DF(i) DE(i);if(i+256<=n){DE(i+256);} /// DF: D512
#define DG(i) DF(i);if(i+512<=n){DF(i+512);} /// DG: D1024
#define DH(i) DG(i);if(i+1024<=n){DG(i+1024);} /// DH: D2048
#define DI(i) DH(i);if(i+2048<=n){DH(i+2048);} /// DI: D4096
#define DJ(i) DI(i);if(i+4096<=n){DI(i+4096);} /// DJ: D8192
#define DK(i) DJ(i);if(i+8192<=n){DJ(i+8192);} /// DK: D16384
#define DL(i) DK(i);if(i+16384<=n){DK(i+16384);} /// DL: D32768
#define DM(i) DL(i);if(i+32768<=n){DL(i+32768);} /// DM: D65536
#define DN(i) DM(i);if(i+65536<=n){DM(i+65536);} /// DN: D131072
#define DO(i) DN(i);if(i+131072<=n){DN(i+131072);} /// DO: D262144int main() {int n; scanf("%d", &n);#define P inputDK(1); ///DO(0); 会导致编译器崩溃 #undef P#define P sumDK(1);#undef Pprintf("%d\n", cnt);return 0;
}
程序可以通过编译并得出正确的结果。
无循环冒泡排序
光是想一想就觉得很爽。
#include <cstdio>#define D1(i) P(i);
#define D2(i) D1(i);if(i+1<=n){D1(i+1);}
#define D4(i) D2(i);if(i+2<=n){D2(i+2);}
#define D8(i) D4(i);if(i+4<=n){D4(i+4);}
#define DA(i) D8(i);if(i+8<=n){D8(i+8);} /// DA: D16
#define DB(i) DA(i);if(i+16<=n){DA(i+16);} /// DB: D32
#define DC(i) DB(i);if(i+32<=n){DB(i+32);} /// DC: D64
#define DD(i) DC(i);if(i+64<=n){DC(i+64);} /// DD: D128
#define DE(i) DD(i);if(i+128<=n){DD(i+128);} /// DE: D256
#define DF(i) DE(i);if(i+256<=n){DE(i+256);} /// DF: D512
#define DG(i) DF(i);if(i+512<=n){DF(i+512);} /// DG: D1024
#define DH(i) DG(i);if(i+1024<=n){DG(i+1024);} /// DH: D2048
#define DI(i) DH(i);if(i+2048<=n){DH(i+2048);} /// DI: D4096
#define DJ(i) DI(i);if(i+4096<=n){DI(i+4096);} /// DJ: D8192
#define DK(i) DJ(i);if(i+8192<=n){DJ(i+8192);} /// DK: D16384
#define DL(i) DK(i);if(i+16384<=n){DK(i+16384);} /// DL: D32768
#define DM(i) DL(i);if(i+32768<=n){DL(i+32768);} /// DM: D65536
#define DN(i) DM(i);if(i+65536<=n){DM(i+65536);} /// DN: D131072
#define DO(i) DN(i);if(i+131072<=n){DN(i+131072);} /// DO: D262144#define LOOP(i) for(int t=i;t<=n;t+=1024){DG(t);} /// 实现循环展开 int n;int cnt = 0, A[10001];inline void input(int i) {scanf("%d", &A[i]);
}
void put(int x) {if(x > 9) put(x/10);putchar('0' + x%10);
}
inline void output(int i) {put(A[i]); putchar(' '); /// 输出优化
}void nxt(int i) {if(A[i-1] > A[i]) {int t = A[i-1]; A[i-1] = A[i]; A[i] = t;}
}void line(int i) { /// 进行一轮冒泡 #define P nxtLOOP(2);#undef P
}int main() {scanf("%d", &n);#define P input /// 输入数据 LOOP(1);#undef P#define P line /// 冒泡 LOOP(1);#undef P#define P output /// 输出 LOOP(1);#undef Preturn 0;
}
洛谷 U165361 bubble-sort 冒泡排序测试处
使用C++#define进行循环与递归展开相关推荐
- 入门C语言第三话:数组之实战篇——扫雷(进阶版——图形化界面,递归展开,播放音乐与音效,标记取消雷,记录雷的个数,鼠标点击,文末附有完整代码)
文章目录 前言 每日鸡汤 基本思路 衔接基础班扫雷 准备阶段 正文 一.雷盘信息的存储 1.设置雷盘11*11与初始化 2.放置雷 3.放置雷周围的信息 二.图形化界面 1.创建与初始化窗口 2.加载 ...
- 硬币找零——背包问题,以及循环、递归、动规共通性
在这个题目的基础上,我了解了一下这几个"编程写法",并对循环.递归.dp有了新的想法.从原理上,这几个想法都是大事化小.小事化了.只不过方向不同罢了. 根据The Algorith ...
- C语言实现扫雷(内含递归展开)
游戏规则: (1)取胜条件:由玩家找到9x9地图中的10个雷即胜利 (2)结束条件:取胜或者不幸触雷 (3)补充细则:在判断出不是雷的方块上按下左键,可以打开该方块.如果方块上出现数字,则该数字表示其 ...
- 【C语言扫雷游戏详解及如何实现递归展开】
提示:全文已采用物理深色模式,请放心观看 文章目录 一.整体框架 1.设计思路 2.实现细节 二.主要函数 1.打印棋盘 2.递归展开 三.其他函数 一.整体框架 1.设计思路 基础难度的扫雷游戏含有 ...
- C语言 扫雷(含递归展开)
目录 前言 一.设计思路 基本的构思方向 准备基本框架 二.函数功能设置 菜单界面 主函数 初始化 显示棋盘 设置雷 计算周围雷数 排雷 总体game函数 后续优化 1.标记雷 2.递归展开 3.防止 ...
- 算法设计与分析之循环与递归
前言: 循环与递归可以说是算法设计中最基本但却也是最重要的工具方法.循环和递归对于学习过高级程序设计语言的人来说都并不陌生,但还是有必要仔细的探究一下循环和递归之间的相似和区别.循环与递归最大的相似之 ...
- 如何利用循环代替递归以防止栈溢出(译)
摘要:我们经常会用到递归函数,但是如果递归深度太大时,往往导致栈溢出.而递归深度往往不太容易把握,所以比较安全一点的做法就是:用循环代替递归.文章最后的原文里面讲了如何用10步实现这个过程,相当精彩. ...
- 记一次数据结构与算法作业:利用循环和递归输出1-N的正整数的程序分析比较
随便记录一次数据结构与算法的分析作业,内容为分析循环和递归实现输出1-N的正整数的对比.从时间和空间上分析了两种方式实现的递归方法和循环区别. 一.数据记录图表 二.分析 第一张图表制作时由于在打游戏 ...
- 【深度学习(deep learning)】花书第10章 序列建模:循环和递归网络 读书笔记
[深度学习(deep learning)]花书第10章 序列建模:循环和递归网络 读书笔记 第10章 序列建模:循环和递归网络 [深度学习(deep learning)]花书第10章 序列建模:循环和 ...
最新文章
- 新兴AI解决方案将越来越依赖于嵌入式视觉技术
- 利用 CSS selector 改变悬停表格样式
- Hostonly cookie是什么鬼?
- Codeforces 671C Ultimate Weirdness of an Array 线段树 (看题解)
- html 圆环实现多种颜色,SVG实现多彩圆环倒计时效果的示例代码
- Kronos Research推出结合WOO质押机制的新资管产品规模已达1500万美元
- solve stiffness matrix in matlab
- Django【设计】可插拔的插件方式实现
- oracle的unload,Oracle 业务数据unload恢复过程
- matlab为uigetfile设置默认打开地址(打开路径)
- Maven第9篇:多环境构建
- STM32 Bootloader开发记录 3 固件签名校验
- 黄金分割法求函数最小值
- 明解C语言入门篇_第10章_指针
- win10修改中文用户名
- jquery 遍历java对象_jquery遍历数组、对象
- iOS高性能Model转换框架----YYModel学习
- bitbucket安装
- java ocx调用_Java调用ocx控件以及dll
- 如果想进入IT行业工作,需要做准备吗?
热门文章
- 关于前置++和后置++的小题
- 冷笑话精选冷兔一句话冷笑话精选,脑筋急转弯冷笑话精选段子小明滚出去
- 怎么优雅的在主线程获取子线程的返回值
- 亚马逊防关联(收藏 一生受用)
- [论文笔记] the book of why 1.因果推断三步骤:关联、干预、反事实推断
- 【校招VIP】线上实习 推推 书籍详情模块 产品脑图周最佳
- 关于dojo的build系统
- 染布厂ERP新员工培训流程(中山宏瑞计算机科技有限公司编辑)
- Visualsfm与meshlab三维重建
- php stripslashes()函数,PHP stripslashes 函数