定义
穷举法是算法设计中经常使用的一种方法,基本思想是问题的要求将问题的所有可能的输入一一进行验证,看是否满足问题的条件,从而找到可能的解。问题解有三种情况:有多个解,单个解或无解。穷举法又名枚举法,暴力破解法等。
使用数学进行表示如下:
Y
=
F
(
X
)
∈
R
,
w
h
e
r
e
 
X
∈
D
.
\quad \quad Y=\mathrm{F}(X) \in \mathbb{R} \mathrm{, where} \:X \in \mathbb{D}.
Y=F(X)∈R,whereX∈D.
此式中:
- X X X 为问题的输入,其取舍范围定义域为 D \mathbb{D} D;
- Y Y Y为问题的解,即所要达到的目标;
- F F F 为问题的解决算法,即 F ​ : ​ X → Y F\!: \!X \rightarrow Y F:X→Y;
- R \R R 为解空间,当 R = ∅ \R = \empty R=∅ 时,问题无解 。
使用条件
使用穷举法对所有可能的输入进行测试,因此需要花很多的时间。要让其在可以接受的时间范围内解决问题,需要考虑以下三个条件:
- 定义域范围
定义域 D \mathbb{D} D必需是有限的,或者可以看作有限。有限的范围不能太大。举例来说,15X15的棋盘的五子棋的所有输入可能为 225! 种,约为 1.26 × 1 0 + 433 1.26\times10^{+433} 1.26×10+433。 - 优化水平
优化是指将定义域中一些明显不可能的输入在穷举前删除,如五子棋中,第一步是不可能走四边的,这样第一步剩下的格子 (225 - 58) = 167,从而解空间下降了 58/225 = 25.78%。如果再考虑外围两圈,又可以再多减少54个格子,即只要考试113个格子,这样总空间变成了 113 × 254 113\times254 113×254! ,这样解空间基本下降了一半。 - 运算能力
即计算机的运算能力,现在计算机性能越来越强大,而且还可以多台进行联合工作进行并行计算,如在 2019.3.14,谷歌使用 25 虚拟机器用了121天,计算了31.4万亿位的PI。
示例1:鸡兔同笼问题
问题:一个笼子有35个头,94只脚,问鸡和兔各有多少?
分析:设鸡x只,免y只,其中 x + y = 35, 2x+4y=94。
根据题目可以得到x和y的定义域为
x
∈
[
0
,
35
]
x\in[0,35]
x∈[0,35],
y
∈
[
0
,
35
]
y\in[0,35]
y∈[0,35]
优化:由于 23< 94/4 < 24,所以 y 的值域可取得
y
∈
[
0
,
24
]
y\in[0,24]
y∈[0,24]。
根据以上分析,使用java编码如下:
public static void main(String[] args) {
for(int x = 0; x <= 35; x++) {
for(int y = 0; y <= 24; y++) {
if(x+y==35 && 2*x+4*y==94)
System.out.printf("x=%d, y=%d.\n", x, y);
}
}
}
结果为:
x=23, y=12.
由于使用双层循环,循环次数为36*25=900次。实际上可以将y用35-x表示,这样循环次数减少为36次,效率得到大大提高。
public static void main(String[] args) {
for (int x = 0; x <= 35; x++) {
if (2 * x + 4 * (35 - x) == 94)
System.out.printf("x=%d, y=%d.\n", x, (35 - x));
}
}
示例2:水仙花数
水仙花数(Narcissistic number)是每个位上的数字的 3次幂之和等于它本身的三位数。例如,153 就是一个水仙花数,因为它满足 13 + 53+ 33 = 153。水仙花数也被称为超完全数字不变数(pluperfect digital invariant, PPDI)、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数(Armstrong number) [2]。
解决思路:鉴于三位数只有900个(100-999),所以只需要遍历一次,判断每个数是否满足条件即可。为了简化操作,解题时可以使用三层循环,自外向内每层分别表示百位、十位和个位数。另外,由于三位数首位不为0,所以范围是1 ~ 9,而其他的位是0 ~ 9。
以下是用 Java 实现的代码。
// i,j,k 分别表示百位、十位和个位数。
for(int i = 1; i < 10; i++)
for(int j = 0; j < 10; j++)
for(int k = 0; k < 10; k++)
if(i * i * i + j * j * j + k * k * k == 100 * i + 10 * j + k)
System.out.printf("%d is a narcissistic number.\n", 100 * i + 10 * j + k);
输出为:
153 is a narcissistic number.
370 is a narcissistic number.
371 is a narcissistic number.
407 is a narcissistic number.
[1] 谷歌创新PI世界记录,https://www.theverge.com/2019/3/14/18265358/pi-calculation-record-31-trillion-google
[2] 水仙花数,百度百科,https://baike.baidu.com/item/水仙花数/2746160?fr=aladdin