《编程之美》读书笔记22: 1.16 24点游戏(补充)
给定n个数,能否只通过加减乘除计算得到24?
书上给出的最后一种解法,通过使用集合记录中间结果来减少冗余计算。本以为,程序会占用大量的内存,用一个极端的例子(13, 773, 28, 98, 731, 1357,97357246这7个数)测试了一下实现的程序,发现程序竟然占用了1G以上的内存(无论集合的实现采用STL中的set还是unordered_set),但后来,取7个均在1到13之间的数,再重新测试了下发现,程序所占用的内存比想像的小的多,也就几兆。
对数值都在1到13之间的n个数的所有组合进行判断。在n等于4时,实现的程序约过1秒就给出了结果,而n等于5时,程序要运行58秒,效率实在差,可以通过这几方面优化:
① 保存每个集合的子集合的编号:
对给定的n,共有1到2^n – 1个集合,每个集合的子集合编号是固定的,但原程序每计算一个n个数的组合,都要对这些子集合编号计算一遍,可以保存每个集合的子集合编号,减少大量的重复计算。
② 改进计算子集合编号的算法:
原程序的算法的时间复杂度是O(4^n),在n较大时,相当慢。
③ 对最后一个集合是否含有24的判断:
原程序要遍历该集合所有的元素,效率比较差,可以考虑,在将两个集合合并到该集合时,只对取出的两个元素的计算结果是否为24进行判断,这样不仅省去最后的遍历,而且不用将结果插入到最后的那个集合中,省去了大量操作。
采用①和③两种改进方法后,程序运行时间由原来的58秒缩短到了14秒,但这还不能让人满意。对2,3,5,6,8这5个数,如果用书上的第一种方法,可以只调用4次函数就可以得到结果:2+3+5+6+8=24,但用集合的方法来处理,却要对所有的集合进行子集合合并后才能给出结果,这造成了效率极差,可以这样改进该算法:
初始有n个集合:每一次任意取出2个集合,合并后,放回去,再取出任意2个集合,重复前面的操作,直到只剩下一个集合为止。
例如:初始有5个数,把这5个数分别放到5个集合,并分别编号为:1、2、4、8、16。任意取出2个集合,假设为1和4,将1和4合并,得到编号为5(=1+4)的集合,剩下的集合为:5、2、16、8,再取出2个,假设为5和8,合并后,得到13、2、16,再取2个,假设为13和16,合并后得到29、2,当剩下2个集合时,可以直接对这两个集合间的的计算结果是否为24进行判断,直接得出结果,省去不必要的合并(合并后再判断是否有元素近似等于24,程序运行时间8s多,而直接对计算结果判断,程序只要运行1s多)。
优化后的程序,只要运行1s多。但其效率还是不如书上的第一种方法的改进版,仔细想想,n越大,集合的元素也就越多,两个集合之间的合并,就越耗时间。而且采用集合保存中间结果,表面上减少了重复状态,会提高效率,但实际上,由于采用了集合,就多了很多不必要的计算,(比如,对2+3+5+6+8=24,最少只要4次计算就能得出结果,采用集合合并后,则要计算几百次(约为6^4)),再加上实现集合所采用的数据结构的开销,效率高不了。
![](//www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
24_set_1
#include <iostream>
2
#include <fstream>
3
#include <unordered_set>
4
#include <vector>
5
#include <ctime>
6
#include <cmath>
7
using namespace std;
8
typedef unordered_set<double> mset;
9![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
10
unsigned long long all_size=0;
11
unsigned long long big_size=0;
12![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
13![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
14
bool calc(int src[], size_t N, double M = 24.0)
15![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
16
if (N == 0 || src == NULL) return false;
17
mset result[1 << N];
18
for (size_t i = 0; i < N; ++i) result[1<<i].insert((double)src[i]);
19![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i =1; i < (1<<N); ++i) {
20![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t j = 1; j <= (i >> 1); ++j) {
21
if ((i & j) != j) continue;
22![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (mset::iterator p = result[j].begin(); p != result[j].end(); ++p) {
23
double va = *p;
24
size_t k = i ^ j;
25![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (mset::iterator q = result[k].begin(); q != result[k].end(); ++q) {
26
double vb = *q;
27
result[i].insert(va + vb);
28
result[i].insert(va - vb);
29
result[i].insert(vb - va);
30
result[i].insert(va * vb);
31
if (vb != 0.0) result[i].insert(va / vb);
32
if (va != 0.0) result[i].insert(vb / va);
33
}
34
}
35
}
36
}
37
38
size_t j = (1 << N) - 1;
39
all_size=0;
40
big_size=result[j].size();
41
for (size_t i =1; i < (1<<N); ++i) all_size += result[i].size();
42![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (mset::iterator p = result[j].begin(); p != result[j].end(); ++p) {
43
if (fabs(M - *p) < 1e-9) return true;
44
}
45
return false;
46
}
47![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
48![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
49
void calc_range(int first, int last,size_t N, double M = 24, string filename="nul")
50![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
51
if (N ==0 || first >= last || filename.empty()) return;
52
clock_t ta = clock();
53
vector<int> vv(N, first);
54
int *end = &vv[N-1], *p = end, *arr = &vv[0];
55
ofstream ofs(filename.c_str());
56
size_t count = 0;
57
size_t count_b = 0;
58
unsigned long long max_big_size =0, max_all_size = 0;
59![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(true){
60
++count_b;
61![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (calc(arr,N, M)) {
62
ofs.width(4);
63
ofs<< ++count << " ";
64![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i = 0; i < N; ++i) {
65
ofs.width(2);
66
ofs << arr[i]<< " ";
67
}
68
ofs << "\n";
69
if (max_big_size < big_size) max_big_size = big_size;
70
if (max_all_size < all_size) max_all_size = all_size;
71
}
72
while (p >= arr && *p >= last) --p;
73
if (p < arr) break;
74
int tmp = ++*p;
75
while (p < end) *++p = tmp;
76
}
77
ofs.close();
78
const char sep[] = "/";
79
cout << "total: " << count << sep << count_b << "\n"
80
<< max_big_size << sep << max_all_size << "\n"
81
<< "time: " << clock() - ta << "\n\n";
82
}
83![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
84
int main()
85![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
86
calc_range(1,13,5,24,"nul");
87
cin.get();
88
}
89![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
![](//www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
24_set_2.cpp![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
2
#include <iostream>
3
#include <fstream>
4
#include <vector>
5
#include <deque>
6
#include <unordered_set>
7
#include <ctime>
8
#include <cmath>
9
using namespace std;
10![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
11
typedef unordered_set<double> MSET;
12
typedef MSET::iterator MSETI;
13
typedef deque<size_t>::iterator DQI;
14![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
15
const size_t N = 5;
16
const double M = 24;
17
static MSET result[1<<N];
18
//static vector<MSET> result(1<<N);
19
//static vector<MSET> result(1<<N, MSET(256));
20
static deque<size_t> dq[1<<N];
21
size_t max_size = 0;
22![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
23
inline void init()
24![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
25
size_t count = 0;
26![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i =1; i < (1<<N); ++i) {
27![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t j = 1; j <= (i >> 1); ++j) {
28
if ((i & j) != j) continue;
29
dq[i].push_back(j);
30
++count;
31
}
32
}
33
cout << "total sets: " << count << "\n";
34
}
35![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
36
inline bool isequal_m(double na)
37![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
38
const double zero = 1e-9;
39
if (fabs(na - M) < zero) return true;
40
return false;
41
}
42![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
43
bool calc(int src[])
44![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
45
if (N == 0 || src == NULL) return false;
46
const size_t ii = (1 << N) - 1;
47
for (size_t i =1; i <= ii; ++i) result[i].clear();
48
for (size_t i = 0; i < N; ++i) result[1<<i].insert((double)src[i]);
49![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i = 1; i < ii; ++i) {
50![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (DQI r = dq[i].begin(), re = dq[i].end(); r != re; ++r){
51
size_t j = *r;
52![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI p = result[j].begin(), pe = result[j].end(); p != pe; ++p) {
53
double va = *p;
54
size_t k = i - j;
55![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI q = result[k].begin(),qe = result[k].end(); q != qe; ++q) {
56
double vb = *q;
57
result[i].insert(va + vb);
58
result[i].insert(va - vb);
59
result[i].insert(vb - va);
60
result[i].insert(va * vb);
61![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (va != 0 && vb != 0) {
62
result[i].insert(va / vb);
63
result[i].insert(vb / va);
64
}
65
}
66
}
67
}
68
}
69![](//www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
70![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t j = 1; j < ii; ++j) {
71
if (max_size < result[j].size()) max_size = result[j].size();
72
}
73
74![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (DQI r = dq[ii].begin(), re = dq[ii].end(); r != re; ++r) {
75
size_t j = *r;
76![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI p = result[j].begin(), pe = result[j].end(); p != pe; ++p) {
77
double va = *p;
78
size_t k = ii - j;
79![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI q = result[k].begin(),qe = result[k].end(); q != qe; ++q) {
80
double vb = *q;
81
if (isequal_m(va + vb)) return true;
82
if (isequal_m(va - vb)) return true;
83
if (isequal_m(vb - va)) return true;
84
if (isequal_m(va * vb)) return true;
85![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (va != 0 && vb != 0) {
86
if (isequal_m(va / vb)) return true;
87
if (isequal_m(vb / va)) return true;
88
}
89
}
90
}
91
}
92
return false;
93
}
94![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
95![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
96
void calc_range(int first, int last, string filename="nul")
97![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
98
if (N ==0 || first >= last || filename.empty()) return;
99
clock_t ta = clock();
100
init();
101
vector<int> vv(N, first);
102
int *end = &vv[N-1], *p = end, *arr = &vv[0];
103
ofstream ofs(filename.c_str());
104
size_t count = 0;
105
size_t count_b = 0;
106![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(true){
107
++count_b;
108![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (calc(arr)) {
109
ofs.width(4);
110
ofs<< ++count << " ";
111![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i = 0; i < N; ++i) {
112
ofs.width(2);
113
ofs << arr[i]<< " ";
114
}
115
ofs << "\n";
116
}
117
while (p >= arr && *p >= last) --p;
118
if (p < arr) break;
119
int tmp = ++*p;
120
while (p < end) *++p = tmp;
121
}
122
ofs.close();
123
const char sep[] = "/";
124
cout << "total: " << count << sep << count_b << "\n"
125
<< "max_size: " << max_size << "\n"
126
<< "time: " << clock() - ta << "\n\n";
127
128
}
129![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
130
int main()
131![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
132
calc_range(1,13,"nul");
133
cin.get();
134
}
135![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
![](//www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
24_set_3.cpp![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
2
#include <iostream>
3
#include <fstream>
4
#include <vector>
5
#include <unordered_set>
6
#include <ctime>
7
#include <cmath>
8
using namespace std;
9![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
10
typedef unordered_set<double> MSET;
11
typedef MSET::const_iterator MSETI;
12![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
13
const size_t N = 5;
14
const double M = 24;
15
static MSET result[1<<N];
16
static size_t num[N];
17
//static vector<MSET> result(1<<N);
18
//static vector<MSET> result(1<<N, MSET(256));
19
size_t count_expr = 0;
20
size_t count_func= 0;
21![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
22
bool calc(size_t step);
23![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
24
bool calc_num(int src[])
25![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
26![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (M <= 1) {
27
if (M == 1 && src[0] == 24) return true;
28
return false;
29
}
30
for (size_t i = 0, j = 1; i < N; ++i, j *= 2) num[i] = j;
31
for (size_t i = 1; i < (1 << N); ++i) result[i].clear();
32
for (size_t i = 0; i < N; ++i) result[1<<i].insert((double)src[i]);
33
return calc(N);
34
}
35![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
36
inline void merge_set(size_t i, size_t j)
37![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
38
const size_t k = i + j;
39![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI p = result[i].begin(), pe = result[i].end(); p != pe; ++p) {
40
double va = *p;
41![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI q = result[j].begin(),qe = result[j].end(); q != qe; ++q) {
42
double vb = *q;
43
result[k].insert(va + vb);
44
result[k].insert(va - vb);
45
result[k].insert(vb - va);
46
result[k].insert(va * vb);
47![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (va != 0 && vb != 0) {
48
result[k].insert(va / vb);
49
result[k].insert(vb / va);
50
}
51
}
52
}
53
}
54![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
55
inline bool isequal_m(double na)
56![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
57
const double zero = 1e-9;
58
if (fabs(na - M) < zero) return true;
59
return false;
60
}
61![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
62
inline bool merge_set2(size_t i, size_t j)
63![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
64![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI p = result[i].begin(), pe = result[i].end(); p != pe; ++p) {
65
double va = *p;
66![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (MSETI q = result[j].begin(),qe = result[j].end(); q != qe; ++q) {
67
double vb = *q;
68
if (isequal_m(va + vb)) return true;
69
if (isequal_m(va - vb)) return true;
70
if (isequal_m(vb - va)) return true;
71
if (isequal_m(va * vb)) return true;
72![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (va != 0 && vb != 0) {
73
if (isequal_m(va / vb)) return true;
74
if (isequal_m(vb / va)) return true;
75
}
76
}
77
}
78
return false;
79
}
80![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
81
bool calc(size_t step)
82![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
83
++count_func;
84![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (step == 2) {
85
++count_expr;
86
return merge_set2(num[0], num[1]);
87
}
88
// if (step == 1) {
89
// ++count_expr;
90
// size_t j = (1 << N) - 1;
91
// for (MSETI q = result[j].begin(),qe = result[j].end(); q != qe; ++q)
92
// if (isequal_m(*q)) return true;
93
// return false;
94
// }
95
96
--step;
97![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i = 0; i < step; i++){
98![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for(size_t j = i + 1; j <= step; j++) {
99
size_t na = num[i];
100
size_t nb = num[j];
101
num[i] = na + nb;
102
num[j] = num[step];
103
merge_set(na, nb);
104
if (calc(step)) return true;
105
num[i] = na;
106
num[j] = nb;
107
}
108
}
109
return false;
110
}
111![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
112
void calc_range(int first, int last, string filename="nul")
113![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
114
if (N ==0 || first >= last || filename.empty()) return;
115
clock_t ta = clock();
116
vector<int> vv(N, first);
117
int *end = &vv[N-1], *p = end, *arr = &vv[0];
118
ofstream ofs(filename.c_str());
119
size_t count = 0;
120
size_t count_b = 0;
121![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(true){
122
++count_b;
123![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if (calc_num(arr)) {
124
ofs.width(4);
125
ofs<< ++count << " ";
126![](//www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
for (size_t i = 0; i < N; ++i) {
127
ofs.width(2);
128
ofs << arr[i]<< " ";
129
}
130
ofs << "\n";
131
}
132
while (p >= arr && *p >= last) --p;
133
if (p < arr) break;
134
int tmp = ++*p;
135
while (p < end) *++p = tmp;
136
}
137
ofs.close();
138
const char sep[] = "/";
139
cout << "total: " << count << sep << count_b << "\n"
140
<< count_expr << "/" << count_func << "\n"
141
<< "time: " << clock() - ta << "\n\n";
142
143
}
144![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
145
int main()
146![](//www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
147
calc_range(1,13,"nul");
148
cin.get();
149
}
150![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
151![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
152![](//www.cppblog.com/Images/OutliningIndicators/None.gif)
153![](//www.cppblog.com/Images/OutliningIndicators/None.gif)