Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 一级特黄aa大片免费,国产日韩欧美,91av国产在线

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          一文帶你理解Q-Learning的搜索策略,掌握強(qiáng)化

          一文帶你理解Q-Learning的搜索策略,掌握強(qiáng)化學(xué)習(xí)最常用算法

          小新 編譯自 Medium

          量子位 出品 | 公眾號(hào) QbitAI

          Q-Learning是強(qiáng)化學(xué)習(xí)中最常用的算法之一。

          Medium上有篇文章,討論了這種算法的一個(gè)重要部分:搜索策略。

          量子位搬運(yùn)過來,以下為博客譯文:

          我們先介紹下有關(guān)概念和符號(hào)。

          強(qiáng)化學(xué)習(xí)

          強(qiáng)化學(xué)習(xí)(Reinforcement Learning, RL)屬于機(jī)器學(xué)習(xí)的一個(gè)分支,利用智能體(agent)通過狀態(tài)感知、選擇動(dòng)作和接收獎(jiǎng)勵(lì)來與環(huán)境互動(dòng)。每一步中,智能體都會(huì)通過觀察環(huán)境狀態(tài),選擇并執(zhí)行一個(gè)動(dòng)作,來改變其狀態(tài)并獲得獎(jiǎng)勵(lì)。

          馬爾可夫決策過程

          在傳統(tǒng)環(huán)境中,馬爾可夫決策過程(Markov Decision Processes, MDP)可以解決不少RL問題。這里,我們不會(huì)深入討論MDP的理論,有關(guān)MDP算法的更多內(nèi)容可參考:

          https://en.wikipedia.org/wiki/Markov_decision_process

          我們用森林火災(zāi)來解釋下MDP算法,代碼實(shí)現(xiàn)可使用python MDP Toolbox:

          http://pymdptoolbox.readthedocs.io/en/latest/api/example.html

          森林管理包括兩個(gè)動(dòng)作,等待和砍伐。每年要做出一個(gè)決定,一是為林中動(dòng)物保持古老森林,二是砍伐木材來而賺錢。而且,每年有p概率發(fā)生森林火災(zāi),有1-p的概率為森林生長(zhǎng)。

          先定義MDP算法中一些參數(shù)S、A、P、R和γ,其中:

          • S是狀態(tài)空間(有限),包括3種不同年齡樹木,年齡級(jí)分別為0-20年、21-40年和40年以上;

          • A是動(dòng)作空間(有限),即等待或砍伐;

          • P和R分別是轉(zhuǎn)移矩陣和獎(jiǎng)勵(lì)矩陣,很容易寫出它的閉合形式;

          • γ是數(shù)值在0與1之間的貼現(xiàn)因子,用來平衡短時(shí)和未來獎(jiǎng)勵(lì)的關(guān)系;

          • 策略π是當(dāng)前狀態(tài)下決策的靜態(tài)分布;

          該模型的目標(biāo)是在未給出MDP動(dòng)態(tài)知識(shí)的情況下找到一個(gè)最優(yōu)策略π*。

          要注意,如果具有這個(gè)動(dòng)態(tài)知識(shí),直接用最優(yōu)值迭代方法就能找到最優(yōu)策略。

           1def optimal_value_iteration(mdp, V0, num_iterations, epsilon=0.0001):2 V=np.zeros((num_iterations+1, mdp.S))3 V[0][:]=np.ones(mdp.S)*V04 X=np.zeros((num_iterations+1, mdp.A, mdp.S))5 star=np.zeros((num_iterations+1,mdp.S))6 for k in range(num_iterations):7 for s in range(mdp.S):8 for a in range(mdp.A):9 X[k+1][a][s]=mdp.R[a][s] + mdp.discount*np.sum(mdp.P[a][s].dot(V[k]))10 star[k+1][s]=(np.argmax(X[k+1,:,s]))11 V[k+1][s]=np.max(X[k+1,:,s])12 if (np.max(V[k+1]-V[k])-np.min(V[k+1]-V[k])) < epsilon:13 V[k+1:]=V[k+1]14 star[k+1:]=star[k+1]15 X[k+1:]=X[k+1]16 break17 else: pass18 return star, V, X

          獎(jiǎng)勵(lì)變化曲線

          最優(yōu)策略是等到森林處于古老且茂盛的狀態(tài)時(shí)進(jìn)行砍伐,這容易理解,因?yàn)樵谏痔幱谧罟爬系臓顟B(tài)時(shí)砍伐的獎(jiǎng)勵(lì)是等待讓森林生長(zhǎng)的獎(jiǎng)勵(lì)的5倍,有r1=10,r2=50。

          Q-Learning算法

          Q-Learning算法中的“Q”代表著策略π的質(zhì)量函數(shù)(Quality function),該函數(shù)能在觀察狀態(tài)s確定動(dòng)作a后,把每個(gè)狀態(tài)動(dòng)作對(duì) (s, a) 與總期望的折扣未來獎(jiǎng)勵(lì)進(jìn)行映射。

          Q-Learning算法屬于model-free型,這意味著它不會(huì)對(duì)MDP動(dòng)態(tài)知識(shí)進(jìn)行建模,而是直接估計(jì)每個(gè)狀態(tài)下每個(gè)動(dòng)作的Q值。然后,通過在每個(gè)狀態(tài)下選擇具有最高Q值的動(dòng)作,來繪制相應(yīng)的策略。

          如果智能體不斷地訪問所有狀態(tài)動(dòng)作對(duì),則Q-Learning算法會(huì)收斂到最優(yōu)Q函數(shù)[1]。

          下面我們給出關(guān)于Q-Learning算法的Python實(shí)現(xiàn)。

          要注意,這里的學(xué)習(xí)率α是w=4/5時(shí)的多項(xiàng)式,這里使用了引用[3]的結(jié)果。

          這里使用的ε-greedy搜索策略,后面會(huì)詳細(xì)介紹。

           1def q_learning(mdp, num_episodes, T_max, epsilon=0.01):2 Q=np.zeros((mdp.S, mdp.A))3 episode_rewards=np.zeros(num_episodes)4 policy=np.ones(mdp.S)5 V=np.zeros((num_episodes, mdp.S))6 N=np.zeros((mdp.S, mdp.A))7 for i_episode in range(num_episodes):8 # epsilon greedy exploration9 greedy_probs=epsilon_greedy_exploration(Q, epsilon, mdp.A)10 state=np.random.choice(np.arange(mdp.S))11 for t in range(T_max):12 # epsilon greedy exploration13 action_probs=greedy_probs(state)14 action=np.random.choice(np.arange(len(action_probs)), p=action_probs)15 next_state, reward=playtransition(mdp, state, action)16 episode_rewards[i_episode] +=reward17 N[state, action] +=118 alpha=1/(t+1)**0.819 best_next_action=np.argmax(Q[next_state]) 20 td_target=reward + mdp.discount * Q[next_state][best_next_action]21 td_delta=td_target - Q[state][action]22 Q[state][action] +=alpha * td_delta23 state=next_state24 V[i_episode,:]=Q.max(axis=1)25 policy=Q.argmax(axis=1)
          26 return V, policy, episode_rewards, N

          獎(jiǎng)勵(lì)變化曲線

          探索與利用的平衡

          序列學(xué)習(xí)算法會(huì)涉及到一個(gè)基本選擇:

          • 利用:根據(jù)當(dāng)前信息做出最佳決策;

          • 探索:做出其他決策來收集更多信息。

          合理平衡好探索和利用的關(guān)系,對(duì)智能體的學(xué)習(xí)能力有重大影響。過多的探索會(huì)阻礙智能體最大限度地獲得短期獎(jiǎng)勵(lì),因?yàn)檫x擇繼續(xù)探索可能獲得較低的環(huán)境獎(jiǎng)勵(lì)。另一方面,由于選擇的利用動(dòng)作可能不是最優(yōu)的,因此靠不完全知識(shí)來利用環(huán)境會(huì)阻礙長(zhǎng)期獎(jiǎng)勵(lì)的最大化。

          ε-greedy搜索策略

          該策略在每一步利用概率ε來選擇隨機(jī)動(dòng)作。

          這可能是最常用也是最簡(jiǎn)單的搜索策略,即用ε調(diào)整探索動(dòng)作。在許多實(shí)現(xiàn)中,ε會(huì)隨著時(shí)間不斷衰減,但也有不少情況,ε會(huì)被設(shè)置為常數(shù)。

          1def epsilon_greedy_exploration(Q, epsilon, num_actions):2 def policy_exp(state):3 probs=np.ones(num_actions, dtype=float) * epsilon / num_actions4 best_action=np.argmax(Q[state])5 probs[best_action] +=(1.0 - epsilon)6 return probs7 return policy_exp

          不確定優(yōu)先搜索策略

          不確定優(yōu)先(Optimism in Face of Uncertainty)搜索策略,最開始被用來解決隨機(jī)式多臂賭博機(jī)問題 (Stochastic Multi-Armed Bandit),這是一個(gè)很經(jīng)典的決策問題,賭徒要轉(zhuǎn)動(dòng)一個(gè)擁有n個(gè)槽的老虎機(jī),轉(zhuǎn)動(dòng)每個(gè)槽都有固定回報(bào)概率,目標(biāo)是找到回報(bào)概率最高的槽并且不斷地選擇它來獲取最高的回報(bào)。

          賭徒面臨著利用還是探索的問題,利用機(jī)器獲得最高的平均獎(jiǎng)勵(lì)或探索其他未玩過的機(jī)器,以期望獲得更高的獎(jiǎng)勵(lì)。

          這個(gè)問題與Q-Learning算法中的探索問題非常相似:

          • 利用:在給定狀態(tài)下選擇具有最高Q值的動(dòng)作;

          • 探索:做出其他決策來探索更多信息,通過選擇不了解或不夠了解的環(huán)境。

          不確定優(yōu)先狀態(tài):只要我們對(duì)某個(gè)槽的回報(bào)不確定時(shí)不確定手臂的結(jié)果,我們就會(huì)考慮當(dāng)前環(huán)境來選擇最佳的手臂。

          不確定優(yōu)先算法有兩方面:

          • 若當(dāng)前處于最佳環(huán)境,那算法會(huì)直接選擇最佳的手臂;

          • 若當(dāng)前不處于最佳環(huán)境,則算法會(huì)盡量降低不確定性。

          置信區(qū)間上界(Upper Confidence Bound, UCB)是一種常用的不確定優(yōu)先算法[2],我們把它結(jié)合到Q-Learning方法中,有:

          • Q(s, a):狀態(tài)s下動(dòng)作a縮放后的Q值;

          • N(t,s,a):在時(shí)刻t和狀態(tài)s下動(dòng)作a被選擇的次數(shù)。

          此時(shí),智能體的目標(biāo)為Argmax {Q(s, a)/ a ∈ A},這意味著在狀態(tài)s中選擇具有最高Q值的動(dòng)作。但是在t時(shí)刻Q(s,a)值是未知的。

          在t時(shí)刻,Q估計(jì)值為Q(t, s, a),則有Q(s,a)=+ (Q(s,a) ? )。

          (Q(s,a) ? )為相應(yīng)誤差項(xiàng)。

          霍夫不等式 (Hoeffding’s inequality)可用來處理這類誤差。事實(shí)上,當(dāng)t變化時(shí),有:

          優(yōu)先策略可寫成:Argmax {Q+(t, s, a)/ a ∈ A},且有:

          當(dāng)β大于0時(shí),執(zhí)行探索動(dòng)作;當(dāng)β為0時(shí),僅利用已有估計(jì)。

          這種界限方法是目前最常用的,基于這種界限后面也有許多改進(jìn)工作,包括UCB-V,UCB*,KL-UCB,Bayes-UCB和BESA[4]等。

          下面給出經(jīng)典UCB算法的Python實(shí)現(xiàn),及其在Q-Learning上的應(yīng)用效果。

          1def UCB_exploration(Q, num_actions, beta=1):2 def UCB_exp(state, N, t):3 probs=np.zeros(num_actions, dtype=float)4 Q_=Q[state,:]/max(Q[state,:]) + np.sqrt(beta*np.log(t+1)/(2*N[state]))5 best_action=Q_.argmax()6 probs[best_action]=17 return probs8 return UCB_exp

          獎(jiǎng)勵(lì)變化曲線

          UCB搜索算法應(yīng)該能很快地獲得高額獎(jiǎng)勵(lì),但是前期搜索對(duì)訓(xùn)練過程的影響較大,有希望用來解決更復(fù)雜的多臂賭博機(jī)問題,因?yàn)檫@種方法能幫助智能體跳出局部最優(yōu)值。

          下面是兩種策略的對(duì)比圖。

          總結(jié)與展望

          Q-Learning是強(qiáng)化學(xué)習(xí)中最常用的算法之一。在這篇文章中,我們討論了搜索策略的重要性和如何用UCB搜索策略來替代經(jīng)典的ε-greedy搜索算法。

          更多更細(xì)致的優(yōu)先策略可以被用到Q-Learning算法中,以平衡好利用和探索的關(guān)系。

          參考文獻(xiàn)

          [1] T. Jaakkola, M. I. Jordan, and S. P. Singh, “On the convergence of stochastic iterative dynamic programming algorithms” Neural computation, vol. 6, no. 6, pp. 1185–1201, 1994.

          [2] P. Auer, “Using Confidence Bounds for Exploitation-Exploration Trade-offs”, Journal of Machine Learning Research 3 397–422, 2002.

          [3] E. Even-Dar, and Y. Mansour, “Learning Rates for Q-learning”, Journal of Machine Learning Research 5 1–25, 2003.

          [4] A. Baransi, O.-A. Maillard, and S. Mannor, “Sub-sampling for multi-armed bandits”, Joint European Conference on Machine Learning and Knowledge Discovery in Databases, 115–131, 2014.

          原文:https://medium.com/sequential-learning/optimistic-q-learning-b9304d079e11

          — 完 —

          誠(chéng)摯招聘

          量子位正在招募編輯/記者,工作地點(diǎn)在北京中關(guān)村。期待有才氣、有熱情的同學(xué)加入我們!相關(guān)細(xì)節(jié),請(qǐng)?jiān)诹孔游还娞?hào)(QbitAI)對(duì)話界面,回復(fù)“招聘”兩個(gè)字。

          量子位 QbitAI · 頭條號(hào)簽約作者

          ?'?' ? 追蹤AI技術(shù)和產(chǎn)品新動(dòng)態(tài)

          礎(chǔ)算法

          一、排序

          1. 冒泡排序
          //冒泡排序
          function bubbleSort(arr) {
           for(var i=1, len=arr.length; i < len - 1; ++i) {
           for(var j=0; j <=len - i; ++j) {
           if (arr[j] > arr[j + 1]) {
           let temp=arr[j];
           arr[j]=arr[j + 1];
           arr[j + 1]=temp;
           }
           }
           }
          }
          
          1. 插入排序
          //插入排序 過程就像你拿到一副撲克牌然后對(duì)它排序一樣
          function insertionSort(arr) {
           var n=arr.length;
           // 我們認(rèn)為arr[0]已經(jīng)被排序,所以i從1開始
           for (var i=1; i < n; i++) {
           // 取出下一個(gè)新元素,在已排序的元素序列中從后向前掃描來與該新元素比較大小
           for (var j=i - 1; j >=0; j--) {
           if (arr[i] >=arr[j]) { // 若要從大到小排序,則將該行改為if (arr[i] <=arr[j])即可
           // 如果新元素arr[i] 大于等于 已排序的元素序列的arr[j],
           // 則將arr[i]插入到arr[j]的下一位置,保持序列從小到大的順序
           arr.splice(j + 1, 0, arr.splice(i, 1)[0]);
           // 由于序列是從小到大并從后向前掃描的,所以不必再比較下標(biāo)小于j的值比arr[j]小的值,退出循環(huán)
           break; 
           } else if (j===0) {
           // arr[j]比已排序序列的元素都要小,將它插入到序列最前面
           arr.splice(j, 0, arr.splice(i, 1)[0]);
           }
           }
           }
           return arr;
          }
          
          1. 當(dāng)目標(biāo)是升序排序,最好情況是序列本來已經(jīng)是升序排序,那么只需比較n-1次,時(shí)間復(fù)雜度O(n)。最壞情況是序列本來是降序排序,那么需比較n(n-1)/2次,時(shí)間復(fù)雜度O(n^2)。所以平均來說,插入排序的時(shí)間復(fù)雜度是O(n^2)。顯然,次方級(jí)別的時(shí)間復(fù)雜度代表著插入排序不適合數(shù)據(jù)特別多的情況,一般來說插入排序適合小數(shù)據(jù)量的排序
          2. 快速排序
          //快速排序
          function qSort(arr) {
           //聲明并初始化左邊的數(shù)組和右邊的數(shù)組
           var left=[], right=[];
           //使用數(shù)組第一個(gè)元素作為基準(zhǔn)值
           var base=arr[0];
           //當(dāng)數(shù)組長(zhǎng)度只有1或者為空時(shí),直接返回?cái)?shù)組,不需要排序
           if(arr.length <=1) return arr;
           //進(jìn)行遍歷
           for(var i=1, len=arr.length; i < len; i++) {
           if(arr[i] <=base) {
           //如果小于基準(zhǔn)值,push到左邊的數(shù)組
           left.push(arr[i]);
           } else {
           //如果大于基準(zhǔn)值,push到右邊的數(shù)組
           right.push(arr[i]);
           }
           }
           //遞歸并且合并數(shù)組元素
           return [...qSort(left), ...[base], ...qSort(right)]; //return qSort(left).concat([base], qSort(right));
          }
          

          補(bǔ)充:

          在這段代碼中,我們可以看到,這段代碼實(shí)現(xiàn)了通過pivot區(qū)分左右部分,然后遞歸的在左右部分繼續(xù)取pivot排序,實(shí)現(xiàn)了快速排序的文本描述,也就是說該的算法實(shí)現(xiàn)本質(zhì)是沒有問題的。

          雖然這種實(shí)現(xiàn)方式非常的易于理解。不過該實(shí)現(xiàn)也是有可以改進(jìn)的空間,在這種實(shí)現(xiàn)中,我們發(fā)現(xiàn)在函數(shù)內(nèi)定義了left/right兩個(gè)數(shù)組存放臨時(shí)數(shù)據(jù)。隨著遞歸的次數(shù)增多,會(huì)定義并存放越來越多的臨時(shí)數(shù)據(jù),需要Ω(n)的額外儲(chǔ)存空間。

          因此,像很多算法介紹中,都使用了原地(in-place)分區(qū)的版本去實(shí)現(xiàn)快速排序,我們先介紹什么是原地分區(qū)算法

          原地(in-place)分區(qū)算法描述

          1. 從數(shù)列中挑出一個(gè)元素,稱為"基準(zhǔn)"(pivot),數(shù)組第一個(gè)元素的位置作為索引。
          2. 遍歷數(shù)組,當(dāng)數(shù)組數(shù)字小于或者等于基準(zhǔn)值,則將索引位置上的數(shù)與該數(shù)字進(jìn)行交換,同時(shí)索引+1
          3. 將基準(zhǔn)值與當(dāng)前索引位置進(jìn)行交換

          通過以上3個(gè)步驟,就將以基準(zhǔn)值為中心,數(shù)組的左右兩側(cè)數(shù)字分別比基準(zhǔn)值小或者大了。這個(gè)時(shí)候在遞歸的原地分區(qū),就可以得到已排序后的數(shù)組。

          原地分區(qū)算法實(shí)現(xiàn)

          // 交換數(shù)組元素位置
          function swap(array, i, j) {
           var temp=array[i];
           array[i]=array[j];
           array[j]=temp;
          }
          function partition(array, left, right) {
           var index=left;
           var pivot=array[right]; // 取最后一個(gè)數(shù)字當(dāng)做基準(zhǔn)值,這樣方便遍歷
           for (var i=left; i < right; i++) {
           if (array[i] <=pivot) {
           swap(array, index, i);
           index++;
           }
           }
           swap(array, right, index);
           return index;
          }
          

          因?yàn)槲覀冃枰f歸的多次原地分區(qū),同時(shí),又不想額外的地址空間所以,在實(shí)現(xiàn)分區(qū)算法的時(shí)候會(huì)有3個(gè)參數(shù),分別是原數(shù)組array,需要遍歷的數(shù)組起點(diǎn)left以及需要遍歷的數(shù)組終點(diǎn)right

          最后返回一個(gè)已經(jīng)排好序的index值用于下次遞歸,該索引對(duì)應(yīng)的值一定比索引左側(cè)的數(shù)組元素小,比所有右側(cè)的數(shù)組元素大。

          再次基礎(chǔ)上我們還是可以進(jìn)一步的優(yōu)化分區(qū)算法,我們發(fā)現(xiàn) <=pivot可以改為<pivot,這樣可以減少一次交換

          原地分區(qū)版快速排序?qū)崿F(xiàn)

          function quickSort(array) {
           function swap(array, i, j) {
           var temp=array[i];
           array[i]=array[j];
           array[j]=temp;
           }
           function partition(array, left, right) {
           var index=left;
           var pivot=array[right]; // 取最后一個(gè)數(shù)字當(dāng)做基準(zhǔn)值,這樣方便遍歷
           for (var i=left; i < right; i++) {
           if (array[i] < pivot) {
           swap(array, index, i);
           index++;
           }
           }
           swap(array, right, index);
           return index;
           }
           function sort(array, left, right) {
           if (left > right) {
           return;
           }
           var storeIndex=partition(array, left, right);
           sort(array, left, storeIndex - 1);
           sort(array, storeIndex + 1, right);
           }
           sort(array, 0, array.length - 1);
           return array;
          }
          

          二、字符串

          1. 回文字符串
          //判斷回文字符串
          function palindrome(str) {
           var reg=/[\W\_]/g;
           var str0=str.toLowerCase().replace(reg, "");
           var str1=str0.split("").reverse().join("");
           return str0===str1;
          }
          
          1. 翻轉(zhuǎn)字符串
          function reverseString(str) {
           return str.split("").reverse().join("");
          }
          
          1. 字符串中出現(xiàn)最多次數(shù)的字符
          function findMaxDuplicateChar(str) {
           var cnt={}, //用來記錄所有的字符的出現(xiàn)頻次
           c=''; //用來記錄最大頻次的字符
           for (var i=0; i < str.length; i++) {
           var ci=str[i];
           if (!cnt[ci]) {
           cnt[ci]=1;
           } else {
           cnt[ci]++;
           }
           if (c=='' || cnt[ci] > cnt[c]) {
           c=ci;
           }
           }
           console.log(cnt)
           return c;
          }
          

          三、數(shù)組

          1. 數(shù)組去重
          //數(shù)組去重
          function uniqueArray(arr) {
           var temp=[];
           for (var i=0; i < arr.length; i++) {
           if (temp.indexOf(arr[i])==-1) {
           temp.push(arr[i]);
           }
           }
           return temp;
           //or
           return Array.from(new Set(arr));
          }
          

          四、查找

          1. 二分查找
          //二分查找
          function binary_search(arr, l, r, v) {
           if (l > r) {
           return -1;
           }
           var m=parseInt((l + r) / 2);
           if (arr[m]==v) {
           return m;
           } else if (arr[m] < v) {
           return binary_search(arr, m+1, r, v);
           } else {
           return binary_search(arr, l, m-1, v);
           }
          }
          
          1. 將二分查找運(yùn)用到之前的插入排序中,形成二分插入排序,據(jù)說可以提高效率。但我測(cè)試的時(shí)候也許是數(shù)據(jù)量太少,并沒有發(fā)現(xiàn)太明顯的差距。。大家可以自己試驗(yàn)一下~(譬如在函數(shù)調(diào)用開始和結(jié)束使用console.time('插入排序耗時(shí)')和console.timeEnd('插入排序耗時(shí)'))

          五、樹的搜索/遍歷

          1. 深度優(yōu)先搜索
          //深搜 非遞歸實(shí)現(xiàn)
          function dfs(node) {
           var nodeList=[];
           if (node) {
           var stack=[];
           stack.push(node);
           while(stack.length !=0) {
           var item=stack.pop();
           nodeList.push(item);
           var children=item.children;
           for (var i=children.length-1; i >=0; i--) {
           stack.push(children[i]);
           }
           }
           }
           return nodeList;
          }
          //深搜 遞歸實(shí)現(xiàn)
          function dfs(node, nodeList) {
           if (node) {
           nodeList.push(node);
           var children=node.children;
           for (var i=0; i < children.length; i++) {
           dfs(children[i], nodeList);
           }
           }
           return nodeList;
          }
          
          1. 廣度優(yōu)先搜索
          //廣搜 非遞歸實(shí)現(xiàn)
          function bfs(node) {
           var nodeList=[];
           if (node !=null) {
           var queue=[];
           queue.unshift(node);
           while (queue.length !=0) {
           var item=queue.shift();
           nodeList.push(item);
           var children=item.children;
           for (var i=0; i < children.length; i++)
           queue.push(children[i]);
           }
           }
           return nodeList;
          }
          //廣搜 遞歸實(shí)現(xiàn)
          var i=0; //自增標(biāo)識(shí)符
          function bfs(node, nodeList) {
           if (node) {
           nodeList.push(node);
           if (nodeList.length > 1) {
           bfs(node.nextElementSibling, nodeList); //搜索當(dāng)前元素的下一個(gè)兄弟元素
           }
           node=nodeList[i++];
           bfs(node.firstElementChild, nodeList); //該層元素節(jié)點(diǎn)遍歷完了,去找下一層的節(jié)點(diǎn)遍歷
           }
           return nodeList;
          }
          

          高階函數(shù)衍生算法

          1.filter去重

          filter也是一個(gè)常用的操作,它用于把Array的某些元素過濾掉,然后返回剩下的元素。也可以這么理解,filter的回調(diào)函數(shù)把Array的每個(gè)元素都處理一遍,處理結(jié)果返回false則過濾結(jié)果去除該元素,true則留下來

          用filter()這個(gè)高階函數(shù),關(guān)鍵在于正確實(shí)現(xiàn)一個(gè)“篩選”函數(shù)。

          其實(shí)這個(gè)篩選函數(shù)有多個(gè)參數(shù),filter(function (element, index, self),演示一個(gè)使用filter去重,像這樣:

          var r,
           arr=['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
           r=arr.filter(function (element, index, self) {
           return self.indexOf(element)===index;
           //拿到元素,判斷他在數(shù)組里第一次出現(xiàn)的位置,是不是和當(dāng)前位置一樣,一樣的話返回true,不一樣說明重復(fù)了,返回false。
           });
          

          2.sort排序算法

          排序也是在程序中經(jīng)常用到的算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個(gè)元素的大小。如果是數(shù)字,我們可以直接比較,但如果是字符串或者兩個(gè)對(duì)象呢?直接比較數(shù)學(xué)上的大小是沒有意義的,因此,比較的過程必須通過函數(shù)抽象出來。通常規(guī)定,對(duì)于兩個(gè)元素x和y,如果認(rèn)為x < y,則返回-1,如果認(rèn)為x==y,則返回0,如果認(rèn)為x > y,則返回1,這樣,排序算法就不用關(guān)心具體的比較過程,而是根據(jù)比較結(jié)果直接排序。

          值得注意的例子

          // 看上去正常的結(jié)果:
          ['Google', 'Apple', 'Microsoft'].sort(); // ['Apple', 'Google', 'Microsoft'];
          // apple排在了最后:
          ['Google', 'apple', 'Microsoft'].sort(); // ['Google', 'Microsoft", 'apple']
          // 無法理解的結(jié)果:
          [10, 20, 1, 2].sort(); // [1, 10, 2, 20]
          

          解釋原因

          第二個(gè)排序把a(bǔ)pple排在了最后,是因?yàn)樽址鶕?jù)ASCII碼進(jìn)行排序,而小寫字母a的ASCII碼在大寫字母之后。

          第三個(gè)排序結(jié)果,簡(jiǎn)單的數(shù)字排序都能錯(cuò)。

          這是因?yàn)锳rray的sort()方法默認(rèn)把所有元素先轉(zhuǎn)換為String再排序,結(jié)果’10’排在了’2’的前面,因?yàn)樽址?’比字符’2’的ASCII碼小。

          因此我們把結(jié)合這個(gè)原理:

          var arr=[10, 20, 1, 2];
           arr.sort(function (x, y) {
           if (x < y) {
           return -1;
           }
           if (x > y) {
           return 1;
           }
           return 0;
           });
           console.log(arr); // [1, 2, 10, 20]
          

          上面的代碼解讀一下:傳入x,y,如果x<y,返回-1,x與前面排,如果x>y,返回-1,x后面排,如果x=y,無所謂誰拍誰前面。

          還有一個(gè),sort()方法會(huì)直接對(duì)Array進(jìn)行修改,它返回的結(jié)果仍是當(dāng)前Array,一個(gè)栗子:

          入排序概述

          插入排序是將數(shù)組分為待排序和已排序兩個(gè)區(qū)間。依次從待排序區(qū)間中取出一項(xiàng),用該項(xiàng)跟已排序區(qū)間項(xiàng)逐個(gè)對(duì)比,通過位移來實(shí)現(xiàn)插入到對(duì)應(yīng)位置的排序方式。插入排序平均時(shí)間復(fù)雜度是:O(n^2)

          步驟是:

          1. 先建立兩個(gè)循環(huán),外循環(huán)用于遍歷待排序區(qū)間,內(nèi)循環(huán)用來遍歷已排序區(qū)間。
          2. 從待排序中按順序取出一項(xiàng)暫存起來,將該項(xiàng)自右往左與已排序項(xiàng)逐個(gè)對(duì)比,當(dāng)遇到比自己大的項(xiàng)(表示升序)時(shí),將該位置右移1位。
          3. 再將待排序區(qū)間里右移后空出來的位置賦值為暫存項(xiàng)。此時(shí)已排序區(qū)間增加了一項(xiàng),待排序區(qū)間減少了一項(xiàng),繼續(xù)第2步直到待排序遍歷完成。

          插入排序?qū)崿F(xiàn)

          插入排序有多種實(shí)現(xiàn)方式,這里介紹常見的3種:

          1、通用實(shí)現(xiàn)方式,自左往右遍歷待排序數(shù)組,再從當(dāng)前的左側(cè)位置開始自右往左循環(huán)已排序數(shù)組,再逐個(gè)比較和移動(dòng)被比較項(xiàng),最后將當(dāng)前項(xiàng)填入到空缺位置上。

          2、利用數(shù)組splice方法,類似打撲克牌,先拿出要排序的牌,然后找準(zhǔn)位置插入。這種方式利用了原生API,減少了數(shù)組反復(fù)移動(dòng)位置的操作。性能上之前差不多。

          3、新建數(shù)組法與splice結(jié)合法,這種方式會(huì)多建立一個(gè)數(shù)組,也就會(huì)多占用一個(gè)空間,但理解起來最容易,也利用了JS語言的特性。

          插入排序通俗說明

          插入排序與冒泡、選擇都是比較簡(jiǎn)單好懂的排序方式,性能上也差不多。插入排序通俗來講就像打撲克牌排序,你抓了一手牌之后。假如是:2、1、5、3、4,你會(huì):

          1、先把牌分成兩組,假定左側(cè)第一張牌為一組(標(biāo)識(shí)A,這時(shí)只有2),其他牌為另外一組(標(biāo)識(shí)B,包括1、5、3、4)。

          2、從B組里面從左起選擇第一張牌(位置空出等待填充),也就是1,拿這張牌與A組里面從右往左挨個(gè)對(duì)比,當(dāng)遇到比這張牌還小時(shí)就在這個(gè)位置停留下來(如果A組全部比這張牌都大那就在A組最前面停留下來,如果A組里沒有比這張牌大的就在當(dāng)前位置停留)。

          3、然后將A組里比這張牌(也就是1)大的牌逐個(gè)往右移動(dòng)1位,原B組空出位置被填充,此時(shí)剛才停留的位置空出,將1這張牌插入在這里。這時(shí)候A組增加一個(gè)數(shù)字,變?yōu)椋?、2,B組減少1個(gè),變?yōu)椋?、3、4。

          4、移動(dòng)指針,繼續(xù)指向B組的第一個(gè),也就是5。用5這張牌重復(fù)第二部,即拿5去跟A組自右往左逐個(gè)比較,然后插入到A組。此時(shí)A組:1、2、5,B組:3、4。

          5、將B組里數(shù)字按照第二部重復(fù)操作,直到B組為空時(shí)整個(gè)循環(huán)結(jié)束。此時(shí)A組為:1、2、3、4、5。


          主站蜘蛛池模板: 久久久久人妻一区精品性色av| 高清国产AV一区二区三区| 久久久精品人妻一区亚美研究所| 亚洲日本乱码一区二区在线二产线| 色综合视频一区二区三区| 日本激情一区二区三区| 免费萌白酱国产一区二区三区| 国产主播一区二区三区| 亚洲精品色播一区二区| 亚洲电影唐人社一区二区| 国产午夜精品一区理论片| 亚洲一区二区三区在线播放| 日韩高清一区二区三区不卡| 波多野结衣一区视频在线| 亚洲日本乱码一区二区在线二产线| 不卡无码人妻一区三区音频| 日韩视频在线观看一区二区| 无码人妻精一区二区三区| 精品动漫一区二区无遮挡| 精品爆乳一区二区三区无码av| 久久一区二区精品综合| 福利一区二区三区视频在线观看| 波多野结衣高清一区二区三区| 国产综合无码一区二区色蜜蜜| 国产在线观看一区二区三区| 四虎成人精品一区二区免费网站| 无码国产精品一区二区免费式影视| 久久无码AV一区二区三区| 午夜DV内射一区二区| 一区二区三区视频在线观看| 久久精品无码一区二区无码| 中文字幕无线码一区二区| 日韩一区二区三区四区不卡| 理论亚洲区美一区二区三区| 国产a久久精品一区二区三区| 日本一区午夜爱爱| 国产丝袜无码一区二区三区视频| 伊人久久精品无码av一区| 中文字幕一区二区三区5566| 亚洲综合激情五月色一区| 国产视频一区在线播放|