PAT 1033 To Fill or Not to Fill (25分) 貪心思想_台中搬家公司

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

題目

With highways available, driving a car from Hangzhou to any other city is easy. But since the tank capacity of a car is limited, we have to find gas stations on the way from time to time. Different gas station may give different price. You are asked to carefully design the cheapest route to go.

Input Specification:
Each input file contains one test case. For each case, the first line contains 4 positive numbers: C​max​​ (≤ 100), the maximum capacity of the tank; D (≤30000), the distance between Hangzhou and the destination city; D​avg​​ (≤20), the average distance per unit gas that the car can run; and N (≤ 500), the total number of gas stations. Then N lines follow, each contains a pair of non-negative numbers: P​i​​ , the unit gas price, and D​i​​ (≤D), the distance between this station and Hangzhou, for i=1,⋯,N. All the numbers in a line are separated by a space.

Output Specification:
For each test case, print the cheapest price in a line, accurate up to 2 decimal places. It is assumed that the tank is empty at the beginning. If it is impossible to reach the destination, print The maximum travel distance = X where X is the maximum possible distance the car can run, accurate up to 2 decimal places.

Sample Input 1:
50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
Sample Output 1:
749.17
Sample Input 2:
50 1300 12 2
7.10 0
7.00 600
Sample Output 2:
The maximum travel distance = 1200.00

題目解讀

題目大意:汽車從杭州出發可以通過高速公路去任何城市,但是油箱的容量是有限的,路上有很多加油站,每個加油站的價格不同,為汽車設計一個從杭州到終點的最便宜的加油策略。

輸入:第一行:Cmax表示油箱最大容量,D表示杭州到目的地的距離,Davg表示平均每單位的汽油可以讓汽車行駛的距離,N表示途中加油站的數量;接下來 N 行:給出給個加油站的單位油價Pi和杭州(起點)到這個站點的距離Di

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

輸出:求汽車從杭州到終點的最少花費(精確到兩位小數)。如果不能夠到達,就輸出汽車能夠行駛的最大距離(精確到兩位小數)。

思路分析

核心思想:貪心算法(每次尋找局部最優,在最便宜的加油站加最多的油)。

前期準備:

  • 最終輸出無論是距離還是價格都要求精確到兩位小數,雖然從給出的輸入數據來看,距離、郵箱容量等好像都是整數,但為了操作方便,避免運算過程精度丟失,我們全都用double保存。(再說了,它給出的數據說不定就是坑你的呢?)
  • 設置結構體數組保存每個加油站的單價和到杭州的距離。
  • 按照到杭州的距離對結構體數組排序,因為輸入是無序的。
  • 排序完判斷第1個結構體到杭州的距離是否為0,也就是說最近的加油站是不是在起點。因為題目說了假定剛開始郵箱沒有油,那麼如果起點處沒有加油站,就比欸想開車了,直接輸出並返回吧。

貪心核心:怎麼實現每次都做出局部最優的選擇?

對於任意一個站點:如果我們在這個站點加滿油,那麼最多就可以跑cmax*davg的距離,我們對這個距離段中遇到的加油站情況進行分析:

  • 按順序遍歷【當前位置,當前位置+cmax*davg】中的所有加油站,如果某個加油站的收費低於當前站點,那麼我就在當前站點加油,跑到那個站點去,加多少呢?就加能恰好讓我到達那個加油站的油。這樣我去那個站點加油就能更便宜。
  • 如果當前站點後面有不止一個站點更便宜,怎麼選擇?比如我現在在A,價格是10,後面是 B 價格9, 後面是C 價格8, 我先找到的是B,那我就退出本次循環,剛好加油跑到B去,在B處重新繼續分析。為啥不直接加油去C,如果從當前位置直接加油去C,那麼BC之間的花費單價是當前加油站的價格也就是10,但我如果先去了B,那麼從B到C的油價就是B處的價格9,顯然更便宜。這樣才滿足局部最優。
  • 如果當前位置後面沒有更便宜的加油站呢?
    • 如果我在當前位置最多能達到的最遠距離超過了終點,那麼我直接加油跑到終點,因為後面的站點只會更貴。
    • 如果我不能直接到終點,那麼我肯定是需要加油的,那我就找盡可能地找比較便宜的那個加油站,在當前加油站加滿油之後過去。既然沒有比當前更低價格的了,就讓油箱加到最大值,這樣能保證利益最大化,保證最大的距離使用的是便宜的油。
  • 如果當前位置不能直接到達終點,並且後面沒有加油站了呢?那麼肯定不能到達中終點了,只能到達當前位置+cmax*davg,也就是說在當前位置加滿,能跑多遠是多遠。

總結:

  • 當前位置能到達的範圍中如果存在更便宜的加油站,就加合適的油剛好到達那個加油站。
  • 如果不存在更便宜的,但是當前位置能直接到達終點,那就加合適的油到達終點。
  • 不存在更便宜的,並且不能直接到終點,找到可達的加油站的相對而言最便宜那個,在當前位置加滿油,然後去那個站點。
  • 當前位置不能到終點,並且後面沒有加油站了,輸出能到達的最大距離。

代碼

#include <iostream>
#include <algorithm>
using namespace std;

struct Station {
    // 每單位汽油價格
    double price;
    // 從起點到這裏的距離
    double dis;
}stat[500];

// 對加油站 離起點 從近到遠 排序
bool cmp(Station a, Station b) {
    return a.dis < b.dis;
}

