蛮力法.

Slides:



Advertisements
Similar presentations
XX啤酒营销及广告策略.
Advertisements

成功八步 成功一定有方法 失败一定有原因 银河系统.
回归教材、梳理知识、突出能力 ——2015年历史二轮复习思考 李树全 西安市第八十九中学.
地方自治團體之意義與組織 范文清 SS 2011.
報告人:教育部會計處處長 黃 永 傳 日 期:103 年12 月27 日
8月1日后全国营改增我们怎么办? 营改增新政策深度解析 得法网财税讲师 樊剑英.
3.2 农业区位因素与农业地域类型.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
2011年10月31日是一个令人警醒的日子,世界在10月31日迎来第70亿人口。当日凌晨,成为象征性的全球第70亿名成员之一的婴儿在菲律宾降生。 ?
普 通 话.
每节一经典 用系统的观点观察问题 总体设计,逐步求精 计算机科学学院 王小明 (博士/教授/博士生导师)
小学语文毕业总复习 ( 基础知识部分) 牡丹区实验小学侯宪梅.
教学的内容和方法.
26个英语字母 let's go!.
第十章 依赖于机器的优化 在指令级并行的机器上,程序的运行速度依赖于下面几个因素
小学语文教学论 湖南第一师范文史系.
內部審核實務 新竹縣政府主計處四科 王美琪
32* 飞船上的特殊乘客.
计算机三级考试C语言上机试题专题.
“八皇后”问题 崔萌萌 吕金华.
Chapter 2 汉英思维方式 和汉英语言的对比
初级会计实务 第八章 产品成本核算 主讲人:杨菠.
2011年高考复习数学考纲分析 克拉玛依市高级中学 冯祥杰.
湖南师大附中高三政治第二次月考 试题讲评 试题讲评.
2017/3/16 上 (興) 櫃 公 司 僑外資持股情形申報作業.
中考阅读 复习备考交流 西安铁一中分校 向连吾.
第二節 人力資源分配問題 某公司在大陸地區新增三個營業據點為A、B和C,該公司在台灣地區新招募五位行銷管理人員,假設此五人分配各地點之工作效率大致相同,由於分配地區人數不同其獲利狀況也不同,經過該公司評估後,估計各地區配置不同人數之利潤貢獻如下表.
中央广播电视大学开放教育 成本会计(补修)期末复习
贪婪算法.
人教版义务教育课程标准实验教科书 小学数学四年级上册第七单元《数学广角》 合理安排时间 248.
沈阳市场1-9月销售情况及五里河地块竞品销售情况
“08高考化学学业水平(必修科目)测试的命题和教学对策研究”
中考语文积累 永宁县教研室 步正军 2015.9.
小学数学知识讲座 应用题.
北师大版七年级数学 5.5 应用一元一次方程 ——“希望工程”义演 枣庄市第三十四中学 曹馨.
糖尿病肾病的护理 陈佳莉.
倒装句之其他句式.
高中算法与程 序设计 教学建议 ---循环结构部分
专题研讨课二: 数组在解决复杂问题中的作用
海洋存亡 匹夫有责 ——让我们都来做环保小卫士 XX小学三(3)班.
三角形的邊角關係 大綱:三角形邊的不等關係 三角形邊角關係 樞紐定理 背景知識:不等式 顧震宇 台灣數位學習科技股份有限公司.
第 22 课 孙中山的民主追求 1 .近代变法救国主张的失败教训: “师夷之长技以制 夷”“中体西用”、兴办洋务、变法维新等的失败,使孙中山
第八章 第一节 日本 邹旭丹 滨河中学初中部 湘教版地理初一年级.
张沛老师带你玩转国际英标.
26个英语字母.
建國國小英語教學線上課程 字母拼讀篇(一) 製作者:秦翠虹老師、林玉川老師.
数学3(必修)—— 算 法 ALGORITHM 苏州大学数学科学学院 徐稼红
第八章 欧氏空间 8.1 向量的内积 8.2 正交基 8.3 正交变换 8.4 对称变换和对称矩阵.
Introduction to the C Programming Language
中央广播电视大学开放教育试点课程 数据结构 辅导教师 倪政林.
数据结构 第一章 绪论.
Aa, Aa, Aa, Aa is for alligator. Aa is for apple. Aa, / /, alligator.
主讲人: 吕敏 { } Spring 2016 ,USTC 算法基础 主讲人: 吕敏 { } Spring 2016 ,USTC.
第4讲 C++程序控制结构(二) 4.1 循环结构 4.2 转向控制 4.3 综合案例分析.
C++大学基础教程 第3章 C++控制语句 北京科技大学 信息基础科学系.
Module 1 Unit 1 I like the ABC song..
数 据 结 构 与 算 法.
C++语言程序设计 C++语言程序设计 第三章 控制语句 第十一组 C++语言程序设计.
第1章 绪论(二) 教学目标 理解算法的特性及评价标准 掌握算法时间复杂度和空间复杂度的分析方法 1/
程式的時間與空間 Time and Space in Programming
第十章 优化 网上教学系统: : 编译原理 第十章 优化 网上教学系统: : 编译原理.
資料結構與C++程式設計進階 遞迴(Recursion) 講師:林業峻 CSIE, NTU 6/ 17, 2010.
問題解決與流程圖 高慧君 台北市立南港高中 2006年12月22日.
第八节 算术运算符和算术表达式.
第六章 贪心算法.
Chap 7 数 组 7.1 排序问题 7.2 找出矩阵中最大值所在的位置 7.3 进制转换.
畢氏定理(百牛大祭)的故事 張美玲 製作 資料來源:探索數學的故事(凡異出版社).
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
1.2.3 循环语句.
102年人事預算編列說明 邁向頂尖大學辦公室製作.
Presentation transcript:

