标签:
练习
1.4.1 证明从N个数中取三个整数的不同组合为 N(N-1)(N-2)/6. /*下面的证明也恰好符合帕斯卡恒等式(1.1.27)*/
方法一:直接根据排列组合的知识---C(N,3)=N(N-1)(N-2)/6
方法二:数学归纳法如下
1.4.2 修改TreeSum,正确处理两个较大的int值相加可能溢出的情况 /*对一些"极端"情况进行考虑*/
public class ThreeSum { public static int count(int[] a) { int N=a.length; int cnt=0; for(int i=0;i<N;i++) for(int j=i+1;j<N;j++) for(int k=j+1;k<N;k++) if((long)a[i]+a[j]+a[k]==0) //采用long来避免溢出情况 cnt++; return cnt; } public static void main(String[] args) { @SuppressWarnings("deprecation") int[] a=In.readInts(args[0]); StdOut.println(count(a)); } }
1.4.3 修改DoublingTest,使用StdDraw产生类似于正文中的标准图像和对数图形,根据需要调整比例使图像总能够充满窗口的大部分区域 /*进一步对库函数StdDraw的理解和运用,以及根据图像分析增长的数量级*/
public class DoublingTest { public static double timeTrial(int N) { int MAX=1000000; int[] a=new int[N]; for(int i=0;i<N;i++) a[i]=StdRandom.uniform(-MAX, MAX); Stopwatch timer=new Stopwatch(); ThreeSum.count(a); return timer.elapsedTime(); } public static void main(String[] args) { int MAX=4000;int i=0; int len=(int)(Math.log(MAX/250)/Math.log(2))+1; int[] arrN=new int[len]; double[] timeN=new double[len]; for(int N=250;N<=MAX;N+=N) { double time=timeTrial(N); StdOut.printf("%7d %5.1f\n",N,time); arrN[i]=N; timeN[i++]=time; } // drawStandard(arrN,timeN); drawLog(arrN,timeN); } //绘制标准图形 public static void drawStandard(int[] n,double[] time) { StdDraw.setXscale(-0.5,1.2*n[n.length-1]/1000.0); StdDraw.setYscale(-0.5,time[n.length-1]*1.2); //建立坐标系 StdDraw.setPenColor(StdDraw.BLACK); StdDraw.setPenRadius(0.001); StdDraw.line(0, 0, 1.1*n[n.length-1]/1000, 0); StdDraw.line(0, 0,0, 1.1*time[n.length-1]); for(int i=1;i<1.1*n[n.length-1]/1000.0;i++) StdDraw.text(i,-0.2,i+"K"); for(double t=2;t<time[n.length-1]*1.1;t+=2) StdDraw.text(-0.2, t, t+""); //绘制图像 StdDraw.setPenColor(StdDraw.BOOK_RED); StdDraw.setPenRadius(0.02); for(int i=0;i<n.length;i++) StdDraw.point(n[i]/1000.0, time[i]); } //绘制对数图形 public static void drawLog(int[] arrN,double[] timeN) { //将时间转换为其对数 double[] timelog=new double[timeN.length]; for(int i=0;i<timeN.length;i++) timelog[i]=Math.log(timeN[i]); StdDraw.setXscale(-0.5,1.2*arrN[arrN.length-1]/1000.0); StdDraw.setYscale(-0.5,timelog[arrN.length-1]*1.2); //建立坐标系 StdDraw.setPenColor(StdDraw.BLACK); StdDraw.setPenRadius(0.001); StdDraw.line(0, 0, 1.1*arrN[arrN.length-1]/1000, 0); StdDraw.line(0, 0,0, 1.1*timelog[arrN.length-1]); for(int i=1;i<1.1*arrN[arrN.length-1]/1000.0;i++) StdDraw.text(i,-0.2,i+"K"); for(double t=0;t<timelog[arrN.length-1]*1.1;t+=timelog[arrN.length-1]/5) StdDraw.text(-0.2, t, String.format("%.2f",t)); //绘制图像 StdDraw.setPenColor(StdDraw.BOOK_RED); StdDraw.setPenRadius(0.02); for(int i=0;i<arrN.length;i++) StdDraw.point(arrN[i]/1000.0, timelog[i]); } }
注:以下的实现中并没有根据需要比例自动调整,此外也多了一个MAX来确定范围(上面写的还是有一定问题,待改进)
1.4.5 给出下面这些量的近似 /*主要参考表1.4.2典型的近似 进一步理解近似*/
a. N+1~N
b. 1+1/N~1
c. (1+1/N)(1+2/N)~1
d. 2N3-15N2+N~2N3
e. lg(2N)/lgN~1
f. lg(N2+1)/lgN~2
g. N100/2N~?(模拟了一下,N非常大时NaN)
1.4.6 给出以下代码段的运行时间的增长数量级(作为N的函数): /*粗略了解求解增长数量级的方式*/
a. int sum=0; for(int n=N;n>0;n/=2) for(int i=0;i<n;i++) sum++; b. int sum=0; for(int i=1;i<N;i*=2) for(int j=0;j<i;j++) sum++; c. int sum=0; for(int i=1;i<N;i*=2) for(int j=0;j<N;j++) sum++;
增长数量级取决于 sum++的运行次数
a. (N+N/2+N/4...) 总共有 log2N 个元素,最后一个元素满足 2^(k-1)<=N
b. (1+2+4+...) 最后一个元素满足 2k<=N
c. NlogN
1.4.7 以统计涉及输入数字的算术操作(和比较)的成本模型分析ThreeSum.
即统计 if (a[i] + a[j] + a[k] == 0) 语句执行的次数,由表1.4.4可知 D---N3/6-N2/2+N/3~N3/6
1.4.8 编写一个程序,计算输入文件中相等的整数对的数量.如果你的第一个程序是平方级别的,请继续思考并以Array.sort()给出一个线性对数级别的解答
/*如何将一个N2的算法改进到NlogN是一个很重要也经常需要做的优化*/
public class H1_4_8 { public static void main(String[] args) { //这段是为了产生一些随机数 // int max=100000; // Out out=new Out("h1_4_08.txt"); // for(int i=0;i<300000;i++) // out.printf("%d ", StdRandom.uniform(-max, max)); In in = new In("h1_4_08.txt"); int[] a = in.readAllInts(); in.close(); Stopwatch timer = new Stopwatch(); int cnt = countFast(a); StdOut.println("elapsed time = " + timer.elapsedTime()); StdOut.println(cnt); timer = new Stopwatch(); cnt = count(a); StdOut.println("elapsed time = " + timer.elapsedTime()); StdOut.println(cnt); } //暴力解法 public static int count(int[] arr) { int cnt=0; for(int i=0;i<arr.length;i++) for(int j=i+1;j<arr.length;j++) if(arr[i]==arr[j]) cnt++; return cnt; } //采用排序后的快速计数 public static int countFast(int[] arr) { Arrays.sort(arr); int cnt=0; for(int i=0;i<arr.length;i++) { int j=i; while(j<arr.length-1&&arr[i]==arr[j+1]) { cnt++; j++; } } return cnt; } }
1.4.9 已知由倍率实验可得某个程序的时间倍率为2b且问题规模为N0时程序的运行时间为T,给出一个公式预测该程序在处理规模为N的问题时所需的运行时间. /*对1.4.6节倍率实验巩固*/
首先,由倍率实验时间之间关系为 T(2*N)/T(N)=2b -->从而只需知 N=2kN0 -->k=log2(N/N0) --->T(N)=T*k=T*log2(N/N0)
1.4.10 修改二分查找算法,使之总是返回和被查找的键匹配索引的最小元素(且仍然能够保证对数级别的运行时间) /*二分查找重复元素的一种处理情况*/
public class H1_4_10 { public static int rank(int key, int[] a) { int lo = 0; int hi = a.length - 1; while (lo <= hi) { // Key is in a[lo..hi] or not present. int mid = lo + (hi - lo) / 2; if (key < a[mid]) hi = mid - 1; else if (key > a[mid]) lo = mid + 1; else { while(mid>0&&a[--mid]==key); //该语句排除了左边与之相同的值 return mid+1; } } return -1; } public static void main(String[] args) { int[] arr=new int[]{1,4,1,2,3,3,1,4,5,7,3,5}; Arrays.sort(arr); StdOut.println(Arrays.toString(arr)); StdOut.println(rank(3,arr)); StdOut.println(rank(4,arr)); } }
注:但上述算法的最坏情况并不是logN,例如全部数均相同等极端情况
1.4.11 为StaticSETofInts添加一个实例方法howMany(),找出给定键的出现次数且在最坏情况下所需的运行时间和logN成正比. /*对于数组中重复元素的处理*/
public class StaticSETofInts { private int[] a; public StaticSETofInts(int[] keys) { // defensive copy a = new int[keys.length]; for (int i = 0; i < keys.length; i++) a[i] = keys[i]; // sort the integers Arrays.sort(a); } public boolean contains(int key) { return rank(key) != -1; } public int rank(int key) { int lo = 0; int hi = a.length - 1; while (lo <= hi) { // Key is in a[lo..hi] or not present. int mid = lo + (hi - lo) / 2; if (key < a[mid]) hi = mid - 1; else if (key > a[mid]) lo = mid + 1; else return mid; } return -1; } public int howMany(int key) { int cnt=0; int k=rank(key); //该语句耗费logN int temp=k; while(k>=0&&a[k--]==key) //左边重复情况 cnt++; while(temp>=0&&temp<a.length-1&&a[++temp]==key) //右边重复情况 cnt++; return cnt; } public static void main(String[] args) { int[] arr=new int[]{1,4,5,5,2,1,4,2,3,12,2,12}; StaticSETofInts setints=new StaticSETofInts(arr); StdOut.println(setints.howMany(12)); } }
注:以上情况同样全部元素全相同等特殊情况运行时间非logN---暂时没想到改进方案
1.4.12 编写一个程序,有序打印给定的两个有序数组(含有N个int值)中的所有公共元素,程序在最坏情况下所需的运行时间和logN成正比 /*寻找公共元素,以下方法在合并两个有序数组也采用类似方法*/
public class H1_4_12 { //该函数前提是arrA和arrB均是已排序数组 public static void printTheCommonNum(int[] arrA,int[] arrB) { int ai=0,bi=0; int len=arrA.length+arrB.length; //最坏情况每个元素至少参与一次比较 for(int i=0;i<len;i++) { if(arrA[ai]>arrB[bi]) { bi++; if(bi==arrB.length) //避免数组溢出 break; } else if(arrA[ai]<arrB[bi]) { ai++; if(ai==arrA.length) //避免数组溢出 break; } else { StdOut.printf("%d ", arrA[ai]); ai++;bi++; if(ai==arrA.length||bi==arrB.length) //避免数组溢出 break; } } } public static void main(String[] args) { int[] arr1=new int[]{2,2,3}; int[] arr2=new int[]{1,2,3}; printTheCommonNum(arr1, arr2); } }
只有一个循环,长度为2N,因此为2N正比于N
1.4.13 根据正文中的假设分别给出表示以下数据类型的一个对象所需的内存量: /*以下假设基于对象开销16字节,一般内存的使用都会被填充为8字节(总内存8的倍数)*/
标签:
原文地址:http://www.cnblogs.com/Tinyshine/p/4781390.html