【编码】最短路径算法合集(三)SPFA算法

SPFA算法:只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。为什么队列为空就不改变了呢?就是因为要到下一点必须经过它的前一个邻接点。。SPFA可以处理负权边。很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路。

        初始化: dis数组全部赋值为Inf(无穷大,不能是map[s][i]),path数组全部赋值为s(即源点),或者赋值为-1,表示还没有知道前驱,然后dis[s]=0;  表示源点不用求最短路径,或者说最短路就是0。将源点入队;另外记住在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记(可能多次入队)。

        核心:读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队以此循环,直到队空为止就完成了单源最短路的求解。

        判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图),假设这个节点的入度是k(无向权则就是这个节点的连接的边)如果进入这个队列超过k,说明必然有某个边重复了,即成环;换一种思路:用DFS,假设存在负环a1->a2->…->an->a1。那么当从a1深搜下去时又遇到了a1,那么直接可以判断负环了所有用。当某个节点n次进入队列,则存在负环,此时时间复杂度为O(n*m),n为节点,m为边。

        SPFA算法有两个优化算法 SLF 和 LLL: SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。 LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。个人觉得LLL优化每次要求平均值,不太好,为了简单,我们可以之间用c++STL里面的优先队列来进行SLF优化。

  1. function [x_hat, success, k] = decode(y,f0,f1,H)  
  2. % Example:  
  3. % We assume G is systematic G=[A|I] and, obviously, mod(G*H',2)=0   
  4. %         sigma = 1;                          % AWGN noise deviation  
  5. %         x = (sign(randn(1,size(G,1)))+1)/2; % random bits  
  6. %         y = mod(x*G,2);                     % coding   
  7. %         z = 2*y-1;                          % BPSK modulation  
  8. %         z=z + sigma*randn(1,size(G,2));     % AWGN transmission  
  9. %  
  10. %         f1=1./(1+exp(-2*z/sigma^2));        % likelihoods  
  11. %         f0=1-f1;  
  12. %         [z_hat, success, k] = ldpc_decode(z,f0,f1,H);  
  13. %         x_hat = z_hat(size(G,2)+1-size(G,1):size(G,2));  
  14. %         x_hat = x_hat';   
  15.   
  16. [m,n] = size(H);   
  17. if m>n  
  18.     H=H';   
  19.  [m,n] = size(H);   
  20. end  
  21.   
  22. if ~issparse(H)       % make H sparse if it is not sparse yet如果不是稀疏矩阵,将H变为稀疏矩阵  
  23.    [ii,jj,sH] = find(H);  
  24.    H = sparse(ii,jj,sH,m,n);  
  25. end  
  26.   
  27. %initialization初始化  
  28. [ii,jj] = find(H);             % subscript index to nonzero elements of H 将非零元素标上  
  29. indx = sub2ind(size(H),ii,jj); % linear index to nonzero elements of H  找出H中非零元素的位置索引  
  30. q0 = H * spdiags(f0(:),0,n,n);  
  31. sq0 = full(q0(indx));   
  32. sff0 = sq0;  
  33.   
  34. q1 = H * spdiags(f1(:),0,n,n);   
  35. sq1 = full(q1(indx));     
  36. sff1 = sq1;  
  37.   
  38. %iterations  
  39. k=0;  
  40. success = 0;  
  41. max_iter =20;  
  42. while ((success == 0) & (k < max_iter))     
  43.  k = k+1;  
  44. %for iter=1:max_iter     
  45.      
  46.    %horizontal step第一步  
  47.    sdq = sq0 - sq1;  
  48.    sdq(find(sdq==0)) = 1e-20; % if   f0 = f1 = .5若先验概率一样的话,令sdq为一个很小很小的数  
  49.    dq = sparse(ii,jj,sdq,m,n);%以ii,jj为横纵坐标,用概率差sdq构成m*n的稀疏矩阵  
  50.    Pdq_v = full(real(exp(sum(spfun('log',dq),2)))); % this is ugly but works :)  
  51.                                                     %将dq在对数域求和,即将dq相乘,构成数组 Pdq_v                                                  
  52.    Pdq = spdiags(Pdq_v(:),0,m,m) * H;%更新了rij,以坐标形式6*12的矩阵  
  53.    sPdq = full(Pdq(indx));%变成了一列  
  54.    sr0 = (1+sPdq./sdq)./2; sr0(find(abs(sr0) < 1e-20)) = 1e-20;%将每个元素的rij都归一化,更新rij。概率译码的第一步完成  
  55.    sr1 = (1-sPdq./sdq)./2; sr1(find(abs(sr1) < 1e-20)) = 1e-20;%若比值非常小,则设为1e-20  
  56.    r0 = sparse(ii,jj,sr0,m,n);%将更新完的rij存入对应的位置,方便后面要用  
  57.    r1 = sparse(ii,jj,sr1,m,n);  
  58.      
  59.    %vertical step第二步目的更新qij  
  60.    Pr0_v = full(real(exp(sum(spfun('log',r0),1))));  
  61.    Pr0 = H * spdiags(Pr0_v(:),0,n,n);  
  62.    sPr0 = full(Pr0(indx));  
  63.    Q0 = full(sum(sparse(ii,jj,sPr0.*sff0,m,n),1))';  
  64.    sq0 = sPr0.*sff0./sr0;  
  65.      
  66.    Pr1_v = full(real(exp(sum(spfun('log',r1),1))));  
  67.    Pr1 = H * spdiags(Pr1_v(:),0,n,n);  
  68.    sPr1 = full(Pr1(indx));   
  69.    Q1 = full(sum(sparse(ii,jj,sPr1.*sff1,m,n),1))';  
  70.    sq1 = sPr1.*sff1./sr1;  
  71.      
  72.    sqq = sq0+sq1;  
  73.    sq0 = sq0./sqq;  
  74.    sq1 = sq1./sqq;  
  75.      
  76.    %tentative decoding伪后验概率(判决信息)  
  77.    QQ = Q0+Q1;  
  78.    Q0 = Q0./QQ;  
  79.    Q1 = Q1./QQ;  
  80.      
  81.    x_hat = (sign(Q1-Q0)+1)/2;%判决输出  
  82.     if rem(H*x_hat,2) == 0, success = 1;   
  83.         break;  
  84.     end  
  85. end  

猜你喜欢

转载自blog.csdn.net/weixin_39878297/article/details/80302855
今日推荐