蛮力法

蛮力法 蛮力法是基于计算机运算速度快这一特性,在解决问题时采取的一种“懒惰” 策略。这种策略不经过(或者说经过很少)思考,把问题所有情况或所有过程交给计算机去一一尝试,从中找出问题的解。 蛮力策略应用:选择排序、冒泡排序、插入排序、顺序查找、朴素的字符串匹配等。比较常用还有枚举法、盲目搜索算法等。

1 枚举法 枚举法(穷举法)是蛮力策略的一种表现形式,根据问题中条件将可能情况一一列举出来,逐一尝试从中找出满足问题条件的解。但有时一一列举出的情况数目很大,则需要进一步考虑,排除一些明显不合理的情况,尽可能减少问题可能解的列举数目。 通常从两个方面进行算法设计: 1)找出枚举范围:分析问题所涉及的各种情况。 2)找出约束条件:分析问题的解需要满足的条件,并用逻辑表达式表示。

【例3.1】百钱百鸡问题。中国古代数学家张丘建在《算经》中提出了著名的“百钱百鸡问题”:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,翁、母、雏各几何? 算法设计1: 通过对问题的理解,可能会想到列出两个三元一次方程,去解这个不定解方程,就能找出问题的解。这确实是一种办法,但这里我们要用“懒惰”的枚举策略进行算法设计: 设x,y,z分别为公鸡、母鸡、小鸡的数量。 尝试范围:由题意给定共100钱要买百鸡,若全买公鸡最多买100/5=20只,显然x的取值范围1~20之间;同理,y的取值范围在1~33之间,z的取值范围在1~100之间。 约束条件: x+y+z=100 且 5*x+3*y+z/3=100

print("the hen number is", y); print("the chick number is ",z); } } 算法1如下: main( ) { int x,y,z;   for(x=1;x<=20;x=x+1)     for(y=1;y<=34;y=y+1)       for(z=1;z<=100;z=z+1)           if(100=x+y+z and 100=5*x+3*y+z/3)      {print("the cock number is",x); print("the hen number is", y); print("the chick number is ",z); } } 算法分析:此算法需要枚举尝试20*34*100=68000次。算法的效率显然太低。

