2024年4月21日 星期日

what's the mechanism behind hash tables

雜湊表格 (HashTable),雜湊集合 (HashSet),或 雜湊映射 (HashMap),以下統稱雜湊表格,是效能最佳的一種資料結構,其增刪查找的時間複雜度皆為 O(1),和容器存放的元素個數 n 無關。Python語言的集合 (Set) 及字典 (Dictionary) 就是分別基於 雜湊集合 及 雜湊映射 而實現的,所以存取效能很好。

話雖如此,雜湊表格苦於兩個缺點,一是不能發生踫撞,只要踫撞嚴重,存取效能就會降低,退化成 O(n)。二是不能排序,不能查詢比某元素大/小的元素集合(tailSet/headSet)。若容器須要排序,只能退而求其次,改用樹狀結構 (TreeSet/TreeMap),例如搜索樹等,其增刪查找效能降到 O(log(n));或堆積 (Heap),其增刪效能 O(log(n)),但查找最小或最大值效能 O(1)。

以下參考文獻,列出雜湊表格常採用的技術分類,供了解其極佳效能的作法及補救法。

A. Hash code and compression: (雜湊碼及壓縮技術)

  A1. Hash code function 雜湊碼函數
         如何依據元素內容計算雜湊值

  1.  Bit representation (位元表現法)
  2.  Polynomial hash codes (多項式雜湊碼)
  3.  Cyclic-shift hash codes (循環平移雜湊碼)

  A2. Compression function 壓縮函數
        如何將雜湊碼 (hash_code) 依表格大小 (hash_table_size) 轉為找尋起點格下標

  1.  Division method (求餘數法)
         hash_code mod hash_table_size
  2.  MAD (multiply-add-and-divide) method (乘加求餘數法)
         [(a * hash_code + b) mod p] mod hash_table_size

B. Hash collision handling 雜湊踫撞處理法

  B1. Closed addressing / Open hashing or Separate chaining
         封閉式定址         /  開放式雜湊法,個別串接

         放元素的格子全由元素的雜湊碼決定 / 放元素的格子在雜湊表之外

  B2. Open addressing / Closed hashing
         開放式定址       / 封閉式雜湊法

        放元素的格子不全由元素的雜湊碼決定,視當時踫撞情形而定 / 放元素的格子在雜湊表

  1. Linear probing 線性探索
  2. Quadratic probing 平方探索
  3. Double hashing 雙雜湊

Reference:

  1. Open Addressing Collision Handling technique in Hashing - GeeksforGeeks
  2. 2015-goodrich-wiley-data structures & algorithms in java, 6th ed.

2024年3月9日 星期六

cybersecurity and privacy protection related standards