int main() {
    // cmax油箱容量 d 距離 davg 每單位油能跑多遠 n個加油站
    double cmax, d, davg;
    int n;
    cin >> cmax >> d >> davg >> n;
    // 每個加油站的 價格 位置
    for (int i = 0; i < n; ++i) 
        cin >> stat[i].price >> stat[i].dis;
    // 根據離起點的距離排序
    sort(stat, stat + n, cmp);
    // 第一個加油站不在起點,無法起步
    if(stat[0].dis != 0) {
        printf("The maximum travel distance = 0.00");
        return 0;
    } 
    // nowpos當前在哪個加油站,
    int nowpos = 0;
    // nowgas 當前剩餘多少油,total_price 總花費
    double nowgas = 0.00, total_price = 0.00;
    // 油箱加滿一次油,可以跑多遠
    double each_max_run_dis = cmax * davg;
    // 是否到達終點
    bool arrived = false;
    while (!arrived) {
        // 遍歷 【從當前加油站到最遠能跑到的位置】 之間的 全部加油站
        bool exist_stat  = false; // 當前位置後面是否存在可達加油站
        bool exist_cheaper_stat = false; // 是否存在比當前位置更便宜的加油站
        // 不存在比當前便宜的,就找到其中價格最低的
        int min_pos = -1; double min_price = 9999.99;
        for (int i = nowpos + 1; i < n; ++i) {
            // 當前位置到不了這個加油站,退出循環
            if (stat[i].dis - stat[nowpos].dis > each_max_run_dis) {
                // 最多還能走 nowgas * davg
                break;
            }
            exist_stat = true; // 存在可達加油站
            // 如果有比當前加油站價格更低的加油站
            // 算一下恰好跑到那個加油站需要多少油,
            // 加油跑到那個加油站
            if (stat[i].price < stat[nowpos].price) {
                // 設置標誌
                exist_cheaper_stat = true;
                double needgas = (stat[i].dis - stat[nowpos].dis) / davg - nowgas;
                // 加這麼多油,剛好跑到那個加油站,算一下花費
                total_price += stat[nowpos].price * needgas;
                // 到達那個位置后剩餘油量歸0
                nowgas = 0;
                // 改變位置
                nowpos = i;
                // 不再遍歷後面的加油站
                // 比如我現在 在A,價格是10,後面是 B 價格9, 後面是C 價格8
                // 我先找到的是B,那我就剛好加油跑到B去,在B處重新考慮
                // 為啥不直接加油去C,如果從當前位置直接加油去C,那麼BC之間的花費單價是當前加油站的價格也就是10
                // 但我如果先去了B,那麼從B到C的油價就是B處的價格9,顯然更便宜
                // 這樣才滿足局部最優
                break;
            }
            // 如果說我能從當前位置跑1000米,但是在此之間的加油站的價格沒有一個比我現在的價格低
            // 那我就盡量找最便宜的那個,然後在當前位置加滿油,跑到相對而言最便宜的那個加油站去加油
            // 這樣才滿足局部最優(在最便宜的位置加更多的油跑最多的距離)
            // 這個if不會和上面的if同時執行(上面執行完就break了),所以不用加else
            if (stat[i].price < min_price) {
                min_pos = i;
                min_price = stat[i].price;
            }
        }
        // 不存在比當前便宜的,但是當前位置最遠能達到終點
        if (!exist_cheaper_stat && (d - stat[nowpos].dis <= each_max_run_dis)) {
            double needgas = (d - stat[nowpos].dis) / davg - nowgas;
            // 加這麼多油,剛好跑到終點,算一下花費
            total_price += stat[nowpos].price * needgas;
            // 到達終點
            arrived = true;
            break;
        }
        // 不存在比當前便宜的,但是找到了其他加油站中相對最便宜那個
        if (!exist_cheaper_stat && exist_stat) {
            // 後面有加油站,但是都比當前位置的加油站貴
            // 那我就盡量找最便宜的那個,然後在當前位置加滿油,跑到相對而言最便宜的那個加油站去加油
            // 這樣才滿足局部最優(在最便宜的位置加更多的油跑最多的距離)
            double needgas = cmax - nowgas;
            // 在當前位置加滿油,算一下花費
            total_price += stat[nowpos].price * needgas;
            // 到達那個位置后的剩餘油量
            nowgas = cmax - (stat[min_pos].dis - stat[nowpos].dis) / davg;
            // 改變位置
            nowpos = min_pos;
        // 當前位置無法抵達下一個加油站
        } else if (!exist_stat){
            // 最多還能走 cmax * davg
            printf("The maximum travel distance = %.2f", stat[nowpos].dis + each_max_run_dis);
            return 0;
        }
    }
    // while正常結束,說明到達終點
    printf("%.2f", total_price);
    return 0;
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

離散數學 II(最全面的知識點匯總)_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

離散數學 II(知識點匯總)

目錄

  • 離散數學 II(知識點匯總)
    • 代數系統
      • 代數系統定義
        • 例子
      • 二元運算定義
    • 運算及其性質
      • 二元運算的性質
        • 封閉性
        • 可交換性
        • 可結合性
        • 可分配性
        • 吸收律
        • 等冪性
        • 消去律
      • 特殊的元素性質
        • 幺元
        • 零元
        • 逆元
          • 證明逆元且唯一定理
      • 二元運算表中性質的體現
    • 半群
      • 廣群
        • 成立條件
      • 半群
        • 定義
        • 特性
        • 子半群
      • 獨異點
        • 成立條件
        • 特性
      • 證明是半群或獨異點
    • 群和子群
        • 定義
        • 階數、有限群、無限群
          • 1階、2階、3階、4階群
        • 特性
          • 冪特性
          • 運算表特性
        • 運算
        • 子群
          • 定義
          • 判定條件
          • 性質
          • 平凡子群
          • 中心
          • 共軛子群
    • 阿貝爾群和循環群
      • 阿貝爾群 / 交換群
        • 定義
        • 判定
      • 循環群
        • 定義
        • 特性
        • 元素的階
          • 定義
          • 性質
        • 子群性質
    • 置換群和伯恩賽德定理
      • 置換
        • 成立條件
        • 運算
      • 置換群
        • 定義
        • 對稱群
        • 交錯群
        • 輪換
          • 定義
          • 記法
          • 對換
            • 定義
            • 性質
        • 誘導的二元關係
          • 定義
          • 性質
        • 三元素集的置換群
          • 對稱群
          • 交錯群
      • 伯恩賽德定理
    • 陪集和拉格朗日定理
      • 陪集
        • 定義
        • 性質
      • 特殊關係
        • 劃分
        • 等價關係
        • 等價類
        • 商集 A/R
      • 子群的指數
      • 拉格朗日定理
        • 推論
    • 正規子群和商群
      • 正規子群 / 不變子群
        • 定義
        • 判別
        • 單群
        • 性質
      • 商群
        • 運算
        • 定義
        • 性質
        • 推論
    • 同態與同構
      • 同態映射 / 同態 ~
        • 定義
        • 同態象
        • 自然同態
        • 分類
          • 同構
            • 凱萊定理
        • 自同態 / 自同構
      • 同態映射性質
      • 同態核
        • 定義
        • 性質
      • 同態基本定理
      • 第一同構定理 / 商群同構定理
    • 環與域
      • 定義
        • 零元
        • 單位元
        • 負元
        • 逆元
      • 例子
      • 性質
      • 特殊環
        • 交換環
        • 含幺環
        • 無零因子環
          • 零因子
      • 整環
        • 定義
      • 子環
        • 定義
        • 判定定理
        • 定義
        • 例子
      • 域與整環的關係
      • 環的同態定義
        • 分類
        • 同態像及其特性
          • 綜合例題

代數系統

代數系統定義

一個非空集合A,連同若干個定義在該集合上的運算f1,f2,…,fk,所組成的系統就稱為一個代數系統,記作<A, f1,f2,…,fk >。

例子

例:<N,+>,<Z,+,·>,<R,+,·>都是代數系統,其中+和·分別表示普通加法和乘法。
例:<Mn(R),+,·>是代數系統,其中+和·分別表示n階(n≥2)實矩陣的加法和乘法。
例:<ρ(S),∪,∩,~ >也是代數系統,其中含有兩個二元運算∪和∩以及一個一元運算 ~。

二元運算定義

S為非空集合,從S×S->S的映射: f: S×S->S稱為集合S上的一個二元運算。

運算及其性質

二元運算的性質

封閉性

  • Premise:\(*\)是定義在集合A上的二元運算, \(\forall\ x,y\in A\)
  • Condition:\(\ x*y\in A\)
  • Summary:\(*\)在A上是封閉的

可交換性

  • Premise:\(*\)是定義在集合A上的二元運算, \(\forall\ x,y\in A\)
  • Condition:\(x*y=y*x\)
  • Summary:\(*\)在A上是可交換的

可結合性

  • Premise:\(*\)是定義在集合A上的二元運算, \(\forall\ x,y,z\in A\)
  • Condition:\((x*y)*z=x*(y*z)\)
  • Summary:\(*\)在A上是可結合的

可分配性

  • Premise:\(*,\triangle\)是定義在集合A上的二元運算, \(\forall\ x,y,z\in A\)
  • Condition:\(x*(y\triangle z)=(x*y)\triangle (x*z)\)\((y\triangle z)*x=(y*x)\triangle (z*x)\)
  • Summary:在A上,\(*\)對於$\triangle $是可分配的

吸收律

  • Premise:\(*,\triangle\)是定義在集合A上的二元運算, \(\forall\ x,y\in A\)
  • Condition:\(x*(x\triangle y)=x\)\(x\triangle (x*y)=x\)
  • Summary:\(*\)和$\triangle $在A上滿足吸收律

等冪性

  • Premise:設\(*\)是定義在集合A上的二元運算, \(\forall\ x\in A\)
  • Condition:\(x*x=x\)
  • Summary:\(*\)在A上是等冪的

消去律

  • Premise:設\(*\)是定義在集合A上的二元運算, \(\forall\ x,y,z \in A\)
  • Condition:(左消去律)\(x*y=x*z\Rightarrow y=z\)、(右消去律)\(y*x=z*x\Rightarrow y=z\)
  • Summary:\(*\)在A上是滿足消去律的

特殊的元素性質

\(*\)是定義在集合A上的二元運算

幺元

  • 左幺元:對於\(e_l\in A,\ \forall\ x\in A,\ e_l*x=x\)
  • 右幺元:對於\(e_r\in A,\ \forall\ x\in A,\ x*e_r=x\)
  • 幺元:對於\(e\in A\)\(e\)既是左幺元又是右幺元

零元

  • 左零元:對於\(\theta_l\in A,\ \forall\ x\in A,\ \theta_l*x=\theta_l\)
  • 右零元:對於\(\theta_r\in A,\ \forall\ x\in A,\ x*\theta_r=\theta_r\)
  • 零元:對於\(\theta\in A\)\(e\)既是左零元又是右零元

逆元

設在代數系統\(<A,*>\)中,\(*\)為二元運算,e為A中關於\(*\)的幺元,\(a,b\in A\)

  • 左逆元\(b*a=e\),則b為a的左逆元
  • 右逆元\(a*b=e\),則b為a的右逆元
  • 逆元:b​既是a的左逆元又是右逆元,則b為a的逆元,記為a^-1^
    • 此時有a與b互為逆元
證明逆元且唯一定理
  • Premise:\(\forall\ a\in A\),e為A的逆元,\(*\)為A的二元運算
  • Condition:a都有左逆元,\(*\)可結合
  • Summary:a的左逆元為a的逆元且唯一

二元運算表中性質的體現

\(*\)是定義在集合A上的二元運算

  • 封閉性\(\Leftrightarrow\)運算表中所有元素\(\in A\)
  • 可交換性\(\Leftrightarrow\)運算表中所有元素沿對角線對稱
  • 等冪性\(\Leftrightarrow\)運算表中主對角線元素等於本身
  • 零元\(\Leftrightarrow\)該元素運算行列元素與其本身相同
  • 幺元\(\Leftrightarrow\)該元素運算行列元素與其對應的行列元素一致
  • 逆元\(\Leftrightarrow\)兩元素行列相交處都是幺元

半群

廣群

成立條件

  • \(*\)運算封閉

半群

定義

  • \(*\)運算封閉
  • \(*\)運算可結合

特性

  • A元素有限,則必有等冪元

證:

∵ <S, *>是半群,∴對於\(\forall\)b \(\in\)S,由運算*封閉可知:
b^2^=b*b\(\in\)S,b^2^ *b=b*b^2^=b^3^\(\in\)S ,b^4^,b^5^… \(\in\)S
∵ S有限,∴必定\(\exists\)i,j,j>i,有b^i^=b^j^(第一輪)
∴ b^i^ =b^j^ =b^j-i^ * b^i^
令p=j-i ,則有 b^i^ =b^p^ * b^i^
∴ 對任意q≥i, 有b^q^= b^p^ *b^q^ (第二輪)
又∵p≥1 ∴$\exists $k,有kp≥i,則有b^kp^=b^p^ *b^kp^ (第三輪)
由b^kp^=b^p^ *b^kp^得: b^kp^=b^p^ *b^kp^=b^p^ *(b^p^ *b^kp^)=…=b^kp^ *b^kp^
∴令a=b^kp^ \(\in\)S 則a*a=a,∴b^kp^是等冪元。

子半群

  • \(B\subseteq A\)
  • \(*\)在B上運算封閉

獨異點

成立條件

  • 為半群
  • 含幺元

特性

  • 運算表任意兩行兩列都不相同

證:

設獨異點中幺元為e,對於任意 a,bS且a≠b,總有
(1)∵a*e=a ≠ b=b*e
由a,b任意性, 有<S, *>運算表中任兩行不同;
(2)∵e*a = a ≠ b = e*b
由a,b任意性,有<S, *>運算表中任兩列不同。

  • 若a,b均有逆元,則
    • \((a^{-1})^{-1}=a\)
    • \(a*b\)有逆元,且\((a*b)^{-1}=b^{-1}*a^{-1}\)

證:

a) ∵a^-1^是a的逆元