算法设计2: 在公鸡(x)、母鸡(y)的数量确定后,小鸡的数量z就固定为100-x-y,无需再进行枚举了。   此时约束条件只有一个: 5*x+3*y+z/3=100.

main( ) {  int x,y,z;     for(x=1;x<=20;x=x+1)                       for(y=1;y<=33;y=y+1)                   { z=100-x-y;                       if(z mod 3=0 and 5*x+3*y+z/3=100)                                                 {print("the cock number is",x); print("the hen number is", y); print("the chick number is ",z);}          } } 算法分析:以上算法只需枚举尝试20*33=660次。实现时约束条件限定Z能被3整除时,才判断“5*x+3*y+z/3=100”。这样省去了z不整除3时的算术运算和条件判断,进一步提高了算法效率。

六位数表示:A*10000+B*1000+C*100+A*10+B,尝试700次。 2)约束条件为: D D D D D D 算法设计1:按乘法枚举 1)枚举范围为: A:3——9,B:0——9,C:0——9 六位数表示:A*10000+B*1000+C*100+A*10+B,尝试700次。 2)约束条件为: 每次尝试,先求六位数与A的积,再测试积的各位是否相 同,若相同则找到了问题的解。 测试积的各位是否相同比较简单的方法是,从低位开始,每次都取数据的个位,然后整除10,使高位的数字不断变成个位,并逐一比较。 A=1,2时积不会得到六位数

if(G1<>G2 ) break; } if(i=6) print( F,”*”,A,”=”,E); } 算法分析1:该算法的尝试范围是A:3—9,B:0—9, C:0—9 。共尝试700次,不是一个好的算法。 算法1如下: main( ) { int A,B,C,D,E,E1,F,G1,G2,i; for(A=3; A<=9; A++) for(B=0; B<=9; B++) for(C=0; C<=9; C++) { F=A*10000+B*1000+C*100+A*10+B; E=F*A; E1=E; G1=E1 mod 10; for(i=1; i<=5; i++) { G2=G1; E1=E1/10; G1= E1 mod 10; if(G1<>G2 ) break; } if(i=6) print( F,”*”,A,”=”,E); }

算法设计2:将算式变形为除法:DDDDDD/A=ABCAB。此时只需枚举A:3——9 D:1——9,共尝试7

main() {int A,B,C,D,E,F; for(A=3;A<=9;A++) for(D=1;D<=9;D++) { E = D*100000+D*10000+D*1000+D*100+D*10+D; if(E mod A=0) F=E\A; if(F\10000=A and (F mod 100)\10=A) and (F\1000=F mod 10) print( F,”*”,A,”=”,E); }

【例3.3】编程打印有如下规律的n×n方阵。 例如下图:使左对角线和右对角线上的元素为0,它们上方的元素为1,左方的元素为2,下方元素为3,右方元素为4,下图是一个符合条件的5阶矩阵。 0 1 1 1 0 2 0 1 0 4 2 2 0 4 4 2 0 3 0 4 0 3 3 3 0

构造趣味矩阵:经常用二维数组来解决 根据趣味矩阵中的数据规律,设计算法把要输出的数据存储到一个二维数组中,最后按行输出该数组中的元素。 基本常识: 1)当对二维表按行进行操作时,应该“外层循环控制行; 内层循环控制列”;反之若要对二维表按列进行操作时,应该“外层循环控制列;内层循环控制行”。 2)二维表和二维数组的显示输出,只能按行从上到下连续进行,每行各列则只能从左到右连续输出。所以,只能用“外层循环控制行;内层循环控制列”。

