使用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 functionint main():
define.cpp:8:26: internal compiler error: Segmentation fault
    8 | #define put8(x) if(!vis[x]){vis[x]=1;arr[8]=x;cnt++;vis[x]=0;}
      |                          ^
define.cpp:9:33: note: in expansion of macroput89 | #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 macroset811 | #define put7(x) if(!vis[x]){vis[x]=1;arr[7]=x;set8;vis[x]=0;}
      |                                               ^~~~
define.cpp:12:25: note: in expansion of macroput712 | #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 macroset714 | #define put6(x) if(!vis[x]){vis[x]=1;arr[6]=x;set7;vis[x]=0;}
      |                                               ^~~~
define.cpp:15:25: note: in expansion of macroput615 | #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 macroset617 | #define put5(x) if(!vis[x]){vis[x]=1;arr[5]=x;set6;vis[x]=0;}
      |                                               ^~~~
define.cpp:18:49: note: in expansion of macroput518 | #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 macroset520 | #define put4(x) if(!vis[x]){vis[x]=1;arr[4]=x;set5;vis[x]=0;}
      |                                               ^~~~
define.cpp:21:17: note: in expansion of macroput421 | #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 macroset423 | #define put3(x) if(!vis[x]){vis[x]=1;arr[3]=x;set4;vis[x]=0;}
      |                                               ^~~~
define.cpp:24:41: note: in expansion of macroput324 | #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 macroset326 | #define put2(x) if(!vis[x]){vis[x]=1;arr[2]=x;set3;vis[x]=0;}
      |                                               ^~~~
define.cpp:27:41: note: in expansion of macroput227 | #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 macroset229 | #define put1(x) if(!vis[x]){vis[x]=1;arr[1]=x;set2;vis[x]=0;}
      |                                               ^~~~
define.cpp:30:25: note: in expansion of macroput130 | #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 macroset133 |  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 < 8 n < 8 n<8 的情况,最后终于成功的计算出了 n = 6 n = 6 n=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 a

int 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: D262144

int main() {
	DN(0); ///DO(0); 会导致编译器崩溃 
	printf("%d\n", cnt);
	return 0;
}

在我的电脑上,上述程序能够成功展开至 131072 131072 131072,但是当展开至 262144 262144 262144 时,编译因内存不足失败。

因此可以借助这个程序进行 1 ∼ n    ( n ≤ 131072 ) 1\sim n\;(n\leq 131072) 1n(n131072) 的循环展开。

#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: D262144

int main() {
	int n = 1000;
	DN(0); ///DO(0); 会导致编译器崩溃 
	printf("%d\n", cnt);
	return 0;
}

经过了 20 20 20 多秒的漫长编译,上述程序得出了正确的计算结果 1001 1001 1001

循环展开实践

输入 n n n 个数,输出这 n n n 个数的和。

#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: D262144

int main() {
	int n; scanf("%d", &n);
	#define P input
	DK(1); ///DO(0); 会导致编译器崩溃 
	#undef P
	#define P sum
	DK(1);
	#undef P
	printf("%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 nxt
	LOOP(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 P
	return 0;
}

洛谷 U165361 bubble-sort 冒泡排序测试处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值