​ ∴a^-1^既是a的左逆元又是a的右逆元

​ 即:a^-1^ *a=a *a^-1^=e

​ ∴a既是a^-1^的右逆元又是a^-1^的左逆元,

​ ∴ a是a^-1^的逆元 即(a^-1^)^-1^=a

b) 要證(a *b)^-1^=b^-1^ *a^-1^,即證b^-1^ *a^-1^為a*b的逆元。

∵(a*b) *(b^-1^ *a^-1^)=a* (b*b^-1^) *a^-1^=a*e*a^-1^=e

∴b^-1^ *a^-1^是a*b的右逆元,

又∵(b^-1^ *a^-1^)*(a *b)=b^-1^ *(a^-1^ *a)*b=e

∴b^-1^ *a^-1^是a*b的左逆元,

∴(a*b)^-1^=b^-1^ *a^-1^

證明是半群或獨異點

按定義證明

群和子群

定義

  • 運算封閉
  • 可結合
  • 存在幺元e
  • 對於每一個元素\(x\in G\),存在逆元$x^{-1}

階數、有限群、無限群

如果\(<G,*>\)為群且元素有限,則稱為有限群,元素個數稱為群的階數,否則稱為無限群

1階、2階、3階、4階群

1~4階都有循環群,可以用mod運算推

4階還有克萊因四元群,如下

* e a b c
e e a b c
a a e c b
b b c e a
c c b a e

特性

  • 階大於1的群中不可能有零元

證:

(1)當群的階為1時,它的唯一元素視作幺元e;

(2)設|G|>1且群<G, *>中有零元q,那麼群中

​ ∀x∈G,*都有q*x=x*q=q ≠ e

所以零元q不存在逆元,這與<G, *>是群矛盾。

  • $\forall\ a,b\in G,\ \exists\ \(唯一的\)x,\ a*x=b$

證:

(1)存在性
設群<G, *>的單位元為e,令x=a^-1^ *b, 則
a*x=a*(a^-1^ *b)=(a*a^-1^) *b=e*b=b
所以x=a^-1^ *b是方程a*x=b的解。
(2)唯一性
若還有x′∈G, 使得a*x′=b, 則
x′=e*x′
=(a^-1^ *a)*x′=a^-1^ *(a*x′)=a^-1^ *b=x
故x=a^-1^ *b是方程a*x=b的唯一解。

  • 滿足消去律

證:

a*b=a*c

$\Rightarrow $ a^-1^ *(a*b)=a^-1^ *(a*c)

$\Rightarrow $ (a^-1^ *a) *b=(a^-1^ *a)*c

$\Rightarrow $ e*b=e*c

$\Rightarrow $ b=c

冪特性
  • 除了幺元外,不存在其他等冪元
  • 關於逆元,群中任一元素逆元唯一,且有:
    • \((a^{-1})^{-1}=a\)
    • \((a*b)^{-1}=b^{-1}*a^{-1}\)
    • \((a^{n})^{-1}=(a^{-1})^n=a^{-n}\)

證:

已學定理5-2.4:設代數系統<A, *> , A中存在幺元e,且$\forall $x∈A,都存在左逆元,若*是可結合的運算,那麼<A, *> 中任何一個元素的左逆元必定也是該元素的右逆元,且每個元素的逆元唯一。

證明:

∵群滿足結合律,且群中每個元素都有逆元,

∴每個元素都有左逆元,

∴每個元素的逆元唯一。

運算表特性
  • 每一行與每一列都是G元素的一個置換,沒有相同元素
  • 運算表中任意兩行或者兩列都不相同

運算

AB={ab|a∈A,b∈B}
A^-1^={a^-1^|a∈A}
gA={ga|a∈A}

子群

記為H\(\leq\)G,真子群記為H<G

定義
  • 為一個群的非空子集
  • 也為群
判定條件
  1. 非空\(S\subseteq G\),且S也是群
  2. 非空\(S\subseteq G\),G為有限群,S中運算封閉
  3. 非空\(S\subseteq G\),有\(a*b^{-1}\in S\)
性質

若<H, *>和<K, *>為<G, *>子群,則

  • <H\(\cap\)K, *>也是子群
  • <H\(\cup\)K, *>是子群 當且僅當 H\(\subseteq\)K或K\(\subseteq\)H
  • HK是子群 當且僅當 HK=KH
平凡子群

\(S=\{e\}\quad OR\quad S=G\)

中心

對於\(C=\{y|y*a=a*y,y\in G\}\),則<C, *>為子群,稱為G的中心

共軛子群

若H為G子群,則xHx^-1^={x*h*x^-1^|h ∈H}也是G的子群,稱xHx^-1^是H的共軛子群

阿貝爾群和循環群

阿貝爾群 / 交換群

定義

  • 是群
  • \(*\)可交換

判定

  • 是群,且\(\forall\ a,b\in G,\ (a*b)*(a*b)=(a*a)*(b*b)\)

證:

充分性 即證a*b=b*a。
∵ (a*b)*(a*b)=(a*a)*(b*b) 且<G,*>是群,*可結合
∴ a*(b*a)*b=a*(a*b)*b
∴ a^-1^ *(a*(a*b)*b)*b^-1^=a^-1^ *(a*(b*a)*b)*b^-1^
即有:a*b=b*a, ∴ <G,*>是阿貝爾群。
必要性 ∵ <G,*>是阿貝爾群,
∴對∀a,b∈G,有:a*b=b*a
∴ (a*b)*(a*b)=a*(b*a)*b=a*(a*b)*b=(a*a)*(b*b)

循環群

定義

\(\exists\ a\in G,\ \forall\ b\in G\),b都能表示成a的冪,a稱為生成元

特性

  • 是阿貝爾群
  • 如果是有限群,階數為n,則
    • 幺元為a^n^
    • \(\psi(n)\)個生成元,(歐拉函數,表示小於n且與n互質的正整數個數)
    • G的其他生成元即\(a^k\),k與n互質
  • 若階數無限,則只有兩個生成元e和e^-1^

元素的階

定義

最小正整數k使某一元素\(a^k=e\),則k為a的階(周期)

性質
  • a^k^=e \(\iff\) r | k

    (k是r的整數倍,即存在整數m,使得k=rm )

證:

充分性:r | k \(\Rightarrow\) a^k^=e

設 r | k,則存在整數m,使得k=rm,

​ a^k^= a^rm^=(a^r^)^m^=e^m^=e

必要性:a^k^=e \(\Rightarrow\) r | k

若a^k^=e,由帶余除法,一定存在整數p,q,使得

k=pr+q(0≤q<r),於是a^k^=a^pr+q^=a^pr^ *a^q^=(a^r^)^p^ *a^q^ =(e)^p^ *a^q^ =e*a^q^ =a^q^ =e (a^k^=e)

∵ r是a的階,即使得a^r^=e的最小正整數

∴只有q=0才可能有a^q^ =e, ∴ k=pr 即r | k。

  • O(a)= O(a^-1^)(元素與其逆元的階相同)

證:

O(a)= O(a^-1^)(元素與其逆元的階相同)

證:∀a∈G,a的階為r, a^-1^的階為r’,

則 (a^-1^)^r’^=e ,a^r^=e

∵ (a^r^)^-1^ *a^r^=e 且a^r^=e,
∴ (a^r^)^-1^=e( (a^r^)^-1^與e做運算=e,則(a^r^)^-1^必=e)
由紅色部分可得(a^r^)^-1^=(a^-1^)^r’^=e-----①
∵ <G,*>是群,即(a^n^)^-1^=(a^-1^)^n^成立,則
(a^r^)^-1^=(a^-1^)^r^ 成立-----②
由①②可得,(a^-1^)^r^ =(a^-1^)^r’^=e
∵ 已知r’是a^-1^的階,即r’是使得(a^-1^)^k^ =e的最小正整數,
∴ r=mr’(m為正整數),即r’|r。 (定理中的(1)剛證明過)
同理可證r|r’。
(a^-1^)^r’^= (a^r’^)^-1^=e
∵ (a^r’^)^-1^ * a^r’^=e
∴ a^r’^=e
∵ 已知r是a的階,即r是使得(a)^r^ =e的最小正整數,
∴ r’=mr (m為正整數),即r|r’ .由r’|r與 r|r’即可證得r=r’。

  • r ≤ |G|(元素的階一定小於等於群的階)

證:

一個元素a, a的階是r,且r>|G|,則由a可生成一個集合S={a,a^2^,a^3^,…,a^r-1^,a^r^},因為運算*封閉,所以S⊆G, 則S的元素個數小於|G|.
然後證明a,a^2^,a^3^,…,a^r-1^,a^r^各不相同。
若不然,假設S中存在兩個元素相同:
a^i^=a^j^,其中1≤i<j≤r,就有e=a^j-i^ (1≤ j-i<r,a^i^=a^j^右側同*a-i),而已知r是使得a^r^=e的最小整數。
a,a^2^,a^3^,…,a^r-1^,a^r^都各不相同,即集合S的元素個數大於|G|,與S⊆G矛盾。綜上,r≤|G|

子群性質

  • 循環群的子群也是循環群
  • 循環群是無限階的,則其子群除了{e}也是無限階的
  • 循環群是n階的,對於每個n的因子,有且只有一個循環子群

置換群和伯恩賽德定理

置換

成立條件

  • 對於非空集合S,\(S\rightarrow S\)的雙射稱為S的置換

運算

先運用\(\pi_2\),再運用\(\pi_1\)

  • 左複合 $\circ \(:\)\pi_1\circ\pi_2$
  • 右複合 $\diamond \(:\)\pi_2\diamond\pi_1$

置換群

定義

  • 具有n個元素的集合S中所有的置換組成的群\(<S_n,\circ>\),其中元素個數有 n! 個
  • 任意\(<S_n,\circ>\)的子群都是S上的置換群