3)用i代表行下标,以j代表列下标(除特别声明以后都遵守此约定),则对n*n矩阵有以下常识: 主对角线元素:i=j; 2 3)用i代表行下标,以j代表列下标(除特别声明以后都遵守此约定),则对n*n矩阵有以下常识: 主对角线元素:i=j; 副对角线元素:下标下界为1时i+j=n+1; 下标下界为0时i+j=n-1; 主上三角◥元素: i <=j; 主下三角◣元素: i >=j; 次上三角◤元素:下标下界为1时i+j<=n+1, 下标下界为0时i+j<=n-1; 次下三角◢元素:下标下界为1时i+j>=n+1, 下标下界为0时i+j>=n-1;

算法如下: main( ) {int i,j,a[100][100],n; input(n); for(i=1;i<=n;i=i+1) for(j=1;j<=n;j=j+1) {if (i=j or i+j=n+1) a [i][j]=0; if (i+j<n+1 and i<j) a [i][j]=1; if (i+j<n+1 and i>j) a [i][j]=2; if (i+j>n+1 and i>j) a [i][j]=3; if (i+j>n+1 and i<j) a [i][j]=4;} { print( “换行符”); for( j=1;j<=n;j=j+1) print(a[i][j]); }

【例3.5】狱吏问题 某国王对囚犯进行大赦,让一狱吏n次通过一排锁着的n间牢房,每通过一次,按所定规则转动n间牢房中的某些门锁, 每转动一次, 原来锁着的被打开, 原来打开的被锁上;通过n次后,门锁开着的,牢房中的犯人放出,否则犯人不得获释。 转动门锁的规则是这样的,第一次通过牢房,要转动每一把门锁,即把全部锁打开;第二次通过牢房时,从第二间开始转动,每隔一间转动一次;第k次通过牢房,从第k间开始转动,每隔k-1 间转动一次;问通过n次后,哪些牢房的锁仍然是打开的?

算法设计1: 1)用n个空间的一维数组a[n],每个元素记录一个锁的状 态,1为被锁上,0为被打开。 2)用数学运算方便模拟开关锁的技巧,对i号锁的一次开 关锁可以转化为算术运算:a[i]=1-a[i]。 3)第一次转动的是1,2,3,……n号牢房; 第二次转动的是2,4,6,……号牢房; 第三次转动的是3,6,9,……号牢房; …… 第i次转动的是i,2i,3i,4i,……号牢房,是起点为i,公差为i的等差数列。 4)不做其它的优化,用蛮力法通过循环模拟狱吏的开关 锁过程,最后当第i号牢房对应的数组元素a[i]为0 时,该牢房的囚犯得到大赦。

main1( ) { int *a,i,j,n; input(n); a=calloc(n+1,sizeof(int)); //申请存储空间 for (i=1; i<=n;i++) a[i]=1; for (j=i; j<=n;j=j+i) a[i]=1-a[i]; if (a[i]=0) print(i,”is free.”); } 算法分析1:以一次开关锁计算,算法的时间复杂度为 n(1+1/2+1/3+……+1/n)=O(nlogn)。

问题分析2:转动门锁的规则可以有另一种理解,第一次转动的是编号为1的倍数的牢房;第二次转动的是编号为2的倍数的牢房;第三次转动的是编号为3的倍数的牢房;……则狱吏问题是一个关于因子个数的问题。 令d(n)为自然数n的因子个数,这里不计重复的因子,如4的因子为1,2,4共三个因子,而非1,2,2,4。则d(n)或为奇数,或为偶数,见下表: 表3.1 编号与因数个数的关系 n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 …… d(n) 1 2 2 3 2 4 2 4 3 4 2 6 2 4 4 5 ……   数学模型2:d(n)有的为奇数,有的为偶数,由于牢房的门开始是关着的,这样编号为i的牢房,所含1——i之间的不重复因子个数为奇数时,牢房最后是打开的;反之,牢房最后是关闭的。

算法设计2:  1)算法是求出每个牢房编号的不重复的因子个数,当它为奇数时,这里边的囚犯得到大赦。 2)一个数的因子是没有规律的,只能从1——n枚举尝试。 main2( ) { int s,i,j,n; input(n); for (i=1; i<=n;i++) { s=1; for (j=2; j<=i;j=j++) if (i mod j=0) s=s+1; if (s mod 2 =1) print(i,”is free.”); } } 为什么从2开始?