隨著資安及隱私越來越受公私部門重視,以下列出資通安全 (cybersecurity) 及隱私保護常見的 ISO (International Standardization Organization ,國際標準化組織) 及其他標準供參考。

  • ISO 9000系列品質保證管理標準 
    • 9001品質管理系統(QMS)要求

  • ISO 22300系列安全和復原力標準 
    • 22301事業持續性管理系統(BCMS)要求 
    • 22317事業持續性管理系統指導綱要

  • ISO 27000系列資訊安全管理系統(ISMS)標準
    • 27001資訊安全管理系統要求,附錄A含控制目標、控制措施 
    • 27002資訊安全管理系統作業規範,供選擇、實施、管理控制措施之用 
    • 27003資訊安全管理系統規劃指引,有關必要活動的建議(應該),可能(能力可以),允許(權限可以)作法指引 
    • 27004資訊安全管理系統評測指導網要,供監測、測量、分析、評測之用 
    • 27005資訊安全風險管理指導綱要 
    • 27006稽核驗證機構之要求

    • 27017雲端服務資訊安全管理 
    • 27018雲端服務個資保護作業規範 

    • 27102資安管理的網宇保險指導綱要

    • 27701隱私資訊管理要求及指導綱要,含PII控制者及處理者

  • ISO 29100系列隱私權框架標準 
    • 29134隱私衝擊評估指導綱要 
    • 29151個資保護作業規範

  • ISO 31000風險管理指導綱要

  • CIS,網際網路安全中心 (Center for Internet Security) 
    • Critical Security Controls 重要安全控制措施: 基礎型(6),功能型(10),組織型(4)

  • EU,歐洲議會及委員會
    • GDPR 2016/679 一般資料保護法規 (General Data Protection Regulation)

  • IEC,國際電工委員會 (International Electrotechnical Commission)
    •  62443 工業通信及網路-網路及系統安全系列標準,乃工業控制系統資訊安全,風險控管基準

  • ISAC,資訊分享與分析中心 (Information Sharing and Analysis Center)
    • COBIT 2019,  資訊及相關技術控制目標 (Control OBjectives for Information and related Technology) 標準

  • NIST,美國商業部國家標準暨技術研究院 (National Institute of Standards and Technology) 
    • CyberSecurity Framework 網宇安全框架 
    • SP800-137資安持續監視 
    • SP800-207零信任架構 

  • NICS,行政院數位發展部國家資通安全研究所 (National Institute of Cyber Security)
    • GCB,政府組態基準 (Government Configuration Baseline)
    • VANS,弱點分析及通報系統 (Vulnerability Analysis and Notice System)
    • EDR,端點偵測及應變機制 (Endpoint Detection and Response)
    • ZTA,零信任架構 (Zero Trust Architecture)
    • N-SOC,國家資安聯防監控中心 (National Security Operation Center)
    • N-CERT,國家資通安全通報應變中心 (National Computer Emergency Response Team)

  • ACS,行政院數位發展部資通安全署 (Administration for Cyber Security)
    • 資安政策與法規
    • 關鍵基礎設施資安防護
    • 資安事件通報應變
參考:
1.經濟部產業發展署 產業人才能力鑑定計畫 iPAS 數位課程平台 (industry Professional Assessment System)

2023年12月25日 星期一

discontinuous points of 12-hour vs 24-hour time systems

12小時制 (12-hour  time system) 和24小時制時間 (24-hour time system) 乍看很容易轉換。凡遇到12小時制的 PM (Post Meridem, 下午) 就數字加12小時,刪掉 PM,變成24小時制;遇到12小時制的 AM (Ante Meridem,上午) 就數字不動,刪掉 AM,變成24小時制。大部份情況是如此沒錯,但是在半夜12點,半夜1點,中午12點,中午1點前後,兩者轉換有點跳躍,不連續,易生混淆。

簡單講,12小時制是沒有半夜 00:00AM-00:59AM 或中午 00:00PM-00-59PM 這兩種寫法的。

    12小時制的四處不連續點 (discontinuous points) 包括
  1. 中午 11:59AM 的下一分鐘是 中午12點 12:00PM,由 AM 跳成 PM
  2. 中午 12:59AM 的下一分鐘是 中午1點 01:00AM,由 12:59 跳成 01:00
  3. 半夜 11:59PM 的下一分鐘是 半夜12點 12:00AM,由 PM 跳成 AM
  4. 半夜 12:59PM 的下一分鐘是  半夜1點 01:00PM,由 12:59 跳成 01:00
    而相形之下,24小時制只有一處不連續點
  1. 半夜 23:59 的下一分鐘為半夜 00:00,由 23:59 跳成 00:00

以下為兩者24小時對照表,其中紅色標記處為常易混淆處。即半夜0-1點,在12小時制稱為半夜 12:00AM-12:59AM,而非 00:00AM-00:59AM。中午12-13點,在12小時制稱為中午 12:00PM-12:59PM,而非 00:00PM-00-59PM