對稱群

\(S_n\)稱為S的對稱群

交錯群

\(S_n\)中所有偶置換組成的群,記為\(A_n\)\(|A_n|=n!/2\)

輪換

定義

設s是S={1,2,…,n}上的n元置換,且:

\[s(i_1)=i_2, s(i_2)=i_3, …, s(i_k-1)=i_k, s(i_k)=i_1 \]

\(\forall\ x\in S,\ x\ne i_j (j=1,2,…,k)\),有 s(x)=x(即s 不改變其餘元素),稱s是S上的一個k輪換, 當k=2, s也稱為對換

記法

\((i_1,i_2,…,i_k)\)

對換
定義

k=2時

性質
  • 任意輪換可以寫成對換的乘積。即

    (a1 a2…ar)=(a1 ar)(a1 ar-1)…(a1 a3)(a1 a2)

誘導的二元關係

定義

\(<G,\circ>\)為S的一個置換群,則其誘導的二元關係有

\[R=\{<a,b>|\pi(a)=b,\ \pi\in G\} \]

性質
  • 是一個等價關係(條件:自反性、對稱性、傳遞性)

三元素集的置換群

對稱群

S~3~={ (1), (1 2), (1 3), (2 3), (1 2 3), (1 3 2) }

交錯群

A~3~={ (1), (1 2 3), (1 3 2) }

伯恩賽德定理

\(\pi\)是劃分S的置換群的一個置換,\(\phi(\pi)\)指置換中不變元個數

\[等價類數目=\frac{1}{|G|}\sum_{\pi\in G}\phi(\pi) \]

陪集和拉格朗日定理

陪集

定義

設H是G的子群,\(a\in G\),則

  • aH={a*h|h∈H} H關於a的左陪集
  • Ha={h*a|h∈H} H關於a的右陪集

a稱為陪集的代表元素

性質

元素\(\Rightarrow\)陪集

  • 陪集元素個數相等,\(\forall a\in G\),|aH|=|H|

  • a∈H$\iff $aH=H,Ha=H

  • a∈aH

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

  • b∈aH $\iff $ bH=aH

陪集與陪集

  • aH和bH關係只有兩種
    • aH∩bH=\(\varnothing\)(Ha∩Hb=\(\varnothing\)
    • aH=bH(Ha=Hb)

陪集\(\Rightarrow\)元素,a/b屬於同一陪集

  • aRb \(\iff\) a^-1^ *b∈H \(\iff\) b∈aH \(\iff\) aH=bH

所有左陪集的集合∑剛好是G的一個劃分

特殊關係

劃分

  • 每個元素非空。不存在空塊
  • 所有元素並集為G
  • 任兩個元素交集為空

等價關係

關係R滿足自反、對稱、傳遞

  • 若<x,y>\(\in\)R,稱x等價於y,記作x~y

等價類

有等價關係的元素組成的一個集合,記為[a]~R~

  • a稱為[a]~R~的代表元素

商集 A/R

以R的所有等價類作為元素的集合稱為A關於R的商集

子群的指數

G對H的陪集的集合的基數,即陪集的數目,記為[G:H ]

拉格朗日定理

H為G的子群,則:

  • R={<a,b>|a∈G,b∈G且a^-1^ *b∈H}是G上的一個等價關係。對於a∈G,若記[a]~R~={x|x∈G且<a,x>∈R},則[a]~R~=aH
  • 如果G是有限群,|G|=n,|H|=m,則m|n。

推論

  • 素數階群的子群一定是平凡群。(素數階的群不存在非平凡子群)
  • 設<G,*>是n階群,則對任意a∈G,有a^n^=e
  • 有限群中,元素的階能整除群的階
  • 素數階群一定是循環群,且每個非幺元均為生成元

正規子群和商群

正規子群 / 不變子群

定義

H\(\leq\)G,\(\forall g\in G\),gH=Hg,記為H\(\unlhd\)G

判別

\(\forall a\in G\)

  • aH=Ha,(即H\(\unlhd\)G)
  • \(\forall h\in H\),aha^-1^\(\in\)H
  • aHa^-1^\(\subseteq\)H
  • aHa^-1^=H

如果G是交換群,則G的任何子群都是正規子群

[G:H]=2 , 則H是G的正規子群

單群

G除了平凡子群外無其他正規子群

性質

  • 正規子群與子群的乘積是子群
  • 正規子群與正規子群的乘積是正規子群
  • 傳遞性

商群

運算

在G/H上定義陪集乘法運算∙,對於任意aH,bH∈G/H, 有

\[aH·bH=(ab)H \]

定義

設G為群,H為正規子群,則G/H關於運算∙構成一個群,稱為G的商群

性質

  • 商群G/H的單位元是eH(=H)
  • 在G/H中aH的逆元是a^-1^H

推論

  • 若G是交換群,G/H也是交換群
  • 商群的階是G階數的因子

同態與同構

同態映射 / 同態 ~

定義

<A,\(\star\)>與<B,*>滿足\(f(a_1\star a_2)=f(a_1)*f(a_2)\)

稱 f 為同態映射 / 同態,<A,\(\star\)>同態於<B,*>

記為 A~B

同態象

<f(A), *>為<A,\(\star\)>的一個同態象

自然同態

群G到商群G/H的同態,為 a\(\rightarrow\)aH

分類

  • f:A\(\rightarrow\)B 為滿射,f 稱為滿同態
  • f:A\(\rightarrow\)B 為入射,f 稱為單一同態
  • f:A\(\rightarrow\)B 為雙射,f 稱為同構映射
同構

f 為同構映射時,稱<A,\(\star\)>與<B,*>同構,記為A\(\cong\)B

  • 同構關係是等價關係
凱萊定理

任何一個有限群同構於一個置換群。

置換群即運算表中所有行 OR 所有列

自同態 / 自同構

自身到自身的映射

同態映射性質

在 f 作用下

  • <A, $\star $>的所有性質在同態象上保留
  • 若同構,則<B, *>擁有<A, $\star $>的所有性質

同態核

定義

A中元素映射 f 後為幺元。記為 Ker(f),稱為 f 的同態核

Ker(f) = {x|x∈G且f(x)=e’}

性質

  • 同態核N為A的正規子群
  • f 為單同態 \(\iff\) Ker(f)={e}
  • 若Ker(f)=N ,則 f(a)=f(b) \(\iff\) aN=bN

同態基本定理

  • 若 f 為A到B的滿同態,Ker(f)=N,則A/N\(\cong\)B
  • 若h為A自然同態,存在A/N到B的同構g,有f=gh

第一同構定理 / 商群同構定理

  • 若 f 為A到B的滿同態,Ker(f)=N,H\(\unlhd\)A 且 N\(\subseteq\)H
    • 則 A/H \(\cong\) B/f(H)
  • 若 H\(\unlhd\)A 且 K\(\unlhd\)A 且 K\(\subseteq\)H
    • 則 A/H \(\cong\) (A/K) / (H/K)

環與域

定義

對於<A, +, ·>有兩種二元運算的代數系統

  • <A, +>是阿貝爾群

  • <A, ·>是半群

  • 運算 · 對於 + 是可分配的,即\(\forall a,b,c\in A\)

    a·(b+c)=(a·b)+(a·c)

    (b+c)·a=(b·a)+(c·a)

為了區別環中的兩個運算,通常稱+運算為環中的加法,·運算為環中的乘法。

零元

加法單位元,記為0(\(\theta\))

單位元

乘法單位元,記為1

負元

加法逆元,記為-x

逆元

乘法逆元,記為x^-1^

例子

  • <R,+,·> 實數環
  • <Q,+,·> 有理數環
  • <I,+,·> 整數環
  • <M~n~(I),+, ·> n階整數矩陣環
  • <N~k~ , +~k~ , ×~k~> 模k整數環
  • <Z[i], +, ·>(Z[i]=a+bi,a,b\(\in\)Z,i^2^=-1) 高斯整數環 (複數)
  • <R[x] ,+, ·> R[x]為實數多項式

性質

與理解的加法乘法相同,消去律不一定

  • \(\theta\)=\(\theta\)·a=\(\theta\)
  • a·(–b)=(–a)·b = –(a·b)
  • (–a)·(–b)=a·b
  • a·(b–c)=(a·b)–(a·c)
  • (b–c)·a=(b·a)– (c·a)

特殊環

交換環

<A, · >可交換

含幺環

<A, · >含幺元

無零因子環

等價於乘法消去律)

\(\forall a,b\in A, a\neq\theta, b\neq \theta\),則必有\(a·b\neq\theta\)

零因子

\(a,b\in A, a\neq\theta, b\neq \theta\),有\(a·b=\theta\),則a或b為零因子

整環

定義

(基於乘法運算的性質)

交換、無零因子 OR 含幺、無零因子

即同時滿足交換環、含幺環和無零因子環的條件

子環

定義

環的子集,也是環

判定定理

\(\forall a,b\in S,a-b\in S,a·b\in S\)

定義

滿足如下:

  • <A, +>是阿貝爾群
  • <A – {\(\theta\)}, ·>是阿貝爾群
  • 運算 · 對運算+是可分配的

例子

  • 實數域
  • 有理數域
  • 〈Z~n~,+~n~, · ~n~ 〉是域的充要條件是n是素數

域與整環的關係

  • 域一定是整環
  • 有限整環一定是域

環的同態定義

V~1~=<A,*,∘>和V~2~=<B,⊛,◎>是兩環,其中*、∘、⊛和◎都是二元運算。f 是從AB的一個映射,使得對\(\forall\)a, b\(\in\)A有:

f(a*b)=f(a)⊛f(b)

f(ab)=f(a)◎f(b)

則稱f是環V1到環V2的同態映射

分類

如果f單射、滿射和雙射,分別稱f單同態、滿同態和同構