算法分析2: 狱吏开关锁的主要操作是a[i]=1- a[i];共执行n*(1+1/2+1/3+……+1/n)次,时间复杂度近似为O(n logn)。使用了n个空间的一维数组。算法2没有使用辅助空间,但由于求一个编号的因子个数也很复杂,其主要操作是判断i mod j是否为0,共执行了1+2+3+……+n次,时间复杂度为O(n2 /2)。 蛮力法并不总是因为减少了人脑的思维,就一定是效率差的算法。对规模不是太大的问题,蛮力法还是一种比较好的算法策略。

表3.1 编号与因数个数的关系 n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 …… d(n) 1 2 2 3 2 4 2 4 3 4 2 6 2 4 4 5 …… 数学模型3:仔细观察表3.1,发现当且仅当n为完全平方数时,d(n)为奇数;这是因为n的因子是成对出现的,也即当n=a*b且a≠b时,必有两个因子a,b; 只有n为完全平方数,也即当n=a2时,才会出现d(n)为奇数的情形。 算法设计3:只需找出小于n的平方数即可。

main3( ) {int s,i,j,n; input(n); for (i=1;i<=n;i++) if(i*i<=n) print(i,”is free.”); else break; } 结论:在对运行效率要求较高的大规模的数据处理问题时,必须多动脑筋找出效率较高的数学模型及对应算法。

3 蛮力法的优点 可以用来解决广阔领域的问题; 对于一些重要的问题,它可以产生一些合理的算法; 解决问题的实例很少时,它让你花费较少的代价; 3 蛮力法的优点 可以用来解决广阔领域的问题; 对于一些重要的问题,它可以产生一些合理的算法; 解决问题的实例很少时,它让你花费较少的代价; 可以解决一些小规模的问题; 可以作为其他高效算法的衡量标准。

小结 掌握蛮力法的基本思想:蛮力法是一种简单直接地解决问题的方法,通常直接基于问题的描述和所涉及的概念定义; 掌握蛮力策略的具体应用; 蛮力法的主要优点是它广泛的适用性和简单性;它的主要缺点是大多数蛮力算法的效率都不高。

螺旋阵:任意给定n值,按如下螺旋的方式输出方阵: 作业 螺旋阵:任意给定n值,按如下螺旋的方式输出方阵: n=3 输出: 1 8 7 2    9 6 3    4 5 n=4 输出: 1 12 11 10 2    13 16 9 3    14 15 8 4 5 6 7

算法设计1: n=4 输出:1 12 11 10 2    13 16 9 3    14 15 8 4 5 6 7 按照“摆放”数据的过程,逐层(圈)分别处理每圈的左侧、下方、右侧、上方的数据。以n=4为例详细设计如下: 把“1——12”看做一层(一圈)“13-16”看做二层……以层做为外层循环,下标变量为i。以上两个例子,n=3、4均为两层,用n\2表示下取整,(n+1)/2表示对n/2上取整。所以下标变量i的范围1——(n+1)/2。

n=4 输出:1 12 11 10 2    13 16 9 3    14 15 8 4 5 6 7 i层内“摆放”数据的四个过程(四角元素分别归四个边): 1)i列(左侧),从i行到n-i行 (n=4,i=1时“摆放1,2,3”) 2)n+1-i行(下方),从i列到n-i列 (n=4,i=1时“摆放4,5,6”) 3)n+1-i列(右侧),从n+1-i行到i+1行 (n=4,i=1时“摆放7,8,9”) 4)i行(上方),从n+1-i列到i+1列 (n=4,i=1时“摆放10,11,12”) 四个过程通过四个循环实现,用j表示i层内每边中行或列的下标。