24小時制 12小時制
00:00 - 00:59 半夜 12:00AM - 12:59AM
01:00 - 01:59 01:00AM - 01:59AM
02:00 - 02:59 02:00AM - 02:59AM
03:00 - 03:59 03:00AM - 03:59AM
04:00 - 04:59 04:00AM - 04:59AM
05:00 - 05:59 05:00AM - 05:59AM
06:00 - 06:59 06:00AM - 06:59AM
07:00 - 07:59 07:00AM - 07:59AM
08:00 - 08:59 08:00AM - 08:59AM
09:00 - 09:59 09:00AM - 09:59AM
10:00 - 10:59 10:00AM - 10:59AM
11:00 - 11:59 11:00AM - 11:59AM
12:00 - 12:59 中午 12:00PM - 12:59PM
13:00 - 13:59 01:00PM - 01:59PM
14:00 - 14:59 02:00PM - 02:59PM
15:00 - 15:59 03:00PM - 03:59PM
16:00 - 16:59 04:00PM - 04:59PM
17:00 - 17:59 05:00PM - 05:59PM
18:00 - 18:59 06:00PM - 06:59PM
19:00 - 19:59 07:00PM - 07:59PM
20:00 - 20:59 08:00PM - 08:59PM
21:00 - 21:59 09:00PM - 09:59PM
22:00 - 22:59 10:00PM - 10:59PM
23:00 - 23:59 11:00PM - 11:59PM

考慮到12小時制有4處不連續跳躍點,若要設定任何日期時間期限到半夜12點,建議截止期 (deadline) 選擇截止日期的 11:59PM,比較不會出錯。因為選擇截止日期的 12:00PM 將會變成當天中午截止,短少12小時。而若選擇截止日期加1日的 12:00AM,雖然正確,卻有點不自然,會想東想西,怕哪裏出問題。

2023年11月24日 星期五

how to add libraries for a NetBeans project

如何為 NetBeans 專案添加類別庫,下面以添加 Apache Commons-CSV 讀取CSV檔案的類別庫為例。

下載及解開 Apache Commons-CSV 類別庫
    URL: https://commons.apache.org/proper/commons-csv/download_csv.cgi
	     點選 Binaries/commons-csv-1.10.0-bin.zip
    解壓後路徑: C:\...\commons-csv-1.10.0 

從 NetBeans 左欄面板,如下點選填入名稱,類別檔,原始碼,註解檔路徑。
Projects/Project Name/Libraries/Add Library.../Create
	Library Name: Commons-CSV
	Library Type: Class Libraries

	Classpath/Library Classpath: 類別檔路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\commons-csv-1.10.0.jar

	Sources/Library Sources: 原始碼路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\commons-csv-1.10.0-sources.jar

	Javadoc/Library Javadoc: 註解檔路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\apidocs

設定類別檔,原始碼,註解檔的完整路徑,安裝類別庫之後,NetBeans專案就可以使用如下類別庫套件:
  org.apache.commons.csv.*

遇到有疑問的類別或方法,可選擇其名字,按如下選單或快速鍵,查看其註解或原始碼:
看註解: Source/Show Documentation (Ctrl-Shift-Space)
看原始碼: Navigate/Go to Source (Ctrl-Shift-B)

2023年11月3日 星期五

why thrashing occurs in virtual memory systems

依據現代電腦的馮紐曼運算架構,CPU始終只能從記憶體讀取資料執行,不能直接從硬碟讀取資料。因此,程式常受限於記憶體的容量限制而無法載入執行。可是明明電腦系統的硬碟容量常是記憶體的上千倍,難道不能克服困難,將硬碟當成記憶體使用嗎?

虛擬記憶體 (Virtual Memory)就是這樣一個應急措施,可將硬碟分一塊置換空間 (Swap Space),當成記憶體用。其明顯好處是從此電腦不再受記憶體的容量限制,再大、再多的程式也可執行。只要硬碟夠大,頂多暫時存放在硬碟的置換空間,要用時再搬進記憶體執行即可。

但記住虛擬記憶體始終只是一個應急措施,不能當成常態使用。理由是資料在記憶體和硬碟之間搬移 (swapping) 時,要花費很久的等候時間,互動式用戶將等的不耐煩,故只適合非不得已偶一為之。例如,記憶體的批次用戶較多,可暫時挪到硬碟休息,先讓互動用戶使用記憶體。

採用虛擬記憶體有一個副作用是如果管理不好,分配給程式的實體記憶體不夠,須要先將暫時不用的程式從記憶體搬出到硬碟 (page out),才能騰出空間,讓須要的程式碼從硬碟搬回到記憶體 (page in) 執行。萬一記憶體極端不足情況下,很可能剛搬出的程式,等下又要再搬入,造成所謂的掙扎 (thrashing) 現象。出現此反常現象時,系統效能將變極差,只見硬碟不斷亮燈,苦苦忙著將資料從記憶體搬出搬進,I/O使用率陡升,而CPU卻坐等資料搬好,閒閒沒事幹,CPU使用率陡降。