同態像及其特性

<f(A),⊛,◎>是<A,*,∘>的同態像

  • 任何環的同態像是環
綜合例題

設<R,+, · >是環,其乘法單位元記為1,加法單位元記為0,對於任意a,b\(\in\)R,定義

a⊕b=a+b+1,a⊙b=a·b+a+b。求證: <R, ⊕, ⊙ >也是含幺環,並與<R,+, · >同構。

證明:

首先證明<R, ⊕, ⊙ >是環。

(1) <R, ⊕ >是阿貝爾群。

(2) <R, ⊙ >是含幺半群。

(3) ⊙對⊕可分配,再證明同構。

(4)構造雙射f: f(a)=a-1,驗證同構性。

(1) <R, ⊕ >是阿貝爾群。

顯然R關於⊕是封閉的且⊕運算是可交換的。

結合性:對於任意的x,y,z\(\in\)R,有

(x⊕y)⊕z=(x+y+1)⊕z=x+y+z+2,而

x⊕(y⊕z )= x⊕ (y+z+1)=x+y+z+2, 即⊕運算滿足結合律。

幺元:對於任意x\(\in\)R, x⊕-1= x+(-1)+1=x,-1是R關於⊕運算的幺元。

逆元:對於任意x\(\in\)R, x⊕(-x-2)= x+(-x-2)+1=-1, +(-x-2)是x關於⊕運算的逆元。

所以<R, ⊕ >是阿貝爾群。

(2) <R, ⊙ >是含幺半群。

顯然R關於⊙是封閉的、可交換的。

結合性:對於任意的x,y,z ÎR,有

(x ⊙ y) ⊙ z=(xy+x+y) ⊙ z=xyz+xz+yz+xy+x+y+z,而

x ⊙(y ⊙ z )= x ⊙ (yz+y+z)=xyz+xy+xz+yz+x+y+z, 即⊙運算滿足結合律。

幺元:對於任意xÎR, x ⊙ 0=0+ x+0=x,0是R關於⊙運算的幺元。

所以<R, ⊙ >是含幺半群.

(3) ⊙對⊕可分配

對於任意的x,y,z\(\in\)R,有

x⊙(y⊕z )= x⊙(y+z+1)=xy+xz+x+x+y+z+1=xy+xz+2x+y+z+1

(x⊙y)⊕(x⊙z)=(xy+x+y)⊕(xz+x+z)=xy+xz+2x+y+z+1

同理可以證明右可分配性。

綜上所述, <R, ⊕, ⊙ >也是含幺環

再證明同構。

構造雙射f: f(a)=a-1,驗證同構性。

(4)證明同構。構造函數f: f(x)=x-1

雙射:對於任意x\(\in\)R,則有x+1\(\in\)R,使得f(x+1)=x,所以f是滿射

x,y\(\in\)R,若f(x)=f(y),則有x-1=y-1,即x=y,所以f是單射。

同態: f(x+y)=x+y-1

f(x)⊕f(y)=(x-1)⊕(y-1)=x-1+y-1+1=x+y-1

所以f(x+y)= f(x)⊕f(y)

又因為 f(x·y)=x·y-1

f(x)⊙f(y)=(x-1) ⊙(y-1)=(x-1)· (y-1)+x-1+y-1

​ =x·y-x-y+1+x-1+y-1=x·y-1

所以f(x·y)= f(x)⊙f(y)

​ 綜上,<R, ⊕, ⊙ >與<R,+, ∘ >同構。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

在 MacOS 中使用 multipass 安裝 microk8s 環境_潭子電動車

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

在 MacOS 中使用 multipass 安裝 microk8s 環境

Multipass & MicroK8S 介紹

Kubernetes 是什麼?

Kubernetes 集群通過可靠和可擴展的方式對容器化應用進行託管,使得在 DevOps 思維和體系中,讓運維服務、系統升級等工作變得超級簡單。

Multipass 是什麼?

Multipass 是一款可運行於 Linux、Windows 和 MacOS 的輕量級虛擬機管理器,它專為希望使用單個命令即可啟動全新 Ubuntu 環境的開發人員而設計。它在 Linux 上使用 KVM、在 Windows 上使用 Hyper-V、在 MacOS 上使用 HyperKit,以便以最小的開銷運行虛擬機。它還可以在 Windows 和 MacOS 上使用 VirtualBox。Multipass 將協助你獲取最新鏡像,並持續保持更新。

MicroK8S 是什麼?

MicroK8S 是 CNCF 認證的 Kubernetes 部署環境,可在工作站或邊緣設備上運行。作為一個 snap 包,它可以原生的運行所有 Kubernetes 服務,如果需要還可以打包類庫和二進制文件。它的安裝僅受限於你的下載速度,而刪除 MicroK8S 后不會留下任何痕迹。

  • 參考:Install MicroK8s on Windows using Multipass
  • 參考:https://microk8s.io/

安裝 multipass & microk8s

安裝 multipass 服務

brew search multipass brew cask info multipass brew cask install multipass multipass version

通過 multipass 安裝和啟動 microk8s 環境

multipass launch –name microk8s-vm –mem 4G –disk 40G multipass list multipass stop microk8s-vm multipass delete microk8s-vm multipass purge

在虛機中安裝 microk8s 服務

multipass exec microk8s-vm — sudo snap install microk8s –classic multipass exec microk8s-vm — sudo iptables -P FORWARD ACCEPT

查看 microk8s 的 snap 包信息,比如版本信息

 multipass exec microk8s-vm — sudo snap info microk8s

增加賬號訪問權限,簡化操作

# 默認 ubuntu 賬號無權限操作集群,均需要 sudo # 可將 ubuntu 賬號加入 microk8s 用戶組以便簡化訪問 multipass exec microk8s-vm — sudo usermod -a -G microk8s ubuntu multipass exec microk8s-vm — sudo sudo chown -f -R ubuntu ~/.kube

增加訪問公鑰,簡化操作

# 在 ~/.ssh/authorized_keys 增加自己的公鑰,則可方便的進行SSH登錄 multipass shell microk8s-vm ssh ubuntu@192.168.64.2

查看磁盤空間

multipass exec microk8s-vm — df -kh

查看 kubeconfig 配置

multipass exec microk8s-vm — /snap/bin/microk8s.config

在 kubeconfig 中可以找到集群信息,可登錄查看

server: https://192.168.64.2:16443 username: admin password: xxx

增加 DNS 插件,必須安裝,多處依賴使用

multipass exec microk8s-vm — /snap/bin/microk8s.enable dns multipass exec microk8s-vm — /snap/bin/microk8s.enable dashboard

嘗試訪問 Grafana 地址

https://192.168.64.2:16443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy

安裝 Dashboard UI

multipass exec microk8s-vm — /snap/bin/microk8s.kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

使用 Bearer Token 進行鑒權訪問

# 為安全考慮,Dashboard UI 需要使用 Bearer Token 進行鑒權訪問,使用如下命令獲取 Token multipass exec microk8s-vm — /snap/bin/microk8s.kubectl -n kube-system get secret | grep default-token | cut -d ” ” -f1 multipass exec microk8s-vm — /snap/bin/microk8s.kubectl -n kube-system describe secret default-token-qqt75

訪問 Dashboard UI

https://192.168.64.2:16443/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

查看集群組件狀態

multipass exec microk8s-vm — /snap/bin/microk8s.status

可通過指定配置文件進行訪問

# 把kubeconfig保存至本地 /Users/xxx/.kube/microk8s-vm.yml,則可通過指定配置文件進行訪問 kubectl –insecure-skip-tls-verify –kubeconfig=”/Users/xxx/.kube/microk8s-vm.yml” get pods –all-namespaces

# 把kubeconfig保存至本地 ~/.kube/config,則可通過指定配置文件進行訪問 kubectl –insecure-skip-tls-verify get pods –all-namespaces

安裝 registry 組件

# The MicroK8s registry will not be enabled by default, so needs run the following to enable it. multipass exec microk8s-vm — /snap/bin/microk8s.enable registry

查看集群內組件狀態

multipass exec microk8s-vm — /snap/bin/microk8s.status | grep enabled

  • 參考:在 MacOS 中使用 multipass 安裝 microk8s 環境

部署業務應用

業務應用 Demo 代碼

urban-iptable-management # 簡單的IP地址查詢服務,服務自治,無外部依賴 urban-district-management # 簡單的省市區查詢服務,服務自治,無外部依賴 urban-traffic-management # 簡單的模擬服務間調用,依賴 district 服務查詢城市信息 urban-gateway-management # 模擬API網關,將訪問轉發至其他服務

  • Github Repo:https://github.com/gaochundong/urbanboot

本地 docker image 構建

cd urbanboot

docker build -t urban-iptable-management-app:latest –file ./urban-iptable-management/docker/Dockerfile . docker build -t urban-district-management-app:latest –file ./urban-district-management/docker/Dockerfile . docker build -t urban-traffic-management-app:latest –file ./urban-traffic-management/docker/Dockerfile . docker build -t urban-gateway-management-app:latest –file ./urban-gateway-management/docker/Dockerfile .

刪除無用鏡像

docker images docker rmi –force $(docker images | grep “^<none>” | awk ‘{print $3}’) docker images

保存本地鏡像至文件

# Save one or more images to a tar archive docker save -o urban-iptable-management-app.tar urban-iptable-management-app:latest docker save -o urban-district-management-app.tar urban-district-management-app:latest docker save -o urban-traffic-management-app.tar urban-traffic-management-app:latest docker save -o urban-gateway-management-app.tar urban-gateway-management-app:latest

  • 參考:Container Runtimes Part 3: High-Level Runtimes

拷貝鏡像文件至 microk8s 機器

