N個(gè)數(shù)計(jì)算24點(diǎn)
問(wèn)題:
N個(gè)1到13之間的自然數(shù),找出所有能通過(guò)加減乘除計(jì)算(每個(gè)數(shù)有且只能用一次)得到24的組合?
計(jì)算24點(diǎn)常用的算法有:① 任取兩個(gè)數(shù),計(jì)算后,將結(jié)果放回去,再?gòu)氖O碌臄?shù)中任取兩個(gè),如此反復(fù)直到只剩下一個(gè)數(shù);② 先構(gòu)建前綴/后綴表達(dá)式,再計(jì)算該表達(dá)式;③ 用集合保存中間結(jié)果,集合間兩兩進(jìn)行合并計(jì)算得到新集合(或者對(duì)給定的一個(gè)集合,對(duì)其所有的子集合進(jìn)行合并計(jì)算)。
本文采用第一種方法。定義六種操作符:ADD、SUB、MUL、DIV、RSUB、RDIV,分別對(duì)應(yīng)加、減、乘、除、反減和反除(反減/除:先交換兩個(gè)數(shù),再減/除)。
顯然,取兩個(gè)數(shù)計(jì)算時(shí),六種計(jì)算結(jié)果可能有重復(fù),可以對(duì)這6個(gè)結(jié)果進(jìn)行去重(實(shí)際上,只要分別對(duì)加減(ADD、SUB、RSUB)和乘除(MUL、DIV、RDIV)的3個(gè)計(jì)算結(jié)果進(jìn)行去重判斷就可以了,效率和對(duì)6個(gè)結(jié)果去重相差不大)。
另外一種剪枝方法:保存每個(gè)數(shù)上次計(jì)算時(shí)所用的操作符(初始值為空)。所取的兩個(gè)數(shù):
若某個(gè)數(shù)的上次操作符為減(SUB、RSUB),那么不進(jìn)行加減(ADD、SUB、RSUB)計(jì)算。
若某個(gè)數(shù)的上次操作符為除(DIV、RDIV),那么不進(jìn)行乘除(MUL、DIV、RDIV)計(jì)算。
比如:取的兩個(gè)數(shù)為 a-b 和 c(c的上次操作符任意),如果進(jìn)行加減計(jì)算的話,
a-b+c 和 c+a-b重復(fù),
c-(a-b)和 c+b-a重復(fù)
a-b-c 和 c+b RSUB a重復(fù)
也就是說(shuō),上次操作符為減的,進(jìn)行加減計(jì)算時(shí),總可以轉(zhuǎn)為某個(gè)上次操作符為加的表達(dá)式,因而可以不計(jì)算。同樣,上次操作符為除的,不進(jìn)行乘除計(jì)算。
當(dāng)然,還可以考慮記錄位置進(jìn)行剪枝,這樣避免a+b+c和a+c+b都進(jìn)行計(jì)算。但要注意的是:在給定的組合無(wú)解時(shí),越多的剪枝方法,極有可能提高搜索效率,但在給定的組合有解時(shí),很可能反而降低搜索效率。
另外,對(duì)有解時(shí)輸出的表達(dá)式的處理對(duì)程序的性能影響很大。如果每次計(jì)算都保存對(duì)應(yīng)的表達(dá)式,會(huì)進(jìn)行大量的字符串操作,嚴(yán)重影響性能。實(shí)際上,每次計(jì)算只要保存取出的兩個(gè)數(shù)的位置和所進(jìn)行計(jì)算的操作符就夠了,最終需要輸出表達(dá)式時(shí),只要模擬一下遞歸函數(shù)調(diào)用過(guò)程,進(jìn)行相應(yīng)的字符串操作。
下面是程序(gcc 4.5,優(yōu)化參數(shù)-O2)在T4200/2G單核下運(yùn)行的結(jié)果,計(jì)算10個(gè)數(shù),646646個(gè)組合只用了不到13分鐘。
N |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
時(shí)間(ms) |
78 |
610 |
4157 |
19593 |
160922 |
173766 |
764328 |
有解組合數(shù) |
1362 |
6104 |
18554 |
50386 |
125969 |
293930 |
646646 |
總組合 |
1820 |
6188 |
18564 |
50388 |
125970 |
293930 |
646646 |
總表達(dá)式 |
1124776 |
15656645 |
105278906 |
492587616 |
3760744504 |
4535030813 |
19912345238 |
表達(dá)式A |
457549 |
11864184 |
88679768 |
409129896 |
1173803224 |
4535030813 |
19912345238 |
表達(dá)式B |
667227 |
3792461 |
16599138 |
83457720 |
2586941280 |
0 |
0 |
總函數(shù)調(diào)用 |
1482939 |
20950792 |
141892513 |
669790534 |
5258218577 |
6112529945 |
26948662625 |
函數(shù)調(diào)用A |
603206 |
15849306 |
119160441 |
551913059 |
1576965280 |
6112529945 |
26948662625 |
函數(shù)調(diào)用B |
879733 |
5101486 |
22732072 |
117877475 |
3681253297 |
0 |
0 |
其中:表達(dá)式A/B、函數(shù)調(diào)用A/B為:給定的n個(gè)數(shù)有/無(wú)解時(shí),所處理的表達(dá)式總數(shù)和函數(shù)調(diào)用總次數(shù)。
N=8與N=9,兩個(gè)所用時(shí)間只相差13秒,這是由于N為8時(shí),有一個(gè)組合(8個(gè)1)無(wú)解,判斷這個(gè)組合無(wú)解需110多秒,而計(jì)算剩下的125969個(gè)組合還不要50秒。
程序代碼:


1

2

3

4

5

6

7

8

9

10

11

12

13

14



15

16



17

18

19

20

21

22



23



24



25

26

27

28

29

30

31

32

33

34



35

36

37

38

39

40



41



42

43

44

45

46



47

48

49

50

51

52

53

54



55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72



73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88



89

90

91

92

93

94

95

96

97

98



99

100

101

102

103

104

105



106

107

108

109

110



111

112

113

114

115

116



117

118

119

120



121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139



140

141



142

143

144

145

146

147

148



149

150



151

152

153

154

155

156

157

158

159

160

161

162

163



164

165

166

167

168

169



170

171

172



173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190



191

192

193



194

195

196

197

198

199

200

201

202

203

204

205

206



207

208

209

210

211

212

213

214

215

216

217

218



219

220

221

222

223

224

225

226

227

228

229

230

231



232

233



234

235

236



237

238

239

240

241

242

243



244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266



267

268



269



270



271

272

273

274

275

276

277

278

279

280

281

282

283

284

285