註: 上述 掙扎現象的英文 thrashing 查字典有如下諸多意思:
           掙扎,輾轉,擺盪,振盪,顛簸,抖動,猛移,往復移動,頻繁置換;
           徒勞,作虛功,無進展,白費力氣,原地踏步;打敗,痛打,窮忙,窮打,窮追猛打
       常見直譯有 輾轉,擺盪,甚至意譯 窮忙,作虛功,也都很傳神。
       選擇 掙扎 則兼有直譯及意譯,猶如泳者奮力浮水,離水之魚奮拍入水之勢。

active versus passive multiprogramming

多元規劃 (multiprogramming) 是作業系統演進上,早期一項重要發明。早期電腦的記憶體一次只放一支程式,遇到程式執行I/O指令,等候I/O結果,CPU就閒置浪費掉。因此,利用多元規劃技術,在記憶體放進多支程式,遇程式等I/O時,就將CPU分配給其他程式使用,如此即可提升CPU使用率 (utilization)。

至於為何將技術取名為多元規劃,讓人從字面不易猜透其義? 猜測可能是參考當時流行的線性規劃 (linear programming),整數規劃 (integer programming) 等最佳化技術的命名。顧名思義,多元 指的是記憶體一次載入多支程式,規劃 指的是安排多支程式執行。而某種意義上,這也的確是讓CPU使用率提升的一種最佳化技術。如果對多元用語的語義不明有芥蒂,也許稱為 多程式規劃 會更一目暸然。一個電腦系統的 多元規劃度 (degree of multiprogramming) 為記憶體可放進多少支程式的數量,其值越高,表示CPU有越多程式可執行,越有利於提升CPU使用率。

多元規劃依據作業系統排班器對CPU掌控程度分為兩種。主動式多元規劃 (active multiprogramming) 利用計時器中斷主動介入, 給定時間一到,CPU即須讓出跑其他程式,可防止CPU遭程式霸佔;被動式多元規劃 (passive multiprogramming) 的排班器只能被動等候程式志願退出或作I/O,才能重新取得CPU分配給其他程式,容易造成CPU遭程式霸佔。 

2023年10月18日 星期三

how to solve the tower of hanoi by recursion versus simulated call stack?


/*
   TowerOfHanoi.java  遞迴版 及 模擬呼叫堆疊版 求解河內塔 

> java TowerOfHanoi
Hanoi Tower by Implicit Call Stack 遞迴版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]

Hanoi Tower by Explicit Stack 模擬呼叫堆疊版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]
*/
import java.util.Stack;

public class TowerOfHanoi 
{
    static Stack stackA = new Stack<>();  // 柱A
    static Stack stackB = new Stack<>();  // 柱B
    static Stack stackC = new Stack<>();  // 柱C
    static int nDisks = 5; // Number of disks 盤數
    static int count = 0;  // 步數
    static boolean printGoal = false;  // 列印目標否
    static boolean printOperation = true;  // 列印步驟否
    static boolean printStack = false;  // 列印模擬呼叫堆疊否

    // 印n格空白
    public static void printSpaces(int n)
    {
        for(int i=0; i <= n -1; i++) System.out.print(" ");
    }
    
    // 印柱A,柱B,柱C堆疊,前面n層內縮
    public static void printStacks(int n)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("A:" + stackA);
        sb.append(", B:" + stackB);
        sb.append(", C:" + stackC);