scp ./urban-iptable-management-app.tar ubuntu@192.168.64.2:/tmp scp ./urban-district-management-app.tar ubuntu@192.168.64.2:/tmp scp ./urban-traffic-management-app.tar ubuntu@192.168.64.2:/tmp scp ./urban-gateway-management-app.tar ubuntu@192.168.64.2:/tmp

安裝鏡像至 registry

multipass exec microk8s-vm — /snap/bin/microk8s.ctr namespaces list multipass exec microk8s-vm — /snap/bin/microk8s.ctr images list -q

multipass exec microk8s-vm — /snap/bin/microk8s.ctr images import /tmp/urban-iptable-management-app.tar multipass exec microk8s-vm — /snap/bin/microk8s.ctr images import /tmp/urban-district-management-app.tar multipass exec microk8s-vm — /snap/bin/microk8s.ctr images import /tmp/urban-traffic-management-app.tar multipass exec microk8s-vm — /snap/bin/microk8s.ctr images import /tmp/urban-gateway-management-app.tar

multipass exec microk8s-vm — /snap/bin/microk8s.ctr images list -q | grep urban

刪除鏡像

multipass exec microk8s-vm — /snap/bin/microk8s.ctr images remove docker.io/library/urban-iptable-management-app:latest multipass exec microk8s-vm — /snap/bin/microk8s.ctr images remove docker.io/library/urban-district-management-app:latest multipass exec microk8s-vm — /snap/bin/microk8s.ctr images remove docker.io/library/urban-traffic-management-app:latest multipass exec microk8s-vm — /snap/bin/microk8s.ctr images remove docker.io/library/urban-gateway-management-app:latest

在部署文件中配置鏡像位置

# 替換deployment.yaml文件中的image路徑 /Users/xxx/g/github/urbanboot/urban-district-management/kubernetes/deployment.yaml

創建 Namespace

kubectl –insecure-skip-tls-verify create namespace urbanboot

部署應用

kubectl –insecure-skip-tls-verify apply -f /Users/xxx/g/github/urbanboot/urban-iptable-management/kubernetes/deployment.yaml -n urbanboot kubectl –insecure-skip-tls-verify apply -f /Users/xxx/g/github/urbanboot/urban-district-management/kubernetes/deployment.yaml -n urbanboot kubectl –insecure-skip-tls-verify apply -f /Users/xxx/g/github/urbanboot/urban-traffic-management/kubernetes/deployment.yaml -n urbanboot kubectl –insecure-skip-tls-verify apply -f /Users/xxx/g/github/urbanboot/urban-gateway-management/kubernetes/deployment.yaml -n urbanboot

查看部署

kubectl –insecure-skip-tls-verify get deployments -n urbanboot kubectl –insecure-skip-tls-verify get pods -n urbanboot

刪除部署,會自動刪除 Pods

kubectl –insecure-skip-tls-verify delete deployment urban-iptable-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete deployment urban-district-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete deployment urban-traffic-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete deployment urban-gateway-management-app -n urbanboot

  • 參考:Build apps locally and Deploy on MicroK8s

創建 Service

kubectl –insecure-skip-tls-verify get services -n urbanboot multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose -h

multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-iptable-management-app –type=ClusterIP –port=7200 –name=urban-iptable-management-app -n urbanboot multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-iptable-management-app –type=NodePort –port=7200 –name=urban-iptable-management-nodeport -n urbanboot

multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-district-management-app –type=ClusterIP –port=7200 –name=urban-district-management-app -n urbanboot multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-district-management-app –type=NodePort –port=7200 –name=urban-district-management-nodeport -n urbanboot

multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-traffic-management-app –type=ClusterIP –port=7200 –name=urban-traffic-management-app -n urbanboot multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-traffic-management-app –type=NodePort –port=7200 –name=urban-traffic-management-nodeport -n urbanboot

multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-gateway-management-app –type=ClusterIP –port=7200 –name=urban-gateway-management-app -n urbanboot multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-gateway-management-app –type=NodePort –port=7200 –name=urban-gateway-management-nodeport -n urbanboot

multipass exec microk8s-vm — /snap/bin/microk8s.kubectl expose deployment urban-traffic-management-app –type=LoadBalancer –port=7200 –name=urban-traffic-management-loadbalancer -n urbanboot

  • 參考:Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what?
  • The big downside is that each service you expose with a LoadBalancer will get its own IP address, and you have to pay for a LoadBalancer per exposed service, which can get expensive!

使用配置文件創建 Service

kubectl –insecure-skip-tls-verify expose -f /Users/xxx/g/github/urbanboot/urban-traffic-management/kubernetes/service.yaml -n urbanboot kubectl –insecure-skip-tls-verify expose -f /Users/xxx/g/github/urbanboot/urban-traffic-management/kubernetes/nodeport.yaml -n urbanboot

刪除 Service

kubectl –insecure-skip-tls-verify delete service urban-iptable-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-iptable-management-nodeport -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-district-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-district-management-nodeport -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-traffic-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-traffic-management-nodeport -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-gateway-management-app -n urbanboot kubectl –insecure-skip-tls-verify delete service urban-gateway-management-nodeport -n urbanboot

查一下 TCP 端口監聽

multipass exec microk8s-vm — netstat -nl -t

查看部署事件,按照時間排序

kubectl –insecure-skip-tls-verify get events -n urbanboot –sort-by=.metadata.creationTimestamp

查看 Pod 日誌

kubectl –insecure-skip-tls-verify get pods -n urbanboot kubectl –insecure-skip-tls-verify describe pod urban-traffic-management-app-58d7578547-p277h -n urbanboot kubectl –insecure-skip-tls-verify logs urban-traffic-management-app-58d7578547-p277h -n urbanboot kubectl –insecure-skip-tls-verify logs urban-traffic-management-app-58d7578547-p277h -n urbanboot –tail=20

查看 Endpoint 信息

# Spring Cloud Kubernetes 會通過 API 查詢 Endpoints kubectl –insecure-skip-tls-verify get services -n urbanboot kubectl –insecure-skip-tls-verify get endpoints -n urbanboot kubectl –insecure-skip-tls-verify get all –all-namespaces kubectl –insecure-skip-tls-verify get all -n urbanboot kubectl –insecure-skip-tls-verify describe services urban-traffic-management-nodeport -n urbanboot kubectl –insecure-skip-tls-verify describe services urban-traffic-management-app -n urbanboot

訪問 NodePort 端口

curl -s http://192.168.64.2:30211 curl -s http://192.168.64.2:30211 -i curl -s http://192.168.64.2:30211 -v

登錄 Pod 環境

kubectl –insecure-skip-tls-verify exec -it urban-traffic-management-app-58d7578547-p277h -n urbanboot — /bin/bash

查看 Java 進程

java -version env | grep JAVA ps -ef|grep java

版權聲明:本篇文章《在 MacOS 中使用 multipass 安裝 microk8s 環境》由作者 Dennis Gao 發表自博客園個人技術博客,未經作者本人同意禁止以任何的形式轉載,任何自動的或人為的爬蟲轉載行為均為耍流氓。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

[leetcode] 並查集(Ⅱ)_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

最長連續序列

題目[128]:鏈接。

解題思路

節點本身的值作為節點的標號,兩節點相鄰,即允許合併(x, y)的條件為x == y+1

因為數組中可能會出現值為 -1 的節點,因此不能把 root[x] == -1 作為根節點的特徵,所以採取 root[x] == x 作為判斷是否為根節點的條件。默認較小的節點作為連通分量的根。

此外,使用 map<int, int> counter 記錄節點所在連通分量的節點個數(也是merge 的返回值)。

class Solution
{
public:
    unordered_map<int, int> counter;
    unordered_map<int, int> root;
    int longestConsecutive(vector<int> &nums)
    {
        int len = nums.size();
        // use map to discard the duplicate values
        for (int x : nums)
            root[x] = x, counter[x] = 1;
        int result = len == 0 ? 0 : 1;
        for (int x : nums)
        {
            if (root.count(x + 1) == 1)
                result = max(result, merge(x, x + 1));
        }
        return result;
    }
    int find(int x)
    {
        return root[x] == x ? x : (root[x] = find(root[x]));
    }
    int merge(int x, int y)
    {
        x = find(x);
        y = find(y);
        if (x != y)
        {
            root[y] = x;
            counter[x] += counter[y];
        }
        return counter[x];
    }
};

連通網絡的操作次數

題目[1319]:Link.

解題思路

考慮使用並查集。

考慮到特殊情況,要使 N 個點連通,至少需要 N-1 條邊,否則返回 -1 即可。

通過並查集,可以計算出多餘的邊的數目(多餘的邊是指使得圖成環的邊),只要 findroot(x) == findroot(y) 說明邊 (x,y) 使得圖成環。

遍歷所有邊,在並查集中執行合併 merge 操作(多餘的邊忽略不合併,只進行計數)。設 components 為合併后后 root 數組中 -1 的個數(也就是連通分量的個數),要想所有的連通分支都連起來,需要 components - 1 個邊,所以要求「多餘的邊」的數目必須大於等於 components - 1

一個簡單的例子如下:

0--1         0--1                0--1
| /    =>    |          =>       |  | 
2  3         2  3                2  3
             components = 2
             duplicateEdge = 1

代碼實現

class Solution
{
public:
    vector<int> root;
    int result = 0;
    int makeConnected(int n, vector<vector<int>> &connections)
    {
        int E = connections.size();
        // corner cases
        if (n == 0 || n == 1)
            return 0;
        if (E < n - 1)
            return -1;
        root.resize(n), root.assign(n, -1);
        // merge
        for (auto &v : connections)
        {
            int a = v[0], b = v[1];
            merge(a, b);
        }
        int components = count(root.begin(), root.end(), -1);
        if (counter >= (components - 1))
            return components - 1;
        // should not be here
        return -1;
    }
    int find(int x)
    {
        return root[x] == -1 ? x : (root[x] = find(root[x]));
    }
    // the number of duplicate edges
    int counter = 0;
    void merge(int x, int y)
    {
        x = find(x), y = find(y);
        if (x != y)
            root[y] = x;
        else
        {
            // there is a duplicate edge
            counter++;
        }
    }
};

