“从零开始的暴力算法之旅”
(PS:非原创!老师下发的Word文件,现在整理一下,转载出来。因为不知道原作者,所以没办法表明原地址,请见谅!)
从零开始的暴力算法之旅
我发现自己在解决一个问题时没有一个系统的思考方法,意识到这个问题的严重性。想必大家都经历过,面对复杂的问题只是傻乎乎的盯着显示器,或者不经过深思熟虑就开始敲打键盘,结果还要辛辛苦苦修改一塌糊涂的代码。
一本书上说道:与通常所想不同,支配设计算法的并不是一时出现的灵感,而是许多策略性的选择。构想算法不仅需要理解问题的特性,还要理解执行时间和占用内存空间之间的对立关系,而且还要选择适当的数据结构。有些算法会共享出解决问题时最重要的领会,将之积累起来就会领悟到一种模式。
只读这一段会觉得有点难以理解但好似有很有道理,其实,我这几天学到了一种如何思考问题的方法,总结起来就一句话:化繁为简。接下来的文章我会通过实例逐一诠释我的领悟所得。
我们最常见的错误就是把简单的问题复杂化,比如前几天去饭店吃饭,一个二年级的小朋友有一道数学题:一根木棍长22米,请问需要截取几次才能将其分为长度都为2米的木棍。答案很简单:22/2。但是我却本能的告诉那孩子是log2(22/2)。现在想来我都笑了,二分算法写多了。
所以,为了避免类似的错误,每当我们遇到问题时应当先自问:能否用暴力的方法解决,并且是否能否优雅的解决【这体现了一个程序员是否有一颗优雅编程的心】。
接下来看一道问题:
求从0开始按顺序标号的n个元素中,选择4个元素的所有可能的组合。假设n=7,那么大家就会想到写一个4重循环就好,看起来是这个样子:
for(int i = 0; i < n; ++i)
for(int j = i+1; j < n; ++j)
for(int k = j+1; k < n; ++k)
for(int l = k+1; l < n; ++l)
cout<<i<<j<<k<<l<<endl;
但是若是选择5个数字的全排列呢,7个,8个呢,是不是得手动在增加循环次数,比追女生都麻烦是不是。要是我们将每一个循环看成一个递归的话,就很容易优雅解决了。
则构成的飘逸的代码是下面这样的:
//n:元素总量
//picked:已选元素的序号
//toPick:还需选择元素的数量
#include<iostream>
#include<vector>
using namespace std;
void printPicked(vector<int> & picked) {
for(int i = 0; i<picked.size(); i++) {
cout<<picked[i];
}
cout<<endl;
}
void pick(int n, vector<int>& picked, int toPick) {
if(toPick == 0) {
printPicked(picked);
return;
}
int smallest = picked.empty() ? 0 : picked.back() + 1;
for(int next = smallest; next < n; ++next) {
picked.push_back(next);
pick(n,picked,toPick-1);
picked.pop_back();
}
}
int main() {
int n = 0;
cin >> n;
vector<int> picked;
pick(10,picked,n);
}
算法之暴力破解法(穷举法)
一,什么是暴力破解法?
暴力破解法,就是把所有条件,相关情况统统考虑进去,让计算机进行检索,指导得出与之所有条件符合的结果
(但是,暴力破解法对计算机资源耗费严重,如果条件太复杂,运算速度缓慢,为了解决这一问题,我们可以事先把与之不相关的条件进行限制,减少计算机的运算量)
二,暴力破解法应用
- 鸡兔同笼
问题:有鸡兔共50头,共有脚120只。 问 :鸡兔分别的数量?
【理解】
鸡的头和兔子的头数想加为50个,情况数量并不是很多,最多50个最少0个,是有限的,这个问题就可以使用暴力破解的方法来解决。
代码如下:
public class OneDay {
public static void main(String[] args) {
//x为鸡的数目,最小是0,最大为50,在一个循环中一个一个的测试,看哪一个条件能够满足题目要求
for(int x=0; x<=50; x++) {
int y=50-x;
if(x*2+y*4==120) {
System.out.println("x="+x+"y="+y);
}
}
}
}
答案:x=40 y=10
- 韩信点兵
韩信知道部队人数大约1000人左右,具体数字不详,5人一组剩余1人,7个人一组还剩两个人,8个人一组还剩3个人,问:这支部队有多少人?
【理解】可以使用暴力破解法的方式,枚举所有情况,显然人数就是我们列举的情况;代码如下:
public class OneDay {
public static void main(String[] args) {
for(int i=0; i<=2000; i++) {
if(i%5==1 && i%7==2 && i%8==3) {
System.out.println(i);
}
}
}
}
答案:51、331、611、891、1171
【总结】
暴力破解法:仅仅就是对所有可能的情况逐一的去列举,并且用条件进行筛选,把满足条件的列举出来,就可以了。
基础算法之 暴力搜索
- 算法说明
暴力算法也叫蛮力算法,之所以称为暴力,是因为该算法是枚举当前所有出现的情况,从而得到需要的情况。该算法可以求的一些情况较少的问题的解,若问题规模太大,该算法便不适用。
-
算法分析
若给定一个集合 α ,求得该集合的所有子集,求得的子集即为某种情况,列出所有子集及是暴力算法。比如给定一个数组 [1, 3, 5] ,其所有子集为 [ ],[ 1 ],[ 3 ],[ 5 ],[ 1, 3 ],[ 1, 5 ], [ 3, 5 ], [ 1, 3, 5 ] 共八种情况。对于一个元素数量为 n 的集合来说,其子集数目为 2 的 n 次方。证明为: 对于集合中的每个元素,都有选择或者不选择两种情况,则 全部情况为 2 的 n 次方,该证明即为算法的核心。 -
问题示例
-
背包问题
给定一定数量的物品,每个物品都有其重量和价值,给定一个容量为 c 的背包,用该背包装物品,求 装的物品价值之和为最大的情况。
- 问题分析
设集合 A ,其元素为每个物品,利用暴力搜索算法列出集合 A 的所有子集,在其子集中找到物品价值之和最大的情况即可。对于该问题选择的数据结构,在此我用的是一个一维数组存放物品的重量,另一个一维数组存放物品的价值,对应下标即可。
代码如下(C语言)
#include<stdio.h>
int goods_all = 5; /* 物品数量 */
int goods_weight[5] = {1, 3, 6, 8, 12}; /* 物品的重量 */
int goods_value[5] = {1, 4, 8, 10, 20}; /* 物品的价值 */
int package = 20; /* 背包容量 */
int maxValue = 0; /* 当前最大价值(后面用到)*/
int choose[5]; /* 存放最优方案的物品下标 */
/* 输出结果函数
*/
void outputResult() {
for (int i = 0; i < goods_all && choose[i] != -1; i++) {
printf("重量:%d,价值:%d\n", goods_weight[choose[i]], goods_value[choose[i]]);
}
}
/* 存放当前最优方案(不断的更新 choose 数组)
@param goods_list[] 当前的选择项数组
@param goods_num 当前的选择项 数目
*/
void storeChoose(int goods_list[], int goods_num) {
for (int i = 0; i < goods_all; i++) choose[i] = -1;
for (int i = 0; i < goods_num; i++) choose[i] = goods_list[i];
}
/* 暴力枚举所有方案(递归实现)
@param goods_list 存放当前方案的数组
@param goods_num 当前选择的物品数目
@param goods_index 当前选择或不选择的物品下标
*/
void chooseGoods(int goods_list[],int goods_num, int goods_index) {
/* 当物品的下标到最后一个物品时,即枚举完毕 ,
此时 遍历当前方案 goods_list[], 若当前方案价值
大于 当前的 maxValue ,则重写 choose数组
*/
if (goods_index == goods_all) {
int value = 0, weight = 0;
for (int i = 0; i < goods_num; i++) {
weight += goods_weight[goods_list[i]];
value += goods_value[goods_list[i]];
}
if (weight <= package && value > maxValue) {
storeChoose(goods_list, goods_num);
maxValue = value;
}
} else {
/* 不选择当前物品 */
chooseGoods(goods_list, goods_num, goods_index + 1);
/* 选择当前物品 */
goods_list[goods_num] = goods_index;
goods_num ++;
chooseGoods(goods_list, goods_num, goods_index + 1);
}
}
int main() {
int arr[5];
chooseGoods(arr, 0, 0);
outputResult();
}
- 总结
对于暴力搜索,最重要的是对于当前元素的选择或者不选择,在这里能保存数组的多样性是因为下标的原因,因为选择的元素都存在了数组一端。
补充:https://www.cnblogs.com/luyouqi233/category/1142002.html (转载一波其他优秀的暴力算法分类详解qvq)