for(i=1;i<=n/2;i=i+1) main( ) {int i,j,a[100][100],n,k; input(n); k=1; for(i=1;i<=n/2;i=i+1) {for( j=i;j<=n-i;j=j+1) { a [j][i]=k; k=k+1;} /左侧/ for( j=i;j<=n-i;j=j+1) { a [n+1-i][j]=k; k=k+1;} /下方/ for( j=n-i+1;j>=i+1;j=j-1){a[j][n+1-i]=k;k=k+1;} /右侧/ for( j=n-i+1;j>=i+1;j=j-1) {a[i][j]=k; k=k+1;} /上方/ } if (n mod 2=1) {i=(n+1)/2; a[i][i]=n*n;} for(i=1;i<=n;i=i+1) { print(“换行符”); for( j=1;j<=n;j=j+1) print(a[i][j]); n为奇数时,中间一层只有一个数据

算法设计2: n=4 输出:1 12 11 10 2    13 16 9 3    14 15 8 4 5 6 7 约定:四角数据分别属于先处理的行或列;用k纪录行或列处理的元素个数,存放最外圈的情况如下: j=1 i=i+1 1----n k=n //左侧 i=n j=j+1 2----n k=n-1 //下方 j=n i=i-1 n-1----1 k=n-1 //右侧 i=1 j=j-1 n-1----2 k=n-2 //上方

2. 为用统一的表达式表示循环变量的范围,引入变量k,表示在某一方向上数据的个数,k初值是n,每当数据存放到左下角时,k就减1,又存放到右上角时,k又减1;此时k值又恰好是下一圈左侧的数据个数。 将向下和向上“摆放”数据时,行下标i的变化用统一表达式表示;也将向右和向左“摆放”数据时,列下标j的变化用统一表达式表示。引入符号变量t,t初值为1,表示处理前半圈:左侧i向下变大,j向右变大;t变为-1;表示处理后半圈:右侧i向上变小,j向左变小。则一圈内下标变化如下: j=1 i=i+t 1----n k=n i=n j=j+t 2----n k=k-1 t= -t 前半圈共2*k-1个 j=n i=i+t n-1----1 i=1 j=j+t n-1----2 k=k-1 t= -t 后半圈共2*k-1个

b[0]=0; b[1]=1; k=n; t=1; x=1; while x<=n*n main( ) {int i,j,k,n,a[100][100],b[2],x,y; b[0]=0; b[1]=1; k=n; t=1; x=1; while x<=n*n {for (y=1;y<=2*k-1;y++) // t=1时处理左下角 { b[y/(k+1)]=b[y/(k+1)]+t; //t= -1时处理右上角 a[b[0]][b[1]]=x; x=x+1;} k=k-1; t=-t; } for( i=1;i<=n;i++) {print(“换行符”); for(j=1;j<=n;j++) print(a[i][j]); x模拟摆放的数据 y模拟半圈内数据的处理

算法说明: 在“算法设计”中,由“a[b[0]][b[1]]=x;”可知,数组元素b[0]表示存储矩阵的数组a的行下标,数组元素b[1]是数组a的列下标。为什么不用习惯的i,j作行、列的下标变量呢?使用数组元素作下标变量的必要性是什么?表达式“b[y/(k+1)]= b[y/(k+1)]+t;”的意义又是什么? y作循环变量,模拟半圈内数据的处理过程。变化范围是1—2*k-1。在半圈里,当y=1—k时,是行下标在变化,列下标不变;此时y/(k+1)的值为0,确实实现了行下标b[0]的变化(加1或减1,由t决定)。当y=k+1—2*k-1时, 是行下标在不变,列下标在变化;此时y/(k+1)的值为1,确实实现了列下标b[1]的变化。这又验证了利用一维数组“便于循环”的技巧,当然这离不开对数组下标的构造。 综上所述,引入变量t,k和数组b后,通过算术运算将一圈中上下左右四种不同变化情况,归结构造了一个循环“不变式”。