等式方程的可滿足性

題目[990]:Link.

解題思路

考慮並查集。遍歷所有的包含 == 的等式,顯然,相等的 2 個變量就合併。對於不等式 x!=y ,必須滿足 findroot(x) != findroot(y) 才不會出現邏輯上的錯誤。也就是說,不相等的 2 個變量必然在不同的連通分支當中。

#define getidx(x) ((x) - 'a')
class Solution
{
public:
    vector<int> root;
    bool equationsPossible(vector<string> &equations)
    {
        root.resize('z' - 'a' + 1, -1);
        vector<int> notequal;
        int len = equations.size();
        for (int i = 0; i < len; i++)
        {
            auto &s = equations[i];
            if (s[1] == '!')
            {
                notequal.emplace_back(i);
                continue;
            }
            int a = getidx(s[0]), b = getidx(s[3]);
            merge(a, b);
        }
        for (int i : notequal)
        {
            auto &s = equations[i];
            int a = getidx(s[0]), b = getidx(s[3]);
            if (find(a) == find(b))
                return false;
        }
        return true;
    }
    int find(int x)
    {
        return (root[x] == -1) ? x : (root[x] = find(root[x]));
    }
    void merge(int x, int y)
    {
        x = find(x), y = find(y);
        if (x != y)
            root[y] = x;
    }
};

盡量減少惡意軟件的傳播 II

題目[928]:這題有點難。

解題思路

參考 題解1 和 題解2 。

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

首先,對原來的並查集結構添加一點改進,利用 vector<int> size[N] 記錄某個連通分量中節點的數目,注意當且僅當 x 是該連通分量的根節點時,size[x] 才表示該連通分量的節點數目。這是因為在 merge 中,只對根節點的 size 進行了處理。

vector<int> root;
vector<int> size;
int find(int x)
{
    return root[x] == -1 ? (x) : (root[x] = find(root[x]));
}
void merge(int x, int y)
{
    x = find(x), y = find(y);
    if (x != y)
        root[y] = x, size[x] += size[y];	// pay attention here
}
// get the size of the connected component where node x is in
int getComponentSize(int x)
{
    return size[find(x)];
}

然後,建立一個基本圖,該圖是原圖 graph 去除所有感染節點 initial 的結果,並把這個基本圖轉換為上述改進后的並查集。把這個基本圖中的節點暫且稱為 clean nodes 或者 non-infected nodes .

從直覺上來說,我們應該在 initial 中找到那個標號最小,感染最多 non-infected nodes 的節點,但是這樣是否符合預期?

顯然是不符合的,來看個例子,設 initial nodes = [a,b,c] ,並設 2 個沒有被感染的連通分量為 N1, N2 ,且這 2 個連通分量的點數滿足 size(N1) > size(N2),原圖 graph 結構如下:

a--N1--c

b--N2

根據題目的意思,需要找到的是使得最終感染數目 M(initial) 最小的節點。

如果我們按照上述所謂的「直覺」:“在 initial 中找到那個感染最多 non-infected nodes 的節點”,應該去除的是節點 a ,但是由於 c 的存在,N1 依舊會被感染,這樣 M(initial) = size(N1) + size(N2)。(也就是說,某個連通分量相鄰的感染節點多於 1 個,該連通分量最終是必然被感染的,因為我們只能去除一個感染節點。)

實際上,這種情況下正確答案是去除 b ,因為除 b 后:M(initial) = size(N1) ,該結果才是最小的。

所以,我們要找的是:在 initial 中找到那個感染最多 non-infected nodes 的節點 ans,但這些 non-infected nodes 節點只能被 ans 感染,不能被其他的 initial 節點感染(即只能被感染一次)。

代碼實現

class Solution
{
public:
    vector<int> root;
    vector<int> size;
    int minMalwareSpread(vector<vector<int>> &graph, vector<int> &initial)
    {
        int N = graph.size();
        root.resize(N, -1);
        size.resize(N, 1);

        // use hash table to mark infected nodes
        vector<bool> init(N, false);
        for (int x : initial)
            init[x] = true;
        // change the non-infected graph into disjoint union set
        for (int i = 0; i < N; i++)
        {
            if (init[i])
                continue;
            for (int j = 0; j < i; j++)
            {
                if (init[j])
                    continue;
                if (graph[i][j] == 1)
                    merge(i, j);
            }
        }
        // table[x] = {...}
        // the set {...} means the non-infected components which initial node x will infect
        // counter[x] = k
        // k means that the non-infected component x will be infected by initial nodes for k times
        vector<int> counter(N, 0);
        unordered_map<int, unordered_set<int>> table;
        for (int u : initial)
        {
            unordered_set<int> infected;
            for (int v = 0; v < graph[u].size(); v++)
            {
                if (!init[v] && graph[u][v] == 1)
                    infected.insert(find(v));
            }
            table[u] = infected;
            for (int x : infected)
                counter[x]++;
        }

        // find the node we want
        int ans = N + 1, maxInfected = -1;
        for (int u : initial)
        {
            int sum = 0;
            for (int x : table[u])
                if (counter[x] == 1)	// must be infected only once
                    sum += getComponentSize(x);
            if (sum > maxInfected || (sum == maxInfected && u < ans))
            {
                ans = u;
                maxInfected = sum;
            }
        }
        return ans;
    }

    int find(int x)
    {
        return root[x] == -1 ? (x) : (root[x] = find(root[x]));
    }

    void merge(int x, int y)
    {
        x = find(x), y = find(y);
        if (x != y)
            root[y] = x, size[x] += size[y];
    }

    int getComponentSize(int x)
    {
        return size[find(x)];
    }
};

盡量減少惡意軟件的傳播

題目[924]:做了上面那題之後簡單一點。

解題思路

依然是使用上題中 盡量減少惡意軟件的傳播 II 改進后的並查集結構。

對整個原圖處理,轉換為並查集。然後,模擬處理。即 \(\forall x \in initial\) ,使用集合 \(newSet = initial – \{x\}\) 去模擬感染原圖,得到最終的感染節點數 t,選取感染節點數 t 最小且標號值最小的 \(x\) 作為返回結果。

代碼實現

class Solution
{
public:
    vector<int> root, size;
    int minMalwareSpread(vector<vector<int>> &graph, vector<int> &initial)
    {
        int N = graph.size();
        root.resize(N, -1);
        size.resize(N, 1);

        for (int i = 0; i < N; i++)
            for (int j = 0; j < i; j++)
                if (graph[i][j] == 1)
                    merge(i, j);

        int ans = N + 1, minval = N + 1;
        // assume that discard the node x in the initial set
        // get the injected value
        for (int x : initial)
        {
            int t = getInjected(x, initial);
            if (t < minval || (t == minval && ans > x))
            {
                minval = t;
                ans = x;
            }
        }
        return ans;
    }
    // use set initial - {x} to inject the graph
    int getInjected(int x, vector<int> &initial)
    {
        unordered_set<int> s;
        for (int k : initial)
        {
            if (k == x)
                continue;
            s.insert(find(k));
        }
        int sum = 0;
        for (int t : s)
            sum += size[find(t)];
        return sum;
    }
    int find(int x)
    {
        return root[x] == -1 ? (x) : (root[x] = find(root[x]));
    }
    void merge(int x, int y)
    {
        x = find(x), y = find(y);
        if (x != y)
            root[y] = x, size[x] += size[y];
    }
};

被圍繞的區域

題目[130]:本題難度一般。

解題思路

本題最特殊的節點是邊界上的 O 以及內部與邊界 O 相鄰的節點。

首先,通過邊界的 O 入手,從它開始進行 DFS 搜索,把所有這些的特殊節點標記為 Y 。然後,在 board 中剩下的 O 就是普通的節點(必然是不與邊界 O 相鄰且被 X 所圍繞的),可以把它們全部換成 X 。最後,把所有的 Y 還原為 O

對於搜索方法,既可以是 DFS 也可以是 BFS

代碼實現

class Solution
{
public:
    const vector<vector<int>> direction = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
    int row, col;
    void solve(vector<vector<char>> &board)
    {
        row = board.size();
        if (row == 0)
            return;
        col = board[0].size();
        #define func bfs
        for (int j = 0; j < col; j++)
        {
            if (board[0][j] == 'O')
                func(0, j, board);
            if (board[row - 1][j] == 'O')
                func(row - 1, j, board);
        }

        for (int i = 0; i < row; i++)
        {
            if (board[i][0] == 'O')
                func(i, 0, board);
            if (board[i][col - 1] == 'O')
                func(i, col - 1, board);
        }

        for (int i = 0; i < row; i++)
        {
            for (int j = 0; j < col; j++)
            {
                if (board[i][j] == 'O')
                    board[i][j] = 'X';
                if (board[i][j] == 'Y')
                    board[i][j] = 'O';
            }
        }
    }

    void dfs(int i, int j, vector<vector<char>> &board)
    {
        board[i][j] = 'Y';
        for (auto &v : direction)
        {
            int a = i + v[0], b = j + v[1];
            if (a < 0 || b < 0 || a >= row || b >= col)
                continue;
            if (board[a][b] == 'O')
                dfs(a, b, board);
        }
    }

    void bfs(int i, int j, vector<vector<char>> &board)
    {
        typedef pair<int, int> node;
        queue<node> q;
        q.push(node(i, j));
        board[i][j] = 'Y';
        while (!q.empty())
        {
            node n = q.front();
            q.pop();
            for (auto &v : direction)
            {
                int a = n.first + v[0], b = n.second + v[1];
                if (!(a < 0 || b < 0 || a >= row || b >= col) && board[a][b] == 'O')
                    board[a][b] = 'Y', q.push(node(a, b));
            }
        }
    }
};

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

