├── .gitignore ├── README.md ├── _config.yml ├── code ├── 00-other │ ├── 02-hillClimbing │ │ ├── 21-slotFilling │ │ │ └── understandEnglish.js │ │ ├── README.md │ │ ├── hillClimbingFramework │ │ │ ├── README.md │ │ │ ├── hillClimbing.js │ │ │ ├── hillClimbingArray.js │ │ │ ├── hillClimbingEquation.js │ │ │ └── hillClimbingNumber.js │ │ ├── hillClimbingSimple.js │ │ ├── simulatedAnnealing │ │ │ ├── README.md │ │ │ ├── simulatedAnnealing.js │ │ │ ├── simulatedAnnealingArray.js │ │ │ └── simulatedAnnealingNumber.js │ │ └── solution │ │ │ ├── matrix.js │ │ │ ├── solution.js │ │ │ ├── solutionArray.js │ │ │ ├── solutionEquations.js │ │ │ └── solutionNumber.js │ ├── 03-gradientDescendent │ │ └── nn0 │ │ │ ├── README.md │ │ │ ├── example │ │ │ └── 01-gradient │ │ │ │ ├── f.js │ │ │ │ ├── gradientDescendent.js │ │ │ │ └── gradientEx.js │ │ │ ├── lib │ │ │ └── pvector.js │ │ │ └── nn.js │ ├── 04-backPropagation │ │ ├── nn1 │ │ │ ├── README.md │ │ │ ├── example │ │ │ │ ├── 01-gradient │ │ │ │ │ ├── f.js │ │ │ │ │ ├── gradientEx.js │ │ │ │ │ └── optimizeEx.js │ │ │ │ └── 02-backprop │ │ │ │ │ ├── f.js │ │ │ │ │ ├── gradientEx.js │ │ │ │ │ ├── optimize.out │ │ │ │ │ └── optimizeEx.js │ │ │ ├── lib │ │ │ │ ├── fnet.js │ │ │ │ ├── function.js.bak │ │ │ │ ├── gate.js │ │ │ │ ├── grad.js │ │ │ │ ├── net.js │ │ │ │ ├── node.js │ │ │ │ ├── pvector.js │ │ │ │ └── pvector.js.bak │ │ │ └── nn.js │ │ └── svm │ │ │ └── svm.js │ ├── 05-geneticAlgorithm │ │ ├── GA.js │ │ └── README.md │ ├── 16-search │ │ ├── README.md │ │ ├── pathFinder.js │ │ ├── puzzleSearch.js │ │ ├── puzzleSearch.txt │ │ └── shipSearch.js │ ├── 17-gameSearch │ │ ├── README.md │ │ ├── gomoku.js │ │ ├── minMax.js │ │ └── monteCarloSearch.md │ ├── 19-recursiveDescendent │ │ ├── compileExp.js │ │ ├── genChinese.js │ │ ├── genEnglish.js │ │ ├── genMath.js │ │ ├── genexp1.js │ │ └── genexp2.js │ ├── datastructure │ │ ├── hashtable.js │ │ ├── queue.js │ │ └── stack.js │ └── qlearning │ │ ├── README.md │ │ ├── q-learning.js │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ ├── example2.js │ │ ├── example3.html │ │ ├── example3.js │ │ └── q-learning.js │ │ ├── qlearning.js │ │ ├── qwalk1d.js │ │ ├── qwalk2d.js │ │ └── rand.js ├── 01-tableLookup │ ├── README.md │ ├── combinatorial │ │ ├── Cnk.js │ │ ├── CnkR.js │ │ └── CnkRLookup.js │ ├── e2c.js │ └── fiboanacci │ │ ├── fiboanacci.js │ │ └── fiboanacci_lookup.js ├── 02-random │ ├── README.md │ ├── distribution │ │ ├── README.ext.md │ │ ├── README.md │ │ └── rexp.js │ ├── random.js │ ├── random2.js │ ├── random3.js │ ├── random3Test.js │ ├── randomTest.js │ ├── test.js │ └── uuid.js ├── 03-monteCarlo │ ├── BayesLogic │ │ ├── BayesLogic.js │ │ ├── BayesLogicNaive.js │ │ └── README.md │ ├── BayesNet │ │ ├── BayesEnumAsk.js │ │ ├── BayesNet.js │ │ ├── BayesNet.txt │ │ ├── BayesNet1.js │ │ ├── BayesRejectSampling.js │ │ ├── BayesSample.js │ │ ├── myLib.js │ │ └── prob.js │ ├── README.md │ ├── markov │ │ ├── README.md │ │ ├── gibbs.js │ │ ├── markov.js │ │ ├── markov.md │ │ ├── mcmc.js │ │ ├── mcmcGibbs.js │ │ ├── metropolis.js │ │ └── prob.js │ ├── mcmc │ │ └── README.md │ └── pi │ │ ├── README.md │ │ └── monteCarloPi.js ├── 04-iterative │ ├── README.md │ ├── em │ │ ├── em.js │ │ └── em.md │ ├── equation │ │ ├── README.md │ │ ├── bak │ │ │ ├── binarySearch.js │ │ │ ├── bruteForce.js │ │ │ ├── f1.js │ │ │ ├── f2.js │ │ │ ├── hillClimbing.js │ │ │ └── iteration.js.bak │ │ ├── iterative.js │ │ └── iterative3.js │ └── kmean │ │ ├── kmean.js │ │ └── kmeanEx.js ├── 05-dynamicProgramming │ ├── DynamicProgramming.md │ ├── README.md │ ├── combinatorial │ │ └── CnkDynamic.js │ ├── editDistance.js │ ├── hmm.md │ └── viterbi.js ├── 06-divideConquer │ ├── FFT │ │ └── FFT.js │ ├── binSearch.js │ └── mergeSort │ │ ├── mergesort.js │ │ └── sortTest.js ├── 07-hashing │ ├── digest.js │ ├── hash.js │ └── sha256.js ├── 08-bruteForce │ ├── SAT │ │ ├── bak │ │ │ ├── eval.js │ │ │ └── sat.js │ │ └── sat.js │ ├── digitalcach │ │ └── mining.js │ └── password │ │ └── passwordFind.js ├── 09-numerical │ └── calculus │ │ ├── README.md │ │ ├── diff.js │ │ ├── integral.js │ │ └── partial.js ├── 10-greedy │ ├── NaiveBayes │ │ ├── README.md │ │ ├── naiveBayesClassifier.js │ │ ├── naiveBayesProb.js │ │ └── naiveProb.js │ ├── README.md │ ├── decisionTree.js │ ├── huffmanCode │ │ ├── README.md │ │ ├── huffmanCode 拷貝 2.js │ │ ├── huffmanCode 拷貝.js │ │ └── huffmanCode.js │ ├── knn.js │ ├── knnEx.js │ └── minimalSpanningTree │ │ └── minimalSpanningTree.js ├── 11-graph │ └── graphSearch.js ├── 12-transformDomain │ └── Fourier │ │ ├── DFT.js │ │ ├── README.md │ │ └── complex.js ├── 13-stringMatching │ ├── README.md │ ├── eliza.html │ ├── gzip.js │ ├── gzip.js.gz │ ├── knuth-morris-pratt │ │ ├── README.md │ │ ├── kmp.js │ │ └── kmpTest.js │ └── minimalEditDistance │ │ ├── README.md │ │ └── levenshteinDistance.js ├── 14-cryptography │ ├── AES │ │ ├── aes.js │ │ ├── aes.md │ │ └── cipher.js │ ├── Blum │ │ ├── README.md │ │ ├── blumBlumShub.js │ │ └── blumBlumShubTest.js │ ├── GaloisField │ │ ├── GaloisField.js │ │ ├── README.ext.md │ │ └── README.md │ ├── README.md │ ├── RSA │ │ ├── README.md │ │ ├── rsa.md │ │ └── simpleRsa.js │ ├── diffieHellman │ │ ├── README.ext.md │ │ ├── README.md │ │ └── diffieHellman.js │ ├── digest.js │ ├── encrypt.md │ ├── gcd │ │ ├── README.md │ │ └── gcd.js │ ├── guid.js │ ├── hack.md │ ├── hashcash.md │ ├── home.md │ ├── methodList.js │ ├── mining.js │ └── sign │ │ ├── cert.pem │ │ ├── server.pem │ │ └── sign.js ├── 15-reduction │ └── halt │ │ ├── halt.js │ │ └── isHalt.js ├── README.md └── package-lock.json ├── docs ├── book │ ├── SUMMARY.html │ └── deepAlgorithm.html ├── html │ ├── 03-gradientDescendent.html │ ├── 04-backPropagation.html │ ├── deepAlgorithm.html │ └── deepAlgorithm2.html ├── img │ ├── AlphaBetaExample.jpg │ ├── ChineseChess.jpg │ ├── EuropeanChess.jpg │ ├── GoogleGraph2D.jpg │ ├── GoogleGraph2D2vally.jpg │ ├── GoogleGraph2DMountain.jpg │ ├── GoogleGraph3D.jpg │ ├── Gradient.jpg │ ├── Minimax.jpg │ ├── README.md │ ├── RlabIDE.png │ ├── RlabIDE_disk.png │ ├── bfs.jpg │ ├── binarySearch.png │ ├── dfs.jpg │ ├── eliza_talkto2.png │ ├── equationSet.png │ ├── gateNet.png │ ├── graphSearch.jpg │ ├── greedyCoin.png │ ├── greedyGraphSearch.png │ ├── greedyHillClimbing.png │ ├── iteration.png │ ├── linearInterpolation.png │ ├── markov2state.jpg │ ├── markov2state_balance.jpg │ ├── markov2state_gibbs (1).jpg │ ├── markov2state_gibbs.jpg │ └── markov2state_metropolis.jpg └── md.bak │ ├── 01-tableLookup.md │ ├── 02-hillClimbing.md │ ├── 03-gradientDescendent.md │ ├── 04-backPropagation.md │ ├── 05-geneticAlgorithm.md │ ├── 06-random.md │ ├── 07-monteCarlo.md │ ├── 08-transformDomain.md │ ├── 09-dynamicProgramming.md │ ├── 10-divideConquer.md │ ├── 11-hashing.md │ ├── 12-bruteForce.md │ ├── 13-numerical.md │ ├── 14-greedy.md │ ├── 15-graph.md │ ├── 16-search.md │ ├── 17-gameSearch.md │ ├── 18-iterative.md │ ├── 19-recursiveDescendent.md │ ├── 20-stringMatching.md │ ├── 21-slotFilling.md │ ├── 22-cryptography.md │ ├── 23-reduction.md │ ├── README.md │ ├── SUMMARY.md │ ├── _Footer.md │ ├── _Sidebar.md │ ├── backprop.md │ ├── book.md │ ├── deepAlgorithm.md │ ├── deepAlgorithm_.md │ └── gradient.md ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 請點 [此處](https://github.com/cccbook/algjs/wiki) 開始閱讀! 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/21-slotFilling/understandEnglish.js: -------------------------------------------------------------------------------- 1 | var e2c={dog:"狗", cat:"貓", eat:"吃", chase:"追", a:"一隻"}; 2 | 3 | function find(s, from, words) { 4 | for (var i=from; i=0) 6 | return i; 7 | } 8 | return -1; 9 | } 10 | 11 | function understand(s) { 12 | var vi = find(s, 0, ["eat", "chase"]); 13 | var si = find(s, 0, ["dog", "cat"]); 14 | var oi = find(s, vi, ["dog", "cat"]); 15 | console.log("s=%s v=%s o=%s", e2c[s[si]], e2c[s[vi]], e2c[s[oi]]); 16 | } 17 | 18 | understand(process.argv.slice(2)); -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/README.md: -------------------------------------------------------------------------------- 1 | # 爬山演算法 -- 最簡單的優化算法 2 | 3 | ## 爬山演算法 4 | 5 | ``` 6 | Algorithm HillClimbing(f, x) 7 | x = 隨意設定一個解 8 | while (x 有鄰居 x1 比 x 更高) 9 | x = x1 10 | end 11 | return x 12 | end 13 | ``` 14 | 15 | ## 實作:以爬山演算法尋找函數最高點 16 | 17 | ``` 18 | $ node .\hillClimbingSimple.js 19 | f(0.0000)=-5.0000 20 | f(-0.0100)=-4.9701 21 | f(-0.0200)=-4.9404 22 | f(-0.0300)=-4.9109 23 | f(-0.0400)=-4.8816 24 | f(-0.0500)=-4.8525 25 | f(-0.0600)=-4.8236 26 | f(-0.0700)=-4.7949 27 | f(-0.0800)=-4.7664 28 | f(-0.0900)=-4.7381 29 | ... 30 | f(-1.4600)=-2.7516 31 | f(-1.4700)=-2.7509 32 | f(-1.4800)=-2.7504 33 | f(-1.4900)=-2.7501 34 | f(-1.5000)=-2.7500 35 | ``` 36 | 37 | ## 通用爬山演算法框架 38 | 39 | 請看 [hillClimbingFramework](hillClimbingFramework) ! 40 | 41 | ## 模擬退火法 42 | 43 | ```js 44 | # 模擬退火法 (Simulated-Annealing) 45 | 46 | ## 演算法 47 | 48 | ``` 49 | Algorithm SimulatedAnnealing(s) 50 | while (溫度還不夠低,或還可以找到比 s 更好的解 s' 的時候) 51 | 根據能量差與溫度,用機率的方式決定是否要移動到新解 s'。 52 | 將溫度降低一些 53 | end 54 | end 55 | ``` 56 | 57 | ## 通用模擬退火法框架 58 | 59 | 請看 [simulatedAnnealing](hillClimbingFramework) ! 60 | 61 | -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/hillClimbingFramework/README.md: -------------------------------------------------------------------------------- 1 | # 通用的爬山演算法架構 2 | 3 | * [hillClimbing.js](hillClimbing.js) 4 | 5 | ## 實作:通用的爬山演算法架構 6 | 7 | * [hillClimbingNumber.js](hillClimbingNumber.js) 8 | * [solutionNumber.js](../solution/solutionNumber.js) 9 | 10 | ``` 11 | $ node .\hillClimbingNumber 12 | s=energy(0.000)=4.000 13 | 0: energy(0.010)=4.000 14 | 1: energy(0.020)=4.000 15 | 4: energy(0.030)=3.999 16 | 6: energy(0.040)=3.998 17 | 9: energy(0.050)=3.998 18 | ... 19 | 409: energy(1.960)=0.158 20 | 410: energy(1.970)=0.119 21 | 412: energy(1.980)=0.080 22 | 414: energy(1.990)=0.040 23 | 415: energy(2.000)=0.000 24 | solution: energy(2.000)=0.000 25 | ``` 26 | 27 | ## 實例 2 :多變數函數的最佳化 28 | 29 | * [hillClimbingArray.js](hillClimbingArray.js) 30 | * [solutionArray.js](../solution/solutionArray.js) 31 | 32 | ``` 33 | $ node .\hillClimbingArray 34 | s=energy( 1.000 1.000 1.000 )=1.000 35 | 1: energy( 1.000 0.990 1.000 )=0.970 36 | 3: energy( 1.010 0.990 1.000 )=0.950 37 | 4: energy( 1.010 0.990 1.010 )=0.921 38 | 5: energy( 1.020 0.990 1.010 )=0.901 39 | 6: energy( 1.030 0.990 1.010 )=0.881 40 | ... 41 | 710: energy( 2.000 0.500 2.450 )=-2.998 42 | 716: energy( 2.000 0.500 2.460 )=-2.998 43 | 726: energy( 2.000 0.500 2.470 )=-2.999 44 | 728: energy( 2.000 0.500 2.480 )=-3.000 45 | 729: energy( 2.000 0.500 2.490 )=-3.000 46 | 732: energy( 2.000 0.500 2.500 )=-3.000 47 | solution: energy( 2.000 0.500 2.500 )=-3.000 48 | ``` 49 | 50 | ## 實例 3 :線性聯立方程組求解 51 | 52 | 注意: 53 | 54 | 1. 原本算法只會調一個軸,所以如果傾斜的方向才有更低點,就會因找不到更低而停止了。 55 | 2. 後來調成能改 n 個軸,所以如果45度的方向有更低點,就不會停止了。(但是如果不是 45度,那還是有可能找不到) 56 | 3. 也可以改成調整方向是任意的,但這樣會嘗試更多狀況。 57 | 58 | (問題是:聯立方程式的能量函數 ( ()^2 + ()^2 ....+ ()^2 ) 會不會有多個低點呢?) 59 | 60 | 61 | * [hillClimbingEquation.js](hillClimbingEquation.js) 62 | * [solutionEquations.js](../solution/solutionEquations.js) 63 | 64 | ``` 65 | $ node .\hillClimbingEquation 66 | s=energy([0.000 0.000])=26.000 67 | 1: energy([0.000 0.010])=25.920 68 | 4: energy([0.000 0.020])=25.841 69 | 6: energy([0.000 0.030])=25.762 70 | 9: energy([0.010 0.030])=25.642 71 | ... 72 | 1190: energy([2.950 2.000])=0.005 73 | 1196: energy([2.960 2.000])=0.003 74 | 1197: energy([2.970 2.000])=0.002 75 | 1225: energy([2.980 2.000])=0.001 76 | 1231: energy([2.990 2.000])=0.000 77 | 1235: energy([3.000 2.000])=0.000 78 | solution: energy([3.000 2.000])=0.000 79 | ``` 80 | -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/hillClimbingFramework/hillClimbing.js: -------------------------------------------------------------------------------- 1 | var hillClimbing = function() {} // 爬山演算法的物件模版 (類別) 2 | 3 | hillClimbing.prototype.run = function(s, maxGens, maxFails) { // 爬山演算法的主體函數 4 | console.log("s=%s", s); // 印出初始解 5 | var fails = 0; // 失敗次數設為 0 6 | // 當代數 gen= sheight) { // 如果鄰近解比目前解更好 12 | s = snew; // 就移動過去 13 | console.log("%d: %s", gens, s); // 印出新的解 14 | fails = 0; // 移動成功,將連續失敗次數歸零 15 | } else // 否則 16 | fails++; // 將連續失敗次數加一 17 | } 18 | console.log("solution: %s", s); // 印出最後找到的那個解 19 | return s; // 然後傳回。 20 | } 21 | 22 | module.exports = hillClimbing; // 將爬山演算法的類別匯出。 -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/hillClimbingFramework/hillClimbingArray.js: -------------------------------------------------------------------------------- 1 | var hillClimbing = require("./hillClimbing"); // 引入爬山演算法類別 2 | var solutionArray = require("../solution/solutionArray"); // 引入多變數解答類別 (x^2+3y^2+z^2-4x-3y-5z+8) 3 | 4 | var hc = new hillClimbing(); // 建立爬山演算法物件 5 | // 執行爬山演算法 (從「解答(x,y,z)=(1,1,1)」開始尋找, 最多十萬代、失敗一千次就跳出。 6 | hc.run(new solutionArray([1,1,1]), 100000, 1000); -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/hillClimbingFramework/hillClimbingEquation.js: -------------------------------------------------------------------------------- 1 | var hillClimbing = require("./hillClimbing"); // 引入爬山演算法類別 2 | var solutionEquations = require("../solution/solutionEquations"); // 引入線性聯立方程組解答類別 3 | 4 | var hc = new hillClimbing(); // 建立爬山演算法物件 5 | // 執行爬山演算法 (從「解答 x=(0,0)」開始尋找, 最多十萬代、失敗一千次就跳出。 6 | hc.run(solutionEquations.zero(), 100000, 1000); -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/hillClimbingFramework/hillClimbingNumber.js: -------------------------------------------------------------------------------- 1 | var hillClimbing = require("./hillClimbing"); // 引入爬山演算法類別 2 | var solutionNumber = require("../solution/solutionNumber"); // 引入平方根解答類別 3 | 4 | var hc = new hillClimbing(); // 建立爬山演算法物件 5 | // 執行爬山演算法 (從「解答=0.0」開始尋找, 最多十萬代、失敗一千次就跳出。 6 | hc.run(new solutionNumber(0.0), 100000, 1000); -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/hillClimbingSimple.js: -------------------------------------------------------------------------------- 1 | function f (x) { return -1 * ( x * x + 3 * x + 5) } 2 | // function f(x) { return -1*Math.abs(x*x-4); } 3 | 4 | var dx = 0.01 5 | 6 | function hillClimbing (f, x) { 7 | while (true) { 8 | console.log('f(%s)=%s', x.toFixed(4), f(x).toFixed(4)) 9 | if (f(x + dx) >= f(x)) { 10 | x = x + dx 11 | } else if (f(x - dx) >= f(x)) { 12 | x = x - dx 13 | } else { 14 | break 15 | } 16 | } 17 | } 18 | 19 | hillClimbing(f, 0.0) 20 | -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/simulatedAnnealing/README.md: -------------------------------------------------------------------------------- 1 | # 模擬退火法 (Simulated-Annealing) 2 | 3 | ## 演算法 4 | 5 | ```js 6 | Algorithm SimulatedAnnealing(s) 7 | while (溫度還不夠低,或還可以找到比 s 更好的解 s' 的時候) 8 | 根據能量差與溫度,用機率的方式決定是否要移動到新解 s'。 9 | 將溫度降低一些 10 | end 11 | end 12 | ``` 13 | 14 | ## 通用的模擬退火法架構 15 | 16 | 17 | ``` 18 | $ node simulatedAnnealingNumber 19 | 20 | 0 T=99.900 energy(-0.010)=4.000 21 | 1 T=99.800 energy(0.000)=4.000 22 | ... 23 | 12 T=98.708 energy(-0.010)=4.000 24 | 13 T=98.609 energy(-0.020)=4.000 25 | 14 T=98.510 energy(-0.030)=3.999 26 | 15 T=98.412 energy(-0.020)=4.000 27 | 16 T=98.314 energy(-0.030)=3.999 28 | 17 T=98.215 energy(-0.040)=3.998 29 | 18 T=98.117 energy(-0.050)=3.998 30 | 19 T=98.019 energy(-0.040)=3.998 31 | ... 32 | 5072 T=0.625 energy(1.250)=2.437 33 | 5073 T=0.624 energy(1.240)=2.462 34 | 5074 T=0.624 energy(1.230)=2.487 35 | 5075 T=0.623 energy(1.240)=2.462 36 | 5076 T=0.622 energy(1.250)=2.437 37 | 5077 T=0.622 energy(1.260)=2.412 38 | 5078 T=0.621 energy(1.270)=2.387 39 | 5079 T=0.620 energy(1.280)=2.362 40 | ... 41 | 6615 T=0.133 energy(1.950)=0.197 42 | 6617 T=0.133 energy(1.940)=0.236 43 | 6618 T=0.133 energy(1.930)=0.275 44 | 6619 T=0.133 energy(1.920)=0.314 45 | 6620 T=0.133 energy(1.930)=0.275 46 | 6621 T=0.133 energy(1.940)=0.236 47 | 6622 T=0.133 energy(1.930)=0.275 48 | ... 49 | 9377 T=0.008 energy(1.990)=0.040 50 | 9378 T=0.008 energy(2.000)=0.000 51 | 9396 T=0.008 energy(2.010)=0.040 52 | 9397 T=0.008 energy(2.000)=0.000 53 | 9528 T=0.007 energy(2.010)=0.040 54 | 9531 T=0.007 energy(2.000)=0.000 55 | solution: energy(2.000)=0.000 56 | ``` 57 | ``` -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/simulatedAnnealing/simulatedAnnealing.js: -------------------------------------------------------------------------------- 1 | var simulatedAnnealing = function() {} // 模擬退火法的物件模版 (類別) 2 | 3 | simulatedAnnealing.prototype.P = function(e, enew, T) { // 模擬退火法的機率函數 4 | if (enew < e) 5 | return 1; 6 | else 7 | return Math.exp((e-enew)/T); 8 | } 9 | 10 | simulatedAnnealing.prototype.run = function(s, maxGens) { // 模擬退火法的主要函數 11 | var sbest = s; // sbest:到目前為止的最佳解 12 | var ebest = s.energy(); // ebest:到目前為止的最低能量 13 | var T = 100; // 從 100 度開始降溫 14 | for (var gens=0; gens Math.random()) { // 根據溫度與能量差擲骰子,若通過 20 | s = snew; // 則移動到新的鄰居解 21 | console.log("%d T=%s %s", gens, T.toFixed(3), s.toString()); // 印出觀察 22 | } 23 | if (enew < ebest) { // 如果新解的能量比最佳解好,則更新最佳解。 24 | sbest = snew; 25 | ebest = enew; 26 | } 27 | } 28 | console.log("solution: %s", sbest.toString()); // 印出最佳解 29 | return sbest; // 傳回最佳解 30 | } 31 | 32 | module.exports = simulatedAnnealing; // 將模擬退火演算法的類別匯出。 -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/simulatedAnnealing/simulatedAnnealingArray.js: -------------------------------------------------------------------------------- 1 | var simulatedAnnealing = require("./simulatedAnnealing"); // 引入模擬退火法類別 2 | var solutionArray = require("../solution/solutionArray"); // 引入多變數解答類別 (x^2+3y^2+z^2-4x-3y-5z+8) 3 | 4 | var sa = new simulatedAnnealing(); // 建立模擬退火法物件 5 | // 執行模擬退火法 (從「解答(x,y,z)=(1,1,1)」開始尋找, 最多執行 2 萬代。 6 | sa.run(new solutionArray([1,1,1]), 20000); -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/simulatedAnnealing/simulatedAnnealingNumber.js: -------------------------------------------------------------------------------- 1 | var simulatedAnnealing = require("./simulatedAnnealing"); // 引入模擬退火法類別 2 | var solutionNumber = require("../solution/solutionNumber"); // 引入平方根解答類別 3 | 4 | var sa = new simulatedAnnealing(); // 建立模擬退火法物件 5 | // 執行模擬退火法 (從「解答=0.0」開始尋找, 最多一萬代。 6 | sa.run(new solutionNumber(0.0), 10000); -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/solution/matrix.js: -------------------------------------------------------------------------------- 1 | var log = console.log; 2 | 3 | var Matrix=function(mat) { 4 | var m = []; 5 | for (var i=0; i 0.5) // 擲骰子決定要往左或往右移 7 | nv[i] += this.step; 8 | else 9 | nv[i] -= this.step; 10 | return new Solution(nv); // 傳回新建的鄰居解答。 11 | } 12 | 13 | Solution.prototype.energy = function() { // 能量函數 14 | var x=this.v[0], y=this.v[1], z=this.v[2]; 15 | return x*x+3*y*y+z*z-4*x-3*y-5*z+8; // (x^2+3y^2+z^2-4x-3y-5z+8) 16 | } 17 | 18 | var numbersToStr=function(array, precision) { // 將數字陣列轉為字串的函數。 19 | var rzStr = ""; 20 | for (var i=0; i=0) 22 | rzStr+=" "+array[i].toFixed(precision)+" "; 23 | else 24 | rzStr+=array[i].toFixed(precision)+" "; 25 | } 26 | return rzStr; 27 | } 28 | 29 | 30 | Solution.prototype.toString = function() { // 將解答轉為字串的函數,以供列印用。 31 | return "energy("+numbersToStr(this.v, 3)+")="+this.energy().toFixed(3); 32 | } 33 | 34 | module.exports = Solution; // 將解答類別匯出。 -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/solution/solutionEquations.js: -------------------------------------------------------------------------------- 1 | var Matrix = require("./matrix"); 2 | var Solution = require("./solution"); // 引入抽象的解答類別 3 | 4 | /* 5 | // A X = B ,求 X 是多少? 6 | // A=[[1,1],[1,-1]] B=[[5][1]],也就是求: 7 | // x1+x2=5 8 | // x1-x2=1 9 | // 的解答 10 | var A = new Matrix([[1,1],[1,-1]]); 11 | var B = new Matrix([[5,1]]).transpose(); 12 | */ 13 | /* 14 | var A = new Matrix([[1,1,1],[1,-1,1],[1,1,-1]]); 15 | var B = new Matrix([[3,1,1]]).transpose(); 16 | */ 17 | 18 | // 題目來源: http://mail.im.tku.edu.tw/~idliaw/LinTup/99ie/99IEntu.pdf 19 | 20 | var A = new Matrix([[4,3,6],[1,1,2],[2,1,3]]); 21 | var B = new Matrix([[1,2,-1]]).transpose(); 22 | 23 | var log = console.log; 24 | 25 | Solution.zero = function() { 26 | // return new Solution(Matrix.create(2,1,0)); 27 | return new Solution(Matrix.create(3,1,0)); 28 | } 29 | 30 | Solution.prototype.neighbor = function() { // 多變數解答的鄰居函數。 31 | var nx = new Matrix(this.v.m); // 複製目前解的矩陣 32 | // 修改了這裡:最多改變 n 個維度(只是某些 n 維的例子可以,無法確定一定可以,除非能證明能量函數只有一個低點) 33 | for (let d = 0; d < nx.rows(); d++) { // 原本只改一維,會找不到! 34 | var i = Math.floor(Math.random()*nx.rows());// 隨機選取一個變數 35 | if (Math.random() > 0.5) // 擲骰子決定要往左或往右移 36 | nx.m[i][0] += this.step * Math.random(); // 原本是 nx.m[i][0] += this.step 37 | else 38 | nx.m[i][0] -= this.step * Math.random(); // 原本是 nx.m[i][0] -= this.step 39 | } 40 | return new Solution(nx); // 傳回新建的鄰居解答。 41 | } 42 | 43 | Solution.prototype.energy = function() { // 能量函數:計算 ||AX-B||,也就是 ||Y-B|| 44 | var X = this.v; 45 | var Y = A.mul(X); 46 | return Y.sub(B).norm(); 47 | } 48 | 49 | Solution.prototype.toString = function() { // 將解答轉為字串的函數,以供列印用。 50 | return "energy("+this.v.transpose().toString().replace("\n", "")+")="+this.energy().toFixed(3); 51 | } 52 | 53 | module.exports = Solution; // 將解答類別匯出。 -------------------------------------------------------------------------------- /code/00-other/02-hillClimbing/solution/solutionNumber.js: -------------------------------------------------------------------------------- 1 | var Solution = require("./solution"); // 引入解答類別 2 | 3 | Solution.prototype.neighbor = function() { // 單變數解答的鄰居函數。 4 | var x = this.v, dx=this.step; // x:解答 , dx : 移動步伐大小 5 | var xnew = (Math.random() > 0.5)?x+dx:x-dx; // 用亂數決定向左或向右移動 6 | return new Solution(xnew); // 建立新解答並傳回。 7 | } 8 | 9 | Solution.prototype.energy = function() { // 能量函數 10 | var x = this.v; // x:解答 11 | return Math.abs(x*x-4); // 能量函數為 |x^2-4| 12 | } 13 | 14 | Solution.prototype.toString = function() { // 將解答轉為字串,以供印出觀察。 15 | return "energy("+this.v.toFixed(3)+")="+this.energy().toFixed(3); 16 | } 17 | 18 | module.exports = Solution; // 將解答類別匯出。 -------------------------------------------------------------------------------- /code/00-other/03-gradientDescendent/nn0/example/01-gradient/f.js: -------------------------------------------------------------------------------- 1 | module.exports = function f (p) { 2 | let x = p.x, y = p.y 3 | return (x * x + y * y) 4 | } -------------------------------------------------------------------------------- /code/00-other/03-gradientDescendent/nn0/example/01-gradient/gradientDescendent.js: -------------------------------------------------------------------------------- 1 | const nn = require('../../nn') 2 | const f = require('./f') 3 | 4 | nn.optimize(f, {x:1, y:1}) -------------------------------------------------------------------------------- /code/00-other/03-gradientDescendent/nn0/example/01-gradient/gradientEx.js: -------------------------------------------------------------------------------- 1 | const nn = require('../../nn') 2 | const f = require('./f') 3 | 4 | console.log('df(f(x:1,y:1), x) = ', nn.df(f, {x:1, y:1}, 'x')) 5 | 6 | console.log('grad(f(x:1,y:1))=', nn.grad(f, {x:1, y:1})) 7 | -------------------------------------------------------------------------------- /code/00-other/03-gradientDescendent/nn0/lib/pvector.js: -------------------------------------------------------------------------------- 1 | const pv = module.exports = {} 2 | 3 | pv.add = function (p1, p2) { 4 | let p = {} 5 | for (let k in p1) { 6 | p[k] = p1[k] + p2[k] 7 | } 8 | return p 9 | } 10 | 11 | pv.sub = function (p1, p2) { 12 | return pv.add(p1, pv.neg(p2)) 13 | } 14 | 15 | pv.mul = function (p1, c) { 16 | let p = {} 17 | for (let k in p1) { 18 | p[k] = p1[k] * c 19 | } 20 | return p 21 | } 22 | 23 | pv.neg = function (p) { 24 | return pv.mul(p, -1) 25 | } 26 | 27 | pv.norm = function (p) { 28 | let norm = 0 29 | for (let k in p) { 30 | norm += p[k] * p[k] 31 | } 32 | return norm 33 | } 34 | 35 | pv.str = function (p, len=4) { 36 | let lines = [] 37 | for (let k in p) { 38 | lines.push(k+':'+p[k].toFixed(len)) 39 | } 40 | return '{' + lines.join(', ') + '}' 41 | } -------------------------------------------------------------------------------- /code/00-other/03-gradientDescendent/nn0/nn.js: -------------------------------------------------------------------------------- 1 | const pv = require('./lib/pvector') 2 | const nn = module.exports = { 3 | pv: pv 4 | } 5 | 6 | nn.step = 0.01 7 | 8 | nn.clone = function (o) { 9 | return {...o} 10 | } 11 | 12 | // 函數 f 對變數 k 的偏微分: df(p) / dk 13 | nn.df = function (f, p, k, h=nn.step) { 14 | let p1 = nn.clone(p) 15 | p1[k] += h 16 | return (f(p1) - f(p)) / h 17 | } 18 | 19 | // 函數 f 在點 p 上的梯度 ∇f(p) 20 | nn.grad = function (f, p) { 21 | let gp = {} 22 | for (let k in p) { 23 | gp[k] = nn.df(f, p, k) // 對變數 k 取偏導數後,放入梯度向量 gp 中 24 | } 25 | return gp 26 | } 27 | 28 | // 使用梯度下降法尋找函數最低點 29 | nn.optimize = function (f, p0) { 30 | let p = nn.clone(p0) 31 | while (true) { 32 | console.log('p=', pv.str(p), 'f(p)=', f(p)) 33 | let gp = nn.grad(f, p) // 計算梯度 gp 34 | let norm = pv.norm(gp) // norm = 梯度的長度 (步伐大小) 35 | if (norm < 0.00001) { // 如果步伐已經很小了,那麼就停止吧! 36 | break 37 | } 38 | let gstep = pv.mul(gp, -1 * nn.step) // gstep = 逆梯度方向的一小步 39 | p = pv.add(p, gstep) // 向 gstep 方向走一小步 40 | } 41 | return p // 傳回最低點! 42 | } 43 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/README.md: -------------------------------------------------------------------------------- 1 | # nn1 -- 深度學習背後的算法 2 | 3 | 用程式示範《反傳遞演算法》的原理! 4 | 5 | ``` 6 | $ cd example/02-backprop 7 | 8 | $ node .\gradientEx 9 | PS D:\course\aijs\project\nn1\example\02-backprop> node .\gradientEx.js 10 | forward: f() 11 | { x: Node { v: 2, g: 0 }, 12 | y: Node { v: 1, g: 0 }, 13 | x2: Node { v: 4, g: 0 }, 14 | y2: Node { v: 1, g: 0 }, 15 | o: Node { v: 5, g: 0 } } 16 | backward: grad() 17 | { x: Node { v: 2, g: 4 }, 18 | y: Node { v: 1, g: 1 }, 19 | x2: Node { v: 4, g: 1 }, 20 | y2: Node { v: 1, g: 1 }, 21 | o: Node { v: 5, g: 1 } } 22 | 23 | $ node optimizeEx 24 | 25 | p= {x:2.0000, y:1.0000} f(p)= 5 26 | gp= { x: 4, y: 1 } 27 | p= {x:1.9600, y:0.9900} f(p)= 4.8217 28 | gp= { x: 3.8415999999999997, y: 0.9801 } 29 | p= {x:1.9216, y:0.9802} f(p)= 4.653275148657 30 | gp= { x: 3.692485069056, y: 0.960790079601 } 31 | p= {x:1.8847, y:0.9706} f(p)= 4.493987190929792 32 | gp= { x: 3.5519401090757823, y: 0.9420470818540095 } 33 | // ... 中間省略 .... 34 | p= {x:0.0479, y:0.0468} f(p)= 0.004479629245409157 35 | gp= { x: 0.0022920829015963253, y: 0.0021875463438128314 } 36 | p= {x:0.0479, y:0.0467} f(p)= 0.004475389263812065 37 | gp= { x: 0.00228988872537307, y: 0.0021855005384389947 } 38 | p= {x:0.0478, y:0.0467} f(p)= 0.004471155300865204 39 | gp= { x: 0.002287697698821976, y: 0.0021834576020432284 } 40 | p= {x:0.0478, y:0.0467} f(p)= 0.004466927345179781 41 | gp= { x: 0.002285509815916863, y: 0.002181417529262918 } 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/01-gradient/f.js: -------------------------------------------------------------------------------- 1 | module.exports = function f (p) { 2 | let x = p.x, y = p.y 3 | return (x * x + y * y) 4 | } -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/01-gradient/gradientEx.js: -------------------------------------------------------------------------------- 1 | const G = require('../../lib/grad') 2 | const f = require('./f') 3 | 4 | console.log('df(f(x:1,y:1), x) = ', G.df(f, {x:1, y:1}, 'x')) 5 | 6 | console.log('grad(f(x:1,y:1))=', G.grad(f, {x:1, y:1})) 7 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/01-gradient/optimizeEx.js: -------------------------------------------------------------------------------- 1 | const nn = require('../../nn') 2 | const f = require('./f') 3 | 4 | nn.optimize(f, {x:1, y:1}) -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/02-backprop/f.js: -------------------------------------------------------------------------------- 1 | const nn = require('../../nn') 2 | const net = new nn.Net() 3 | 4 | let x = net.variable(2) 5 | let y = net.variable(1) 6 | let x2 = net.mul(x, x) 7 | let y2 = net.mul(y, y) 8 | let o = net.add(x2, y2) 9 | 10 | net.watch({x,y,x2,y2,o}) 11 | 12 | module.exports = new nn.FNet(net, {x:x, y:y}) 13 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/02-backprop/gradientEx.js: -------------------------------------------------------------------------------- 1 | const fnet = require('./f') 2 | 3 | console.log('forward: f()') 4 | 5 | fnet.f() 6 | 7 | console.log(fnet.dump()) 8 | 9 | console.log('backward: grad()') 10 | 11 | fnet.grad() 12 | 13 | console.log(fnet.dump()) 14 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/02-backprop/optimize.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccbook/algjs/501da2f5bd465fd84b587d353478e8fd81db4346/code/00-other/04-backPropagation/nn1/example/02-backprop/optimize.out -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/example/02-backprop/optimizeEx.js: -------------------------------------------------------------------------------- 1 | const nn = require('../../nn') 2 | const f = require('./f') 3 | 4 | nn.optimize(f, {x:2, y:1}) 5 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/fnet.js: -------------------------------------------------------------------------------- 1 | module.exports = class FNet { 2 | 3 | constructor(net, vars) { 4 | this.net = net 5 | this.vars = vars 6 | } 7 | 8 | setValues(p) { 9 | for (let k in p) { 10 | this.vars[k].v = p[k] 11 | } 12 | } 13 | 14 | getGrads() { 15 | let grads = {} 16 | for (let k in this.vars) { 17 | grads[k] = this.vars[k].g 18 | } 19 | return grads 20 | } 21 | 22 | f(p) { 23 | this.setValues(p) 24 | let o = this.net.forward() 25 | return o.v 26 | } 27 | 28 | grad(p) { 29 | this.f(p) 30 | this.net.backward() 31 | return this.getGrads() 32 | } 33 | 34 | dump() { 35 | return this.net.dump() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/function.js.bak: -------------------------------------------------------------------------------- 1 | var F = module.exports = {} 2 | 3 | F.add = function (x, y) { 4 | return x + y 5 | } 6 | 7 | F.gadd = function (x, y) { 8 | return 1 9 | } 10 | 11 | F.mul = function (x, y) { 12 | return x * y 13 | } 14 | 15 | F.gmul = function (x, y) { 16 | return y 17 | } 18 | 19 | F.gmuly = function (x, y) { 20 | return x 21 | } 22 | 23 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/gate.js: -------------------------------------------------------------------------------- 1 | module.exports = class Gate { 2 | constructor(o, x, y, f, gfx, gfy) { 3 | this.p = {o:o, x:x, y:y, f:f, gfx:gfx, gfy:gfy||gfx} 4 | } 5 | 6 | forward() { 7 | let p = this.p 8 | p.o.v = p.f(p.x.v, p.y.v) 9 | } 10 | 11 | backward() { 12 | let p = this.p 13 | p.x.g = p.gfx(p.o.v) 14 | p.y.g = p.gfy(p.o.v) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/grad.js: -------------------------------------------------------------------------------- 1 | const pv = require('./pvector') 2 | const FNet = require('./fnet') 3 | 4 | const G = module.exports = {} 5 | 6 | G.step = 0.01 7 | 8 | // 函數 f 對變數 k 的偏微分: df(p) / dk 9 | G.df = function (f, p, k, h=G.step) { 10 | let p1 = pv.clone(p) 11 | p1[k] += h 12 | return (f(p1) - f(p)) / h 13 | } 14 | 15 | // 函數 f 在點 p 上的梯度 ∇f(p) 16 | G.fgrad = function (f, p) { 17 | let gp = {} 18 | for (let k in p) { 19 | gp[k] = G.df(f, p, k) // 對變數 k 取偏導數後,放入梯度向量 gp 中 20 | } 21 | return gp 22 | } 23 | 24 | G.call = function (f, p) { 25 | if (f instanceof Function) { 26 | return f(p) 27 | } else if (f instanceof FNet) { 28 | return f.f(p) 29 | } else { 30 | throw Error('G.call: f should be in {Function, FNet}') 31 | } 32 | } 33 | 34 | G.grad = function (f, p) { 35 | if (f instanceof Function) { 36 | return G.fgrad(f, p) 37 | } else if (f instanceof FNet) { 38 | return f.grad(p) 39 | } else { 40 | throw Error('G.grad: f should be in {Function, FNet}') 41 | } 42 | } 43 | 44 | // 使用梯度下降法尋找函數最低點 45 | G.gradientDescendent = function (f, p0) { 46 | let p = pv.clone(p0) 47 | while (true) { 48 | console.log('p=', pv.str(p), 'f(p)=', G.call(f, p)) 49 | let gp = G.grad(f, p) // 計算梯度 gp 50 | console.log(' gp=', gp) 51 | // break; 52 | let norm = pv.norm(gp) // norm = 梯度的長度 (步伐大小) 53 | if (norm < 0.00001) { // 如果步伐已經很小了,那麼就停止吧! 54 | break 55 | } 56 | let gstep = pv.mul(gp, -1 * G.step) // gstep = 逆梯度方向的一小步 57 | p = pv.add(p, gstep) // 向 gstep 方向走一小步 58 | } 59 | return p // 傳回最低點! 60 | } 61 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/net.js: -------------------------------------------------------------------------------- 1 | const Node = require('./node') 2 | const Gate = require('./gate') 3 | 4 | module.exports = class Net { 5 | 6 | constructor () { 7 | this.gates = [] 8 | } 9 | 10 | variable (v, g) { 11 | return new Node(v, g) 12 | } 13 | 14 | op (x, y, f, gfx, gfy) { 15 | let o = new Node() 16 | let g = new Gate(o, x, y, f, gfx, gfy) 17 | this.gates.push(g) 18 | this.o = o 19 | return o 20 | } 21 | 22 | add (x, y) { return this.op(x, y, (x,y)=>x+y, (x,y)=>1) } 23 | mul (x, y) { return this.op(x, y, (x,y)=>x*y, (x,y)=>y, (x,y)=>x) } 24 | 25 | forward() { // 正向傳遞計算結果 26 | for (let gate of this.gates) { 27 | gate.forward() 28 | } 29 | return this.o 30 | } 31 | 32 | backward() { // 反向傳遞計算梯度 33 | this.o.g = 1 // 設定輸出節點 o 的梯度為 1 34 | for (let i=this.gates.length-1; i>=0; i--) { // 反向傳遞計算每個節點 Node 的梯度 g 35 | let gate = this.gates[i] 36 | gate.backward() 37 | } 38 | } 39 | 40 | watch (nodes) { 41 | this.nodes = nodes 42 | } 43 | 44 | dump() { 45 | return this.nodes 46 | } 47 | } 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/node.js: -------------------------------------------------------------------------------- 1 | module.exports = class Node { 2 | constructor(v = 0, g = 0) { 3 | this.v = v // 輸出值 (f(x)) 4 | this.g = g // 梯度值 (偏微分) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/pvector.js: -------------------------------------------------------------------------------- 1 | const pv = module.exports = {} 2 | 3 | pv.clone = function (o) { 4 | return {...o} 5 | } 6 | 7 | pv.add = function (p1, p2) { 8 | let p = {} 9 | for (let k in p1) { 10 | p[k] = p1[k] + p2[k] 11 | } 12 | return p 13 | } 14 | 15 | pv.sub = function (p1, p2) { 16 | return pv.add(p1, pv.neg(p2)) 17 | } 18 | 19 | pv.mul = function (p1, c) { 20 | let p = {} 21 | for (let k in p1) { 22 | p[k] = p1[k] * c 23 | } 24 | return p 25 | } 26 | 27 | pv.neg = function (p) { 28 | return pv.mul(p, -1) 29 | } 30 | 31 | pv.norm = function (p) { 32 | let norm = 0 33 | for (let k in p) { 34 | norm += p[k] * p[k] 35 | } 36 | return norm 37 | } 38 | 39 | pv.str = function (p, len=4) { 40 | let lines = [] 41 | for (let k in p) { 42 | lines.push(k+':'+p[k].toFixed(len)) 43 | } 44 | return '{' + lines.join(', ') + '}' 45 | } -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/lib/pvector.js.bak: -------------------------------------------------------------------------------- 1 | module.exports = class PVector { 2 | constructor (p) { 3 | this.p = p 4 | } 5 | 6 | add (p2) { 7 | let p = new PVector() 8 | for (let k in p2) { 9 | p[k] = this.p[k] + p2[k] 10 | } 11 | return p 12 | } 13 | 14 | sub (p2) { 15 | return this.add(p2.neg()) 16 | } 17 | 18 | mul (c) { 19 | let p = {} 20 | for (let k in p1) { 21 | p[k] = this.p[k] * c 22 | } 23 | return p 24 | } 25 | 26 | neg () { 27 | return this.mul(-1) 28 | } 29 | 30 | norm () { 31 | let norm = 0 32 | for (let k in p) { 33 | norm += p[k] * p[k] 34 | } 35 | return norm 36 | } 37 | 38 | str(len=4) { 39 | let lines = [] 40 | for (let k in this.p) { 41 | lines.push(k + ':' + this.p[k].toFixed(len)) 42 | } 43 | return '{' + lines.join(', ') + '}' 44 | } 45 | } 46 | /* 47 | pv.add = function (p1, p2) { 48 | let p = {} 49 | for (let k in p1) { 50 | p[k] = p1[k] + p2[k] 51 | } 52 | return p 53 | } 54 | 55 | pv.sub = function (p1, p2) { 56 | return pv.add(p1, pv.neg(p2)) 57 | } 58 | 59 | pv.mul = function (p1, c) { 60 | let p = {} 61 | for (let k in p1) { 62 | p[k] = p1[k] * c 63 | } 64 | return p 65 | } 66 | 67 | pv.neg = function (p) { 68 | return pv.mul(p, -1) 69 | } 70 | 71 | pv.norm = function (p) { 72 | let norm = 0 73 | for (let k in p) { 74 | norm += p[k] * p[k] 75 | } 76 | return norm 77 | } 78 | 79 | pv.str = function (p, len=4) { 80 | let lines = [] 81 | for (let k in p) { 82 | lines.push(k+':'+p[k].toFixed(len)) 83 | } 84 | return '{' + lines.join(', ') + '}' 85 | } 86 | */ -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/nn1/nn.js: -------------------------------------------------------------------------------- 1 | const G = require('./lib/grad') 2 | 3 | const nn = module.exports = Object.assign(G, { 4 | pv: require('./lib/pvector'), 5 | Node: require('./lib/node'), 6 | Gate: require('./lib/gate'), 7 | Net: require('./lib/net'), 8 | FNet: require('./lib/fnet') 9 | }) 10 | 11 | nn.optimize = nn.gd = nn.gradientDescendent 12 | -------------------------------------------------------------------------------- /code/00-other/04-backPropagation/svm/svm.js: -------------------------------------------------------------------------------- 1 | var j6 = require('../../lib/j6') 2 | 3 | var x = [[0.4, 0.5, 0.5, 0., 0., 0.], 4 | [0.5, 0.3, 0.5, 0., 0., 0.01], 5 | [0.4, 0.8, 0.5, 0., 0.1, 0.2], 6 | [1.4, 0.5, 0.5, 0., 0., 0.], 7 | [1.5, 0.3, 0.5, 0., 0., 0.], 8 | [0., 0.9, 1.5, 0., 0., 0.], 9 | [0., 0.7, 1.5, 0., 0., 0.], 10 | [0.5, 0.1, 0.9, 0., -1.8, 0.], 11 | [0.8, 0.8, 0.5, 0., 0., 0.], 12 | [0., 0.9, 0.5, 0.3, 0.5, 0.2], 13 | [0., 0., 0.5, 0.4, 0.5, 0.], 14 | [0., 0., 0.5, 0.5, 0.5, 0.], 15 | [0.3, 0.6, 0.7, 1.7, 1.3, -0.7], 16 | [0., 0., 0.5, 0.3, 0.5, 0.2], 17 | [0., 0., 0.5, 0.4, 0.5, 0.1], 18 | [0., 0., 0.5, 0.5, 0.5, 0.01], 19 | [0.2, 0.01, 0.5, 0., 0., 0.9], 20 | [0., 0., 0.5, 0.3, 0.5, -2.3], 21 | [0., 0., 0.5, 0.4, 0.5, 4], 22 | [0., 0., 0.5, 0.5, 0.5, -2]]; 23 | 24 | var y = [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,1,1,1,1,1,1,1]; 25 | 26 | var svm = new j6.ML.SVM({ 27 | x : x, 28 | y : y 29 | }); 30 | 31 | svm.train({ 32 | C : 1.1, // default : 1.0. C in SVM. 33 | tol : 1e-5, // default : 1e-4. Higher tolerance --> Higher precision 34 | max_passes : 20, // default : 20. Higher max_passes --> Higher precision 35 | alpha_tol : 1e-5, // default : 1e-5. Higher alpha_tolerance --> Higher precision 36 | 37 | kernel : { type: "polynomial", c: 1, d: 5} 38 | // default : {type : "gaussian", sigma : 1.0} 39 | // {type : "gaussian", sigma : 0.5} 40 | // {type : "linear"} // x*y 41 | // {type : "polynomial", c : 1, d : 8} // (x*y + c)^d 42 | // Or you can use your own kernel. 43 | // kernel : function(vecx,vecy) { return dot(vecx,vecy);} 44 | }); 45 | 46 | console.log("Predict : ",svm.predict([1.3, 1.7, 0.5, 0.5, 1.5, 0.4])); -------------------------------------------------------------------------------- /code/00-other/05-geneticAlgorithm/GA.js: -------------------------------------------------------------------------------- 1 | var GA={ 2 | population:[], // 族群 3 | mutationRate:0.1, // 突變率 4 | } 5 | 6 | GA.run=function(size, maxGen) { // 遺傳演算法主程式 7 | GA.population = GA.newPopulation(size); // 產生初始族群 8 | for (t = 0; t < maxGen; t++) { // 最多產生 maxGen 代 9 | console.log("============ generation", t, "===============") 10 | GA.population = GA.reproduction(GA.population); // 產生下一代 11 | GA.dump(); // 印出目前族群 12 | } 13 | } 14 | 15 | GA.newPopulation=function(size) { 16 | var newPop=[]; 17 | for (var i=0; ic1.fitness - c2.fitness; 27 | 28 | // 輪盤選擇法: 隨機選擇一個個體 -- 落點在 i*i ~ (i+1)*(i+1) 之間都算是 i 29 | GA.selection = function() { 30 | var n = GA.population.length; 31 | var shoot = randomInt(0, n*n/2); 32 | var select = Math.floor(Math.sqrt(shoot*2)); 33 | return GA.population[select]; 34 | } 35 | 36 | // 產生下一代 37 | GA.reproduction=function() { 38 | var newPop = [] 39 | for (var i = 0; i < GA.population.length; i++) { 40 | var parent1 = GA.selection(); // 選取父親 41 | var parent2 = GA.selection(); // 選取母親 42 | var chromosome = GA.crossover(parent1, parent2); // 父母交配,產生小孩 43 | var prob = random(0,1); 44 | if (prob < GA.mutationRate) // 有很小的機率 45 | chromosome = GA.mutate(chromosome); // 小孩會突變 46 | newPop[i] = { chromosome:chromosome, fitness:GA.calcFitness(chromosome) }; // 將小孩放進下一代族群裡 47 | } 48 | newPop.sort(fitnessCompare); // 對新一代根據適應性(分數)進行排序 49 | return newPop; 50 | } 51 | 52 | GA.dump = function() { // 印出一整代成員 53 | for (var i=0; i0&&m[x][y-1]==' ') //向左 27 | if (findPath(m, x,y-1)) return true; 28 | if(x>0&&m[x-1][y]==' ') //向上 29 | if (findPath(m, x-1,y)) return true; 30 | m[x][y]='+'; 31 | return false; 32 | } 33 | 34 | var m =['********', 35 | '** * ***', 36 | ' ***', 37 | '* ******', 38 | '* **', 39 | '***** **']; 40 | 41 | findPath(m, 2, 0); 42 | log('========================='); 43 | matrixPrint(m); -------------------------------------------------------------------------------- /code/00-other/16-search/puzzleSearch.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var log = console.log; 3 | var up = 1, right=2, down=3, left=4; 4 | 5 | function enqueue(a, o) { a.push(o); } 6 | function dequeue(a) { return a.shift(); } 7 | function equal(a, b) { return JSON.stringify(a)===JSON.stringify(b); } 8 | function board2str(b) { return b.join('\n'); } 9 | 10 | function findXY(board, value) { 11 | for (var x=0; x 2 || y2<0 || y2>2) 28 | return false; 29 | var t = b[x1][y1]; 30 | b[x1][y1]=b[x2][y2]; 31 | b[x2][y2]=t; 32 | return true; 33 | } 34 | 35 | function move(board, dir) { // 加入所有可能的移動方式 36 | var xy = findXY(board, 0); // 找出空格 0 的位置 37 | var x = xy.x, y=xy.y; 38 | var nboard = boardClone(board); 39 | var s = false; 40 | switch (dir) { 41 | case up: s=swap(nboard,x,y,x-1,y); break; // 空格和上面一格交換 42 | case right: s=swap(nboard,x,y,x,y+1); break; // 空格和右邊一格交換 43 | case down: s=swap(nboard,x,y,x+1,y); break; // 空格和下面一格交換 44 | case left: s=swap(nboard,x,y,x,y-1); break; // 空格和左邊一格交換 45 | } 46 | if (s) 47 | return nboard; 48 | else 49 | return null; 50 | } 51 | 52 | function moveAdd(board, dir, neighbors) { // 向 dir 方向移動,並加入到 neighbors 陣列中 53 | var nboard = move(board, dir); 54 | if (nboard !== null) { 55 | neighbors.push(nboard); 56 | } 57 | } 58 | 59 | function getNeighbors(board) { // 取得所有鄰居 60 | var neighbors = []; 61 | moveAdd(board, up, neighbors); 62 | moveAdd(board, down, neighbors); 63 | moveAdd(board, right, neighbors); 64 | moveAdd(board, left, neighbors); 65 | return neighbors; 66 | } 67 | 68 | var goal = [[1,2,3], 69 | [8,0,4], 70 | [7,6,5]]; 71 | 72 | var start= [[1,3,4], 73 | [8,2,5], 74 | [7,0,6]]; 75 | 76 | var queue=[start]; // BFS 用的 queue, 起始點為 1。 77 | var visited={}; 78 | var parent={}; 79 | var level={}; 80 | 81 | function bfs(q, goal) { // 廣度優先搜尋 82 | while (q.length > 0) { 83 | var node = dequeue(q); // 否則、取出 queue 的第一個節點。 84 | var nodestr = board2str(node); 85 | // log('q.length=%d level=%d\n===node===\n%s==parent==\n%s', q.length, level[nodestr], nodestr, parent[nodestr]); // 印出節點 86 | if (equal(node, goal)) return true; 87 | if (visited[nodestr]===undefined) // 如果該節點尚未拜訪過。 88 | visited[nodestr] = true; // 標示為已拜訪 89 | else // 否則 (已訪問過) 90 | continue; // 不繼續搜尋,直接返回。 91 | var neighbors = getNeighbors(node); // 取出鄰居。 92 | for (var i in neighbors) { // 對於每個鄰居 93 | var n = neighbors[i]; 94 | var nstr = board2str(n); 95 | if (!visited[nstr]) { // 假如該鄰居還沒被拜訪過 96 | parent[nstr] = nodestr; 97 | level[nstr] = level[nodestr] + 1; 98 | enqueue(q, n); // 就放入 queue 中 99 | } 100 | } 101 | } 102 | return false; 103 | } 104 | 105 | function backtrace(goal) { 106 | log('======= backtrace ========='); 107 | var nodestr = board2str(goal); 108 | while (nodestr !== undefined) { 109 | log('%s\n', nodestr); 110 | nodestr = parent[nodestr]; 111 | } 112 | } 113 | 114 | level[board2str(start)]=0; 115 | var found = bfs(queue, goal); // 呼叫廣度優先搜尋。 116 | log('bfs:found=%s', found); 117 | if (found) 118 | backtrace(goal); 119 | -------------------------------------------------------------------------------- /code/00-other/16-search/puzzleSearch.txt: -------------------------------------------------------------------------------- 1 | bfs()= true 2 | ======= backtrace ========= 3 | 1,2,3 4 | 8,0,4 5 | 7,6,5 6 | 7 | 1,0,3 8 | 8,2,4 9 | 7,6,5 10 | 11 | 1,3,0 12 | 8,2,4 13 | 7,6,5 14 | 15 | 1,3,4 16 | 8,2,0 17 | 7,6,5 18 | 19 | 1,3,4 20 | 8,2,5 21 | 7,6,0 22 | 23 | 1,3,4 24 | 8,2,5 25 | 7,0,6 26 | 27 | -------------------------------------------------------------------------------- /code/00-other/16-search/shipSearch.js: -------------------------------------------------------------------------------- 1 | var c = console; 2 | var objs = ['人', '狼', '羊', '菜']; 3 | var state= [ 0, 0 , 0, 0 ]; 4 | 5 | function neighbors(s) { 6 | var side = s[0]; 7 | var next = []; 8 | checkAdd(next, move(s,0)); 9 | for (var i=1; inode chess P2C 9 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 | 0 - - - - - - - - - - - - - - - - 0 11 | 1 - - - - - - - - - - - - - - - - 1 12 | 2 - - - - - - - - - - - - - - - - 2 13 | 3 - - - - - - - - - - - - - - - - 3 14 | 4 - - - - - - - - - - - - - - - - 4 15 | 5 - - - - - - - - - - - - - - - - 5 16 | 6 - - - - - - - - - - - - - - - - 6 17 | 7 - - - - - - - - - - - - - - - - 7 18 | 8 - - - - - - - - - - - - - - - - 8 19 | 9 - - - - - - - - - - - - - - - - 9 20 | a - - - - - - - - - - - - - - - - a 21 | b - - - - - - - - - - - - - - - - b 22 | c - - - - - - - - - - - - - - - - c 23 | d - - - - - - - - - - - - - - - - d 24 | e - - - - - - - - - - - - - - - - e 25 | f - - - - - - - - - - - - - - - - f 26 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 27 | 28 | 將 o 下在 : 66 29 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 30 | 0 - - - - - - - - - - - - - - - - 0 31 | 1 - - - - - - - - - - - - - - - - 1 32 | 2 - - - - - - - - - - - - - - - - 2 33 | 3 - - - - - - - - - - - - - - - - 3 34 | 4 - - - - - - - - - - - - - - - - 4 35 | 5 - - - - - - - - - - - - - - - - 5 36 | 6 - - - - - - o - - - - - - - - - 6 37 | 7 - - - - - - - - - - - - - - - - 7 38 | 8 - - - - - - - - - - - - - - - - 8 39 | 9 - - - - - - - - - - - - - - - - 9 40 | a - - - - - - - - - - - - - - - - a 41 | b - - - - - - - - - - - - - - - - b 42 | c - - - - - - - - - - - - - - - - c 43 | d - - - - - - - - - - - - - - - - d 44 | e - - - - - - - - - - - - - - - - e 45 | f - - - - - - - - - - - - - - - - f 46 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 47 | 48 | best={"r":6,"c":7,"score":31} 49 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 50 | 0 - - - - - - - - - - - - - - - - 0 51 | 1 - - - - - - - - - - - - - - - - 1 52 | 2 - - - - - - - - - - - - - - - - 2 53 | 3 - - - - - - - - - - - - - - - - 3 54 | 4 - - - - - - - - - - - - - - - - 4 55 | 5 - - - - - - - - - - - - - - - - 5 56 | 6 - - - - - - o x - - - - - - - - 6 57 | 7 - - - - - - - - - - - - - - - - 7 58 | 8 - - - - - - - - - - - - - - - - 8 59 | 9 - - - - - - - - - - - - - - - - 9 60 | a - - - - - - - - - - - - - - - - a 61 | b - - - - - - - - - - - - - - - - b 62 | c - - - - - - - - - - - - - - - - c 63 | d - - - - - - - - - - - - - - - - d 64 | e - - - - - - - - - - - - - - - - e 65 | f - - - - - - - - - - - - - - - - f 66 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 67 | 68 | ... 69 | 70 | best={"r":6,"c":3,"score":144} 71 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 72 | 0 - - - - - - - - - - - - - - - - 0 73 | 1 - - - - - - - - - - - - - - - - 1 74 | 2 - - - - - - - - - - - - - - - - 2 75 | 3 - - - - - - - - - - - - - - - - 3 76 | 4 - - - - x - - - - - - - - - - - 4 77 | 5 - - - - - o - - - - - - - - - - 5 78 | 6 - - - x o o o x - - - - - - - - 6 79 | 7 - - - - - - - o - - - - - - - - 7 80 | 8 - - - - - - - - x - - - - - - - 8 81 | 9 - - - - - - - - - x - - - - - - 9 82 | a - - - - - - - - - - - - - - - - a 83 | b - - - - - - - - - - - - - - - - b 84 | c - - - - - - - - - - - - - - - - c 85 | d - - - - - - - - - - - - - - - - d 86 | e - - - - - - - - - - - - - - - - e 87 | f - - - - - - - - - - - - - - - - f 88 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 89 | ... 90 | 91 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 92 | 0 - - - - - - - - - - - - - - - - 0 93 | 1 - - - - - - - - - - - - - - - - 1 94 | 2 - - - - - - - - - - - - - - - - 2 95 | 3 - - - - - - - - - - - - - - - - 3 96 | 4 - - - - x - - - - - - - - - - - 4 97 | 5 - - o - - o - - - - - - - - - - 5 98 | 6 - - o x o o o x - - - - - - - - 6 99 | 7 - - - - x o o o - - - - - - - - 7 100 | 8 - - - - - x - - x - - - - - - - 8 101 | 9 - - - - - - x - - x - - - - - - 9 102 | a - - - - - - - x - - - - - - - - a 103 | b - - - - - - - - - - - - - - - - b 104 | c - - - - - - - - - - - - - - - - c 105 | d - - - - - - - - - - - - - - - - d 106 | e - - - - - - - - - - - - - - - - e 107 | f - - - - - - - - - - - - - - - - f 108 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 109 | 110 | x 贏了! 111 | ``` 112 | 113 | ## AlphaGo 圍棋 114 | 115 | https://github.com/Rochester-NRT/RocAlphaGo -------------------------------------------------------------------------------- /code/00-other/17-gameSearch/minMax.js: -------------------------------------------------------------------------------- 1 | // 參考 -- https://gist.github.com/byanofsky/c8dd06cd1b1fb8d06a9dd695d07e403e 2 | 3 | var calcBestMove = function(depth, game, playerColor, 4 | alpha=Number.NEGATIVE_INFINITY, 5 | beta=Number.POSITIVE_INFINITY, 6 | isMaximizingPlayer=true) { 7 | // Base case: evaluate board 8 | if (depth === 0) { 9 | value = evaluateBoard(game.board(), playerColor); 10 | return [value, null] 11 | } 12 | 13 | // Recursive case: search possible moves 14 | var bestMove = null; // best move not set yet 15 | var possibleMoves = game.moves(); 16 | // Set random order for possible moves 17 | possibleMoves.sort(function(a, b){return 0.5 - Math.random()}); 18 | // Set a default best move value 19 | var bestMoveValue = isMaximizingPlayer ? Number.NEGATIVE_INFINITY 20 | : Number.POSITIVE_INFINITY; 21 | // Search through all possible moves 22 | for (var i = 0; i < possibleMoves.length; i++) { 23 | var move = possibleMoves[i]; 24 | // Make the move, but undo before exiting loop 25 | game.move(move); 26 | // Recursively get the value from this move 27 | value = calcBestMove(depth-1, game, playerColor, alpha, beta, !isMaximizingPlayer)[0]; 28 | // Log the value of this move 29 | console.log(isMaximizingPlayer ? 'Max: ' : 'Min: ', depth, move, value, 30 | bestMove, bestMoveValue); 31 | 32 | if (isMaximizingPlayer) { 33 | // Look for moves that maximize position 34 | if (value > bestMoveValue) { 35 | bestMoveValue = value; 36 | bestMove = move; 37 | } 38 | alpha = Math.max(alpha, value); 39 | } else { 40 | // Look for moves that minimize position 41 | if (value < bestMoveValue) { 42 | bestMoveValue = value; 43 | bestMove = move; 44 | } 45 | beta = Math.min(beta, value); 46 | } 47 | // Undo previous move 48 | game.undo(); 49 | // Check for alpha beta pruning 50 | if (beta <= alpha) { 51 | console.log('Prune', alpha, beta); 52 | break; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /code/00-other/17-gameSearch/monteCarloSearch.md: -------------------------------------------------------------------------------- 1 | # Monte Carlo 搜尋樹 2 | 3 | * 西洋棋 js 實作 -- https://github.com/FSUcilab/stochess (好) 4 | * 畫棋盤使用 -- https://github.com/oakmac/chessboardjs 5 | * 下棋邏輯使用 -- https://github.com/jhlywa/chess.js 6 | 7 | * [What is the Monte Carlo tree search?](https://blog.theofekfoundation.org/artificial-intelligence/2016/06/27/what-is-the-monte-carlo-tree-search/) 8 | * 專案 -- https://github.com/The-Ofek-Foundation/Online-Go/tree/0bf051ffdf96a4892c2a9613a5b966529192ccaf (爛) 9 | * 展示 -- https://www.theofekfoundation.org/games/OnlineGo/ (爛) -------------------------------------------------------------------------------- /code/00-other/19-recursiveDescendent/compileExp.js: -------------------------------------------------------------------------------- 1 | /* 語法 2 | E = T ([+-/*] E)* 3 | T = N | (E) 4 | 5 | 範例:3+(5*4)-2 6 | */ 7 | 8 | var c = console; 9 | 10 | var tagMap={ 11 | N : ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], 12 | OP: ["+", "-", "*", "-"] 13 | }; 14 | 15 | var wi = 0; 16 | 17 | function isNext(tag) { 18 | if (words[wi] === tag) return true; 19 | var tagWords=tagMap[tag]; 20 | if (typeof tagWords === "undefined") 21 | return false; 22 | else 23 | return (tagWords.indexOf(words[wi])>=0); 24 | } 25 | 26 | function next(tag) { 27 | c.log("tag="+tag+" word="+words[wi]); 28 | if (isNext(tag)) { 29 | return words[wi++]; 30 | } 31 | throw Error("Error !"); 32 | } 33 | 34 | var tempIdx = 0; 35 | var getTemp=function() { 36 | return tempIdx++; 37 | } 38 | // E = T ([+-/*] E)* 39 | function E() { 40 | var t = T(); 41 | while (isNext("OP")) { 42 | var op = next("OP"); 43 | var e = E(); 44 | var result = getTemp(); 45 | console.log(" T%d=T%d%sT%d", result, t, op, e); 46 | t = result; 47 | } 48 | return t; 49 | } 50 | 51 | // T = N | (E) 52 | function T() { 53 | if (isNext("N")) { 54 | var t = getTemp(); 55 | var number = next("N"); 56 | console.log(" T%d=%d", t, number); 57 | return t; 58 | } else { 59 | next("("); 60 | var e = E(); 61 | next(")"); 62 | return e; 63 | } 64 | } 65 | 66 | var words="3+(5*4)-2"; 67 | c.log("%j", words); 68 | E(words); -------------------------------------------------------------------------------- /code/00-other/19-recursiveDescendent/genChinese.js: -------------------------------------------------------------------------------- 1 | var R = require('./random') 2 | /* 3 | for (var i=0; i<10; i++) { 4 | var animal = randSelect(['dog', 'cat']); 5 | console.log("%s", animal); 6 | } 7 | */ 8 | /* 9 | S = NP VP 10 | NP = DET N 11 | VP = V NP 12 | N = dog | cat 13 | V = chase | eat 14 | DET = a | the 15 | */ 16 | 17 | function S() { 18 | return NP()+" "+VP(); 19 | } 20 | 21 | function NP() { 22 | return DET()+" "+N(); 23 | } 24 | 25 | function VP() { 26 | return V()+" "+NP(); 27 | } 28 | 29 | function N() { 30 | return R.randSelect(["狗", "貓"]); 31 | } 32 | 33 | function V() { 34 | return R.randSelect(["追", "吃"]); 35 | } 36 | 37 | function DET() { 38 | return R.randSelect(["一隻", "這隻"]); 39 | } 40 | 41 | console.log(S()); -------------------------------------------------------------------------------- /code/00-other/19-recursiveDescendent/genEnglish.js: -------------------------------------------------------------------------------- 1 | var R = require('./random') 2 | /* 3 | for (var i=0; i<10; i++) { 4 | var animal = randSelect(['dog', 'cat']); 5 | console.log("%s", animal); 6 | } 7 | */ 8 | /* 9 | S = NP VP 10 | NP = DET N 11 | VP = V NP 12 | N = dog | cat 13 | V = chase | eat 14 | DET = a | the 15 | */ 16 | 17 | function S() { 18 | return NP()+" "+VP(); 19 | } 20 | 21 | function NP() { 22 | return DET()+" "+N(); 23 | } 24 | 25 | function VP() { 26 | return V()+" "+NP(); 27 | } 28 | 29 | function N() { 30 | return R.randSelect(["dog", "cat"]); 31 | } 32 | 33 | function V() { 34 | return R.randSelect(["chase", "eat"]); 35 | } 36 | 37 | function DET() { 38 | return R.randSelect(["a", "the"]); 39 | } 40 | 41 | console.log(S()); -------------------------------------------------------------------------------- /code/00-other/19-recursiveDescendent/genMath.js: -------------------------------------------------------------------------------- 1 | var R=require("./random"); 2 | /* 3 | 問題: 小華有6個蘋果 4 | 給了大雄3個 5 | 又給了小明2個 6 | 請問小華還有幾個蘋果? 7 | 8 | 答案: 1個 9 | */ 10 | var peoples = ["小明", "小華", "小莉", "大雄"]; 11 | var objects = ["蘋果", "橘子", "柳丁", "番茄"]; 12 | var owner = People(); 13 | var object = Object(); 14 | var nOwn = R.randInt(3, 20); 15 | 16 | remove(peoples, owner); 17 | 18 | function remove(array, item) { 19 | array.splice(array.indexOf(item), 1); 20 | } 21 | 22 | function MathTest() { 23 | return "問題:\t"+Own()+"\n\t"+Give() 24 | +"\n\t又"+Give()+"\n\t"+Question(); 25 | } 26 | 27 | function Own() { 28 | return owner+"有"+nOwn+"個"+object; 29 | } 30 | 31 | function Give() { 32 | var nGive = R.randInt(1, nOwn); 33 | nOwn-=nGive; 34 | return "給了"+People()+nGive+"個"; 35 | } 36 | 37 | function Question() { 38 | return "請問"+owner+"還有幾個"+object+"?"; 39 | } 40 | 41 | function People() { 42 | return R.randSelect(peoples); 43 | } 44 | 45 | function Object() { 46 | return R.randSelect(objects); 47 | } 48 | 49 | console.log(MathTest()); 50 | console.log("\n答案:\t"+nOwn+"個"); -------------------------------------------------------------------------------- /code/00-other/19-recursiveDescendent/genexp1.js: -------------------------------------------------------------------------------- 1 | var R = require('./random') 2 | 3 | // === BNF Grammar ===== 4 | // E = T [+-*/] E | T 5 | // T = [0-9] | (E) 6 | 7 | function print(s) { 8 | process.stdout.write(s); 9 | } 10 | 11 | function E() { 12 | if (R.randInt(1,10) <= 5) { 13 | T(); print(R.randChar("+-*/")); E(); 14 | } else { 15 | T(); 16 | } 17 | } 18 | 19 | function T() { 20 | if (R.randInt(1,10) < 7) { 21 | print(R.randChar("0123456789")); 22 | } else { 23 | print("("); E(); print(")"); 24 | } 25 | } 26 | 27 | for (var i=0; i<10; i++) { 28 | E(); 29 | print("\n"); 30 | } -------------------------------------------------------------------------------- /code/00-other/19-recursiveDescendent/genexp2.js: -------------------------------------------------------------------------------- 1 | var R = require('./random') 2 | 3 | /* 4 | E = N | E [+/-*] E 5 | N = 0-9 6 | */ 7 | 8 | function E() { 9 | var gen = R.randSelect(["N", "EE"]); 10 | if (gen === "N") { 11 | return N(); 12 | } else { 13 | return E() + R.randSelect(["+", "-", "*", "/"]) + E(); 14 | } 15 | } 16 | 17 | function N() { 18 | return R.randSelect(["1", "2", "3", "4", "5", "6", "7", "8", "9"]); 19 | } 20 | 21 | var e = E(); 22 | console.log(e, "=", eval(e)); 23 | -------------------------------------------------------------------------------- /code/00-other/datastructure/hashtable.js: -------------------------------------------------------------------------------- 1 | var hashtable = {} 2 | 3 | hashtable['ccc']= '123' 4 | hashtable['ddd']= '456' 5 | 6 | console.log('hashtable=%j', hashtable) 7 | 8 | console.log('hashtable[ccc]=', hashtable['ccc']) 9 | -------------------------------------------------------------------------------- /code/00-other/datastructure/queue.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccbook/algjs/501da2f5bd465fd84b587d353478e8fd81db4346/code/00-other/datastructure/queue.js -------------------------------------------------------------------------------- /code/00-other/datastructure/stack.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | constructor() { 3 | this.a = [] 4 | } 5 | 6 | push(o) { 7 | this.a.push(o) 8 | } 9 | 10 | pop() { 11 | return this.a.pop() 12 | } 13 | } 14 | 15 | var s = new Stack() 16 | s.push(3) 17 | s.push(5) 18 | console.log("s=%j", s) 19 | s.push(2) 20 | console.log("s=%j", s) 21 | var t1 = s.pop() 22 | console.log("s=%j", s) 23 | console.log("t1=%j", t1) 24 | -------------------------------------------------------------------------------- /code/00-other/qlearning/README.md: -------------------------------------------------------------------------------- 1 | # Machine Learning 2 | 3 | ## Q-Learning 4 | 5 | * https://github.com/nrox/q-learning.js.git 6 | * 請看資料夾 q-learning.js (example1 jQuery找不到的問題我修正了一下) -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.idea/* 3 | -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Nuno Rocha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/README.md: -------------------------------------------------------------------------------- 1 | q-learning.js 2 | ============= 3 | 4 | Q-Learning Algorithm in JavaScript 5 | 6 | It's based on this tutorial: [A Painless Q-Learning Tutorial](http://mnemstudio.org/path-finding-q-learning-tutorial.htm). 7 | 8 | This belongs to a set of AI algorithms in JS to be used by [assemblino.js](https://github.com/nrox/assemblino.js). 9 | 10 | This algorithm is suitable to search, path finding, control, as it retains in memory an heuristics to achieve the goal, from 11 | any reachable discrete state. 12 | 13 | Demo 14 | ----- 15 | 16 | [Example 1: basic](http://nrox.github.io/q-learning.js/example1.html) 17 | 18 | [Example 2: game agent](http://nrox.github.io/q-learning.js/example2.html) 19 | 20 | [Example 3: learning to keep distance](http://nrox.github.io/q-learning.js/example3.html) 21 | 22 | 23 | Usage Example 24 | ======= 25 | 26 | Learning 27 | ------ 28 | 29 | The argument to the constructor is the gamma parameter. Default 0.5 30 | 31 | var learner = new QLearner(0.8); 32 | 33 | 34 | Add transitions like this: 35 | 36 | learner.add(fromState, toState, reward, actionName); 37 | 38 | In this last expression, if fromState or toState do not exist they are added automatically. If no reward is know pass 39 | *undefined*, if actionName is not important leave it undefined. 40 | 41 | If no reward is known and actionName is not important: 42 | 43 | learner.add(fromState, toState); 44 | 45 | Reward is known and actionName is not important: 46 | 47 | learner.add(fromState, toState, reward); 48 | 49 | Reward is not known and actionName is important 50 | 51 | learner.add(fromState, toState, undefined, actionName); 52 | 53 | States and actions set, then make it learn. The argument is the number of iterations. 54 | 55 | learner.learn(1000); 56 | 57 | Running 58 | ------- 59 | 60 | To use what the learner *knows*. Set an initial state 61 | 62 | learner.setState('s0'); 63 | 64 | then call to choose the best action and automatically apply it. 65 | 66 | learner.runOnce(); 67 | 68 | and get the next state with 69 | 70 | var cur = learner.getState(); 71 | 72 | or get the best action: 73 | 74 | var ba = learner.bestAction(); 75 | 76 | or run it until it stays in the same state, or solution. 77 | 78 | var current = null; 79 | while (current!==learner.getState()){ 80 | current = learner.getState(); 81 | learner.runOnce(); 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | q-learning.js 5 | 6 | 7 | 25 | 26 | 27 | 28 | 29 |

30 | GitHub repo 31 |

32 |

33 | Regarding the tutorial A Painless Q-Learning Tutorial, 34 | this simple demo iterates trough the steps of the solution. 35 | 36 | Click on a cell to set it as the current state. 37 |

38 | 39 | 40 | -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/example1.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var ql = new QLearner(0.8); 4 | 5 | ql.add(0,4,0); 6 | ql.add(1,3,0); 7 | ql.add(1,5,100); 8 | ql.add(2,3,0); 9 | ql.add(3,1,0); 10 | ql.add(3,2,0); 11 | ql.add(3,4,0); 12 | ql.add(4,0,0); 13 | ql.add(4,5,100); 14 | ql.add(5,1,0); 15 | ql.add(5,4,0); 16 | ql.add(5,5,100); 17 | 18 | ql.learn(500); 19 | 20 | setInterval(function(){ 21 | var cur = ql.currentState; 22 | var st = ql.runOnce(); 23 | if (cur==st) return; 24 | setStateDiv(st.name); 25 | }, 1500); 26 | 27 | function setStateDiv(state){ 28 | $('.state').css('background-color', 'white'); 29 | $('[state="' + state + '"]').css('background-color', 'orange'); 30 | } 31 | 32 | function addStateDiv(state){ 33 | var $state = $('
',{ 34 | state: state, 35 | 'class': 'state' 36 | }); 37 | $state.on('click', function(){ 38 | ql.setState(state); 39 | setStateDiv(state); 40 | }); 41 | $state.text(state); 42 | $('body').append($state); 43 | } 44 | 45 | for (var i = 0; i <= 5; i++){ 46 | addStateDiv(i); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | q-learning.js test 2 5 | 6 | 7 | 33 | 34 | 35 | 36 |

37 | GitHub repo 38 |

39 |

40 | This is a practical implementation of the artificial intelligence algorithm Q-Learning. The tutorial 41 | A Painless Q-Learning Tutorial is a very nice introduction. 42 | The Q-Learning maximizes the expected reward for an action. 43 |

44 | 45 |
46 | 47 | 48 | 49 |
50 |

51 | The black circle represent an agent. A green circle represent food (+1) and a gray represent poison (-1). Food and poison are inserted with the same probability. 52 | The agent can move left, right or stay. The states are string representations of the objects in the 3x3 square immediately in front of the agent, 53 | that's what he sees. There are therefore 3^9=19683 possible states. 54 |

55 |

56 | In some states, exploration of new actions are done with a probability of 10%, if the outcomes of that action is not known. 57 |

58 |

59 | We are using online learning, where training and using the algorithm are done simultaneously, i.e. we don't need to collect 60 | considerable amounts of data before the algorithm is trained. A consequence is that in early stages the performance is poor. 61 | In the beginning the outcome should be something like a random walk and as time goes by, the performance should be considerably better: the agent not only avoids poison, 62 | but catch more food. 63 |

64 |

65 | Press fast to speed up. To understand this example read the code in test2.js. Simple changes to this code can produce agents 66 | which battle, chase others, run away or follow paths. 67 |

68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /code/00-other/qlearning/q-learning.js/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | q-learning.js test 2 5 | 6 | 7 | 32 | 33 | 34 | 35 |

36 | GitHub repo 37 |

38 | 39 |

40 | This could be used in a game, to train agents to have a particular behavior. 41 | In this case, the green points are all ruled by the same state machine, and 42 | we want to improve their behavior. The red point is an agent which should be avoided. 43 | The green points can move only to empty spaces. The actions are therefore either to move to an empty space or to stay. 44 | The red point moves according to a predefined periodic path. 45 |

46 | 47 | 48 |
49 | 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /code/00-other/qlearning/qlearning.js: -------------------------------------------------------------------------------- 1 | // 參考 https://en.wikipedia.org/wiki/Q-learning 2 | // https://morvanzhou.github.io/tutorials/machine-learning/reinforcement-learning/2-1-general-rl/ 3 | 4 | class Q { 5 | constructor () { 6 | this.init() 7 | } 8 | learning(arg) { 9 | let q = this.q 10 | // this.init() 11 | for (var loop=0; loop < arg.maxLoops; loop ++) { 12 | let s0 = this.getStart() 13 | let s0s = JSON.stringify(s0) 14 | while (!this.isGoal(s0)) { 15 | let a = this.chooseAction(s0) 16 | let {s1, r} = this.doAction(s0, a) 17 | let s1s = JSON.stringify(s1) 18 | q[s0s][a] = (1-arg.rate) * q[s0s][a] + arg.rate * (r + arg.decay * this.argmax(q[s1s])) // this.argmax(q[s1]) 是下個狀態的最大報酬 19 | s0 = s1 20 | } 21 | console.log('======= dump: %d =====\n%s', loop, this.dump(q)) 22 | } 23 | } 24 | init() { throw new Error('not defined!') } // 隨機初始化 q 表 25 | isGoal(s) { throw new Error('not defined!') } // 判斷是否已到達目標 26 | getStart() { throw new Error('not defined!') } // 取得起始點 27 | chooseAction(s) { throw new Error('not defined!') } // 選取在 s 狀態的動作 a 28 | doAction(s,a) { throw new Error('not defined!') } // 在狀態 s 做 a 動作時,會跑到 s1 狀態,並得到 reward r 29 | argmax(qs) { // 取的報酬最大的狀態 30 | let max = Number.NEGATIVE_INFINITY 31 | for (let k in qs) { 32 | if (qs[k] > max) max = qs[k] 33 | } 34 | return max 35 | } 36 | dump(q) { throw new Error('not defined!') } // 傳回想印出的狀態字串 37 | } 38 | 39 | module.exports = Q 40 | -------------------------------------------------------------------------------- /code/00-other/qlearning/qwalk1d.js: -------------------------------------------------------------------------------- 1 | const R = require('./rand') 2 | const Q = require('./qlearning') 3 | 4 | class QWalk1D extends Q { 5 | constructor() { 6 | super() 7 | } 8 | init() { 9 | this.q = [] 10 | this.len = 6 11 | this.goal = 2 12 | for (let i=0; i<=this.len; i++) { 13 | this.q[i] = {left:0, right:0} 14 | } 15 | } 16 | isGoal(s) { return s === this.goal } 17 | getStart() { 18 | return R.randInt(0, this.len+1) 19 | } 20 | chooseAction(s) { 21 | let a = R.randChoose(['left', 'right']) 22 | if (s <= 0) return 'right' 23 | if (s >= this.len) return 'left' 24 | return a 25 | } 26 | doAction(s, a) { 27 | let s1 = (a === 'right') ? s+1 : s-1 28 | let r = this.isGoal(s1) ? 1 : 0 29 | return {s1, r} 30 | } 31 | dump() { 32 | const q = this.q 33 | let r = [] 34 | for (let i=0; i= this.height) return '↑' 28 | if (a === '←' && s.y <= 0) return '→' 29 | if (a === '→' && s.y >= this.width) return '←' 30 | return a 31 | } 32 | doAction(s, a) { 33 | const s1 = { x: s.x, y: s.y } 34 | switch (a) { 35 | case '↑' : s1.x = s.x - 1; break 36 | case '↓' : s1.x = s.x + 1; break 37 | case '←' : s1.y = s.y - 1; break 38 | case '→': s1.y = s.y + 1; break 39 | } 40 | const r = (this.isGoal(s1)) ? 1 : 0 41 | return {s1, r} 42 | } 43 | dump(q) { 44 | let r = [] 45 | for (let x=0; x <= this.width; x++) { 46 | for (let y=0; y <= this.height; y++) { 47 | let xy = JSON.stringify({x:x,y:y}) 48 | r.push(x+','+y +':←' + q[xy]['←'].toFixed(4) + '→' + q[xy]['→'].toFixed(4) + '↑' + q[xy]['↑'].toFixed(4) + '↓' + q[xy]['↓'].toFixed(4)) 49 | } 50 | } 51 | return r.join('\n') 52 | } 53 | } 54 | 55 | const q = new QWalk2D() 56 | q.learning({maxLoops:100, rate:0.1, decay: 0.8}) 57 | -------------------------------------------------------------------------------- /code/00-other/qlearning/rand.js: -------------------------------------------------------------------------------- 1 | const R = module.exports = {} 2 | 3 | R.rand = function (a,b) { 4 | return a + (b-a)*Math.random() 5 | } 6 | 7 | R.randInt = function (a,b) { 8 | return Math.floor(R.rand(a,b)) 9 | } 10 | 11 | R.randChoose = function (list) { 12 | return list[R.randInt(0, list.length)] 13 | } 14 | -------------------------------------------------------------------------------- /code/01-tableLookup/README.md: -------------------------------------------------------------------------------- 1 | # 查表法 (Table Lookup) 2 | 3 | ## 翻譯系統 4 | 5 | ```js 6 | var e2c = { dog:'狗', cat:'貓', a: '一隻', chase:'追', eat:'吃' } 7 | 8 | function mt(e) { 9 | var c = [] 10 | for (i in e) { 11 | var eword = e[i] 12 | var cword = e2c[eword] 13 | c.push(cword) 14 | } 15 | return c 16 | } 17 | 18 | var c = mt(process.argv.slice(2)) 19 | console.log(c) 20 | ``` 21 | 22 | 執行結果 23 | 24 | ``` 25 | $ node e2c a dog chase a cat 26 | [ '一隻', '狗', '追', '一隻', '貓' ] 27 | ``` 28 | 29 | ## 用查表加速 -- 以費氏數列為例 30 | 31 | 傳統用遞迴方式的費氏數列算法,會耗費很久的時間: 32 | 33 | ```js 34 | function fibonacci (n) { 35 | if (n < 0) throw Error('fibonacci:n < 0') 36 | if (n === 0) return 0 37 | if (n === 1) return 1 38 | return fibonacci(n - 1) + fibonacci(n - 2) 39 | } 40 | 41 | var startTime = Date.now() 42 | console.log('fibonacci(43)=', fibonacci(43)) 43 | var endTime = Date.now() 44 | var milliSeconds = endTime - startTime 45 | console.log('time:%dms', milliSeconds) 46 | 47 | ``` 48 | 49 | 執行結果: 50 | 51 | ``` 52 | $ node .\fiboanacci.js 53 | fibonacci(43)= 433494437 54 | time:25530ms 55 | ``` 56 | 57 | 加入查表,讓已經算過的就不需要算第二次,第二次之後改用查的! 58 | 59 | ```js 60 | var fib = [0, 1] 61 | 62 | function fibonacci (n) { 63 | if (n < 0) throw Error('fibonacci:n < 0') 64 | if (fib[n] != null) return fib[n] 65 | fib[n] = fibonacci(n - 1) + fibonacci(n - 2) 66 | return fib[n] 67 | } 68 | 69 | var startTime = Date.now() 70 | console.log('fibonacci(43)=', fibonacci(43)) 71 | var endTime = Date.now() 72 | var milliSeconds = endTime - startTime 73 | console.log('time:%dms', milliSeconds) 74 | 75 | ``` 76 | 77 | 執行結果 78 | 79 | ``` 80 | $ node .\fiboanacci_lookup.js 81 | fibonacci(43)= 433494437 82 | time:14ms 83 | ``` 84 | 85 | -------------------------------------------------------------------------------- /code/01-tableLookup/combinatorial/Cnk.js: -------------------------------------------------------------------------------- 1 | function factorial(n) { 2 | var p = 1 3 | for (i=1; i<=n; i++) { 4 | p = p * i; 5 | } 6 | return p 7 | } 8 | 9 | function c(n, k) { 10 | return factorial(n) / (factorial(k)*factorial(n-k)) 11 | } 12 | 13 | console.log("c(5,2)=", c(5,2)) 14 | console.log("c(7,3)=", c(7,3)) 15 | console.log("c(12,5)=", c(12,5)) 16 | console.log("c(60,30)=", c(60,30)) -------------------------------------------------------------------------------- /code/01-tableLookup/combinatorial/CnkR.js: -------------------------------------------------------------------------------- 1 | function c(n, k) { 2 | if (k < 0 || k > n) return 0 3 | if (k > n-k) k = n - k 4 | if (k==0 || n <= 1) return 1 5 | return c(n-1, k) + c(n-1, k-1) 6 | } 7 | 8 | console.log("c(5,2)=", c(5,2)) 9 | console.log("c(7,3)=", c(7,3)) 10 | console.log("c(12,5)=", c(12,5)) 11 | console.log("c(60,30)=", c(60,30)) 12 | 13 | /* 14 | 15 | https://en.wikipedia.org/wiki/Binomial_coefficient 16 | 17 | def binomialCoefficient(n, k): 18 | if k < 0 or k > n: 19 | return 0 20 | if k > n - k: # take advantage of symmetry 21 | k = n - k 22 | if k == 0 or n <= 1: 23 | return 1 24 | return binomialCoefficient(n-1, k) + binomialCoefficient(n-1, k-1) 25 | */ -------------------------------------------------------------------------------- /code/01-tableLookup/combinatorial/CnkRLookup.js: -------------------------------------------------------------------------------- 1 | var C = [] 2 | 3 | function c(n, k) { 4 | if (k < 0 || k > n) return 0 5 | if (k > n-k) k = n - k 6 | if (C[n] == null) C[n] = [] 7 | if (C[n][k] != null) return C[n][k] 8 | if (k==0 || n <= 1) 9 | C[n][k] = 1 10 | else 11 | C[n][k] = c(n-1,k) + c(n-1, k-1) 12 | return C[n][k] 13 | } 14 | 15 | console.log("c(5,2)=", c(5,2)) 16 | console.log("C=%j", C); 17 | console.log("c(7,3)=", c(7,3)) 18 | console.log("c(12,5)=", c(12,5)) 19 | console.log("c(60,30)=", c(60,30)) 20 | 21 | /* 22 | 23 | https://en.wikipedia.org/wiki/Binomial_coefficient 24 | 25 | def binomialCoefficient(n, k): 26 | if k < 0 or k > n: 27 | return 0 28 | if k > n - k: # take advantage of symmetry 29 | k = n - k 30 | if k == 0 or n <= 1: 31 | return 1 32 | return binomialCoefficient(n-1, k) + binomialCoefficient(n-1, k-1) 33 | */ -------------------------------------------------------------------------------- /code/01-tableLookup/e2c.js: -------------------------------------------------------------------------------- 1 | var e2c = { dog:'狗', cat:'貓', a: '一隻', chase:'追', eat:'吃' } 2 | 3 | function mt(e) { 4 | var c = [] 5 | for (i in e) { 6 | var eword = e[i] 7 | var cword = e2c[eword] 8 | c.push(cword) 9 | } 10 | return c 11 | } 12 | 13 | var c = mt(process.argv.slice(2)) 14 | console.log(c) -------------------------------------------------------------------------------- /code/01-tableLookup/fiboanacci/fiboanacci.js: -------------------------------------------------------------------------------- 1 | function fibonacci (n) { 2 | if (n < 0) throw Error('fibonacci:n < 0') 3 | if (n === 0) return 0 4 | if (n === 1) return 1 5 | return fibonacci(n - 1) + fibonacci(n - 2) 6 | } 7 | 8 | var startTime = Date.now() 9 | console.log('fibonacci(43)=', fibonacci(43)) 10 | var endTime = Date.now() 11 | var milliSeconds = endTime - startTime 12 | console.log('time:%dms', milliSeconds) 13 | -------------------------------------------------------------------------------- /code/01-tableLookup/fiboanacci/fiboanacci_lookup.js: -------------------------------------------------------------------------------- 1 | var fib = [0, 1] 2 | 3 | function fibonacci (n) { 4 | if (n < 0) throw Error('fibonacci:n < 0') 5 | if (fib[n] != null) return fib[n] 6 | fib[n] = fibonacci(n - 1) + fibonacci(n - 2) 7 | return fib[n] 8 | } 9 | 10 | var startTime = Date.now() 11 | console.log('fibonacci(43)=', fibonacci(43)) 12 | var endTime = Date.now() 13 | var milliSeconds = endTime - startTime 14 | console.log('time:%dms', milliSeconds) 15 | -------------------------------------------------------------------------------- /code/02-random/README.md: -------------------------------------------------------------------------------- 1 | # 機率推論 (Probability & Inference) 2 | 3 | ## Naive Bayes Algorithm 4 | 5 | P(x)=0.5 6 | P(y)=0.2 7 | P(z)=0.3 8 | 9 | P(x,y,z) = P(x) * P(y) * P(z) = 0.03 10 | 11 | 12 | 執行: 13 | 14 | ``` 15 | $ node naiveBayes 16 | P(x,y,z) = 0.03 17 | ``` 18 | 19 | ## Gibbs Sampling Algorithm 20 | 21 | * https://mdbookspace.com/view/ai/gibbs.md 22 | 23 | ``` 24 | $ node gibbs 25 | P = [0.5,0.5] 26 | P = [0.6,0.4] 27 | P = [0.62,0.38] 28 | P = [0.624,0.376] 29 | P = [0.6248,0.3752] 30 | 5/8=0.625 3/8=0.375 31 | ``` 32 | 33 | ## Metropolis-Hasting Algorithm 34 | 35 | * https://mdbookspace.com/view/ai/metropolis.md 36 | 37 | ``` 38 | $ node .\metropolis.js 39 | Q = [[0.5,0.5],[0.5,0.5]] 40 | A = [[1,0.6],[1.6666666666666667,1]] 41 | diff = 0.2 42 | Q = [[0.7,0.3],[0.5,0.5]] 43 | A = [[1,1],[1,1]] 44 | diff = 0 45 | ``` 46 | 47 | ## EM Algorithm 48 | 49 | ``` 50 | $ npm i j6 51 | 52 | $ node em 53 | pA=0.6,0.4 pB=0.5,0.5 delta=9.9999 54 | pA=0.7130122354005162,0.28698776459948394 pB=0.5813393083136625,0.41866069168633735 delta=0.113 55 | pA=0.7452920360819945,0.25470796391800554 pB=0.5692557501718727,0.4307442498281272 delta=0.0323 56 | pA=0.768098834367321,0.23190116563267898 pB=0.5495359141383478,0.45046408586165226 delta=0.0228 57 | pA=0.7831645842999736,0.2168354157000264 pB=0.5346174541475204,0.4653825458524797 delta=0.0151 58 | pA=0.7910552458637528,0.2089447541362473 pB=0.526281167029932,0.4737188329700681 delta=0.0083 59 | pA=0.7945325379936994,0.2054674620063006 pB=0.5223904375178748,0.47760956248212527 delta=0.0039 60 | pA=0.7959286672497986,0.20407133275020142 pB=0.5207298780860259,0.4792701219139741 delta=0.0017 61 | ``` 62 | 63 | ## Markov Model 64 | 65 | * 參考: 自然語言處理 -- Hidden Markov Model 66 | * http://cpmarkchang.logdown.com/posts/192352 67 | 68 | ``` 69 | // 狀態機率: P(0) = 0.2, P(1) = 0.8 70 | // 轉移機率: P(x => y) 71 | // 0 1 72 | // 0 0.3 0.7 73 | // 1 0.6 0.4 74 | ``` 75 | 76 | 執行結果: 77 | 78 | ``` 79 | $ node markov 80 | P([1,0,1,1])=0.1344 // = 0.8*0.6*0.7*0.4 81 | ``` 82 | 83 | ## Viterbi Algorithm 84 | 85 | * 參考: https://zh.wikipedia.org/wiki/%E7%BB%B4%E7%89%B9%E6%AF%94%E7%AE%97%E6%B3%95 86 | 87 | 機率表格: 88 | 89 | ``` 90 | // N 0.6 => 喵 0.4 | 汪 0.6 91 | // V 0.4 => 喵 0.5 | 汪 0.5 92 | // N V 93 | // N 0.3 0.7 94 | // V 0.8 0.2 95 | ``` 96 | 97 | 執行結果: 98 | 99 | ``` 100 | $ node viterbi 101 | t=1 path={"N":["V","N"],"V":["N","V"]} 102 | t=2 path={"N":["N","V","N"],"V":["V","N","V"]} 103 | T=[{"N":0.24,"V":0.2},{"N":0.06400000000000002,"V":0.08399999999999999},{"N":0.040319999999999995,"V":0.022400000000000003}] 104 | prob=0.040319999999999995 path=["N","V","N"] 105 | ``` 106 | 107 | 108 | -------------------------------------------------------------------------------- /code/02-random/distribution/README.ext.md: -------------------------------------------------------------------------------- 1 | # 隨機分佈 2 | 3 | ## 0-1 之間的均等分布之亂數產生 4 | 5 | 6 | 7 | ## 8 | 9 | 10 | ## 非均等分佈之亂數產生 -- 逆變換法 11 | 12 | 定理: 對任一連續分佈 F, 隨機變量 $X = F^{-1}(U)$ 的分佈為 F 13 | 14 | 參考: https://zh.wikipedia.org/wiki/%E9%80%86%E5%8F%98%E6%8D%A2%E9%87%87%E6%A0%B7 15 | 16 | 範例: 指數分佈的密度函數為 $f(x) = \lambda e^{-\lambda x}$ 17 | 18 | 其累積密度函數為 $F(x) = 1-e^{-\lambda} x$ , 19 | 20 | F的逆變換為 $F^{-1} = \frac{-1}{\lambda} log(1-U)$ 21 | 22 | 因此我們可以用 $F^{-1}$ 來產生該分佈的樣本。 23 | 24 | -------------------------------------------------------------------------------- /code/02-random/distribution/README.md: -------------------------------------------------------------------------------- 1 | # 隨機分佈 2 | 3 | ## 0-1 之間的均等分布之亂數產生 4 | 5 | 6 | 7 | ## 8 | 9 | 10 | ## 非均等分佈之亂數產生 -- 逆變換法 11 | 12 | 定理: 對任一連續分佈 F, 隨機變量 的分佈為 F 13 | 14 | 參考: https://zh.wikipedia.org/wiki/%E9%80%86%E5%8F%98%E6%8D%A2%E9%87%87%E6%A0%B7 15 | 16 | 範例: 指數分佈的密度函數為 17 | 18 | 其累積密度函數為 , 19 | 20 | F的逆變換為 21 | 22 | 因此我們可以用 來產生該分佈的樣本。 23 | 24 | -------------------------------------------------------------------------------- /code/02-random/distribution/rexp.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 定理: 對任一連續分佈 F, 隨機變量 $X = F^{-1}(U)$ 的分佈為 F 4 | 5 | 參考: https://zh.wikipedia.org/wiki/%E9%80%86%E5%8F%98%E6%8D%A2%E9%87%87%E6%A0%B7 6 | 7 | 範例: 指數分佈的密度函數為 $f(x) = \lambda e^{-lambda x}$ 8 | 9 | 其累積密度函數為 $F(x) = 1-e^{-\lambda} x$ , 10 | 11 | F的逆變換為 $invF = -1/{\lambda} log(1-U)$ 12 | 13 | 因此我們可以用 invF 來產生該分佈的樣本。 14 | 15 | */ 16 | 17 | function rexp(lambda) { 18 | return (-1/lambda) * Math.log(1-Math.random()) 19 | } 20 | 21 | for (let i=0; i<100; i++) { 22 | console.log('rexp(20)=', rexp(20)) 23 | } 24 | -------------------------------------------------------------------------------- /code/02-random/random.js: -------------------------------------------------------------------------------- 1 | var seed = 371 2 | const SEED_MAX = 9999997 3 | 4 | function random() { 5 | seed = (seed+37 ) % SEED_MAX 6 | var x = Math.sin(seed) * 93177 7 | return x - Math.floor(x); 8 | } 9 | 10 | module.exports = random -------------------------------------------------------------------------------- /code/02-random/random2.js: -------------------------------------------------------------------------------- 1 | // 來源:https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript 2 | var m_w = 123456789; 3 | var m_z = 987654321; 4 | var mask = 0xffffffff; 5 | 6 | // Takes any integer 7 | function seed(i) { 8 | m_w = i; 9 | m_z = 987654321; 10 | } 11 | 12 | // Returns number between 0 (inclusive) and 1.0 (exclusive), 13 | // just like Math.random(). 14 | function random() 15 | { 16 | m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; 17 | m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; 18 | var result = ((m_z << 16) + m_w) & mask; 19 | result /= 4294967296; 20 | return result + 0.5; 21 | } 22 | 23 | module.exports = { random, seed } -------------------------------------------------------------------------------- /code/02-random/random3.js: -------------------------------------------------------------------------------- 1 | const M = {} 2 | 3 | M.seed = function(s) { 4 | return function() { 5 | s = Math.sin(s) * 10000; 6 | return s - Math.floor(s); 7 | }; 8 | }; 9 | 10 | // usage: 11 | var random1 = M.seed(42); 12 | var random2 = M.seed(random1()); 13 | M.random = M.seed(random2()); 14 | 15 | module.exports = M 16 | -------------------------------------------------------------------------------- /code/02-random/random3Test.js: -------------------------------------------------------------------------------- 1 | const R = require('./random3') 2 | 3 | for (let i=0; i<100; i++) { 4 | console.log(R.random()) 5 | } 6 | -------------------------------------------------------------------------------- /code/02-random/randomTest.js: -------------------------------------------------------------------------------- 1 | const random = require('./random') 2 | 3 | for (let i=0; i<100; i++) { 4 | console.log(random()) 5 | } 6 | -------------------------------------------------------------------------------- /code/02-random/test.js: -------------------------------------------------------------------------------- 1 | const random = require('./random') 2 | 3 | for (let i=0; i<100; i++) { 4 | console.log('random()=', random()) 5 | } -------------------------------------------------------------------------------- /code/02-random/uuid.js: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript 2 | 3 | function uuidv4() { 4 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 5 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8) 6 | return v.toString(16) 7 | }) 8 | } 9 | 10 | console.log(uuidv4()) -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesLogic/BayesLogic.js: -------------------------------------------------------------------------------- 1 | // var ml = require('./myLib'); 2 | 3 | class BayesLogic { 4 | P(Q, E) { // P(Q,E) = P(Q<=E) 5 | if (Q == E || Q.length==0) return 1.0; 6 | if (E.length == 0) 7 | return this.pmap[Q]; // P(Q) 8 | else 9 | return this.pmap[Q+'<='+E]; // P(Q<=E) 10 | } 11 | ask(query) { 12 | console.log('BL:ask(%s)', query); 13 | var QE=query.trim().split('<='); 14 | QE.push(''); 15 | var prob=this.P(QE[0], QE[1]); 16 | console.log(' prob=%d', prob); 17 | } 18 | static test(bl) { 19 | bl.pmap = { 'A':0.5, 'B':0.3, 'C':0.2, 'D':0.7, 20 | 'A<=B':0.5, 'A<=C':0.3, 'A<=D':0.6, 'A<=E':0.2, 21 | 'B<=A':0.2, 'B<=C':0.3, 'B<=D':0.5, 'B<=E':0.6, 22 | 'C<=A':0.4, 'C<=B':0.5, 'C<=D':0.6, 'C<=E':0.1, 23 | 'D<=A':0.2, 'D<=B':0.3, 'D<=C':0.5, 'D<=E':0.6 24 | }; 25 | bl.ask('C'); 26 | bl.ask('A<=C'); 27 | bl.ask('A&B<=C'); 28 | bl.ask('A<=C&D'); 29 | bl.ask('A&B'); 30 | bl.ask('C&A&B'); // 0.075 31 | bl.ask('A&B&C'); // 0.018 32 | // 對於 Naive Bayes Model 而言,為何叫 Naive 或 Idiot ?因為 P(C,A,B) != P(A,B,C) .... 33 | bl.ask('A&B<=C&D'); 34 | } 35 | } 36 | 37 | var BL = function() { 38 | this.pmap = null; 39 | } 40 | 41 | module.exports = BayesLogic 42 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesLogic/BayesLogicNaive.js: -------------------------------------------------------------------------------- 1 | var BayesLogic = require('./BayesLogic') 2 | 3 | class NaiveBayesLogic extends BayesLogic { 4 | P(Q, E) { // P(Q,E) = P(Q<=E) 5 | var prob = super.P(Q, E) 6 | if (prob != null) return prob 7 | var q = Q.split('&').filter((x)=>x!=='') 8 | var e = E.split('&').filter((x)=>x!=='') 9 | prob = 1.0 10 | switch (e.length) { 11 | case 0: // P(q1...qn) = P(q1..qn-1|qn)*P(qn) 12 | if (q.length == 1) throw new Error('pmap' + qs + ' is not found!') 13 | var n = q.length-1 14 | var qhead = q.slice(0, n) // qhead = q1..qn-1 15 | var qn = q[n] 16 | return this.P(qhead.join('&'), qn)*this.P(qn,'') 17 | case 1: // P(q1,...qn|e) = P(q1|e)*...*P(qn|e) ; 這就是 Naive 的地方! 18 | for (var i in q) { 19 | prob *= this.pmap[q[i]+'<='+E] 20 | } 21 | return prob 22 | default: // >= 2 : P(q|e)=P(q,e)/P(e) 23 | return this.P(Q+'&'+E, '')/this.P(E, '') 24 | } 25 | } 26 | } 27 | 28 | BayesLogic.test(new NaiveBayesLogic()) 29 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesLogic/README.md: -------------------------------------------------------------------------------- 1 | # 機率式邏輯推論 (Bayes Logic) 2 | 3 | ``` 4 | $ node BayesLogicNaive.js 5 | BL:ask(C) 6 | prob=0.2 7 | BL:ask(A<=C) 8 | prob=0.3 9 | BL:ask(A&B<=C) 10 | prob=0.09 11 | BL:ask(A<=C&D) 12 | prob=0.6 13 | BL:ask(A&B) 14 | prob=0.15 15 | BL:ask(C&A&B) 16 | prob=0.075 17 | BL:ask(A&B&C) 18 | prob=0.018 19 | BL:ask(A&B<=C&D) 20 | prob=0.3 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/BayesEnumAsk.js: -------------------------------------------------------------------------------- 1 | var ml = require("./myLib"); 2 | var prob = require("./prob"); 3 | var BayesNet = require("./BayesNet"); 4 | 5 | function enumAsk(bn, q, e) { // 傳回一個機率分布 6 | var pset = {}; 7 | var vars = bn.vars(); 8 | ml.log("enumAsk(q=%j e=%j) vars=%j", q, e, vars); 9 | for (var v in bn.nodes[q].values) { 10 | var ev = ml.clone(e); 11 | ev[q] = v; 12 | pset[v]= enumAll(bn, vars, 0, ev); 13 | } 14 | ml.log("pset=%j", pset); 15 | return prob.normalize(pset); 16 | } 17 | 18 | function enumAll(bn, vars, i, e) { // 傳回一個機率值 19 | if (i>=vars.length) return 1.0; 20 | ml.log("enumAll: i=%d e=%j", i, e); 21 | var y = vars[i]; 22 | var ynode = bn.nodes[y]; 23 | var yparents = bn.parentValues(ynode, e).join(""); 24 | var prob = 0.0; 25 | if (e[y] != null) { // y 是證據變數 (y has value in e) 26 | var pye = bn.p(y, e[y], yparents); 27 | prob = pye*enumAll(bn, vars, i+1, e); 28 | } else { // y 不是證據變數 29 | for (var value in ynode.values) { 30 | var pye = bn.p(y, value, yparents); 31 | var ey = ml.clone(e); 32 | ey[y] = value; 33 | prob += pye*enumAll(bn, vars, i+1, ey); 34 | } 35 | } 36 | return prob; 37 | } 38 | 39 | var bn = require("./BayesSample"); 40 | var q = enumAsk(bn, "B", {"J":"1", "M":"1"}); // 答案應該是 P(B|j,m)={0.284, 0.716}. 41 | ml.log("q=%j", q); 42 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/BayesNet.js: -------------------------------------------------------------------------------- 1 | var ml = require("./myLib"); 2 | var prob = require("./prob"); 3 | 4 | var BayesNet = function() { 5 | this.nodes = {}; 6 | } 7 | 8 | BayesNet.prototype.addNode = function(name, values, parents, ptable) { 9 | var node = { name:name, values:values, parents:parents, ptable:ptable }; 10 | this.nodes[name] = node; 11 | return node; 12 | } 13 | 14 | BayesNet.prototype.vars = function() { return Object.keys(this.nodes); } 15 | 16 | BayesNet.prototype.p = function(name, value, cond) { // P(name=value|cond) 17 | var node = this.nodes[name]; 18 | var ptable = node.ptable; 19 | var p1=ptable[cond]; 20 | if (p1==null) 21 | ml.error("node.ptable[cond]==null node=%j cond=%s", node, cond); 22 | var prob = null; 23 | if (value == "1") 24 | prob=ptable[cond]; 25 | else if (value == "0") 26 | prob=1.0-ptable[cond]; 27 | else 28 | ml.error("value is not (0,1)!"); 29 | ml.log(" p(%s=%s|%s)=%d", node.name, value, cond, prob); 30 | return prob; 31 | } 32 | 33 | BayesNet.prototype.showp = function(name, value, cond) { 34 | var node = this.nodes[name]; 35 | ml.log("P(%s=%s|%s=%s)=%d", name, value, node.parents, cond, this.p(name, value, cond)); 36 | } 37 | 38 | BayesNet.prototype.parentValues = function(node, e) { 39 | var values = []; 40 | for (var i in node.parents) { 41 | var parent = node.parents[i]; 42 | if (e[parent]== null) 43 | ml.error("e[%s] is null", parent); 44 | values.push(e[parent]); 45 | } 46 | return values; 47 | } 48 | 49 | BayesNet.prototype.sample = function() { 50 | var bn = new BayesNet(); 51 | bn.addNode("B", ["0","1"], [], {"":0.001}); 52 | bn.addNode("E", ["0","1"], [], {"":0.002}); 53 | bn.addNode("A", ["0","1"], ["B","E"], {"00":0.001, "01":0.29, "10":0.94, "11":0.95}); 54 | bn.addNode("J", ["0","1"], ["A"], {"1":0.90, "0":0.05}); 55 | bn.addNode("M", ["0","1"], ["A"], {"1":0.70, "0":0.01}); 56 | return bn; 57 | } 58 | 59 | module.exports = BayesNet; -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/BayesNet.txt: -------------------------------------------------------------------------------- 1 | enumAsk(q="B" e={"J":"1","M":"1"}) vars=["B","E","A","J","M"] 2 | enumAll: i=0 e={"J":"1","M":"1","B":"0"} 3 | p(B=0|)=0.999 4 | enumAll: i=1 e={"J":"1","M":"1","B":"0"} 5 | p(E=0|)=0.998 6 | enumAll: i=2 e={"J":"1","M":"1","B":"0","E":"0"} 7 | p(A=0|00)=0.999 8 | enumAll: i=3 e={"J":"1","M":"1","B":"0","E":"0","A":"0"} 9 | p(J=1|0)=0.05 10 | enumAll: i=4 e={"J":"1","M":"1","B":"0","E":"0","A":"0"} 11 | p(M=1|0)=0.01 12 | p(A=1|00)=0.001 13 | enumAll: i=3 e={"J":"1","M":"1","B":"0","E":"0","A":"1"} 14 | p(J=1|1)=0.9 15 | enumAll: i=4 e={"J":"1","M":"1","B":"0","E":"0","A":"1"} 16 | p(M=1|1)=0.7 17 | p(E=1|)=0.002 18 | enumAll: i=2 e={"J":"1","M":"1","B":"0","E":"1"} 19 | p(A=0|01)=0.71 20 | enumAll: i=3 e={"J":"1","M":"1","B":"0","E":"1","A":"0"} 21 | p(J=1|0)=0.05 22 | enumAll: i=4 e={"J":"1","M":"1","B":"0","E":"1","A":"0"} 23 | p(M=1|0)=0.01 24 | p(A=1|01)=0.29 25 | enumAll: i=3 e={"J":"1","M":"1","B":"0","E":"1","A":"1"} 26 | p(J=1|1)=0.9 27 | enumAll: i=4 e={"J":"1","M":"1","B":"0","E":"1","A":"1"} 28 | p(M=1|1)=0.7 29 | enumAll: i=0 e={"J":"1","M":"1","B":"1"} 30 | p(B=1|)=0.001 31 | enumAll: i=1 e={"J":"1","M":"1","B":"1"} 32 | p(E=0|)=0.998 33 | enumAll: i=2 e={"J":"1","M":"1","B":"1","E":"0"} 34 | p(A=0|10)=0.06000000000000005 35 | enumAll: i=3 e={"J":"1","M":"1","B":"1","E":"0","A":"0"} 36 | p(J=1|0)=0.05 37 | enumAll: i=4 e={"J":"1","M":"1","B":"1","E":"0","A":"0"} 38 | p(M=1|0)=0.01 39 | p(A=1|10)=0.94 40 | enumAll: i=3 e={"J":"1","M":"1","B":"1","E":"0","A":"1"} 41 | p(J=1|1)=0.9 42 | enumAll: i=4 e={"J":"1","M":"1","B":"1","E":"0","A":"1"} 43 | p(M=1|1)=0.7 44 | p(E=1|)=0.002 45 | enumAll: i=2 e={"J":"1","M":"1","B":"1","E":"1"} 46 | p(A=0|11)=0.050000000000000044 47 | enumAll: i=3 e={"J":"1","M":"1","B":"1","E":"1","A":"0"} 48 | p(J=1|0)=0.05 49 | enumAll: i=4 e={"J":"1","M":"1","B":"1","E":"1","A":"0"} 50 | p(M=1|0)=0.01 51 | p(A=1|11)=0.95 52 | enumAll: i=3 e={"J":"1","M":"1","B":"1","E":"1","A":"1"} 53 | p(J=1|1)=0.9 54 | enumAll: i=4 e={"J":"1","M":"1","B":"1","E":"1","A":"1"} 55 | p(M=1|1)=0.7 56 | pset={"0":0.001491857649,"1":0.0005922425899999999} 57 | q={"0":0.7158281646356071,"1":0.2841718353643929} 58 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/BayesNet1.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccbook/algjs/501da2f5bd465fd84b587d353478e8fd81db4346/code/03-monteCarlo/BayesNet/BayesNet1.js -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/BayesRejectSampling.js: -------------------------------------------------------------------------------- 1 | var ml = require("./myLib"); 2 | var prob = require("./prob"); 3 | var BayesNet = require("./BayesNet"); 4 | 5 | function rejectSampling(bn, q, e, N) { // 傳回 P(Q|e) 的機率分布 6 | 7 | } 8 | 9 | function priorSample(bn) { // 隨機取一個樣本 10 | 11 | } 12 | 13 | var bn = require("./BayesSample"); 14 | var q = enumAsk(bn, "B", {"J":"1", "M":"1"}); // 答案應該是 P(B|j,m)={0.284, 0.716}. 15 | ml.log("q=%j", q); 16 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/BayesSample.js: -------------------------------------------------------------------------------- 1 | var BayesNet = require("./BayesNet"); 2 | 3 | var bn = new BayesNet(); 4 | bn.addNode("B", ["0","1"], [], {"":0.001}); 5 | bn.addNode("E", ["0","1"], [], {"":0.002}); 6 | bn.addNode("A", ["0","1"], ["B","E"], {"00":0.001, "01":0.29, "10":0.94, "11":0.95}); 7 | bn.addNode("J", ["0","1"], ["A"], {"1":0.90, "0":0.05}); 8 | bn.addNode("M", ["0","1"], ["A"], {"1":0.70, "0":0.01}); 9 | 10 | module.exports = bn; -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/myLib.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); 2 | 3 | var ml = {}; 4 | 5 | ml.log = console.log; 6 | 7 | ml.format = function() { 8 | return util.format.apply(null, arguments); 9 | } 10 | 11 | ml.error = function() { 12 | util.print("Error:"); 13 | ml.log(util.format.apply(null, arguments)); 14 | process.exit(1); 15 | } 16 | 17 | ml.split = function(str, sp) { 18 | var tokens = str.split(sp); 19 | if (tokens[tokens.length-1]=="") 20 | tokens.pop(); 21 | return tokens; 22 | } 23 | 24 | ml.erase = function(array, v) { 25 | var anew = []; 26 | for (var i in array) { 27 | if (array[i] != v) 28 | anew.push(array[i]); 29 | } 30 | return anew; 31 | } 32 | 33 | ml.isEmpty = function(map) { 34 | for(var i in map) { 35 | return false; 36 | } 37 | return true; 38 | } 39 | 40 | ml.clone = function(map) { 41 | var o={}; 42 | for (var key in map) { 43 | o[key] = map[key]; 44 | } 45 | return o; 46 | } 47 | 48 | ml.merge = function(map1,map2) { 49 | var map = {}; 50 | for (var key in map1) 51 | map[key] = map1[key]; 52 | for (var key in map2) 53 | map[key] = map2[key]; 54 | return map; 55 | } 56 | 57 | module.exports = ml; 58 | -------------------------------------------------------------------------------- /code/03-monteCarlo/BayesNet/prob.js: -------------------------------------------------------------------------------- 1 | var prob = {} 2 | 3 | prob.sum = function(p) { 4 | var s = 0.0; 5 | for (var v in p) { 6 | s+=p[v]; 7 | } 8 | return s; 9 | } 10 | 11 | prob.normalize = function(p) { 12 | var s = this.sum(p); 13 | var np = {}; 14 | for (var v in p) { 15 | np[v] = p[v]/s; 16 | } 17 | return np; 18 | } 19 | 20 | module.exports = prob; -------------------------------------------------------------------------------- /code/03-monteCarlo/markov/README.md: -------------------------------------------------------------------------------- 1 | # 馬可夫鏈 Markov Chain 2 | 3 | ## markov.js 4 | 5 | 計算某序列 [b,a,b,b] 的機率 6 | 7 | ``` 8 | $ node markov 9 | P(["b","a","b","b"])=0.06 10 | ``` 11 | 12 | ## gibbs.js 13 | 14 | 15 | Gibbs Algorithm 的範例 16 | 17 | 問題:機率式有限狀態機,P(a=>b)=0.3, P(b=>a)=0.5 ; P(a=>b)=0.7, P(b=>b)=0.5 18 | 19 | 目標:尋找該「機率式有限狀態機」的穩態,也就是 P(a) = ?, P(b)=? 時系統會達到平衡。 20 | 21 | 22 | ``` 23 | $ node gibbs 24 | P1 = {"a":0.54,"b":0.46} 25 | P1 = {"a":0.608,"b":0.392} 26 | P1 = {"a":0.6215999999999999,"b":0.37839999999999996} 27 | P1 = {"a":0.62432,"b":0.37567999999999996} 28 | P1 = {"a":0.624864,"b":0.37513599999999997} 29 | 標準答案:P(a)=5/8=0.625 P(b)=3/8=0.375 30 | ``` 31 | 32 | ## metropolis.js 33 | 34 | Metropolis Hasting 的範例 35 | 36 | 問題:機率式有限狀態機,P(a)=0.2, P(b)=0.3, P(c)=0.1 37 | 38 | 目標:尋找「轉移矩陣」的穩態,也就是 Q(x→y)=? 時系統會達到細緻平衡 (每條連線都平衡了)。 39 | 40 | ``` 41 | $ node metropolis 42 | Q = {"a":0.2,"b":0.7,"c":0.1,"a=>a":0.5,"a=>b":0.3,"a=>c":0.2,"b=>a":0.1,"b=>b":0.2,"b=>c":0.7,"c=>a":0.3,"c=>b":0.3,"c=>c":0.4} 43 | x=a y=a, A[x=>y]=1 P[y]*Q[y=>x]=0.1 P(x)*Q[x=>y]=0.1 44 | x=a y=b, A[x=>y]=1.1666666666666665 P[y]*Q[y=>x]=0.06 P(x)*Q[x=>y]=0.06999999999999999 45 | x=a y=c, A[x=>y]=0.7499999999999998 P[y]*Q[y=>x]=0.04000000000000001 P(x)*Q[x=>y]=0.03 46 | x=b y=a, A[x=>y]=0.8571428571428572 P[y]*Q[y=>x]=0.06999999999999999 P(x)*Q[x=>y]=0.06 47 | x=b y=b, A[x=>y]=1 P[y]*Q[y=>x]=0.13999999999999999 P(x)*Q[x=>y]=0.13999999999999999 48 | x=b y=c, A[x=>y]=0.06122448979591837 P[y]*Q[y=>x]=0.48999999999999994 P(x)*Q[x=>y]=0.03 49 | x=c y=a, A[x=>y]=1.3333333333333337 P[y]*Q[y=>x]=0.03 P(x)*Q[x=>y]=0.04000000000000001 50 | x=c y=b, A[x=>y]=16.333333333333332 P[y]*Q[y=>x]=0.03 P(x)*Q[x=>y]=0.48999999999999994 51 | x=c y=c, A[x=>y]=1 P[y]*Q[y=>x]=0.04000000000000001 P(x)*Q[x=>y]=0.04000000000000001 52 | A = {"a=>a":1,"a=>b":1.1666666666666665,"a=>c":0.7499999999999998,"b=>a":0.8571428571428572,"b=>b":1,"b=>c":0.06122448979591837,"c=>a":1.3333333333333337,"c=>b":16.333333333333332,"c=>c":1} 53 | diff = 0.7214285714285714 54 | Q = {"a=>a":0.55,"a=>b":0.3,"a=>c":0.14999999999999997,"b=>a":0.08571428571428573,"b=>b":0.8714285714285714,"b=>c":0.04285714285714286,"c=>a":0.3,"c=>b":0.3,"c=>c":0.4} 55 | x=a y=a, A[x=>y]=1 P[y]*Q[y=>x]=0.11000000000000001 P(x)*Q[x=>y]=0.11000000000000001 56 | x=a y=b, A[x=>y]=1.0000000000000002 P[y]*Q[y=>x]=0.06 P(x)*Q[x=>y]=0.060000000000000005 57 | x=a y=c, A[x=>y]=1.0000000000000002 P[y]*Q[y=>x]=0.029999999999999995 P(x)*Q[x=>y]=0.03 58 | x=b y=a, A[x=>y]=0.9999999999999999 P[y]*Q[y=>x]=0.060000000000000005 P(x)*Q[x=>y]=0.06 59 | x=b y=b, A[x=>y]=1 P[y]*Q[y=>x]=0.61 P(x)*Q[x=>y]=0.61 60 | x=b y=c, A[x=>y]=1 P[y]*Q[y=>x]=0.03 P(x)*Q[x=>y]=0.03 61 | x=c y=a, A[x=>y]=0.9999999999999999 P[y]*Q[y=>x]=0.03 P(x)*Q[x=>y]=0.029999999999999995 62 | x=c y=b, A[x=>y]=1 P[y]*Q[y=>x]=0.03 P(x)*Q[x=>y]=0.03 63 | x=c y=c, A[x=>y]=1 P[y]*Q[y=>x]=0.04000000000000001 P(x)*Q[x=>y]=0.04000000000000001 64 | A = {"a=>a":1,"a=>b":1.0000000000000002,"a=>c":1.0000000000000002,"b=>a":0.9999999999999999,"b=>b":1,"b=>c":1,"c=>a":0.9999999999999999,"c=>b":1,"c=>c":1} 65 | diff = 6.938893903907228e-17 66 | ``` 67 | -------------------------------------------------------------------------------- /code/03-monteCarlo/markov/gibbs.js: -------------------------------------------------------------------------------- 1 | // Gibbs Algorithm 的範例 2 | // 問題:機率式有限狀態機,P(a=>b)=0.3, P(b=>a)=0.5 ; P(a=>b)=0.7, P(b=>b)=0.5 3 | // 目標:尋找該「機率式有限狀態機」的穩態,也就是 P(a) = ?, P(b)=? 時系統會達到平衡。 4 | const P = require('./prob') 5 | function gibbs (P) { 6 | var P0 = {'a': P['a'], 'b': P['b'] } 7 | do { 8 | var P1 = { // 下一輪的機率分布。 9 | 'a': P0['a'] * P['a=>a'] + P0['b'] * P['b=>a'], 10 | 'b': P0['a'] * P['a=>b'] + P0['b'] * P['b=>b'] 11 | } 12 | console.log('P1 = %j', P1) 13 | var da = P1['a'] - P0['a'], db = P1['b'] - P0['b'] // 兩輪間的差異。 14 | var step = Math.sqrt(da * da + db * db) // 差異的大小 15 | P0 = P1 16 | } while (step > 0.001) // 假如差異夠小的時候,就可以停止了。 17 | console.log('標準答案:P(a)=5/8=%d P(b)=3/8=%d', 5 / 8, 3 / 8) // 印出標準答案,以便看看我們找到的答案是否夠接近。 18 | } 19 | 20 | gibbs(P) 21 | -------------------------------------------------------------------------------- /code/03-monteCarlo/markov/markov.js: -------------------------------------------------------------------------------- 1 | // 參考: 自然語言處理 -- Hidden Markov Model http://cpmarkchang.logdown.com/posts/192352 2 | const P = require('./prob') 3 | function markov(s) { 4 | let p = P[s[0]] 5 | for (let i=1; i'+s[i] 7 | p = p * P[key] 8 | } 9 | return p 10 | } 11 | 12 | const seq = ['b', 'a', 'b', 'b'] 13 | 14 | console.log('P(%j)=%d', seq, markov(seq)) -------------------------------------------------------------------------------- /code/03-monteCarlo/markov/mcmc.js: -------------------------------------------------------------------------------- 1 | const P = require('./prob') 2 | 3 | const rnd = Math.random 4 | function mcmc(s) { // Monte Carlo Markov Chain 5 | if (rnd() > P[s[0]]) return 0 6 | for (let i=1; i'+s[i] 8 | if (rnd() > P[key]) return 0 9 | } 10 | return 1 11 | } 12 | 13 | function markov(s, n) { 14 | let pass = 0 15 | for (let i=0; i'+'a']) ? 'a' : 'b' 7 | if (s1 == s2) return 8 | P[s1] -= 0.0001 9 | P[s2] += 0.0001 10 | } 11 | 12 | function gibbs(n) { 13 | for (let i=0; ia': 0.5, 'a=>b':0.3, 'a=>c':0.2, 8 | 'b=>a': 0.1, 'b=>b':0.2, 'b=>c':0.7, 9 | 'c=>a': 0.3, 'c=>b':0.3, 'c=>c':0.4, 10 | } 11 | 12 | function MetropolisHasting (P, S) { 13 | var Q = P // 初始機率分佈 14 | var A = {} // 入出比 = 流入/流出 15 | do { 16 | console.log('Q = %j', Q) 17 | var Qn = {} 18 | for (let x of S) { // 計算 A 矩陣 = 入出比 = 流入/流出 19 | for (let y of S) { 20 | Qn[x+'=>'+y] = Q[x+'=>'+y] 21 | A[x+'=>'+y] = (P[y] * Q[y+'=>'+x]) / (P[x] * Q[x+'=>'+y]) // 入出比 = 流入/流出 22 | console.log('x=%s y=%s, A[x=>y]=%d P[y]*Q[y=>x]=%d P(x)*Q[x=>y]=%d', x, y, A[x+'=>'+y], P[x]*Q[x+'=>'+y], P[y]*Q[y+'=>'+x]) 23 | } 24 | } 25 | console.log('A = %j', A) 26 | var diff = 0 27 | for (let x of S) { 28 | for (let y of S) { // 計算下一代 Qn 矩陣 29 | if (A[x+'=>'+y] < 1) { // 入出比 < 1 ,代表流入太少,流出太多 30 | Qn[x+'=>'+y] = Q[x+'=>'+y] * A[x+'=>'+y] // 降低流出量 31 | Qn[x+'=>'+x] = Qn[x+'=>'+x] + Q[x+'=>'+y] * (1 - A[x+'=>'+y]) // 『規一化』調整 32 | diff += Math.abs(Qn[x+'=>'+y] - Q[x+'=>'+y]) // 計算新舊矩陣差異 33 | } 34 | } 35 | } 36 | Q = Qn 37 | console.log('diff = %d', diff) 38 | } while (diff > 0.001) // 假如差異夠小的時候,就可以停止了。 39 | } 40 | 41 | MetropolisHasting(P, ['a', 'b', 'c']) 42 | -------------------------------------------------------------------------------- /code/03-monteCarlo/markov/prob.js: -------------------------------------------------------------------------------- 1 | // 狀態機率: P(a) = 0.2, P(b) = 0.8 2 | // 轉移機率: P(x => y) 3 | // a b 4 | // a 0.7 0.3 5 | // b 0.5 0.5 6 | const P = { 7 | 'a': 0.2, 'b': 0.8, 8 | 'a=>a': 0.7, 'a=>b':0.3, 9 | 'b=>a': 0.5, 'b=>b':0.5, 10 | } 11 | 12 | module.exports = P 13 | -------------------------------------------------------------------------------- /code/03-monteCarlo/mcmc/README.md: -------------------------------------------------------------------------------- 1 | # MCMC 2 | 3 | * [隨機採樣方法整理與講解(MCMC、Gibbs Sampling等)](https://read01.com/zLn6K.html) 4 | * [[数据分析] Markov Chain Monte Carlo](https://zhuanlan.zhihu.com/p/25610149) 5 | 6 | * [A Zero-Math Introduction to Markov Chain Monte Carlo Methods](https://towardsdatascience.com/a-zero-math-introduction-to-markov-chain-monte-carlo-methods-dcba889e0c50) 7 | MCMC methods are used to approximate the posterior distribution of a parameter of interest by random sampling in a probabilistic space. 8 | 9 | * [中央研究院:馬可夫鏈蒙地卡羅法(Markov chain Monte Carlo) 陳定立助研究員 ( 統計科學研究所 ) (PDF)](http://newsletter.sinica.edu.tw/file/file/55/5526.pdf) 10 | * 目前最被廣泛使用的兩種馬可夫鏈蒙地卡羅法,一個是 Metropolis-Hasting algorithm (Metropolis et al. 1953; Hasting 1970),另一個是 Gibbs Sampler (Geman and Geman 1984)。 11 | 12 | * [bayes.js: A Small Library for Doing MCMC in the Browser](http://www.sumsar.net/blog/2015/12/bayes-js-a-small-library-for-doing-mcmc-in-the-browser/) 13 | * http://chi-feng.github.io/mcmc-demo/ 14 | * http://bl.ocks.org/sathomas/cf526d6495811a8ca779946ef5558702 15 | 16 | ## Metropolis-Hasting Algorithm 17 | 18 | * [梅特羅波利斯-黑斯廷斯算法](https://zh.wikipedia.org/wiki/%E6%A2%85%E7%89%B9%E7%BD%97%E6%B3%A2%E5%88%A9%E6%96%AF%EF%BC%8D%E9%BB%91%E6%96%AF%E5%BB%B7%E6%96%AF%E7%AE%97%E6%B3%95) -------------------------------------------------------------------------------- /code/03-monteCarlo/pi/README.md: -------------------------------------------------------------------------------- 1 | # MonteCarlo 2 | 3 | * [R语言与Markov Chain Monte Carlo(MCMC)方法学习笔记(1)] (https://blog.csdn.net/yujunbeta/article/details/21303341) (讚讚,有 R 代碼) 4 | * 蒙特卡洛方法被誉为20世纪最伟大的十大算法之一。它由美国拉斯阿莫斯国家实验室的三位科学家John von Neumann, Stan Ulam 和 Nick Metropolis于1946年提出。 5 | * 接受-拒绝抽样(Acceptance-Rejectionsampling): (重要!) 6 | * Metropolis算法 7 | * Metropolis-Hastings 算法 8 | * Bayes推断中的MCMC方法 9 | * Gibbs抽样 10 | * [R语言与Markov Chain Monte Carlo(MCMC)方法学习笔记(2)](https://blog.csdn.net/yujunbeta/article/details/23205331) 11 | 12 | 13 | * [漫談蒙地卡羅法的原理及其應用](https://www.kdais.gov.tw/htmlarea_file/web_articles/kdais/4680/23-1-3.pdf) 14 | * Pi 的估計! 15 | * 用中央極限定理作檢定。 16 | * 高維度積分難以實踐,只好採用蒙地卡羅法估計。 17 | 18 | -------------------------------------------------------------------------------- /code/03-monteCarlo/pi/monteCarloPi.js: -------------------------------------------------------------------------------- 1 | function monteCarloPi(n) { 2 | let hits = 0 3 | for (let i=0;i 0) { // 如果 counts[gi] == 0 不能除,這一群沒有人! 65 | newCenters[gi] = newCenters[gi].div(counts[gi]) 66 | } 67 | } 68 | return newCenters 69 | } 70 | 71 | KMean.run = function (data, k, maxLoop) { 72 | KMean.loadData(data) 73 | KMean.initialize(k) 74 | for (var i = 0; i < maxLoop; i++) { 75 | console.log('============== loop ' + i + '================') 76 | KMean.groups = KMean.grouping(KMean.euclidDistance) 77 | console.log('groups=%j', KMean.groups) 78 | KMean.centers = KMean.centering() 79 | console.log('centers=%j', KMean.centers) 80 | } 81 | return KMean.data 82 | } 83 | -------------------------------------------------------------------------------- /code/04-iterative/kmean/kmeanEx.js: -------------------------------------------------------------------------------- 1 | const KMean = require('./kmean') 2 | 3 | var data = [ 4 | [0, 0], [0, 1], [1, 0], [1, 1], 5 | [8, 0], [8, 1], [9, 0], [9, 1], 6 | [5, 7], [4, 6], [5, 6], [4, 7] 7 | ] 8 | 9 | KMean.run(data, 3, 10) 10 | -------------------------------------------------------------------------------- /code/05-dynamicProgramming/README.md: -------------------------------------------------------------------------------- 1 | # HMM 2 | 3 | ## viterbi.js 4 | 5 | 問題:尋找產生某『表現序列』最可能的『隱狀態序列』 6 | 7 | 範例:根據下列規則,請問『喵 喵 汪』中每個詞彙最可能的詞性會是什麼? 8 | 9 | 轉移機率與規則 10 | 11 | ``` 12 | N 0.6 => 喵 0.4 | 汪 0.6 13 | V 0.4 => 喵 0.5 | 汪 0.5 14 | 15 | N V 16 | N 0.3 0.7 17 | V 0.8 0.2 18 | ``` 19 | 20 | 執行結果 21 | 22 | ``` 23 | $ node viterbi 24 | t=1 path={"N":["V","N"],"V":["N","V"]} 25 | t=2 path={"N":["N","V","N"],"V":["V","N","V"]} 26 | T=[{"N":0.24,"V":0.2},{"N":0.06400000000000002,"V":0.08399999999999999},{"N":0.040319999999999995,"V":0.022400000000000003}] 27 | prob=0.040319999999999995 path=["N","V","N"] 28 | ``` 29 | 30 | ## 觀念 31 | 32 | * [維基百科:維特比演算法](https://zh.wikipedia.org/wiki/%E7%BB%B4%E7%89%B9%E6%AF%94%E7%AE%97%E6%B3%95) 33 | * [自然語言處理 -- Hidden Markov Model](https://ckmarkoh.github.io/blog/2014/04/03/natural-language-processing-hidden-markov-models/) 34 | * [自然語言處理 -- Viterbi Algorithm](https://ckmarkoh.github.io/blog/2014/04/06/natural-language-processing-viterbi-algorithm/) 35 | 36 | ## 實作 37 | 38 | * https://github.com/miguelmota/hidden-markov-model 39 | * https://github.com/123jimin/hmm.js/blob/master/hmm.js 40 | * https://github.com/123jimin/hmm.js/tree/master -------------------------------------------------------------------------------- /code/05-dynamicProgramming/combinatorial/CnkDynamic.js: -------------------------------------------------------------------------------- 1 | // http://mathworld.wolfram.com/PascalsFormula.html 2 | // https://en.wikipedia.org/wiki/Pascal%27s_rule 3 | // https://en.wikipedia.org/wiki/Pascal%27s_triangle 4 | // https://en.wikipedia.org/wiki/Binomial_coefficient 5 | /* 6 | c(n, k) = 0 , if n < k 7 | = 1 , if k = 0 or k = n 8 | = c(n-1, k-1) + c(n-1, k) , if k <= n-k 9 | = c(n, n-k) , if k > n-k 10 | */ 11 | 12 | function c(N, K) { 13 | var C = []; 14 | for (let n=0; n<=N; n++) { 15 | C[n] = [1] 16 | C[0][n] = 0 17 | } 18 | for (let n=1; n<=N; n++) { 19 | for (let k=1; k<=N; k++) { 20 | let k0 = (k <= n-k) ? k : n-k 21 | if (n < k) 22 | C[n][k] = 0 23 | else if (n===k) 24 | C[n][k] = 1 25 | else 26 | C[n][k] = C[n][n-k] = C[n-1][k0-1] + C[n-1][k0] 27 | } 28 | } 29 | /* 30 | for(let n=0; n<=N; n++) { 31 | console.log("C[%d]=%j", n, C[n]) 32 | } 33 | */ 34 | return C[N][K]; 35 | } 36 | 37 | console.log("c(5,2)=", c(5,2)) 38 | console.log("c(7,3)=", c(7,3)) 39 | console.log("c(12,5)=", c(12,5)) 40 | console.log("c(60,30)=", c(60,30)) -------------------------------------------------------------------------------- /code/05-dynamicProgramming/editDistance.js: -------------------------------------------------------------------------------- 1 | function editDistance (b, a){ 2 | if(a.length == 0) return b.length; 3 | if(b.length == 0) return a.length; 4 | 5 | var m = []; 6 | for (let i = 0; i <= b.length; i++) m[i] = [i] 7 | for(let j = 0; j <= a.length; j++) m[0][j] = j 8 | 9 | for(i = 1; i <= b.length; i++){ 10 | for(j = 1; j <= a.length; j++){ 11 | if(b.charAt(i-1) == a.charAt(j-1)){ 12 | m[i][j] = m[i-1][j-1] 13 | } else { 14 | m[i][j] = Math.min(m[i-1][j-1] + 1, // 取代 15 | Math.min(m[i][j-1] + 1, // 插入 16 | m[i-1][j] + 1)); // 刪除 17 | } 18 | } 19 | } 20 | 21 | return {d:m[b.length][a.length], m:m}; 22 | } 23 | 24 | function align(b, a, m) { 25 | let i = b.length, j = a.length 26 | let bx = '', ax = '' 27 | while (i > 0 && j > 0) { 28 | if (m[i][j] === m[i-1][j] + 1) { // 插入 b[i] 29 | i-- 30 | ax = ' ' + ax 31 | bx = b[i] + bx 32 | } else if (m[i][j] === m[i][j-1] + 1) { // 插入 a[j] 33 | j-- 34 | ax = a[j] + ax 35 | bx = ' ' + bx 36 | } else if ((m[i][j] === m[i-1][j-1] + 1) // 取代 37 | || (m[i][j] === m[i-1][j-1])) { // 相同 38 | i-- 39 | j-- 40 | bx = b[i] + bx 41 | ax = a[j] + ax 42 | } 43 | } 44 | while (i> 0) { 45 | i-- 46 | bx = b[i] + bx 47 | ax = ' ' + ax 48 | } 49 | while (j > 0) { 50 | j-- 51 | ax = a[j] + ax 52 | bx = ' ' + bx 53 | } 54 | console.log('bx=', bx) 55 | console.log('ax=', ax) 56 | } 57 | 58 | var a, b 59 | /* 60 | a = "010100001" 61 | b = "010100010" 62 | console.log("dist(%s,%s) = %s", a, b, editDistance(a,b)) 63 | */ 64 | //b = "ATG ATCCG" 65 | a = "ATGCAATCCC" 66 | b = "ATGATCCG" 67 | //b = " TCCGAA" 68 | // a = "ATCCCAAA" 69 | // b = "TCCGAA" 70 | let e = editDistance(b, a) 71 | console.log("dist(%s,%s) = %s", b, a, e.d) 72 | console.log('======m=========\n', e.m) 73 | align(b, a, e.m) 74 | -------------------------------------------------------------------------------- /code/05-dynamicProgramming/hmm.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccbook/algjs/501da2f5bd465fd84b587d353478e8fd81db4346/code/05-dynamicProgramming/hmm.md -------------------------------------------------------------------------------- /code/05-dynamicProgramming/viterbi.js: -------------------------------------------------------------------------------- 1 | // 參考: https://zh.wikipedia.org/wiki/%E7%BB%B4%E7%89%B9%E6%AF%94%E7%AE%97%E6%B3%95 2 | 3 | // N 0.6 => 喵 0.4 | 汪 0.6 4 | // V 0.4 => 喵 0.5 | 汪 0.5 5 | // N V 6 | // N 0.3 0.7 7 | // V 0.8 0.2 8 | const P = { 9 | 'N': 0.6, 10 | 'V': 0.4, 11 | 'N=>N': 0.3, 12 | 'N=>V': 0.7, 13 | 'V=>N': 0.8, 14 | 'V=>V': 0.2, 15 | 'N=>喵': 0.4, 16 | 'N=>汪': 0.6, 17 | 'V=>喵': 0.5, 18 | 'V=>汪': 0.5, 19 | } 20 | 21 | function argmax(list) { 22 | let max = Number.NEGATIVE_INFINITY, index = null 23 | for (let k in list) { 24 | if (list[k] > max) { index=k; max=list[k] } 25 | } 26 | return {max, index} 27 | } 28 | 29 | function viterbi(obs, states, P) { 30 | console.log('觀察到的序列=', obs) 31 | const T = [{}] // Viterbi Table 32 | let path = {} // path[state] = 從 0 到 t 到達 state 機率最大的 path 33 | for (let y of states) { // # Initialize base cases (t == 0) 34 | T[0][y] = P[y] * P[y+'=>'+obs[0]] 35 | path[y] = [y] 36 | } 37 | // console.log('T=%j path=%j', T, path) 38 | for (let t=1; t 0 39 | T[t] = {} 40 | let newpath = {} 41 | for (let y of states) { 42 | let {max:prob, index:si} = argmax(states.map((y0)=>T[t-1][y0] * P[y0+'=>'+y] * P[y+'=>'+obs[t]])) 43 | let state = states[si] 44 | // console.log('y=%s prob=%d state=%s', y, prob, state) 45 | T[t][y] = prob 46 | newpath[y] = path[state].concat(y) 47 | } 48 | // console.log('t=%d T=%j', t, T) 49 | path = newpath 50 | console.log('t=%d path=%j', t, path) 51 | } 52 | let {max:prob, index:si} = argmax(states.map((y)=>T[obs.length - 1][y])) 53 | console.log('T=%j', T) 54 | return {prob, path:path[states[si]]} 55 | } 56 | 57 | let {prob, path} = viterbi('喵 喵 汪'.split(' '), ['N', 'V'], P) 58 | console.log('prob=%d path=%j=最可能的隱序列', prob, path) -------------------------------------------------------------------------------- /code/06-divideConquer/FFT/FFT.js: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/mbitsnbites/a065127577ff89ff885dd0a932ec2477 2 | // https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2 3 | // 庫利-圖基演算法 4 | 5 | // This is a tiny radix-2 FFT implementation in JavaScript. 6 | // The function takes a complex valued input signal, and performs an in-place 7 | // Fast Fourier Transform (i.e. the result is returned in x_re, x_im). The 8 | // function arguments can be any Array type (including typed arrays). 9 | // Code size: <300 bytes after Closure Compiler. 10 | function FFT(x_re, x_im) { 11 | var m = x_re.length / 2, k, X_re = [], X_im = [], Y_re = [], Y_im = [], 12 | a, b, tw_re, tw_im; 13 | 14 | for (k = 0; k < m; ++k) { 15 | X_re[k] = x_re[2 * k]; 16 | X_im[k] = x_im[2 * k]; 17 | Y_re[k] = x_re[2 * k + 1]; 18 | Y_im[k] = x_im[2 * k + 1]; 19 | } 20 | 21 | if (m > 1) { 22 | FFT(X_re, X_im); 23 | FFT(Y_re, Y_im); 24 | } 25 | 26 | for (k = 0; k < m; ++k) { 27 | a = -Math.PI * k / m, tw_re = Math.cos(a), tw_im = Math.sin(a); 28 | a = tw_re * Y_re[k] - tw_im * Y_im[k]; 29 | b = tw_re * Y_im[k] + tw_im * Y_re[k]; 30 | x_re[k] = X_re[k] + a; 31 | x_im[k] = X_im[k] + b; 32 | x_re[k + m] = X_re[k] - a; 33 | x_im[k + m] = X_im[k] - b; 34 | } 35 | } -------------------------------------------------------------------------------- /code/06-divideConquer/binSearch.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cccbook/algjs/501da2f5bd465fd84b587d353478e8fd81db4346/code/06-divideConquer/binSearch.js -------------------------------------------------------------------------------- /code/06-divideConquer/mergeSort/mergesort.js: -------------------------------------------------------------------------------- 1 | function sort(array) { 2 | var length = array.length, 3 | mid = Math.floor(length * 0.5), 4 | left = array.slice(0, mid), 5 | right = array.slice(mid, length) 6 | 7 | if(length === 1) return array 8 | return merge(sort(left), sort(right)) 9 | } 10 | 11 | function merge(left, right) { 12 | var result = []; 13 | while(left.length || right.length) { 14 | if(left.length && right.length) { 15 | (left[0] < right[0]) ? result.push(left.shift()) : result.push(right.shift()); 16 | } else if (left.length) { 17 | result.push(left.shift()); 18 | } else { 19 | result.push(right.shift()); 20 | } 21 | } 22 | return result; 23 | } 24 | 25 | module.exports = sort 26 | -------------------------------------------------------------------------------- /code/06-divideConquer/mergeSort/sortTest.js: -------------------------------------------------------------------------------- 1 | const msort = require('./mergesort') 2 | 3 | console.log(msort([3,7,2,9,5,1,8,4])) 4 | -------------------------------------------------------------------------------- /code/07-hashing/digest.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | const secret = 'abcdefg'; 4 | const hash = crypto.createHmac('sha256', secret) 5 | .update('I love cupcakes') 6 | .digest('hex') 7 | console.log(hash) -------------------------------------------------------------------------------- /code/07-hashing/hash.js: -------------------------------------------------------------------------------- 1 | function hash(str) { 2 | let hash = 5381 3 | let i = str.length 4 | 5 | while(i) { 6 | hash = (hash * 33) ^ str.charCodeAt(--i); 7 | } 8 | 9 | /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed 10 | * integers. Since we want the results to be always positive, convert the 11 | * signed int to an unsigned by doing an unsigned bitshift. */ 12 | return hash >>> 0; 13 | } 14 | 15 | 16 | console.log('hash(hello)=', hash('hello').toString(16)) 17 | console.log('hash(hello!)=', hash('hello!').toString(16)) -------------------------------------------------------------------------------- /code/07-hashing/sha256.js: -------------------------------------------------------------------------------- 1 | const sha256 = require('crypto-js/sha256') 2 | const base64 = require('crypto-js/enc-base64') 3 | 4 | let record = { 5 | nonce: 0, 6 | data: 'john => mary $2.7', 7 | } 8 | 9 | const digest = sha256(JSON.stringify(record, null, 2)) 10 | 11 | console.log('digest=', base64.stringify(digest)) 12 | -------------------------------------------------------------------------------- /code/08-bruteForce/SAT/bak/eval.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | let assign = {x:0, y:1, z:0} 4 | let exp = '(x||y)&&(x||!z)' 5 | with (assign) { 6 | let result = eval(exp) 7 | console.log('exp=', exp, ' assign=', assign, ' result=', result?1:0) 8 | } 9 | */ -------------------------------------------------------------------------------- /code/08-bruteForce/SAT/bak/sat.js: -------------------------------------------------------------------------------- 1 | function satisfy(exp, vars, values) { // 測試 exp 在指令 vars[0..i]=values[0..i] 時,是否能被滿足。 2 | if (values.length === vars.length) { 3 | let assign = {} 4 | for (var i in vars) { 5 | assign[vars[i]] = values[i] 6 | } 7 | with (assign) { 8 | let result = eval(exp) 9 | console.log('exp=', exp, ' assign=', assign, ' result=', result?1:0) 10 | if (result) return values 11 | } 12 | return 13 | } 14 | let v0 = values.slice(0) 15 | let v1 = values.slice(0) 16 | v0.push(0) 17 | v1.push(1) 18 | return satisfy(exp, vars, v0) || satisfy(exp, vars, v1) 19 | } 20 | 21 | function SAT(exp, vars) { 22 | let values = satisfy(exp, vars, []) 23 | return values 24 | } 25 | 26 | console.log(SAT('(x||y)&&(x||z)', ['x', 'y', 'z'])) 27 | console.log(SAT('(x)&&(!x)&&(!y)&&(!z)', ['x', 'y', 'z'])) 28 | -------------------------------------------------------------------------------- /code/08-bruteForce/SAT/sat.js: -------------------------------------------------------------------------------- 1 | function satisfy(exp, vars, values) { // 測試 exp 在指令 vars[0..i]=values[0..i] 時,是否能被滿足。 2 | if (values.length === vars.length) { 3 | let assign = {} 4 | for (var i in vars) { 5 | assign[vars[i]] = values[i] 6 | } 7 | with (assign) { 8 | let result = eval(exp) 9 | console.log('%j => %d', assign, result) 10 | if (result) return values 11 | } 12 | return 13 | } 14 | let v0 = values.slice(0) 15 | let v1 = values.slice(0) 16 | v0.push(0) 17 | v1.push(1) 18 | return satisfy(exp, vars, v0) || satisfy(exp, vars, v1) 19 | } 20 | 21 | function SAT(exp, vars) { 22 | console.log('exp=', exp) 23 | let values = satisfy(exp, vars, []) 24 | return values 25 | } 26 | 27 | console.log(SAT('(x||y)&&(!x||!z)&&(x)&&(y)', ['x', 'y', 'z'])) 28 | console.log(SAT('(x)&&(!x)&&(!y)&&(!z)', ['x', 'y', 'z'])) -------------------------------------------------------------------------------- /code/08-bruteForce/digitalcach/mining.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | let record = { 4 | nonce: 0, 5 | data: 'john => mary : $2.7; george => john : $1.3', 6 | } 7 | 8 | function hash (text) { 9 | return crypto.createHmac('sha256', '').update(text).digest('hex') 10 | } 11 | 12 | function mining(record) { 13 | for (var nonce=0; nonce<1000000000000; nonce++) { 14 | record.nonce = nonce 15 | let h = hash(JSON.stringify(record)) 16 | if (h.startsWith('00000')) return { nonce: nonce, hash: h } 17 | } 18 | } 19 | 20 | console.log(mining(record)) -------------------------------------------------------------------------------- /code/08-bruteForce/password/passwordFind.js: -------------------------------------------------------------------------------- 1 | var bcrypt = require('bcryptjs'); 2 | var salt = bcrypt.genSaltSync(10); 3 | var hash = bcrypt.hashSync("B4c0/\/", salt); 4 | console.log('hash=', hash) 5 | -------------------------------------------------------------------------------- /code/09-numerical/calculus/README.md: -------------------------------------------------------------------------------- 1 | # 微積分 2 | 3 | * []() 4 | 5 | -------------------------------------------------------------------------------- /code/09-numerical/calculus/diff.js: -------------------------------------------------------------------------------- 1 | var diff = function (f, x, dx = 0.000000001) { 2 | var dy = f(x + dx) - f(x - dx) 3 | return dy / (dx + dx) 4 | } 5 | 6 | console.log('diff(sin(x), pi/3) = ', diff(Math.sin, Math.PI / 3)) 7 | console.log('cos(pi/3) = ', Math.cos(Math.PI / 3)) 8 | -------------------------------------------------------------------------------- /code/09-numerical/calculus/integral.js: -------------------------------------------------------------------------------- 1 | // 積分 integral calculus 2 | var integral = function (f, a, b, dx = 0.01) { 3 | var area = 0.0 4 | for (var x = a; x < b; x = x + dx) { 5 | area = area + f(x) * dx 6 | } 7 | return area 8 | } 9 | 10 | console.log('integral(sqrt, 0, 1) =', integral(Math.sqrt, 0, 1)) 11 | console.log('integral(sin, 0, pi) =', integral(Math.sin, 0, Math.PI)) 12 | -------------------------------------------------------------------------------- /code/09-numerical/calculus/partial.js: -------------------------------------------------------------------------------- 1 | // 函數 f 對變數 k 的偏微分: partial(p) / dk 2 | let partial = function (f, p, k, h=0.01) { 3 | let p1 = Object.assign({}, p) 4 | p1[k] += h 5 | return (f(p1) - f(p)) / h 6 | } 7 | 8 | let f = function (p) { 9 | let x = p.x, y = p.y 10 | return x*x + y*y 11 | } 12 | 13 | console.log('df(x:3,y:2)/dx=', partial(f, {x:3, y:2}, 'x')) 14 | console.log('df(x:3,y:2)/dy=', partial(f, {x:3, y:2}, 'y')) 15 | 16 | -------------------------------------------------------------------------------- /code/10-greedy/NaiveBayes/README.md: -------------------------------------------------------------------------------- 1 | # Naive Bayes 2 | 3 | ## Naive Bayes Algorithm 4 | 5 | Naive Bayes Algorithm : P(x,y,z) = P(x) P(y) P(z) 6 | 7 | ## 參考專案 8 | 9 | * https://github.com/kwichmann/bayes-classifier-js 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /code/10-greedy/NaiveBayes/naiveBayesClassifier.js: -------------------------------------------------------------------------------- 1 | const argmax = require('../lib/argmax') 2 | // P(C|F1...Fn) = P(C) * P(F1|C) * ....* P(Fn|C) 3 | function naiveBayesProb(prob, c, f) { 4 | let p = prob[c] 5 | for (let fi of f) p = p*prob[c+'=>'+fi] 6 | return p 7 | } 8 | 9 | const prob = { 10 | 'c1': 0.6, 'c2': 0.4, 11 | 'c1=>f1': 0.5, 'c1=>f2': 0.8, 'c1=>f3': 0.6, 12 | 'c2=>f1': 0.7, 'c2=>f2': 0.6, 'c2=>f3': 0.2, 13 | } 14 | 15 | const f = ['f1', 'f2'] 16 | const c = ['c1', 'c2'] 17 | const p = c.map((ci) => naiveBayesProb(prob, ci, f)) 18 | for (let i=0; i'+fi] 5 | return p 6 | } 7 | 8 | const prob = { 9 | 'c1': 0.6, 'c2': 0.4, 10 | 'c1=>f1': 0.5, 'c1=>f2': 0.8, 'c1=>f3': 0.6, 11 | 'c2=>f1': 0.7, 'c2=>f2': 0.6, 'c2=>f3': 0.2, 12 | } 13 | 14 | for (let c of ['c1', 'c2']) { 15 | console.log('P(%s|f1,f2) = ', c, naiveBayesProb(prob, c, ['f1','f2']).toFixed(3)) 16 | } 17 | -------------------------------------------------------------------------------- /code/10-greedy/NaiveBayes/naiveProb.js: -------------------------------------------------------------------------------- 1 | // P(x,y,z) = P(x) * P(y) * P(z) 2 | function naiveProb(prob, list) { 3 | let p = 1 4 | for (let e of list) p = p*prob[e] 5 | return p 6 | } 7 | 8 | const prob = { 9 | x: 0.5, 10 | y: 0.2, 11 | z: 0.3 12 | } 13 | 14 | console.log('P(x,y,z) = ', naiveProb(prob, ['x','y','z'])) 15 | -------------------------------------------------------------------------------- /code/10-greedy/README.md: -------------------------------------------------------------------------------- 1 | [DATASETS FOR MACHINE LEARNING](https://blog.webkid.io/datasets-for-machine-learning/) 2 | 3 | 4 | ## Document Classificiation 5 | 6 | [DOCUMENT CLASSIFICATION IN JAVASCRIPT](https://blog.webkid.io/document-classification-in-javascript/) 7 | -------------------------------------------------------------------------------- /code/10-greedy/decisionTree.js: -------------------------------------------------------------------------------- 1 | // Reference : 'Programming Collective Intellignece' by Toby Segaran. 2 | 3 | var j6 = require('../../lib/j6') 4 | 5 | var data =[['slashdot','USA','yes',18], 6 | ['google','France','yes',23], 7 | ['digg','USA','yes',24], 8 | ['kiwitobes','France','yes',23], 9 | ['google','UK','no',21], 10 | ['(direct)','New Zealand','no',12], 11 | ['(direct)','UK','no',21], 12 | ['google','USA','no',24], 13 | ['slashdot','France','yes',19], 14 | ['digg','USA','no',18,], 15 | ['google','UK','no',18,], 16 | ['kiwitobes','UK','no',19], 17 | ['digg','New Zealand','yes',12], 18 | ['slashdot','UK','no',21], 19 | ['google','UK','yes',18], 20 | ['kiwitobes','France','yes',19]]; 21 | var result = ['None','Premium','Basic','Basic','Premium','None','Basic','Premium','None','None','None','None','Basic','None','Basic','Basic']; 22 | 23 | var dt = new j6.ML.DecisionTree({ 24 | data : data, 25 | result : result 26 | }); 27 | 28 | dt.build(); 29 | 30 | // dt.print(); 31 | 32 | console.log("Classify : ", dt.classify(['(direct)','USA','yes',5])); 33 | 34 | dt.prune(1.0); // 1.0 : mingain. 35 | dt.print(); -------------------------------------------------------------------------------- /code/10-greedy/huffmanCode/README.md: -------------------------------------------------------------------------------- 1 | # Huffman 編碼法 2 | 3 | * [維基百科:霍夫曼編碼](https://zh.wikipedia.org/zh-tw/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81) 4 | 5 | ## JavaScript 實作 6 | 7 | * http://khaledm.com/projects/huffman/ (線上展示) 8 | * https://github.com/KhaledMohamedP/huffman 9 | * https://gist.github.com/1995eaton/86f10f4d0247b4e4e65e 10 | * https://github.com/sbryant31/huffman-tree -------------------------------------------------------------------------------- /code/10-greedy/huffmanCode/huffmanCode 拷貝 2.js: -------------------------------------------------------------------------------- 1 | // 程式修改自 -- https://gist.github.com/1995eaton/86f10f4d0247b4e4e65e 2 | 3 | // 參考 -- https://en.wikipedia.org/wiki/Binary_heap 4 | /* 5 | 插入節點: 在陣列的最末尾插入新節點。然後自下而上調整子節點與父節點(稱作 bubble-up 或 sift-up) 6 | 比較當前節點與父節點,不滿足「堆積性質」則交換。從而使得當前子樹滿足二元堆積的性質。 7 | 時間複雜度為 O(log n)。 8 | 刪除根節點:刪除時,把堆積儲存的最後那個節點移到填在根節點處。再從上而下調整父節點與它的子節點。 9 | */ 10 | class Heap { // 堆積結構 Heap 11 | constructor (fn) { 12 | this.fn = fn // fn 會取得排序欄位值 13 | this.items = [] // items 為存放堆積的陣列 14 | } 15 | swap(i, j) { 16 | let t = this.items[i] 17 | this.items[i] = this.items[j] 18 | this.items[j] = t 19 | } 20 | bubble(index) { // 冒泡調整,將大的往上調 21 | var parent = ~~((index - 1) / 2) 22 | if (this.item(parent) < this.item(index)) { 23 | this.swap(index, parent) 24 | this.bubble(parent) 25 | } 26 | } 27 | item(index) { 28 | return this.fn(this.items[index]) 29 | } 30 | pop() { 31 | return this.items.pop() 32 | } 33 | sift(index, end) { 34 | var child = index * 2 + 1 35 | if (child < end) { 36 | if (child + 1 < end && this.item(child + 1) > this.item(child)) { 37 | child++ 38 | } 39 | if (this.item(index) < this.item(child)) { 40 | this.swap(index, child) 41 | return this.sift(child, end) 42 | } 43 | } 44 | } 45 | push() { 46 | var lastIndex = this.items.length 47 | for (var i = 0; i < arguments.length; i++) { 48 | this.items.push(arguments[i]) 49 | this.bubble(lastIndex++) 50 | } 51 | } 52 | length() { 53 | return this.items.length 54 | } 55 | } 56 | 57 | var Huffman = { 58 | encode: function(data) { 59 | var prob = {} 60 | var tree = new Heap((e)=>e[0]) 61 | // 計算每個字出現的頻率 62 | for (var i = 0; i < data.length; i++) { 63 | if (prob.hasOwnProperty(data[i])) { 64 | prob[data[i]]++ 65 | } else { 66 | prob[data[i]] = 1 67 | } 68 | } 69 | // 將整個陣列順序打亂,然後放進堆積中(節點:以 [出現次數, 字元] 的方式儲存。 70 | Object.keys(prob).sort((a, b) => ~~(Math.random() * 2)) 71 | .forEach((e) => tree.push([prob[e], e])) 72 | while (tree.length() > 1) { // 當還沒有全部形成一棵樹 (還有很多棵)的時候 73 | var first = tree.pop(), second = tree.pop() // 取出頻率最小的兩個 74 | tree.push([first[0] + second[0], [first[1], second[1]]]) // 將兩者合併成一個 75 | } 76 | // 上面迴圈完成後,樹已經建好了,開始進行編碼! 77 | var dict = {} 78 | var recurse = function(root, string) { 79 | if (root.constructor === Array) { 80 | recurse(root[0], string + '0') // 左邊為 0 81 | recurse(root[1], string + '1') // 右邊為 1 82 | } else { 83 | dict[root] = string // 已經到樹葉節點,設定該字元的編碼。 84 | } 85 | } 86 | tree.items = tree.pop()[1] // 取得樹根 87 | recurse(tree.items, '') // 對樹上每個節點進行編碼 88 | var result = '' 89 | for (var i = 0; i < data.length; i++) { 90 | result += dict[data.charAt(i)] // 對每個字元編碼後加入結果的 0101.... 字串 91 | } 92 | 93 | console.log('dict=%j', dict) 94 | var emap = Object.keys(dict).map(function(ch) { return {ch:ch, binary:dict[ch]}}) 95 | return {emap:emap, result:result} 96 | }, 97 | decode: function(h) { 98 | var data = h.result.split(''), dmap = {} 99 | h.emap.forEach((e)=>{dmap[e.binary] = e.ch}) 100 | var result = '' 101 | while (data.length) { 102 | var i = 0, cur = '' 103 | while (data.length) { 104 | cur += data.shift() 105 | if (dmap.hasOwnProperty(cur)) { 106 | result += dmap[cur] 107 | break 108 | } 109 | } 110 | } 111 | return result 112 | } 113 | } 114 | 115 | var enc = Huffman.encode('TESTTESTTESTTESTTESTTESTTESTTEST123abc') 116 | console.log('encode=', enc) 117 | var dec = Huffman.decode(enc) 118 | console.log('decode=', dec) 119 | -------------------------------------------------------------------------------- /code/10-greedy/huffmanCode/huffmanCode 拷貝.js: -------------------------------------------------------------------------------- 1 | // 程式修改自 -- https://gist.github.com/1995eaton/86f10f4d0247b4e4e65e 2 | 3 | // 參考 -- https://en.wikipedia.org/wiki/Binary_heap 4 | /* 5 | 插入節點: 在陣列的最末尾插入新節點。然後自下而上調整子節點與父節點(稱作 bubble-up 或 sift-up) 6 | 比較當前節點與父節點,不滿足「堆積性質」則交換。從而使得當前子樹滿足二元堆積的性質。 7 | 時間複雜度為 O(log n)。 8 | 刪除根節點:刪除時,把堆積儲存的最後那個節點移到填在根節點處。再從上而下調整父節點與它的子節點。 9 | */ 10 | class Heap { // 堆積結構 Heap 11 | constructor (fn) { 12 | this.fn = fn // fn 會取得排序欄位值 13 | this.items = [] // items 為存放堆積的陣列 14 | } 15 | swap(i, j) { 16 | let t = this.items[i] 17 | this.items[i] = this.items[j] 18 | this.items[j] = t 19 | } 20 | bubble(index) { // 冒泡調整,將大的往上調 21 | var parent = ~~((index - 1) / 2) 22 | if (this.item(parent) < this.item(index)) { 23 | this.swap(index, parent) 24 | this.bubble(parent) 25 | } 26 | } 27 | item(index) { 28 | return this.fn(this.items[index]) 29 | } 30 | pop() { 31 | return this.items.pop() 32 | } 33 | sift(index, end) { 34 | var child = index * 2 + 1 35 | if (child < end) { 36 | if (child + 1 < end && this.item(child + 1) > this.item(child)) { 37 | child++ 38 | } 39 | if (this.item(index) < this.item(child)) { 40 | this.swap(index, child) 41 | return this.sift(child, end) 42 | } 43 | } 44 | } 45 | push() { 46 | var lastIndex = this.items.length 47 | for (var i = 0; i < arguments.length; i++) { 48 | this.items.push(arguments[i]) 49 | this.bubble(lastIndex++) 50 | } 51 | } 52 | length() { 53 | return this.items.length 54 | } 55 | } 56 | 57 | var Huffman = { 58 | encode: function(data) { 59 | var prob = {} 60 | var tree = new Heap((e)=>e[0]) 61 | // 計算每個字出現的頻率 62 | for (var i = 0; i < data.length; i++) { 63 | if (prob.hasOwnProperty(data[i])) { 64 | prob[data[i]]++ 65 | } else { 66 | prob[data[i]] = 1 67 | } 68 | } 69 | // 將整個陣列順序打亂,然後放進堆積中(節點:以 [出現次數, 字元] 的方式儲存。 70 | Object.keys(prob).sort((a, b) => ~~(Math.random() * 2)) 71 | .forEach((e) => tree.push([prob[e], e])) 72 | while (tree.length() > 1) { // 當還沒有全部形成一棵樹 (還有很多棵)的時候 73 | var first = tree.pop(), second = tree.pop() // 取出頻率最小的兩個 74 | tree.push([first[0] + second[0], [first[1], second[1]]]) // 將兩者合併成一個 75 | } 76 | // 上面迴圈完成後,樹已經建好了,開始進行編碼! 77 | var dict = {} 78 | var recurse = function(root, string) { 79 | if (root.constructor === Array) { 80 | recurse(root[0], string + '0') // 左邊為 0 81 | recurse(root[1], string + '1') // 右邊為 1 82 | } else { 83 | dict[root] = string // 已經到樹葉節點,設定該字元的編碼。 84 | } 85 | } 86 | tree.items = tree.pop()[1] // 取得樹根 87 | recurse(tree.items, '') // 對樹上每個節點進行編碼 88 | var result = '' 89 | for (var i = 0; i < data.length; i++) { 90 | result += dict[data.charAt(i)] // 對每個字元編碼後加入結果的 0101.... 字串 91 | } 92 | var header = Object.keys(dict).map((e) => e.charCodeAt(0) + '|' + dict[e]).join('-') 93 | console.log('dict=%j', dict) 94 | // var header = Object.keys(dict).map(function(e) { return {ch:e, binary:dict[e]}}) 95 | return {header:header, result:result} 96 | }, 97 | decode: function(h) { 98 | var data = h.result.split(''), header = {} 99 | h.header.split('-').forEach(function(e) { 100 | var values = e.split('|') 101 | header[values[1]] = String.fromCharCode(values[0]) 102 | }) 103 | var result = '' 104 | while (data.length) { 105 | var i = 0, cur = '' 106 | while (data.length) { 107 | cur += data.shift() 108 | if (header.hasOwnProperty(cur)) { 109 | result += header[cur] 110 | break 111 | } 112 | } 113 | } 114 | return result 115 | } 116 | } 117 | 118 | var enc = Huffman.encode('TESTTESTTESTTESTTESTTESTTESTTEST123abc') 119 | console.log('encode=', enc) 120 | var dec = Huffman.decode(enc) 121 | console.log('decode=', dec) 122 | -------------------------------------------------------------------------------- /code/10-greedy/huffmanCode/huffmanCode.js: -------------------------------------------------------------------------------- 1 | // 程式修改自 -- https://gist.github.com/1995eaton/86f10f4d0247b4e4e65e 2 | 3 | // 參考 -- https://en.wikipedia.org/wiki/Binary_heap 4 | /* 堆積: 5 | 插入節點: 在陣列的最末尾插入新節點。然後自下而上調整子節點與父節點(稱作 bubble-up 或 sift-up) 6 | 比較當前節點與父節點,不滿足「堆積性質」則交換。從而使得當前子樹滿足二元堆積的性質。 7 | 時間複雜度為 O(log n)。 8 | 刪除樹根:刪除時,把堆積儲存的最後那個節點移到填在根節點處。再從上而下調整父節點與它的子節點。 9 | */ 10 | class Heap { // 堆積結構 Heap 11 | constructor (fn) { 12 | this.fn = fn // fn 會取得排序欄位值 13 | this.items = [] // items 為存放堆積的陣列 14 | } 15 | swap(i, j) { 16 | let t = this.items[i] 17 | this.items[i] = this.items[j] 18 | this.items[j] = t 19 | } 20 | bubble(index) { // 冒泡調整,將大的往上調 21 | var parent = ~~((index - 1) / 2) 22 | if (this.item(parent) < this.item(index)) { 23 | this.swap(index, parent) 24 | this.bubble(parent) 25 | } 26 | } 27 | item(index) { 28 | return this.fn(this.items[index]) 29 | } 30 | pop() { 31 | return this.items.pop() 32 | } 33 | sift(index, end) { 34 | var child = index * 2 + 1 35 | if (child < end) { 36 | if (child + 1 < end && this.item(child + 1) > this.item(child)) { 37 | child++ 38 | } 39 | if (this.item(index) < this.item(child)) { 40 | this.swap(index, child) 41 | return this.sift(child, end) 42 | } 43 | } 44 | } 45 | push() { 46 | var lastIndex = this.items.length 47 | for (var i = 0; i < arguments.length; i++) { 48 | this.items.push(arguments[i]) 49 | this.bubble(lastIndex++) 50 | } 51 | } 52 | length() { 53 | return this.items.length 54 | } 55 | } 56 | 57 | var Huffman = { 58 | encode: function(data) { 59 | var prob = {} 60 | var tree = new Heap((e)=>e[0]) 61 | // 計算每個字出現的頻率 62 | for (var i = 0; i < data.length; i++) { 63 | if (prob.hasOwnProperty(data[i])) { 64 | prob[data[i]]++ 65 | } else { 66 | prob[data[i]] = 1 67 | } 68 | } 69 | // 將整個陣列順序打亂,然後放進堆積中(節點:以 [出現次數, 字元] 的方式儲存。 70 | Object.keys(prob).sort((a, b) => ~~(Math.random() * 2)) 71 | .forEach((e) => tree.push([prob[e], e])) 72 | while (tree.length() > 1) { // 當還沒有全部形成一棵樹 (還有很多棵)的時候 73 | var first = tree.pop(), second = tree.pop() // 取出頻率最小的兩個 74 | tree.push([first[0] + second[0], [first[1], second[1]]]) // 將兩者合併成一個 75 | } 76 | // 上面迴圈完成後,樹已經建好了,開始進行編碼! 77 | var dict = {} 78 | var recurse = function(root, string) { 79 | if (root.constructor === Array) { 80 | recurse(root[0], string + '0') // 左邊為 0 81 | recurse(root[1], string + '1') // 右邊為 1 82 | } else { 83 | dict[root] = string // 已經到樹葉節點,設定該字元的編碼。 84 | } 85 | } 86 | tree.items = tree.pop()[1] // 取得樹根 87 | recurse(tree.items, '') // 對樹上每個節點進行編碼 88 | var result = '' 89 | for (var i = 0; i < data.length; i++) { 90 | result += dict[data.charAt(i)] // 對每個字元編碼後加入結果的 0101.... 字串 91 | } 92 | return {emap:dict, result:result} 93 | }, 94 | decode: function(h) { 95 | var data = h.result.split(''), dmap = {} 96 | // 將 emap(ch)=>binary 反轉為 dmap(binary)=>ch 97 | for (let ch in h.emap) { 98 | let binary = h.emap[ch] 99 | dmap[binary] = ch 100 | } 101 | var result = '' 102 | while (data.length) { 103 | var i = 0, cur = '' 104 | while (data.length) { 105 | cur += data.shift() 106 | if (dmap.hasOwnProperty(cur)) { // 查查看這個長度的二進位是否在 dmap 中 107 | result += dmap[cur] // 有的話就進行編碼 108 | break 109 | } 110 | } 111 | } 112 | return result 113 | } 114 | } 115 | 116 | var enc = Huffman.encode('TESTTESTTESTTESTTESTTESTTESTTEST123abc') 117 | console.log('encode=', enc) 118 | var dec = Huffman.decode(enc) 119 | console.log('decode=', dec) 120 | -------------------------------------------------------------------------------- /code/10-greedy/knn.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | module.exports = function (ai6) { 3 | var KNN = {} 4 | 5 | KNN.loadQA = function (QA) { 6 | KNN.QA = QA 7 | } 8 | 9 | KNN.kNearestNeighbors = function (item, distance, k = 1) { 10 | var QA = KNN.QA 11 | var d = [] 12 | for (var i = 0; i < QA.length; i++) { 13 | d.push({dist: distance(item, QA[i].Q), qa: QA[i]}) 14 | } 15 | d.sort((o1, o2) => o1.dist > o2.dist) 16 | return d.slice(0, k) 17 | } 18 | 19 | return KNN 20 | } 21 | -------------------------------------------------------------------------------- /code/10-greedy/knnEx.js: -------------------------------------------------------------------------------- 1 | var ai6 = require('../../source/ai6') 2 | var KNN = ai6.ML.KNN 3 | var QA = [ 4 | {Q: [0, 0], A: ['L']}, 5 | {Q: [0, 1], A: ['L']}, 6 | {Q: [1, 0], A: ['L']}, 7 | {Q: [1, 1], A: ['L']}, 8 | {Q: [8, 0], A: ['H']}, 9 | {Q: [8, 1], A: ['H']}, 10 | {Q: [9, 0], A: ['H']}, 11 | {Q: [9, 1], A: ['H']} 12 | ] 13 | 14 | KNN.loadQA(QA) 15 | 16 | var distance = function (a, b) { 17 | var dist = ai6.j6.euclidDistance(a, b) 18 | return dist 19 | } 20 | 21 | var k = 3 22 | var neighbors = KNN.kNearestNeighbors([1, 2], distance, k) 23 | console.log(JSON.stringify(neighbors)) 24 | -------------------------------------------------------------------------------- /code/10-greedy/minimalSpanningTree/minimalSpanningTree.js: -------------------------------------------------------------------------------- 1 | // See http://en.wikipedia.org/wiki/Kruskal's_algorithm 2 | // and http://programmingpraxis.com/2010/04/06/minimum-spanning-tree-kruskals-algorithm/ 3 | 4 | var nodes = ["A", "B", "C", "D", "E", "F", "G"] 5 | var edges = [ 6 | ["A", "B", 7], ["A", "D", 5], 7 | ["B", "C", 8], ["B", "D", 9], ["B", "E", 7], 8 | ["C", "E", 5], 9 | ["D", "E", 15], ["D", "F", 6], 10 | ["E", "F", 8], ["E", "G", 9], 11 | ["F", "G", 11] 12 | ]; 13 | 14 | 15 | function minimalSpanningTree(nodes, edges) { 16 | var mst = [], nodeSet = {} 17 | for (let node of nodes) { 18 | nodeSet[node] = [node] 19 | } 20 | let sortedEdges = edges.sort((a,b) => a[2] - b[2]) 21 | for (let edge of sortedEdges) { 22 | var n1 = edge[0], n2 = edge[1] 23 | var t1 = nodeSet[n1] 24 | var t2 = nodeSet[n2] 25 | if (t1 != t2) { 26 | var t = t1.concat(t2) 27 | nodeSet[n1] = nodeSet[n2] = t 28 | mst.push(edge) 29 | if (mst.length == nodes.length-1) break 30 | } 31 | } 32 | return mst 33 | } 34 | 35 | console.log(minimalSpanningTree(nodes, edges)) 36 | -------------------------------------------------------------------------------- /code/11-graph/graphSearch.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | var printf = function() { 4 | return process.stdout.write(util.format.apply(null, arguments)); 5 | } 6 | 7 | function enqueue(a, o) { a.push(o); } 8 | function dequeue(a) { return a.shift(); } 9 | 10 | var g = { // graph: 被搜尋的網路 11 | 1: {n:[2,5], v:0}, // n: neighbor (鄰居), v: visited (是否被訪問過) 12 | 2: {n:[3,4], v:0}, 13 | 3: {n:[4,5,6], v:0}, 14 | 4: {n:[5,6], v:0}, 15 | 5: {n:[6], v:0}, 16 | 6: {n:[], v:0} 17 | }; 18 | 19 | function init(g) { // 初始化、設定 visited 為 0 20 | for (i in g) g[i].v = 0; 21 | } 22 | 23 | function dfs(g, node) { // 深度優先搜尋 24 | if (g[node].v !=0) return; // 如果已訪問過,就不再訪問 25 | printf('%d=>', node); // 否則、印出節點 26 | g[node].v = 1; // 並設定為已訪問 27 | var neighbors = g[node].n; // 取出鄰居節點 28 | for (var i in neighbors) { // 對於每個鄰居 29 | dfs(g, neighbors[i]); // 逐一進行訪問 30 | } 31 | } 32 | 33 | var queue=[1]; // BFS 用的 queue, 起始點為 1。 34 | 35 | function bfs(g, q) { // 廣度優先搜尋 36 | if (q.length == 0) return; // 如果 queue 已空,則返回。 37 | var node = dequeue(q); // 否則、取出 queue 的第一個節點。 38 | if (g[node].v == 0) // 如果該節點尚未拜訪過。 39 | g[node].v = 1; // 標示為已拜訪 40 | else // 否則 (已訪問過) 41 | return; // 不繼續搜尋,直接返回。 42 | printf('%d=>', node); // 印出節點 43 | var neighbors = g[node].n; // 取出鄰居。 44 | for (var i in neighbors) { // 對於每個鄰居 45 | var n = neighbors[i]; 46 | if (!g[n].visited) // 假如該鄰居還沒被拜訪過 47 | q.push(n); // 就放入 queue 中 48 | } 49 | bfs(g, q); 50 | } 51 | 52 | printf('dfs:'); init(g); dfs(g, 1); printf('\n'); // 呼叫深度優先搜尋。 53 | printf('bfs:'); init(g); bfs(g, queue); printf('\n'); // 呼叫廣度優先搜尋。 54 | 55 | -------------------------------------------------------------------------------- /code/12-transformDomain/Fourier/DFT.js: -------------------------------------------------------------------------------- 1 | var Complex = require('./complex') 2 | var p = Complex.parse 3 | 4 | var DFT = function (f) { 5 | let N = f.length 6 | let F = [] 7 | for (let n=0; n -1) { 21 | m = m+i - t[i]; 22 | i = t[i]; 23 | } else { 24 | i = 0; 25 | m = m + 1; 26 | } 27 | } 28 | } 29 | 30 | return -1; 31 | } 32 | 33 | /** 34 | * table 35 | * @desc Returns "Partial match" table 36 | * @param {string} word - search word 37 | */ 38 | kmp.table = function kmpTable(w) { 39 | var t = []; 40 | var pos = 2; // current position in T 41 | var cnd = 0; // index in w of the next character of the current candidate substring 42 | 43 | t[0] = -1; 44 | t[1] = 0; 45 | 46 | while (pos < w.length) { 47 | // substring continues 48 | if (w[pos-1] === w[cnd]) { 49 | cnd = cnd + 1; 50 | t[pos] = cnd; 51 | pos = pos + 1; 52 | // it doesn't but we can fall back 53 | } else if (cnd > 0) { 54 | cnd = t[cnd]; 55 | // we have run out of candidates 56 | } else { 57 | t[pos] = 0; 58 | pos = pos + 1; 59 | } 60 | } 61 | 62 | return t; 63 | }; 64 | -------------------------------------------------------------------------------- /code/13-stringMatching/knuth-morris-pratt/kmpTest.js: -------------------------------------------------------------------------------- 1 | let kmp = require('./kmp') 2 | 3 | console.log('kmp(she sells seashells by the seashore, shell) = ', kmp('she sells seashells by the seashore', 'shell')); // 13 4 | console.log('kmp(she sells seashells by the seashore, seaweed) = ', kmp('she sells seashells by the seashore', 'seaweed')); // -1 -------------------------------------------------------------------------------- /code/13-stringMatching/minimalEditDistance/README.md: -------------------------------------------------------------------------------- 1 | # Levenshtein 最小編輯距離 2 | 3 | * [維基百科:萊文斯坦距離](https://zh.wikipedia.org/wiki/%E8%90%8A%E6%96%87%E6%96%AF%E5%9D%A6%E8%B7%9D%E9%9B%A2) 4 | * https://en.wikipedia.org/wiki/Levenshtein_distance 5 | 6 | * [自然語言處理 -- Minimum Edit Distance](http://cpmarkchang.logdown.com/posts/222651-minimum-edit-distance) 7 | * https://gist.github.com/andrei-m/982927 8 | * https://github.com/hiddentao/fast-levenshtein -------------------------------------------------------------------------------- /code/13-stringMatching/minimalEditDistance/levenshteinDistance.js: -------------------------------------------------------------------------------- 1 | // Compute the edit distance between the two given strings 2 | /* 動態規劃法 */ 3 | var levenshteinDistance = function(a, b){ 4 | if(a.length == 0) return b.length; 5 | if(b.length == 0) return a.length; 6 | 7 | var matrix = []; 8 | // increment along the first column of each row 9 | for(let i = 0; i <= b.length; i++) matrix[i] = [i]; 10 | // increment each column in the first row 11 | for(let j = 0; j <= a.length; j++) matrix[0][j] = j; 12 | 13 | // Fill in the rest of the matrix 14 | for(let i = 1; i <= b.length; i++){ 15 | for(let j = 1; j <= a.length; j++){ 16 | if(b.charAt(i-1) == a.charAt(j-1)){ 17 | matrix[i][j] = matrix[i-1][j-1] 18 | } else { 19 | matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution 20 | matrix[i][j-1] + 1, // insertion 21 | matrix[i-1][j] + 1) // deletion 22 | } 23 | } 24 | } 25 | 26 | return matrix[b.length][a.length] 27 | } 28 | 29 | /* 遞迴法 30 | var levenshteinDistance = function (s, t) { 31 | if (s.length===0) return t.length 32 | if (t.length===0) return s.length 33 | 34 | return Math.min( 35 | levenshteinDistance(s.substr(1), t) + 1, 36 | levenshteinDistance(s, t.substr(1)) + 1, 37 | levenshteinDistance(s.substr(1), t.substr(1)) + (s[0] !== t[0] ? 1 : 0) 38 | ) 39 | } 40 | */ 41 | 42 | let s = 'kitten' 43 | let t = 'sitting' 44 | 45 | console.log('levenshteinDistance(%s,%s)=%d', s, t, levenshteinDistance (s, t)) 46 | -------------------------------------------------------------------------------- /code/14-cryptography/AES/aes.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto') 2 | var algorithm = 'aes-256-ctr' 3 | var password = 'd6F3Efeq' 4 | 5 | function encrypt(text) { 6 | var cipher = crypto.createCipher(algorithm, password) 7 | var crypted = cipher.update(text,'utf8','hex') 8 | crypted += cipher.final('hex'); 9 | return crypted; 10 | } 11 | 12 | function decrypt(text) { 13 | var decipher = crypto.createDecipher(algorithm, password) 14 | var dec = decipher.update(text,'hex','utf8') 15 | dec += decipher.final('utf8'); 16 | return dec; 17 | } 18 | 19 | var plainText = "hello world" 20 | var cipherText = encrypt(plainText) 21 | var decryptText = decrypt(cipherText) 22 | console.log('plainText =', plainText) 23 | console.log('cipherText =', cipherText) 24 | console.log('decryptText=', decryptText) 25 | -------------------------------------------------------------------------------- /code/14-cryptography/AES/cipher.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto') 2 | 3 | // 加密 4 | function encrypt(algorithm, key, plainText) { 5 | var list = [] 6 | var encrypter = crypto.createCipher(algorithm, key) 7 | list.push(encrypter.update(plainText, 'binary', 'hex')) 8 | list.push(encrypter.final('hex')) 9 | return list.join('') 10 | } 11 | 12 | // 解密 13 | function decrpyt(algorithm, key, cipherText) { 14 | var list = [] 15 | var decrypter = crypto.createDecipher(algorithm, key) 16 | list.push(decrypter.update(cipherText, 'hex', 'binary')) 17 | list.push(decrypter.final('binary')) 18 | return list.join('') 19 | } 20 | 21 | let key = 'adsjfa@#!(Oxxay' 22 | let algorithm = 'aes-128-cbc' 23 | let sendText = 'This is the plain text !' 24 | console.log('sendText = ', sendText) 25 | let cipherText = encrypt(algorithm, key, sendText) 26 | console.log('cipherText = ', cipherText) 27 | let recvText = decrpyt(algorithm, key, cipherText) 28 | console.log('recvText = ', recvText) 29 | -------------------------------------------------------------------------------- /code/14-cryptography/Blum/README.md: -------------------------------------------------------------------------------- 1 | # Blum 亂數產生器與加解密系統 2 | 3 | 圖靈獎得主 Manuel Blum : https://en.wikipedia.org/wiki/Manuel_Blum 4 | 5 | ## Blum Blum Shub 偽隨機亂數產生器 6 | 7 | 執行 8 | 9 | ``` 10 | $ node blumBlumShubTest.js 11 | 18098567 12 | 14489865 13 | 10612342 14 | 20647940 15 | 19395661 16 | 21368461 17 | 18118427 18 | 12120277 19 | 3205170 20 | 8426854 21 | ``` 22 | 23 | ## Blum–Goldwasser (BG) Cryptosystem 24 | 25 | BG 非對稱型加解密系統在 1984 年由 Manuel Blum 和 Shafi Goldwasser 提出,是個《機率安全型系統》。 26 | 27 | 加密時使用 XOR 法,將明文 plainText 與 Blum Blum Shub (BBS) 所產生的 bbsKey,透過逐個位元 xor 的方式形成密文。 (這有點像是 one time pad) 28 | 29 | 然後再利用 private key 與 BBS 的最終狀態找出亂數種子 seed,找出 seed 之後就能完整地將密文解回來了。 30 | 31 | 詳細演算法在以下網址: 32 | 33 | * https://en.wikipedia.org/wiki/Blum%E2%80%93Goldwasser_cryptosystem 34 | 35 | 關鍵是: 36 | 37 | Bob 送 c[0..L-1], y 給 Alice,然後 Alice 可以透過 y, L 與 (p,q) 輕鬆計算出種子 seed,於是就能解碼了。 -------------------------------------------------------------------------------- /code/14-cryptography/Blum/blumBlumShub.js: -------------------------------------------------------------------------------- 1 | // 來源:A Blum Blum Shub implementation in JavaScript. 2 | // -- https://gist.github.com/schas002/757c0b948b469cd591c24f27eb16edf0 3 | 4 | /** 5 | *** blum_blum_shub.js *** 6 | An implementation of the Blum Blum Shub pseudorandom number generator proposed 7 | in 1986 by Lenore Blum, Manuel Blum and Michael Shub that is derived from 8 | Michael O. Rabin's oblivious transfer mapping. 9 | Blum Blum Shub takes the form 10 | 2 11 | x = x mod M 12 | n+1 n 13 | where M = pq is the product of two large primes p and q. At each step of the 14 | algorithm, some output is derived from x[n+1]; the output is commonly either 15 | the bit parity of x[n+1] or one or more of the least significant bits of 16 | x[n+1]. 17 | The seed x[0] should be an integer that is co-prime to M (i.e. p and q are not 18 | factors of x[0]) and not 1 or 0. 19 | The two primes, p and q, should both be congruent to 3 (mod 4) (this guarantees 20 | that each quadratic residue has one square root which is also a quadratic 21 | residue) and gcd(phi(p - 1), phi(q - 1)) should be small (this makes the cycle 22 | length large). 23 | In this implementation, p = 5651 and q = 5623. 24 | This software is licensed under the zlib/libpng license: 25 | Copyright (c) 2016 Andrew Zyabin 26 | This software is provided 'as-is', without any express or implied warranty. In 27 | no event will the authors be held liable for any damages arising from the use 28 | of this software. 29 | Permission is granted to anyone to use this software for any purpose, including 30 | commercial applications, and to alter it and redistribute it freely, subject to 31 | the following restrictions: 32 | 1. The origin of this software must not be misrepresented; you must not 33 | claim that you wrote the original software. If you use this software in a 34 | product, an acknowledgment in the product documentation would be 35 | appreciated but is not required. 36 | 2. Altered source versions must be plainly marked as such, and must not be 37 | misrepresented as being the original software. 38 | 3. This notice may not be removed or altered from any source distribution. 39 | */ 40 | 41 | var R = module.exports = {} 42 | 43 | var p = 5651; 44 | var q = 5623; 45 | var M = p * q; 46 | 47 | var x = undefined; 48 | 49 | /** Get the gcd of two numbers, A and B. */ 50 | function gcd(a, b) { 51 | while(a != b) { 52 | if(a > b) { 53 | a = a - b; 54 | } else { 55 | b = b - a; 56 | } 57 | } 58 | return a; 59 | } 60 | 61 | /** Seed the random number generator. */ 62 | R.seed = function(s) { 63 | if(s == 0) { 64 | throw new Error("The seed x[0] cannot be 0"); 65 | } else if(s == 1) { 66 | throw new Error("The seed x[0] cannot be 1"); 67 | } else if(gcd(s, M) != 1) { 68 | throw new Error("The seed x[0] must be co-prime to " + M.toString()); 69 | } else { 70 | x = s; 71 | return s; 72 | } 73 | } 74 | 75 | /** Get next item from the random number generator. */ 76 | R.next = function () { 77 | var cachedx = x; 78 | cachedx = cachedx * x; 79 | cachedx = cachedx % M; 80 | x = cachedx; 81 | return x; 82 | } -------------------------------------------------------------------------------- /code/14-cryptography/Blum/blumBlumShubTest.js: -------------------------------------------------------------------------------- 1 | var r = require('./blumBlumShup') 2 | r.seed(132747) 3 | for (let i=0; i<10; i++) { 4 | console.log(r.next()) 5 | } -------------------------------------------------------------------------------- /code/14-cryptography/GaloisField/GaloisField.js: -------------------------------------------------------------------------------- 1 | var groupList = function (ra, op, rb, p) { 2 | console.log('============================') 3 | for (let a = ra.min; a <= ra.max; a++) { 4 | console.log() 5 | for (let b = rb.min; b <= rb.max; b++) { 6 | let ab = (op==='*')?a*b:a+b 7 | ab = ab % p 8 | console.log('%d %s %d = %d (mod %d)', a, op, b, ab, p) 9 | } 10 | } 11 | } 12 | 13 | let ra = {min: 0, max: 6} 14 | groupList(ra, '+', ra, 7) 15 | groupList(ra, '*', ra, 7) -------------------------------------------------------------------------------- /code/14-cryptography/GaloisField/README.ext.md: -------------------------------------------------------------------------------- 1 | # Galois Field (有限體) 2 | 3 | * [](https://www.slideshare.net/ccckmit/ss-68579935) 4 | * [維基百科:群](https://zh.wikipedia.org/wiki/%E7%BE%A4) 5 | * https://en.wikipedia.org/wiki/Group_(mathematics) 6 | * [維基百科:有限體 (伽羅瓦體)](https://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E5%9F%9F) 7 | * https://en.wikipedia.org/wiki/Finite_field 8 | 9 | ## 群 10 | 11 | * 封閉性: a * b in G 12 | * 結合性: (a * b) * c = a * (b * c) 13 | * 單位元素: a * I = a // 在乘法運算中 I=1, 加法運算中 I=0 14 | * 反元素: a * ia = I // ia 為 a 的反元素,也是 G 的成員 15 | 16 | 具備『交換性』的群稱為『交換群』或『阿貝爾群』(Abelian group) -------------------------------------------------------------------------------- /code/14-cryptography/GaloisField/README.md: -------------------------------------------------------------------------------- 1 | # Galois Field (有限體) 2 | 3 | * [](https://www.slideshare.net/ccckmit/ss-68579935) 4 | * [維基百科:群](https://zh.wikipedia.org/wiki/%E7%BE%A4) 5 | * https://en.wikipedia.org/wiki/Group_(mathematics) 6 | * [維基百科:有限體 (伽羅瓦體)](https://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E5%9F%9F) 7 | * https://en.wikipedia.org/wiki/Finite_field 8 | 9 | ## 群 10 | 11 | * 封閉性: a * b in G 12 | * 結合性: (a * b) * c = a * (b * c) 13 | * 單位元素: a * I = a // 在乘法運算中 I=1, 加法運算中 I=0 14 | * 反元素: a * ia = I // ia 為 a 的反元素,也是 G 的成員 15 | 16 | 具備『交換性』的群稱為『交換群』或『阿貝爾群』(Abelian group) -------------------------------------------------------------------------------- /code/14-cryptography/README.md: -------------------------------------------------------------------------------- 1 | # 密碼學 2 | 3 | ## 理論基礎 4 | 5 | * [維基百科:有限體](https://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E5%9F%9F) 6 | * https://en.wikipedia.org/wiki/Finite_field 7 | ## 加解密 8 | 9 | * [Node.js加密算法库Crypto](http://blog.fens.me/nodejs-crypto/) (讚!) 10 | * https://nodejs.org/api/crypto.html 11 | * [維基百科:密碼雜湊函式](https://zh.wikipedia.org/wiki/%E5%AF%86%E7%A2%BC%E9%9B%9C%E6%B9%8A%E5%87%BD%E6%95%B8) 12 | * https://en.wikipedia.org/wiki/Cryptographic_hash_function 13 | * [RSA and ECC in JavaScript](http://www-cs-students.stanford.edu/~tjw/jsbn/) 14 | * 包含 SHA1 15 | * [Which symmetric key algorithm does SSL use? 16 | ](https://stackoverflow.com/questions/6088583/which-symmetric-key-algorithm-does-ssl-use) 17 | * Symmetric algorithms supported in SSL are DES, 3DES, ARCFOUR, AES, Camellia, RC2, IDEA, SEED, NULL (no encryption). 18 | * [維基百科:RSA加密演算法](https://zh.wikipedia.org/zh-tw/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95) 19 | * http://www.mathland.idv.tw/life/rsa576.htm 20 | * [維基百科:橢圓曲線密碼學](https://zh.wikipedia.org/wiki/%E6%A4%AD%E5%9C%86%E6%9B%B2%E7%BA%BF%E5%AF%86%E7%A0%81%E5%AD%A6) 21 | * 橢圓曲線密碼學(英語:Elliptic curve cryptography,縮寫為 ECC) 22 | * [List of random number generators](https://en.wikipedia.org/wiki/List_of_random_number_generators) 23 | * https://en.wikipedia.org/wiki/Blum_Blum_Shub (x[n+1] = x[n]^2 mod M) 24 | * https://en.wikipedia.org/wiki/Linear_congruential_generator (x[n+1] = a x[n] + c mod M) 25 | * https://en.wikipedia.org/wiki/Lehmer_random_number_generator (x[n+1] = a x[n] mod M) 26 | * https://en.wikipedia.org/wiki/Yao%27s_test (偽隨機數通過此測試者,難以和真的隨機數做區分) 27 | 28 | ## 區塊鏈 29 | 30 | * [比特幣 (Bit Coin) 的運作原理](http://pansci.asia/archives/53571) -- 陳鍾誠 31 | * [A blockchain in 200 lines of code](https://medium.com/@lhartikk/a-blockchain-in-200-lines-of-code-963cc1cc0e54) 32 | * https://github.com/lhartikk/naivechain 33 | * https://lhartikk.github.io/ 34 | * [Blockchain Demo](https://anders.com/blockchain/) 35 | * https://anders.com/blockchain/ 36 | * https://github.com/anders94/blockchain-demo/ 37 | 38 | * https://en.wikipedia.org/wiki/List_of_important_publications_in_cryptography 39 | -------------------------------------------------------------------------------- /code/14-cryptography/RSA/README.md: -------------------------------------------------------------------------------- 1 | # RSA 2 | 3 | 參考: https://en.wikipedia.org/wiki/RSA_(cryptosystem) 4 | 5 | ## 執行 6 | 7 | ``` 8 | $ node rsa.js 9 | M1= [ 65, 22, 37, 18, 29 ] 10 | M2= [ 65, 22, 37, 18, 29 ] 11 | ``` 12 | 13 | ## 數學背景 14 | 15 | 1. n = pq // p, q 均為質數 16 | 2. r = (p-1)(q-1) 17 | 3. 找一 e 與 r 互質,並解得 e 的反元素 d, e*d = -1 mod r 18 | 19 | e 為公鑰,d 為私鑰 20 | 21 | ## 程序 22 | 23 | 1. Alice.broadcast(n, e) 24 | 2. Bob.sendTo(Alice, c) 25 | * c = m^e (mod n) // m 為訊息 26 | 3. Alice.receiveFrom(Bob, c) 27 | * c^d =m (mod n) 28 | 29 | c^d = m^{ed} = m (mod N) 30 | 31 | 範例: 32 | 33 | ``` 34 | p=61, q=53, n=61*53=3233, λ(3233)=lcm(61,53)=780 35 | 36 | let e = 17 , compute d = 413 37 | 38 | e*d = 1 mod λ(n) 39 | 17*413 = 1 mod 780 40 | ``` 41 | 42 | 假如 Bob 要傳 m 給 Alice,則可以傳送 c=m^e (mod n) 這個密文,然後 Alice 透過下列方式解碼: 43 | 44 | c^d = m^ed = m (mod n) 45 | 46 | -------------------------------------------------------------------------------- /code/14-cryptography/RSA/simpleRsa.js: -------------------------------------------------------------------------------- 1 | function mpower(a, n, p) { 2 | let r = a 3 | for (let i=2; i<=n; i++) { 4 | r = (r * a) % p 5 | } 6 | return r 7 | } 8 | 9 | var p = 61, q = 53, N = p*q // lcm(61,53)=780 10 | let e = 17 , d = 413 11 | 12 | // var p = 37, q = 67, N = p * q 13 | // let e = 23, d = 14 | 15 | var M1 = [65, 22, 37, 18, 29] 16 | var E1 = [] 17 | var M2 = [] 18 | for (let m of M1) { 19 | let c = mpower(m, e, N) 20 | E1.push(c) 21 | let m2 = mpower(c, d, N) 22 | M2.push(m2) 23 | } 24 | 25 | console.log('M1=', M1) 26 | console.log('E1=', E1) 27 | console.log('M2=', M2) 28 | -------------------------------------------------------------------------------- /code/14-cryptography/diffieHellman/README.ext.md: -------------------------------------------------------------------------------- 1 | # Diffie Hallman Key Exchange 2 | 3 | ## 執行 4 | 5 | ``` 6 | $ node .\diffieHellman.js 7 | Alice.K = 2 8 | Bob.K = 2 9 | ``` 10 | 11 | ## 原理 12 | 13 | * [維基百科:迪菲-赫爾曼密鑰交換](https://zh.wikipedia.org/wiki/%E8%BF%AA%E8%8F%B2-%E8%B5%AB%E7%88%BE%E6%9B%BC%E5%AF%86%E9%91%B0%E4%BA%A4%E6%8F%9B) (讚!) 14 | * [Public key cryptography: What is it?](https://www.khanacademy.org/computing/computer-science/cryptography/modern-crypt/v/diffie-hellman-key-exchange-part-1) 15 | * [Diffie-hellman key exchange](https://www.khanacademy.org/computing/computer-science/cryptography/modern-crypt/v/diffie-hellman-key-exchange-part-2) 16 | 17 | 18 | ## 數學 19 | 20 | 定理: $(g^a)^b = g^{ab} = (g^b)^a (mod\;p)$ 21 | 22 | 23 | ```puml 24 | @startuml 25 | Alice -> Alice: choose g, p, a 26 | Alice -> Alice: A=g^a%p 27 | Alice -> Bob: g, p, A 28 | Bob -> Bob: choose b 29 | Bob -> Bob: B = g^b %p 30 | Bob -> Bob: K = A^b %p = g^ab %p 31 | Bob -> Alice: B 32 | Alice -> Alice: K=B^a % p = g^ab % p 33 | @enduml 34 | ``` 35 | 36 | 47 | 48 | -------------------------------------------------------------------------------- /code/14-cryptography/diffieHellman/README.md: -------------------------------------------------------------------------------- 1 | # Diffie Hallman Key Exchange 2 | 3 | ## 執行 4 | 5 | ``` 6 | $ node .\diffieHellman.js 7 | Alice.K = 2 8 | Bob.K = 2 9 | ``` 10 | 11 | ## 原理 12 | 13 | * [維基百科:迪菲-赫爾曼密鑰交換](https://zh.wikipedia.org/wiki/%E8%BF%AA%E8%8F%B2-%E8%B5%AB%E7%88%BE%E6%9B%BC%E5%AF%86%E9%91%B0%E4%BA%A4%E6%8F%9B) (讚!) 14 | * [Public key cryptography: What is it?](https://www.khanacademy.org/computing/computer-science/cryptography/modern-crypt/v/diffie-hellman-key-exchange-part-1) 15 | * [Diffie-hellman key exchange](https://www.khanacademy.org/computing/computer-science/cryptography/modern-crypt/v/diffie-hellman-key-exchange-part-2) 16 | 17 | 18 | ## 數學 19 | 20 | 定理: 21 | 22 | 23 | 24 | 25 | 36 | 37 | -------------------------------------------------------------------------------- /code/14-cryptography/diffieHellman/diffieHellman.js: -------------------------------------------------------------------------------- 1 | // 參考: [維基百科:迪菲-赫爾曼密鑰交換](https://zh.wikipedia.org/wiki/%E8%BF%AA%E8%8F%B2-%E8%B5%AB%E7%88%BE%E6%9B%BC%E5%AF%86%E9%91%B0%E4%BA%A4%E6%8F%9B) 2 | var Alice = { g:5, p:23, a:6 } 3 | var Bob = { b:15 } 4 | 5 | function mpower(a, n, p) { 6 | let r = a 7 | for (let i=2; i<=n; i++) { 8 | r = (r * a) % p 9 | } 10 | return r 11 | } 12 | 13 | Alice.send = function (receiver) { 14 | let {g, p, a} = Alice 15 | let A = mpower(g, a, p) 16 | receiver.receive(Alice, g, p, A) 17 | } 18 | 19 | Alice.receive = function (sender, B) { 20 | let {g, p, a} = Alice 21 | this.K = mpower(B, a, p) 22 | } 23 | 24 | Bob.receive = function (sender, g, p, A) { 25 | let b = Bob.b 26 | let B = mpower(g, b, p) 27 | this.K = mpower(A, b, p) 28 | Bob.send(sender, B) 29 | } 30 | 31 | Bob.send = function (receiver, B) { 32 | receiver.receive(Bob, B) 33 | } 34 | 35 | Alice.send(Bob) 36 | 37 | console.log('Alice.K = %d', Alice.K) 38 | console.log('Bob.K = %d', Bob.K) 39 | -------------------------------------------------------------------------------- /code/14-cryptography/digest.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | const secret = 'abcdefg'; 4 | const hash = crypto.createHmac('sha256', secret) 5 | .update('I love cupcakes') 6 | .digest('hex'); 7 | console.log(hash); -------------------------------------------------------------------------------- /code/14-cryptography/gcd/README.md: -------------------------------------------------------------------------------- 1 | # Number 數論 2 | 3 | * http://numbers.github.io/index.html 4 | * [numbers.js, 面向 node.js 和JavaScript的高級數學庫](http://hant.helplib.com/GitHub/article_93511) 5 | 6 | ## 大整數 7 | 8 | * https://github.com/MikeMcl/decimal.js 9 | * https://github.com/MikeMcl/bignumber.js 10 | * https://github.com/MikeMcl/big.js 11 | 12 | ## 質數測試 13 | 14 | * https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test 15 | * https://github.com/indutny/miller-rabin/blob/master/lib/mr.js 16 | * https://github.com/indutny/primal 17 | 18 | ## 大數相乘 19 | 20 | * [Karatsuba算法](https://zh.wikipedia.org/wiki/Karatsuba%E7%AE%97%E6%B3%95) 21 | * https://gist.github.com/haocong/c2d9b2169d28eb15a94d 22 | * https://stackoverflow.com/questions/28372569/implementing-karatsuba-multiplication-in-javascript 23 | -------------------------------------------------------------------------------- /code/14-cryptography/gcd/gcd.js: -------------------------------------------------------------------------------- 1 | // Euclid's Algorithm 2 | // https://stackoverflow.com/questions/32042240/euclids-algorithm-javascript 3 | 4 | var gcd = function(a, b) { 5 | if (!b) return a 6 | return gcd(b, a % b) 7 | } 8 | 9 | console.log(gcd(462, 910)) -------------------------------------------------------------------------------- /code/14-cryptography/guid.js: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript 2 | function s4() { 3 | return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); 4 | } 5 | 6 | function guid() { 7 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); 8 | } 9 | 10 | console.log(guid()) -------------------------------------------------------------------------------- /code/14-cryptography/home.md: -------------------------------------------------------------------------------- 1 | # 密碼學 2 | 3 | | 主題 | 說明 | 4 | |:--------------------------------------|:---------------| 5 | | [加密技術](encrypt.html)| | 6 | | [破解密碼](hack.html)| | 7 | | [AES 對稱式加解密法](aes.html)| | 8 | | [RSA 非對稱型加解密演算法](rsa.html)| | 9 | | [比特幣挖礦的背後 -- SHA 安全雜湊演算法](hashcash.html)| | -------------------------------------------------------------------------------- /code/14-cryptography/methodList.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto') 2 | console.log('============== Hash ====================\n', crypto.getHashes()) // 印出所有 hash 算法 3 | console.log('============== Ciphers ====================\n', crypto.getCiphers())// 印出所有 cipher 算法 -------------------------------------------------------------------------------- /code/14-cryptography/mining.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | 3 | function coin(id, nonce) { 4 | var cert = 5 | ` 6 | $1 7 | ` 8 | return cert 9 | } 10 | 11 | function digest(str) { 12 | const secret = '' 13 | const hash = crypto.createHmac('sha256', secret) 14 | .update(str) 15 | .digest('hex') 16 | return hash 17 | } 18 | 19 | function mining(id, zeros) { 20 | let zeroStr = '00000000000000000000000' 21 | for (let nonce = 0; nonce < Number.MAX_SAFE_INTEGER; nonce++) { 22 | var co = coin(id, nonce) 23 | var dig = digest(co) 24 | if (dig.startsWith(zeroStr.substr(0, zeros))) return {nonce: nonce, digest: dig, coin: co} 25 | } 26 | } 27 | 28 | let t0 = Date.now() 29 | console.log(mining('0003979', 4)) 30 | let t1 = Date.now() 31 | console.log('t1-t0=%d', t1 - t0) 32 | console.log(mining('0003979', 5)) 33 | let t2 = Date.now() 34 | console.log('t2-t1=%d', t2 - t1) 35 | console.log(mining('0003979', 6)) 36 | let t3 = Date.now() 37 | console.log('t3-t2=%d', t3 - t2) 38 | -------------------------------------------------------------------------------- /code/14-cryptography/sign/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC0DCCAjmgAwIBAgIJAIiOsd9hNnwuMA0GCSqGSIb3DQEBCwUAMIGAMQswCQYD 3 | VQQGEwJUVzEPMA0GA1UECAwGS2lubWVuMQ8wDQYDVQQHDAZLaW5tZW4xDDAKBgNV 4 | BAoMA25xdTENMAsGA1UECwwEY3NpZTEQMA4GA1UEAwwHY2Nja21pdDEgMB4GCSqG 5 | SIb3DQEJARYRY2Nja21pdEBnbWFpbC5jb20wHhcNMTgxMjA3MDQwNDU5WhcNMTkw 6 | MTA2MDQwNDU5WjCBgDELMAkGA1UEBhMCVFcxDzANBgNVBAgMBktpbm1lbjEPMA0G 7 | A1UEBwwGS2lubWVuMQwwCgYDVQQKDANucXUxDTALBgNVBAsMBGNzaWUxEDAOBgNV 8 | BAMMB2NjY2ttaXQxIDAeBgkqhkiG9w0BCQEWEWNjY2ttaXRAZ21haWwuY29tMIGf 9 | MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEqfU9px64CK3iuBz3GugetMhO46R8 10 | TYqeHshxaJSHesj0dLTqNH+dQZLrNS6mWDcdDGuL0bwXNRj9yRRD1TAYa8kni5eT 11 | FXy0sf/9dxDjT3JcL7s1vDCeFBY2D/enVrq6e8KvDqlm19SowfkMyM2psd/vwJnK 12 | jBhj5VMMXRsL5QIDAQABo1AwTjAdBgNVHQ4EFgQUwKXjWASaLWZOAbIqiG/l3GMf 13 | uWAwHwYDVR0jBBgwFoAUwKXjWASaLWZOAbIqiG/l3GMfuWAwDAYDVR0TBAUwAwEB 14 | /zANBgkqhkiG9w0BAQsFAAOBgQCt0Hq+nIBZL0WRMccd60T96JpS0CZml6IVuMEM 15 | SgiYVJK5EaUPQBbZ5WNKVdqvNZImsR20QFG81/+Pwkv3U5IqKgcAP7j97bo3gyom 16 | IDl7yBVvb+MxGQG/ao3xY9LaMG9Q1UPzXWXVRVOT0oI/dTgc7mpcT/FNP/u8CYHn 17 | Hm9T+A== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /code/14-cryptography/sign/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDEqfU9px64CK3iuBz3GugetMhO46R8TYqeHshxaJSHesj0dLTq 3 | NH+dQZLrNS6mWDcdDGuL0bwXNRj9yRRD1TAYa8kni5eTFXy0sf/9dxDjT3JcL7s1 4 | vDCeFBY2D/enVrq6e8KvDqlm19SowfkMyM2psd/vwJnKjBhj5VMMXRsL5QIDAQAB 5 | AoGBAMOJHL5NEJz4fDOEzb/fL9V/9/2AF4ZD7sQiZTdlPPCrY2yzMUIAKrYngbV8 6 | 7kIUSdAHDlM5mKMWdx+2MwDy0dpn947Q1a4Znf0SW3D+F1D1vS1um1cZull8J2Et 7 | sz9Ms93kRCJAgO3xwCBaH4iFI7f//l2/0S+e5AnlBh14gWMBAkEA4RmsTdlJNsRJ 8 | nNxjWHJg5/u7mmCzTU2J5fyq+yCr8+HkvM3DHUpg8bI+ZIv8E4iEFhJyNZOXxXv5 9 | OtPRdTr8wQJBAN+o/fPSgo51wsA2r7m8bCvXJs4u+/fZQZQZFxXYBurJzSEzz4wj 10 | jVXY/LOJUXNveHd25PNRCPZIlxaBb6SBhCUCQGoymYbU12S+MFiP5VLKC4wGyJhp 11 | vM3gogleLjByVo2l6itlDANanugIhsUbAd8ZTe+33igGrNjfx1KIsk4TeUECQQCV 12 | WREK6TzIyBXdqCa7TfilZavB7lnObMJ/lCl4KqKj0VEqpKAooezUsGo9sKo0/PBi 13 | RYAogaoMWviYzKGyJdIJAkEApAKhmlOjYwll8riG+SbIySSVqlbF9wmH7twg9L5c 14 | AtVO7B3M7YvjofTDBtMH9Y4DcfXHEzaKQ8Rn2/fHUtHUSA== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /code/14-cryptography/sign/sign.js: -------------------------------------------------------------------------------- 1 | // openssl genrsa -out server.pem 1024 2 | // openssl req -key server.pem -new -x509 -out cert.pem 3 | 4 | var crypto = require('crypto') 5 | var fs = require('fs') 6 | 7 | function signer(algorithm, privateKey, data){ 8 | var signer = crypto.createSign(algorithm); 9 | signer.update(data); 10 | sigature = signer.sign(privateKey, 'hex'); 11 | return sigature 12 | } 13 | 14 | function verify(algorithm, publicKey, sigature, data){ 15 | var verify = crypto.createVerify(algorithm); 16 | verify.update(data); 17 | return verify.verify(publicKey, sigature, 'hex') 18 | } 19 | 20 | var algorithm = 'RSA-SHA256' 21 | var data = "The data to be signed!" 22 | var privateKey = fs.readFileSync('server.pem').toString() 23 | var signature = signer(algorithm, privateKey, data) 24 | 25 | var publicKey = fs.readFileSync('cert.pem').toString() 26 | console.log('verify(data)=', verify(algorithm, publicKey, sigature, data)) // 驗證是否為公鑰發出者 (私鑰擁有者) 27 | console.log('verify(data+xxx)=', verify(algorithm, publicKey, sigature, data + "xxx")) 28 | -------------------------------------------------------------------------------- /code/15-reduction/halt/halt.js: -------------------------------------------------------------------------------- 1 | function h(f, input) { 2 | let result = f(input) 3 | return true 4 | } 5 | 6 | function f1(n) { 7 | return n * n 8 | } 9 | 10 | function f2(n) { 11 | let s = 0 12 | for (let i=0; i var e2c = { dog:"狗", cat:"貓", a: "一隻", chase:"追", eat:"吃" }; 50 | undefined 51 | > e2c 52 | { dog: '狗', cat: '貓', a: '一隻', chase: '追', eat: '吃' } 53 | > e2c.eword 54 | undefined 55 | > var eword='dog' 56 | undefined 57 | > eword 58 | 'dog' 59 | > e2c.eword 60 | undefined 61 | > e2c['dog'] 62 | '狗' 63 | > e2c[eword] 64 | '狗' 65 | ``` 66 | 67 | 68 | ## 用查表加速 -- 以費氏數列為例 69 | 70 | 傳統用遞迴方式的費氏數列算法,會耗費很久的時間: 71 | 72 | ```js 73 | function fibonacci (n) { 74 | if (n < 0) throw Error('fibonacci:n < 0') 75 | if (n === 0) return 0 76 | if (n === 1) return 1 77 | return fibonacci(n - 1) + fibonacci(n - 2) 78 | } 79 | 80 | var startTime = Date.now() 81 | console.log('fibonacci(43)=', fibonacci(43)) 82 | var endTime = Date.now() 83 | var milliSeconds = endTime - startTime 84 | console.log('time:%dms', milliSeconds) 85 | 86 | ``` 87 | 88 | 執行結果: 89 | 90 | ``` 91 | $ node .\fiboanacci.js 92 | fibonacci(43)= 433494437 93 | time:25530ms 94 | ``` 95 | 96 | 加入查表,讓已經算過的就不需要算第二次,第二次之後改用查的! 97 | 98 | ```js 99 | var fib = [0, 1] 100 | 101 | function fibonacci (n) { 102 | if (n < 0) throw Error('fibonacci:n < 0') 103 | if (fib[n] != null) return fib[n] 104 | fib[n] = fibonacci(n - 1) + fibonacci(n - 2) 105 | return fib[n] 106 | } 107 | 108 | var startTime = Date.now() 109 | console.log('fibonacci(43)=', fibonacci(43)) 110 | var endTime = Date.now() 111 | var milliSeconds = endTime - startTime 112 | console.log('time:%dms', milliSeconds) 113 | 114 | ``` 115 | 116 | 執行結果 117 | 118 | ``` 119 | $ node .\fiboanacci_lookup.js 120 | fibonacci(43)= 433494437 121 | time:14ms 122 | ``` 123 | -------------------------------------------------------------------------------- /docs/md.bak/06-random.md: -------------------------------------------------------------------------------- 1 | # 第 6 章 - 亂數產生法 Random 2 | -------------------------------------------------------------------------------- /docs/md.bak/07-monteCarlo.md: -------------------------------------------------------------------------------- 1 | # 第 7 章 - 蒙地卡羅法 Monte-Carlo 2 | 3 | ## 蒙地卡羅演算法 (Monte Carlo Algorithm) 4 | 5 | 利用亂數隨機抽樣的方式以計算某種解答的演算法,被稱為蒙地卡羅演算法,其中最簡單的方法是直接取樣算法。 6 | 7 | 舉例而言,假如我們不知道半徑為 1 的圓形面積,那麼就可以利用亂數隨機取樣 1百萬個 X=random(-1...1), Y=random)(-1...1) 之間的的值,然後看看有多少點落在 x^2 + y^2 <=1 的範圍之內來計算 P(在圓之內)。最後利用 4 * P(在圓之內) 就可以計算出該圓形的面積。 8 | 9 | 10 | ## 貝氏網路 (Bayesian Network) 11 | 12 | 貝氏網路是用來描述機率因果關係的網路,對於一個已知的貝氏網路 (Bayesian Network),其中的某個樣本 $$(x_1, ..., x_n)$$ 的機率可以用下列算式表示 13 | 14 | $$P(x_1, ..., x_n) = \prod_{i=1}^{n} P(x_i | parent(X_i))$$ 15 | 16 | 貝氏網路也可以被視為某種隱馬可夫模型,其中某些節點是可觀察節點 (X),某些節點是隱含節點 (Z) ,我們可以透過蒙地卡羅馬可夫算法計算某個分布 $$P(x_1, ..., x_n)$$ 的機率值。 17 | 18 | ## 參考文獻 19 | 20 | * [A Brief Introduction to Graphical Models and Bayesian Networks](http://www.cs.ubc.ca/~murphyk/Bayes/bnintro.html) 21 | * [A brief introduction to Bayes' Rule](http://www.cs.ubc.ca/~murphyk/Bayes/bayesrule.html) 22 | * 23 | 24 | 25 | ## 簡介 26 | 27 | 在機率理論中,所謂的機率模型,通常是指某種機率獨立性的假設。舉例而言,在簡單貝氏模型 (Naive Bayes Model) 當中,就假設所有的隨機變數 X1, X2,..., Xn 相對於某個前提 C 而言都是條件獨立的,因此可以寫成如下算式。 28 | 29 | $$P(x_1 ... x_n| c) = P(x_1|c) \cdots P(x_n | c)$$ 30 | 31 | 這種機率獨立性的假設,就是一種統計上的假說,我們必須驗證這樣的假說是否合理,如果驗證合理才能使用該公式,否則將會造成龐大的誤差。 32 | 33 | ## 計算統計學中的假說 34 | 35 | 有時候,我們會將假說的概念 h 放入機率分布函數中,當成機率分布的參數之一,例如 P(x, h) 其實代表了由 h 假說所決定的一個機率特定機率分布 p,作用在樣本 x 上的結果 。 36 | 37 | 在具有假說 h 的情況之下,P(h) 代表由假說 h 所決定的一個機率分布,這是一個特定的機率分布,按照上述規則,原本應該用某個小寫的 p 所代表,但是由於引入了函數形式的關係,我們用 P(h) 代表該假說所決定的特定機率分布。 38 | 39 | 大寫的 P 符號通常則代表假說 $P(h_1), P(h_2), ... P(h_n)$ 所形成的機率分布集合,計算統計學的主要任務是找出最好的假說,以便用該假說的機率分布進行預測。這個尋找最佳假說的過程可用下列公式表達。 40 | 41 | $$\arg\max_h P(h|x,y) = \arg\max_h P(x,y|h) \frac{P(h)}{P(x,y)}$$ 42 | 43 | 計算統計學通常會用程式 (演算法) 尋找最符合訓練資料 $(x_1,y_1) (x_2,y_2) ...., (x_n,y_n)$ 的假說 P(h),這個過程稱為學習。當電腦完成學習的程序之後,就可以利用 P(h) 預測整個系統的下一個輸出之機率。 44 | 45 | 通常在預測進行時系統會取得某些輸入值 x,然後再利用該輸入值找到一個最可能的輸出值,也就是找到讓 P(y|x,h) 最大的輸出 y,因此整個預測程序仍然是一個最佳化的過程,如下列公式所示。 46 | 47 | $$\arg\max_y P(y|x,h)$$ 48 | 49 | ## 計算統計學中的學習 50 | 51 | 要找出計算統計學中的最佳假說,通常採用最大似然法則作為最佳化的目標算式,但實際上最大似然法則與最大商法則乃是一體的兩面,因此也常採用最大商法則進行學習。 52 | 53 | ### 『最大熵法則』 與 『最大似然法則』 54 | 55 | $$ 56 | \begin{aligned}\sum_z P(Z=z|x,h) L(x,Z=z|h) &= \sum_z \frac{P(x,Z=z,h)}{P(x,h)} \log P(x,Z=z|h) \\&= \frac{1}{P(x,h)} \sum_z P(x,Z=z,h) \log P(x,Z=z|h) \\&= \frac{1}{P(x,h)}H(x,Z|h)\end{aligned} 57 | $$ 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/md.bak/08-transformDomain.md: -------------------------------------------------------------------------------- 1 | # 第 8 章 - 轉換領域法 Transform Domain 2 | -------------------------------------------------------------------------------- /docs/md.bak/09-dynamicProgramming.md: -------------------------------------------------------------------------------- 1 | # 第 9 章 - 動態規劃法 Dynamic Programming 2 | 3 | -------------------------------------------------------------------------------- /docs/md.bak/10-divideConquer.md: -------------------------------------------------------------------------------- 1 | # 第 10 章 - 分割擊破法 Divide & Conquer 2 | 3 | ## 二分搜尋法 4 | 5 | 如果你曾經學過《演算法》, 應該曾經使用過《二分搜尋法》 6 | 7 | 對於一個《連續函數》而言, 假如我們知道兩個點 (a,b) ,其值 f(a)>0 且 f(b)<0 ,這樣的話勢必有一個介於 (a,b) 之間的 c 值使得 f(c)=0, 假如我們每次都取 ,然後判斷要繼續搜 尋哪一半的話,這樣我們就得到了一個《二分搜 尋法》,可以較快速的找出 f(x)=0 的解答! 8 | 9 | 其想法圖示如下: 10 | 11 | ![](https://cccbook.github.io/algjs/docs/img/binarySearch.png) 12 | 13 | 二分搜尋法求根的程式如下: 14 | 15 | 檔案: binarySearch.js 16 | 17 | ```javascript 18 | function f(x) { 19 | return x*x-4*x+1; 20 | } 21 | 22 | function bsolve(f,a,b) { 23 | var c = (a+b)/2; 24 | if (Math.abs(a-b) < 0.00001) 25 | return c; 26 | if (f(c)*f(a)>=0) 27 | return bsolve(f, c, b); 28 | else 29 | return bsolve(f, a, c); 30 | } 31 | 32 | var x=bsolve(f, 0, 1); 33 | console.log("x=", x, " f(x)=", f(x)); 34 | ``` 35 | 36 | 執行結果: 37 | 38 | ``` 39 | D:\Dropbox\gitbook\rlab\code\solveEquation>node binarySearch.js 40 | x= 0.2679481506347656 f(x)= 0.0000036088895285502076 41 | ``` 42 | 43 | 當然, 我們也可以改用另一種中間值的取法,像是用《線性內插法》在某些狀況下會更好! 44 | 45 | ![](https://cccbook.github.io/algjs/docs/img/linearInterpolation.png) 46 | 47 | 48 | 以上的這種搜尋法,不管是二分搜尋法,或者是線性內插法,速度通常都不會太慢! 49 | 50 | 如果您學過演算法中的 Big O 的複雜度概念,就會知道二分搜尋法的複雜度為 O(log n),只是在此問題中 n 應該改為兩個邊界值之間的差,也就是 (b-a),所以複雜度是 O(log b-a)。 51 | 52 | 但是、二分搜尋法求根的一個小問題,是必須要先找出一組 (a,b),滿足 f(a) 和 f(b) 兩者正負號相反。 53 | 54 | 而且二分搜尋法並不是找出所有的根,而是只找出一個根,這和暴力法找範圍內全部的根有所不同! 55 | 56 | 現在、我們已經學過兩個方法了, 而且這兩個方法都要先鎖定一個範圍,這種鎖定範圍的方法,稱為《界定法》 (Bracketing Method) 。 57 | 58 | 接著, 讓我們看看另外一類的方法, 這種方法不需要鎖定範圍,因此稱為《開放式方法》! 59 | -------------------------------------------------------------------------------- /docs/md.bak/11-hashing.md: -------------------------------------------------------------------------------- 1 | # 第 11 章 - 雜湊法 Hashing 2 | -------------------------------------------------------------------------------- /docs/md.bak/14-greedy.md: -------------------------------------------------------------------------------- 1 | # 第 14 章 - 貪婪演算法 Greedy Algorithm 2 | -------------------------------------------------------------------------------- /docs/md.bak/15-graph.md: -------------------------------------------------------------------------------- 1 | # 第 15 章 - 圖形演算法 Graph Algorithm 2 | 3 | ## 簡介 4 | 5 | 在離散數學、演算法與人工智慧的領域,很多問題可以表示為「節點與連線所形成的圖形」,一個程式要解決某問題其實是在這個圖形裏把目標節點給找出來,於是問題求解就簡化成了圖形的搜尋,我們只要把解答給「找出來」就行了。 6 | 7 | 圖形搜尋的方法大致可以分為「深度優先搜尋 (Depth-First Search, DFS)、廣度優先搜尋 (Breath-First Search, BFS)、最佳優先搜尋 (Best-First Search, BestFS) 等三類。 8 | 9 | 然後針對最佳優先搜尋的部份,還有一種具有理論背景,且較為強大好用的 A* 搜尋法可採用。 10 | 11 | ## 圖形的表達 12 | 13 | 圖形是由節點 (node) 與連線 (edge) 所組成的。舉例而言,以下是一個包含六個節點與十條連線的簡單圖形。 14 | 15 | ![圖、圖形 Graph 的範例](https://cccbook.github.io/algjs/docs/img/graphSearch.jpg) 16 | 17 | ## 深度優先搜尋 18 | 19 | 所謂的「深度優先搜尋」 (Depth-First Search, DFS),就是一直往尚未訪問過的第一個鄰居節點走去的一種方法,這種方法可以採用程式設計中的「遞迴技巧」完成,以下是深度搜尋的演算法: 20 | 21 | ``` 22 | Algorithm DFS(graph, node) { // 深度優先搜尋,graph : 圖形, node:節點 23 | if (node.visited) return; // 如果已訪問過,就不再訪問 24 | node.visited = 1; // 並設定為已訪問 25 | foreach (neighbor of node) // 對於每個鄰居 26 | DFS(graph, neighbor); // 逐一進行深度優先搜尋的訪問。 27 | end 28 | ``` 29 | 30 | 您可以看到上述的演算法中,我們單純採用遞迴的方式,就可以輕易的完成整個 DFS 演算法。 31 | 32 | 當然、實作為程式的時候,會稍微複雜一點,以下是使用 Javascript 的實作方式: 33 | 34 | ```javascript 35 | function dfs(g, node) { // 深度優先搜尋 36 | if (g[node].v !=0) return; // 如果已訪問過,就不再訪問 37 | printf("%d=>", node); // 否則、印出節點 38 | g[node].v = 1; // 並設定為已訪問 39 | var neighbors = g[node].n; // 取出鄰居節點 40 | for (var i in neighbors) { // 對於每個鄰居 41 | dfs(g, neighbors[i]); // 逐一進行訪問 42 | } 43 | } 44 | ``` 45 | 46 | 針對上述的範例圖形,若採用深度優先搜尋,其結果可能如下所示 (圖中紅色的數字代表訪問順序) 47 | 48 | ![圖、深度優先搜尋的順序](https://cccbook.github.io/algjs/docs/img/dfs.jpg) 49 | 50 | ## 廣度優先搜尋 51 | 52 | 雖然深度優先搜尋可以搜尋整個圖形,但是卻很可能繞了很久才找到目標,於是從起點到目標可能會花費很久的時間 (或說路徑長度過長)。 53 | 54 | 如果我們想找出到達目標最少的步驟,那麼就可以採用「廣度優先搜尋」 (Breath-First Search, BFS) 的方式。 55 | 56 | 廣度優先搜尋 BFS 是從一個節點開始,將每個鄰居節點都一層一層的拜訪下去,深度最淺的節點會優先被拜訪的方式。 57 | 58 | 舉例而言,針對上述的圖形範例,若採用「廣度優先搜尋 BFS 」的方式,那麼拜訪順序將會如下所示: 59 | 60 | ![圖、廣度優先搜尋的順序](https://cccbook.github.io/algjs/docs/img/bfs.jpg) 61 | 62 | 要能用程式進行廣度優先搜尋,必須採用「先進先出」(First-in First-Out, FIFO) 的方式管理節點,因此通常在「廣度優先搜尋」裏會有個佇列 (queue) 結構,以下是 BFS 的演算法: 63 | 64 | ``` 65 | Algorithm BFS(graph, queue) 66 | if queue.empty() return; 67 | node = queue.dequeue(); 68 | if (!node.visited) 69 | node.visited = true 70 | else 71 | return; 72 | foreach (neighbor of node) 73 | if (!neighbor.visited) 74 | queue.push(neighbor) 75 | end 76 | ``` 77 | 78 | 以下是使用 Javascript 的 BFS 程式實作片段: 79 | 80 | ```javascript 81 | function bfs(g, q) { // 廣度優先搜尋 82 | if (q.length == 0) return; // 如果 queue 已空,則返回。 83 | var node = dequeue(q); // 否則、取出 queue 的第一個節點。 84 | if (g[node].v == 0) // 如果該節點尚未拜訪過。 85 | g[node].v = 1; // 標示為已拜訪 86 | else // 否則 (已訪問過) 87 | return; // 不繼續搜尋,直接返回。 88 | printf("%d=>", node); // 印出節點 89 | var neighbors = g[node].n; // 取出鄰居。 90 | for (var i in neighbors) { // 對於每個鄰居 91 | var n = neighbors[i]; 92 | if (!g[n].visited) // 假如該鄰居還沒被拜訪過 93 | q.push(n); // 就放入 queue 中 94 | } 95 | bfs(g, q); 96 | } 97 | ``` 98 | 99 | ## 最佳優先搜尋 100 | 101 | 但是、上述兩個方法其實都還不夠好,深度搜尋會猛衝亂衝,而廣度搜尋則會耗費太多的記憶體,並且沒有效率,無法很快的找到目標點。 102 | 103 | 假如我們能夠知道哪些點距離目標點最近,也就是哪些點比較好的話,那就能採用「最佳優先搜尋 (Best-First Search) 的方式來搜尋了。 104 | 105 | 最佳優先搜尋的實作方法與廣度優先搜尋類似,但是並不採用佇列 (queue) ,而是採用一種根據優先程度排序的結構,每次都取出最好的那個繼續進行搜尋。 106 | 107 | 但是、節點的好壞通常很難評估,單純採用某種距離去評估往往會過度簡化問題,這點往往是最佳優先搜尋的困難之所在。 108 | 109 | 還好、有時我們不需要非常精確的評估,只要問題符合 $$h(x) \leq d(x,y)+h(y)$$ 這樣的單調 (monotone) 特性,就可以使用 `A*` 演算法來進行較快速的搜尋,這種方法比廣度優先搜尋通常快很多,因為 `A*` 不會搜尋所有節點,而是有系統的朝著整體較好的方向前進,這種方法在電腦遊戲 (Game) 上常被用在 NPC (非人類角色) 的智慧型搜尋行為設計上面,是人工智慧搜尋方法中較強大的一種。 110 | 111 | ## 參考文獻 112 | * [Wikipedia:A* search algorithm](http://en.wikipedia.org/wiki/A*_search_algorithm) 113 | * [維基百科:A*搜尋演算法](http://zh.wikipedia.org/wiki/A*%E6%90%9C%E5%AF%BB%E7%AE%97%E6%B3%95) 114 | * [維基百科:廣度優先搜索](http://zh.wikipedia.org/zh-tw/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2) 115 | * [維基百科:深度優先搜索](http://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2) 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/md.bak/21-slotFilling.md: -------------------------------------------------------------------------------- 1 | # 第 21 章 - 欄位填充法 Slot-Filling 2 | -------------------------------------------------------------------------------- /docs/md.bak/22-cryptography.md: -------------------------------------------------------------------------------- 1 | # 第 22 章 - 密碼學算法 Cryptography 2 | -------------------------------------------------------------------------------- /docs/md.bak/23-reduction.md: -------------------------------------------------------------------------------- 1 | # 第 23 章 - 轉換化約法 Reduction 2 | -------------------------------------------------------------------------------- /docs/md.bak/README.md: -------------------------------------------------------------------------------- 1 | # 書籍:專業程序員一定要會的 23 種算法 2 | 3 | 1. [查表法](01-tableLookup.md) 4 | 2. [爬山演算法](02-hillClimbing.md) 5 | 3. [梯度下降法](https://cccbook.github.io/algjs/docs/html/03-gradientDescendent.html) 6 | 4. [反傳遞算法](https://cccbook.github.io/algjs/docs/html/04-backPropagation.html) 7 | 5. [遺傳演算法](05-geneticAlgorithm.md) 8 | 6. [亂數產生法](06-random.md) 9 | 7. [蒙地卡羅法](07-monteCarlo.md) 10 | 8. [轉換領域法](08-transformDomain.md) 11 | 9. [動態規劃法](09-dynamicProgramming.md) 12 | 10. [分割擊破法](10-divideConquer.md) 13 | 11. [雜湊法](11-hashing.md) 14 | 12. [暴力法](12-bruteForce.md) 15 | 13. [數值算法](13-numerical.md) 16 | 14. [貪婪法](14-greedy.md) 17 | 15. [圖形表示法](15-graph.md) 18 | 16. [搜尋法](16-search.md) 19 | 17. [對局搜尋法](17-gameSearch.md) 20 | 18. [迭代法](18-iterative.md) 21 | 19. [遞迴下降法](19-recursiveDescendent.md) 22 | 20. [字串比對法](20-stringMatching.md) 23 | 21. [框架填充法](21-slotFilling.md) 24 | 22. [密碼學算法](22-cryptography.md) 25 | 23. [轉換化約法](23-reduction.md) -------------------------------------------------------------------------------- /docs/md.bak/SUMMARY.md: -------------------------------------------------------------------------------- 1 | --- 2 | ebook: 3 | title: 深度學習背後的算法 -- 以 node.js 為例 4 | authors: 陳鍾誠 5 | --- 6 | 7 | # 深度學習背後的算法 -- 以 node.js 為例 8 | 9 | > 作者: 陳鍾誠 10 | 11 | # 目錄 12 | 13 | * [梯度下降法](gradient.md) 14 | * [反傳遞算法](backprop.md) 15 | 16 | -------------------------------------------------------------------------------- /docs/md.bak/_Footer.md: -------------------------------------------------------------------------------- 1 | [陳鍾誠](http://www.nqu.edu.tw/educsie/index.php?act=blog&code=list&ids=4) 於 [金門大學](http://www.nqu.edu.tw/) [資訊工程系](http://www.nqu.edu.tw/educsie/index.php) -- 本書衍生自 [維基百科](https://www.wikipedia.org/) ,採用 [CC: BY-SA](https://zh.wikipedia.org/zh-hant/Wikipedia%3ACC_BY-SA_3.0%E5%8D%8F%E8%AE%AE%E6%96%87%E6%9C%AC) 授權 -------------------------------------------------------------------------------- /docs/md.bak/_Sidebar.md: -------------------------------------------------------------------------------- 1 | # 程序員 23 種算法 2 | 3 | 1. [查表法](01-tableLookup) 4 | 2. [爬山演算法](02-hillClimbing) 5 | 3. [梯度下降法](https://cccbook.github.io/algjs/docs/html/03-gradientDescendent.html) 6 | 4. [反傳遞算法](https://cccbook.github.io/algjs/docs/html/04-backPropagation.html) 7 | 5. [遺傳演算法](05-geneticAlgorithm) 8 | 6. [亂數產生法](06-random) 9 | 7. [蒙地卡羅法](07-monteCarlo) 10 | 8. [轉換領域法](08-transformDomain) 11 | 9. [動態規劃法](09-dynamicProgramming) 12 | 10. [分割擊破法](10-divideConquer) 13 | 11. [雜湊法](11-hashing) 14 | 12. [暴力法](12-bruteForce) 15 | 13. [數值算法](13-numerical) 16 | 14. [貪婪法](14-greedy) 17 | 15. [圖形表示法](15-graph) 18 | 16. [搜尋法](16-search) 19 | 17. [對局搜尋法](17-gameSearch) 20 | 18. [迭代法](18-iterative) 21 | 19. [遞迴下降法](19-recursiveDescendent) 22 | 20. [字串比對法](20-stringMatching) 23 | 21. [框架填充法](21-slotFilling) 24 | 22. [加密演算法](22-cryptography) 25 | 23. [轉換化約法](23-reduction) 26 | -------------------------------------------------------------------------------- /docs/md.bak/book.md: -------------------------------------------------------------------------------- 1 | --- 2 | ebook: 3 | title: 專業程序員一定要會的 23 種算法 -- 以 node.js 實作 4 | authors: 陳鍾誠 5 | --- 6 | 7 | # 專業程序員一定要會的 23 種算法 -- 以 node.js 實作 8 | 9 | > 作者:陳鍾誠 10 | 11 | # 前言 12 | 13 | ``` 14 | 我想 JavaScript 應該還可以活過 2050 年,直到我八十歲的時候 .... 15 | 16 | 所以我決定把畢生所學精華,全部寫成一本書。 17 | 18 | 然後用 JavaScript 實作範例 .... 19 | 20 | 書名就叫《專業程序員一定要會的 23 種算法》好了 ..... 21 | 22 | 我承認自己是學《一生一定要去的 108 種地方》來命名的 .... 23 | 24 | 這本書就不《免費公開》了,決定發行《電子書+紙本》兩種版本 .... 25 | 26 | -- 陳鍾誠 於 金門大學 2018/11/07 27 | ``` 28 | 29 | # 目錄 30 | 31 | * [01 - 暴力法](01-bruteForce.md) 32 | * [02 - 爬山演算法](02-hillClimbing.md) 33 | * [03 - 梯度下降法](03-gradientDescendent.md) 34 | * [04 - 反傳遞算法](04-backPropagation.md) 35 | * [05 - 遺傳演算法](05-geneticAlgorithm.md) 36 | * [06 - 亂數產生法](06-random.md) 37 | * [07 - 蒙地卡羅法](07-monteCarlo.md) 38 | * [08 - 動態規劃法](08-dynamicProgramming.md) 39 | * [09 - 轉換領域法](09-transformDomain.md) 40 | * [10 - 分割擊破法](10-divideConquer.md) 41 | * [11 - 雜湊法](11-hashing.md) 42 | * [12 - 查表法](12-tableLookup.md) 43 | * [13 - 數值算法](13-numerical.md) 44 | * [14 - 貪婪法](14-greedy.md) 45 | * [15 - 圖形表示法](15-graph.md) 46 | * [16 - 搜尋法](16-search.md) 47 | * [17 - 對局搜尋法](17-gameSearch.md) 48 | * [18 - 迭代法](18-iterative.md) 49 | * [19 - 遞迴下降法](19-recursiveDescendent.md) 50 | * [20 - 字串比對法](20-stringMatching.md) 51 | * [21 - 欄位填充法](21-slotFilling.md) 52 | * [22 - 密碼學算法](22-cryptography.md) 53 | * [23 - 轉換化約法](23-reduction.md) 54 | -------------------------------------------------------------------------------- /docs/md.bak/deepAlgorithm.md: -------------------------------------------------------------------------------- 1 | --- 2 | ebook: 3 | title: 深度學習背後的算法 -- 以 node.js 為例 4 | authors: 陳鍾誠 5 | --- 6 | 7 | # 深度學習背後的算法 -- 以 node.js 為例 8 | 9 | > 作者: 陳鍾誠 10 | 11 | # 目錄 12 | 13 | * [梯度下降法](gradient.md) 14 | * [反傳遞算法](backprop.md) -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "algjs", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "crypto-js": { 8 | "version": "3.1.9-1", 9 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", 10 | "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "algjs", 3 | "version": "0.0.1", 4 | "description": "Algorithm in JS", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "test": "mocha" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ccckmit/aljs.git" 15 | }, 16 | "keywords": [ 17 | "Algorithm" 18 | ], 19 | "author": "ccc", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/ccckmit/aljs/issues" 23 | }, 24 | "homepage": "https://github.com/ccckmit/aljs#readme", 25 | "dependencies": { 26 | "crypto-js": "^3.1.9-1" 27 | } 28 | } 29 | --------------------------------------------------------------------------------