        System.out.println();
        printSpaces(n*8);  // 每層內縮8格
        System.out.print(sb.toString());
    }
    
    // 搬移柱from頂一個盤子到柱to
    public static void transfer(char from, char to)
    {
        if(from=='A' && to=='B') stackB.push(stackA.pop());
        if(from=='A' && to=='C') stackC.push(stackA.pop());
        if(from=='B' && to=='A') stackA.push(stackB.pop());
        if(from=='B' && to=='C') stackC.push(stackB.pop());
        if(from=='C' && to=='A') stackA.push(stackC.pop());
        if(from=='C' && to=='B') stackB.push(stackC.pop());
    }
    
    // 遞迴版解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void solveHanoi(int n, char source, char auxiliary, char target) 
    {        
        if(printGoal) 
        {
            System.out.println();
            printSpaces((nDisks - n)*8);
            System.out.print(String.format("hanoi(n:%d,s:%c -> t:%c)",n,source,target));
        }

        if (n == 1) 
        {
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

            transfer(source, target);            
        } 
        else 
        {
            solveHanoi(n - 1, source, target, auxiliary);

            printStacks(nDisks - n);            
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
            transfer(source, target);
            printStacks(nDisks - n);

            solveHanoi(n - 1, auxiliary, source, target);
        }
    }

    // 模擬呼叫記錄    
    static class HanoiCallRecord
    {
        int num;
        char source;
        char auxiliary;
        char target;
        int stage; // 0 for moving n-1 disks from source to auxiliary rods; 
                   // 1 for moving the disk n from source to target rods
                   //   and moving n-1 disks from auxiliary to target rods
                   // 2 for backtracking to the previous call record
        
        // 建構子
        public HanoiCallRecord(int num, char source, char auxiliary, char target)
        {
            this.num = num;
            this.source = source;
            this.auxiliary = auxiliary;
            this.target = target;
            this.stage = 0;  // 預設從階段0開始
        }
        
        // 列印呼叫記錄
        public String toString()
        {
            return String.format("(n:%d,s:%c,a:%c,t:%c,s:%d)",
                    num, source, auxiliary, target, stage);
        }
    }
    
    // 模擬呼叫堆疊,解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void hanoiUsingStacks(int num, char src, char aux, char tgt) 
    {
        Stack stack = new Stack<>();
        
        HanoiCallRecord initial = new HanoiCallRecord(num, src, aux, tgt);
        stack.push(initial);  // 壓入第1層呼叫記錄

        while (stack.isEmpty()==false) 
        {
            if(printStack) System.out.print("\n" + stack);
            
            HanoiCallRecord current = stack.peek();  // 檢視本層呼叫記錄
            int n = current.num;
            char source = current.source;
            char auxiliary = current.auxiliary;
            char target = current.target;
            int stage = current.stage;
            
           if (n == 1) // 執行特別任務,然後退回上一層任務
           {
                if(printOperation) 
                    System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

                transfer(source, target);
                stack.pop();  // 彈出本層呼叫記錄,
            } 
            else if(stage == 0) // 階段0, 執行本層第0階段任務
            {
                // solveHanoi(n - 1, source, target, auxiliary);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, source, target, auxiliary);
                stack.push(next); // 壓入下層呼叫記錄
                current.stage++;  // 更新本層呼叫記錄的階段欄位
            }
            else if(stage == 1) // 階段1, 執行本層第1階段任務
            {
                printStacks(nDisks - n);            
                if(printOperation)
                    System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
                transfer(source, target);
                printStacks(nDisks - n);

                // solveHanoi(n - 1, auxiliary, source, target);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, auxiliary, source, target);
                stack.push(next);  // 壓入下層呼叫記錄
                current.stage++; // 更新本層呼叫記錄的階段欄位
            }
            else if(current.stage == 2) // 階段2,本層任務完成,退回上一層任務
            {
                stack.pop();  // 彈出本層呼叫記錄,
            }
        }
    } 

    // 測試主程式
    public static void main(String[] args) 
    {
        nDisks = 3; // Number of disks
        count = 0;
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
        
        System.out.print("Hanoi Tower by Implicit Call Stack 遞迴版");

        printStacks(0);
        solveHanoi(nDisks, 'A', 'B', 'C');
        printStacks(0);
        
        // ===================================
    
        count = 0;
        stackA.clear();
        stackB.clear();
        stackC.clear();
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
                    
        System.out.print("\n\nHanoi Tower by Explicit Stack 模擬呼叫堆疊版");
        
        printStacks(0);
        hanoiUsingStacks(nDisks, 'A', 'B', 'C');
        printStacks(0);
    }
}