【Java8新特性】不了解Optional類,簡歷上別說你懂Java8!!_台中搬家

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

寫在前面

最近,很多讀者出去面試都在Java8上栽了跟頭,事後自己分析,確實對Java8的新特性一知半解。然而,卻在簡歷顯眼的技能部分寫着:熟練掌握Java8的各種新特性,能夠迅速使用Java8開發高併發應用!這不,又一名讀者因為寫了熟練掌握Java8的新特性而被面試官虐的體無完膚!我不是說不能寫,可以這樣寫!但是,咱在寫熟練掌握Java8新特性的時候,應該靜下心來好好想想自己是否真的掌握了Java8。如果自己心中對是否掌握了Java8這個問題模稜兩可的話,那確實要好好靜下心來為自己充電了!一定要從模稜兩可到徹底掌握Java8,那到時就不是面試官虐你了,而是你吊打面試官!!

什麼是Optional類?

Optional 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在,原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且可以避免空指針異常。

Optional類常用方法:

  • Optional.of(T t) : 創建一個 Optional 實例。
  • Optional.empty() : 創建一個空的 Optional 實例。
  • Optional.ofNullable(T t):若 t 不為 null,創建 Optional 實例,否則創建空實例。
  • isPresent() : 判斷是否包含值。
  • orElse(T t) : 如果調用對象包含值,返回該值,否則返回t。
  • orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值。
  • map(Function f): 如果有值對其處理,並返回處理后的Optional,否則返回 Optional.empty()。
  • flatMap(Function mapper):與 map 類似,要求返回值必須是Optional。

Optional類示例

1.創建Optional類

(1)使用empty()方法創建一個空的Optional對象:

Optional<String> empty = Optional.empty();

(2)使用of()方法創建Optional對象:

String name = "binghe";
Optional<String> opt = Optional.of(name);
assertEquals("Optional[binghe]", opt.toString());

傳遞給of()的值不可以為空,否則會拋出空指針異常。例如,下面的程序會拋出空指針異常。

String name = null;
Optional<String> opt = Optional.of(name);

如果我們需要傳遞一些空值,那我們可以使用下面的示例所示。

String name = null;
Optional<String> opt = Optional.ofNullable(name);

使用ofNullable()方法,則當傳遞進去一個空值時,不會拋出異常,而只是返回一個空的Optional對象,如同我們用Optional.empty()方法一樣。

2.isPresent

我們可以使用這個isPresent()方法檢查一個Optional對象中是否有值,只有值非空才返回true。

Optional<String> opt = Optional.of("binghe");
assertTrue(opt.isPresent());

opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());

在Java8之前,我們一般使用如下方式來檢查空值。

if(name != null){
    System.out.println(name.length);
}

在Java8中,我們就可以使用如下方式來檢查空值了。

Optional<String> opt = Optional.of("binghe");
opt.ifPresent(name -> System.out.println(name.length()));

3.orElse和orElseGet

(1)orElse

orElse()方法用來返回Optional對象中的默認值,它被傳入一個“默認參數‘。如果對象中存在一個值,則返回它,否則返回傳入的“默認參數”。

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("binghe");
assertEquals("binghe", name);

(2)orElseGet

與orElse()方法類似,但是這個函數不接收一個“默認參數”,而是一個函數接口。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "binghe");
assertEquals("binghe", name);

(3)二者有什麼區別?

要想理解二者的區別,首先讓我們創建一個無參且返回定值的方法。

public String getDefaultName() {
    System.out.println("Getting Default Name");
    return "binghe";
}

接下來,進行兩個測試看看兩個方法到底有什麼區別。

String text;
System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultName);
assertEquals("binghe", defaultText);

System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getDefaultName());
assertEquals("binghe", defaultText);

在這裏示例中,我們的Optional對象中包含的都是一個空值,讓我們看看程序執行結果:

Using orElseGet:
Getting default name...
Using orElse:
Getting default name...

兩個Optional對象中都不存在value,因此執行結果相同。

那麼,當Optional對象中存在數據會發生什麼呢?我們一起來驗證下。

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

String name = "binghe001";

System.out.println("Using orElseGet:");
String defaultName = Optional.ofNullable(name).orElseGet(this::getDefaultName);
assertEquals("binghe001", defaultName);

System.out.println("Using orElse:");
defaultName = Optional.ofNullable(name).orElse(getDefaultName());
assertEquals("binghe001", defaultName);

運行結果如下所示。

Using orElseGet:
Using orElse:
Getting default name...

可以看到,當使用orElseGet()方法時,getDefaultName()方法並不執行,因為Optional中含有值,而使用orElse時則照常執行。所以可以看到,當值存在時,orElse相比於orElseGet,多創建了一個對象。如果創建對象時,存在網絡交互,那系統資源的開銷就比較大了,這是需要我們注意的一個地方。

4.orElseThrow

orElseThrow()方法當遇到一個不存在的值的時候,並不返回一個默認值,而是拋出異常。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow( IllegalArgumentException::new);

5.get

get()方法表示是Optional對象中獲取值。

Optional<String> opt = Optional.of("binghe");
String name = opt.get();
assertEquals("binghe", name);

使用get()方法也可以返回被包裹着的值。但是值必須存在。當值不存在時,會拋出一個NoSuchElementException異常。

Optional<String> opt = Optional.ofNullable(null);
String name = opt.get();

6.filter

接收一個函數式接口,當符合接口時,則返回一個Optional對象,否則返回一個空的Optional對象。

String name = "binghe";
Optional<String> nameOptional = Optional.of(name);
boolean isBinghe = nameOptional.filter(n -> "binghe".equals(name)).isPresent();
assertTrue(isBinghe);
boolean isBinghe001 = nameOptional.filter(n -> "binghe001".equals(name)).isPresent();
assertFalse(isBinghe001);

使用filter()方法會過濾掉我們不需要的元素。

接下來,我們再來看一例示例,例如目前有一個Person類,如下所示。

public class Person{
    private int age;
    public Person(int age){
        this.age = age;
    }
    //省略get set方法
}

例如,我們需要過濾出年齡在25歲到35歲之前的人群,那在Java8之前我們需要創建一個如下的方法來檢測每個人的年齡範圍是否在25歲到35歲之前。

public boolean filterPerson(Peron person){
    boolean isInRange = false;
    if(person != null && person.getAge() >= 25 && person.getAge() <= 35){
        isInRange =  true;
    }
    return isInRange;
}

看上去就挺麻煩的,我們可以使用如下的方式進行測試。

assertTrue(filterPerson(new Peron(18)));
assertFalse(filterPerson(new Peron(29)));
assertFalse(filterPerson(new Peron(16)));
assertFalse(filterPerson(new Peron(34)));
assertFalse(filterPerson(null));

如果使用Optional,效果如何呢?

public boolean filterPersonByOptional(Peron person){
     return Optional.ofNullable(person)
       .map(Peron::getAge)
       .filter(p -> p >= 25)
       .filter(p -> p <= 35)
       .isPresent();
}

使用Optional看上去就清爽多了,這裏,map()僅僅是將一個值轉換為另一個值,並且這個操作並不會改變原來的值。

7.map

如果有值對其處理,並返回處理后的Optional,否則返回 Optional.empty()。

List<String> names = Arrays.asList("binghe001", "binghe002", "", "binghe003", "", "binghe004");
Optional<List<String>> listOptional = Optional.of(names);

int size = listOptional
    .map(List::size)
    .orElse(0);
assertEquals(6, size);

在這個例子中,我們使用一個List集合封裝了一些字符串,然後再把這個List使用Optional封裝起來,對其map(),獲取List集合的長度。map()返回的結果也被封裝在一個Optional對象中,這裏當值不存在的時候,我們會默認返回0。如下我們獲取一個字符串的長度。

String name = "binghe";
Optional<String> nameOptional = Optional.of(name);

int len = nameOptional
    .map(String::length())
    .orElse(0);
assertEquals(6, len);

我們也可以將map()方法與filter()方法結合使用,如下所示。

String password = " password ";
Optional<String> passOpt = Optional.of(password);
boolean correctPassword = passOpt.filter(
    pass -> pass.equals("password")).isPresent();
assertFalse(correctPassword);

correctPassword = passOpt
    .map(String::trim)
    .filter(pass -> pass.equals("password"))
    .isPresent();
assertTrue(correctPassword);

上述代碼的含義就是對密碼進行驗證,查看密碼是否為指定的值。

8.flatMap

與 map 類似,要求返回值必須是Optional。

假設我們現在有一個Person類。

public class Person {
    private String name;
    private int age;
    private String password;
 
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
 
    public Optional<Integer> getAge() {
        return Optional.ofNullable(age);
    }
 
    public Optional<String> getPassword() {
        return Optional.ofNullable(password);
    }
    // 忽略get set方法
}

接下來,我們可以將Person封裝到Optional中,並進行測試,如下所示。

Person person = new Person("binghe", 18);
Optional<Person> personOptional = Optional.of(person);

Optional<Optional<String>> nameOptionalWrapper = personOptional.map(Person::getName);
Optional<String> nameOptional = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
String name1 = nameOptional.orElse("");
assertEquals("binghe", name1);

String name = personOptional
    .flatMap(Person::getName)
    .orElse("");
assertEquals("binghe", name);

注意:方法getName返回的是一個Optional對象,如果使用map,我們還需要再調用一次get()方法,而使用flatMap()就不需要了。

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Java8新特性。

最後,附上Java8新特性核心知識圖,祝大家在學習Java8新特性時少走彎路。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司