LeetCode第50題_Pow(x, n)_Divide & Conquer

題目
Implement pow(xn), which calculates x raised to the power n (xn).



Example 1:
Input: 2.00000, 10
Output: 1024.00000
Example 2:
Input: 2.10000, 3
Output: 9.26100
Example 3:
Input: 2.00000, -2
Output: 0.25000
Explanation: 2-2 = 1/22 = 1/4 = 0.25
Note:
  • -100.0 < x < 100.0
  • n is a 32-bit signed integer, within the range [−231, 231 − 1]




需要考量的問題:
1.輸入的指數正負性
2.指數為奇為偶的情況


較直觀且簡易的一般作法
是直接跑Loop

當次方為1時代表保持不變回傳原本輸入的數值
其餘狀況
則判定指數n是否>1
是 ,則依序做n次累乘
否 ,則做累除



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package leetcode50_pow;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 *
 * @author chous
 */
public class LeetCode50_Pow {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        // TODO code application logic here
        BufferedReader inputBase, inputExponent;
        inputBase = new BufferedReader(new InputStreamReader(System.in));
        inputExponent = new BufferedReader(new InputStreamReader(System.in));

        System.out.println("Input:");
        String strBase = inputBase.readLine();
        String strExponent = inputExponent.readLine();

        double numBase = Double.parseDouble(strBase);
        int numExponent = Integer.parseInt(strExponent);

        System.out.println(myPow(numBase, numExponent));

    }
    //x^n次方
    //解法1.
    public static double myPow(double x, int n) {
        double res = 1;
        if (n == 1) {
            res = x;
        } else {
            if (n > 1) {
                for (int i = 1; i <= n; i++) {
                    res = (double) (res * x);
                }
            } else {
                for (int i = 1; i <= n * (-1); i++) {
                    res = (double) (res / x);
                }
            }
        }
        return res;
    }
}

當然這題如果直接用這種作法會  Exceed Time
所以要換另一種演算法來實踐改進
當輸入情況為如下case的時候

底數: 0.00001 指數: 2147483647

如果你拿去自己本機試跑會發現
跑了3分鐘多都還沒算完
沒耐心就中斷了


在此要介紹一個演算法思維技巧

就是「分治演算法」(Divide & Conquer)

在許多情況下分治法很常會搭配遞迴方式去對問題進行拆解
一般較為具體的流程

Step1. 分解:把遇到的問題可能規模一開始較大拆分為若干個小的模式且相互獨立維持原問題形式的子問題

Step2. 解決:如果透過上一步驟獲得之子問題可以解則進行解決反之則回至上一步以相同方式再次分解,直到可解決為止。

Step3. 合併:將第二步所解出來各個子問題解以某種規則合併出最終原始規模大的問題解



較困難的點在於如何去拆解問題然後做合併

這類解法主要目的
常為了解決像是降低時間複雜度(讓計算量不要負擔太大!!!!)
比方1000個數字要進行排序
把它一分為二變成兩個各自處裡500個數字的排序最和再合併
有點像做任務多一點分工的概念
直到最後將排序工作簡化為單獨去實踐兩個數字排序(比對)的概念
而此模式可以重複去調用而更能變得容易解決。

剛剛上述舉的例子就是快速排序的實作原理
================================================

還是不太懂!!!
我們舉一個更加淺顯易懂的例子
比方說有A、B兩個人在進行1到1000之間
猜數遊戲(A在問B來猜,A只會回答yes/no)

B就開始問
是1嗎?
是2嗎?
是3嗎?
....
是999嗎?

這就是O(n) --> 依序窮舉出來(又稱為順序查找)
最好的情況下是只需要一次就猜中(也就是查找目標就是第一個)
也就是O(1) -->只要比較1次
最差情況就是
要命中的該數是最後一個999
而我們的B則從1開始依序非常有耐心地猜問到第999次才命中
也就是O(n) --> 要比較n次

平均下來只少就要問500次


聰明的作法就是
大於500嗎?  --> 是
大於750嗎?  --> 是
大於625嗎?
...
每次透過縮小猜測範圍至上一次的一半

所以只需要10次以內即可把目標數字猜測出來
這種思維又稱為  二分查找
================================================

原作法和分治算法比較







當我們給的輸入
底數:2 指數:4的時候

原本我們要做4次累乘
2的1次方-> 2*1=2
2的2次方-> 2*2 = 4
2的3次方-> 4*2=8
2的4次方-> 8*2=16

經由二分法直接簡化成
可以將
2^4 -->4^2 -> 16
這部就是先將2的四次方 演變為
2的平方的平方
先計算2的平方部分獲得4
4在做一次  4*4即可獲得最終解
因此累計做乘法運算的次數為2




2^9 --> 2 *2^8 = 2* (2^2)^4=2*4^4
2*4^4 --> 2*(4^2)2 = 2*16^2







 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package leetcode50_pow;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 *
 * @author chous
 */
public class LeetCode50_Pow {
    private static int loopTimes = 0;
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        // TODO code application logic here
        BufferedReader inputBase, inputExponent;
        inputBase = new BufferedReader(new InputStreamReader(System.in));
        inputExponent = new BufferedReader(new InputStreamReader(System.in));

        System.out.println("Input:");
        String strBase = inputBase.readLine();
        String strExponent = inputExponent.readLine();

        double numBase = Double.parseDouble(strBase);
        int numExponent = Integer.parseInt(strExponent);
        System.out.println(myPow(numBase, numExponent));
        System.out.println("迴圈執行次數:" + loopTimes);

        System.out.println(myPow2(numBase, numExponent));
        System.out.println("執行次數:" + loopTimes);
    }
    //x^n次方
    //解法1.
    public static double myPow(double x, int n) {
        double res = 1;
        if (n == 1) {
            res = x;
        } else {
            if (n > 1) {
                for (int i = 1; i <= n; i++) {
                    loopTimes ++;
                    res = (double) (res * x);
                }
            } else {
                for (int i = 1; i <= n * (-1); i++) {
                    res = (double) (res / x);
                }
            }
        }
        return res;
    }
    //解法2.分治法(二分法)
    //2^4 -> 4^2 -> 16
    public static double myPow2(double x,int n){
        double res = 1;
        if(n==0 || x==1) // 任何數值的0次方結果皆為1 , 1的任何次方結果皆為1
            return 1;
        if(n==1) //任何數值的1次方結果皆為自己本身
            return x;
        if(n<0)
            return 1/(x*myPow2(x,-(n+1)));
        
        while(n>1){
            if(n%2==1)//為奇數 2^9 = 2*2^8
                res*=x; //--> 2
            x = x*x;//
            n/=2; //  9/2=8
        }
        res*=x;
        return res;
    }
}



留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題