演算法第四版學習(chapter1.1)
這是我個人真正意義上的第一篇blog,今天我來講一下,我在這個暑假學習的一本書,演算法(第四版)。
第一章主要講述了一些標準工具的用法,這本書提供了一個標準庫,我們可以在他的網上下載下來,或者我這裡也可以
連結: https://pan.baidu.com/s/1Y89qdn5SWQ3KQfiF08ruPw 密碼: xsc7
我所遇到的難題:
1.1.25:使用數學歸納法證明歐幾里得演算法可以計算任意一對非負整數p和q的最大公約數
//歐幾里得演算法的java實現 public static int gcd(int p,int q){ if(q==0) return p; int r = p%q; return gcd(q,r); }
其實歐幾里得演算法也可以叫做輾轉相除
我們可以先分析一下這個演算法,想要這個演算法通過,我們要證明
當p%q=r
gcd(p,q)=gcd(q,r)
因為p%q=r,所以p=q*k+r(k為自然數)
我們設p和q的公約數為d,則p可以表示為d*i1,卻可以表示為d*i2(i1,i2均為正整數)
得d*i1=d*i2*k+r—>r=d*(i1-i2*k),所以在該情況下r和q的公約數也是d
由於d並不是單指其中一個,在所有情況下,皆能使r=d*(i1-i2*k)
所以我們能說gcd(p,q)=gcd(q,r).
鄙人的證明若有問題,歡迎指出。
1.1.27 :二態分佈。估計用一下程式碼計算(100,50,0.25)將會產生的遞迴呼叫次數:
我們先來講一下二態分佈公式的原理,二態公式其實就是一種統計學手法,binomial(n,k,p)n是獨立個體的個數,k是需要被選上的個體,p是被選上的機率。b(n,k,p)=(1-p)*b(n-1,k,p)+p*b(n-1,k-1,p)的意思其實是n個人裡面選k個,p是被選上的概率,有兩個方向:①沒選上概率1-p,等待被選人數減一,需要選的人數不變②被選上了概率p,等待被選人數減一,需要選的人數-1,以此類推最後得出的結果其實是,當人被挑選的概率為p,在n個人裡面選剛好k個人的機率。
我一開始失了智,還想測一下執行時長,果然這種不要命的遞迴測不得,編譯器都崩掉了。
long l = System.currentTimeMillis(); System.out.println("開始"+l); double binomial = day02.binomial(100, 50, 0.25); long l1=System.currentTimeMillis(); System.out.println(binomial+"--耗時"+(l1-l)); public static double binomial(int N,int k,double p){ if(N == 0 && k==0) return 1.0; if(N<0 || k<0) return 0.0; return (1.0-p)*binomial(N-1,k,p)+p*binomial(N-1,k-1,p); }
其實我們不難發現可以將它遞迴過程總結成公式b(N,k)=b(N-1,k)+b(N-1,k-1),那個p不影響遞迴,先不考慮,每次由一個式子進行兩次遞迴,遞迴後N-1,k不變或-1,當N,k都為0,或其一小於0就結束,由N來影響一共會有多少層遞迴呼叫,該題目N為100,N由100到99,呼叫兩次遞迴,產生兩個式子,這兩個式子也進行遞迴,各分成兩個,先不考慮k,當N從100到0,會有2++。。+,約為1.26*,現在來考慮k,k來決定那些式子提前結束遞迴(說明一個道理,當需要的k個人被選完了,N多少都不管用了,減掉選人-k分支,不選人的k不變的分支可以繼續往下走),這個我就不太會算了大概就是,等大佬來教,知道遞迴為什麼會炸了吧,這每次遞迴往下索值,這款記憶體並不會釋放,等會計算機就記憶體溢位了,或者直接崩掉。
現在來說一下怎麼去開進這個演算法,既然遞迴會炸,我們就不用遞迴了,改用迴圈來求,先給出我的程式碼。
public class day03 {
public static double binomial(int N,int k,double p){
//先定義一個數組,來儲存計算資料
double[][] array=new double[N+1][k+1];
//array[0][0]的意思是,有0個人供你選擇,從其總選中0個人的概率,那就必定是1啦
array[0][0]=1.0;
//現將從頭到尾都不選的這一列都給上值,而都選是不可能的,所以不初始化了
for(int i=1;i<N+1;i++){
array[i][0]=array[i-1][0]*(1-p);
}
//我來解釋一下這一段,先是逐層往下計算,因為這個問題有明顯的上下層關係
//j在遍歷過程中不能大於能被選的人數,不能大於需要被選的人數,減少一些多餘的分支
// array[i][j]就是有i個人選j個人的機率,有兩種情況可以達成,i-1人選j人後不選,i-1人選j-1人後選一個
for(int i=1;i<N+1;i++){
for(int j=1;j<N+1&&j<k+1;j++){
array[i][j]=(1-p)*array[i-1][j]+p*array[i-1][j-1];
}
}
//最後返回需要的情況的機率
return array[N][k];
}
}
1.1.28:陣列去重,方法太多了,不打算寫,什麼排序後set,什麼Arraylist的方法indexof檢查是否在Arraylist裡,再做新增,blablabla。。。
1.1.30:呼叫gcd,好吧,查最大公因子是不是1,沒什麼好說的了。
1.1.31:隨機連線,上程式碼吧,為了看起來至觀,我把不同的線分層了。
public static void randomLine(int N,double p){
StdDraw.setXscale(0,N);
StdDraw.setYscale(0,N*N);
double count=0.0;
for(int i=0;i<N+1;i++){
for(int j=i+1;j<N+1;j++){
if(StdRandom.random()<p){
StdDraw.line(i*0.5,count*4,j*0.5,count*4);
count+=0.5;
}
}
}
}
大概長這個樣子吧。
1.1.35:骰子問題:呼叫StdRandom生成一些隨機數就好了,沒什麼難度。
public static void dice(int i) {
DecimalFormat df = new DecimalFormat("######0.000");
int N = 10;
double count = 0;
boolean flag = true;
for(int j=1;j<=6;j++){
for(int k=1;k<=6;k++){
if(k+j==i){
count++;
}
}
}
double rate=count/36.0;
System.out.println(df.format(rate));
while (flag) {
count=0.0;
int x = N;
while (x-- > 0) {
int a = (int) (StdRandom.uniform(1, 6 + 1));
int b = (int) (StdRandom.uniform(1, 6 + 1));
if (a + b == i) {
count++;
//System.out.println(a + " " + b);
}
}
double rate1=count / (double) N;
if(df.format(rate1).equals(df.format(rate))){
System.out.println("find it"+N);
flag=false;
}else{
System.out.println(N+"--"+df.format(rate1)+"--"+df.format(rate));
N*=10;
}
}
}
1.1.39隨機匹配:
public static void match(int T) {
int t=T;
int count3 = 0;
int count4 = 0;
int count5 = 0;
int count6 = 0;
ArrayList arrayList = new ArrayList();
int[] q3=new int[1000];
int[] q4=new int[10000];
int[] q5=new int[100000];
int[] q6=new int[1000000];
while (t-- > 0) {
for (int i = 0; i < 1000; i++) {
int a = StdRandom.uniform(1000000, 10000000);
q3[i]=a;
}
Arrays.sort(q3);
for (int i = 0; i < 1000; i++) {
int b = StdRandom.uniform(1000000, 10000000);
if(day02.sort(q3,b)>-1){
count3++;
}
}
System.out.println("go");
arrayList.clear();
for (int i = 0; i < 10000; i++) {
int a = StdRandom.uniform(1000000, 10000000);
q4[i]=a;
}
for (int i = 0; i < 10000; i++) {
int b = StdRandom.uniform(1000000, 10000000);
if(day02.sort(q4,b)>-1){
count4++;
}
}
arrayList.clear();
for (int i = 0; i < 100000; i++) {
int a = StdRandom.uniform(1000000, 10000000);
q5[i]=a;
}
for (int i = 0; i < 100000; i++) {
int b = StdRandom.uniform(1000000, 10000000);
if(day02.sort(q5,b)>-1){
count5++;
}
}
arrayList.clear();
for (int i = 0; i < 1000000; i++) {
int a = StdRandom.uniform(1000000, 10000000);
q6[i]=a;
}
for (int i = 0; i < 1000000; i++) {
int b = StdRandom.uniform(1000000, 10000000);
if(day02.sort(q6,b)>-1){
count6++;
}
}
}
System.out.println("10的三次方平均數"+count3/T);
System.out.println("10的四次方平均數"+count4/T);
System.out.println("10的五次方平均數"+count5/T);
System.out.println("10的六次方平均數"+count6/T);
}
這一塊的學習就先到此為止了,有不太明白的各位,可以在下面留言問我,我會盡快回答