算法實際應用集

使用笛卡爾算法進行sku組合

需求

對商品規格進行排列組合,電商的sku商品組合node

功能截圖,對商品規格進行組合排列

圖中16和9是商品的分類id,分類id和商品id用豎杆分隔,爲了後續根據id去查抄回顯使用。有了這些組合的結果就能夠作後續一些列操做。

核心代碼,使用笛卡爾乘積,遞歸調用組合

function doExchange(doubleArrays) {
    var len = doubleArrays.length;
    if (len >= 2) {
        var arr1 = doubleArrays[0];
        var arr2 = doubleArrays[1];
        var len1 = doubleArrays[0].length;
        var len2 = doubleArrays[1].length;
        var newlen = len1 * len2;
        var temp = new Array(newlen);
        var index = 0;
        for (var i = 0; i < len1; i++) {
            for (var j = 0; j < len2; j++) {
                temp[index] = arr1[i] + "," + arr2[j];
                index++;
            }
        }
        //先把前兩個數組進行組合
        var newArray = new Array(len - 1);
        newArray[0] = temp;

        if (len > 2) {
            var _count = 1;
            for (var i = 2; i < len; i++) {
                newArray[_count] = doubleArrays[i];
                _count++;
            }
        }
        //建立新數組,把前兩項組合後數組做爲新數組第一項,從原數組第二項開始,剩餘未組合的值放進新數組,遞歸調用新數組進行組合
        return this.doExchange(newArray);
    } else {
        return doubleArrays[0];
        //長度小於2直接返回
    }
}
複製代碼

快遞員配送距離問題

需求

好比美團啊餓了麼,或者是電商類的平臺,須要配送餐飲和商品,這時候快遞員送餐員手裏有多個訂單,這時候要告訴他應該以最優的路線進行配送算法

這是幾個送餐點,這個需求就是找出最優化路線。

核心代碼

/**
 * 定義一個最小堆對象
 */
var heapMin = function () {
  this.set = [];
}

/**
 * 調整堆使其知足最小堆性質
 */
heapMin.prototype.adjust = function (index) {
  let len = this.set.length
  let l = index * 2 + 1
  let r = index * 2 + 2
  let min = index
  let node = null

  if (l <= len -1 && this.set[min].cost > this.set[l].cost) {
    min = l
  }

  if (r <= len -1 && this.set[min].cost > this.set[r].cost) {
    min = r
  }

  if (min != index) {
    node = this.set[index];
    this.set[index] = this.set[min]
    this.set[min] = node
    this.adjust(min)
  }
}

/**
 * 插入一個元素
 */
heapMin.prototype.push = function (node) {
  this.set.push(node)

  for (let i = Math.floor(this.set.length / 2); i >= 0; i--) {
    this.adjust(i)
  }

}

/**
 * 移除最小元素
 */
heapMin.prototype.pop = function () {
  let node

  node = this.set.shift()
  this.adjust(0)

  return node
}

/**
 * 獲取當前堆大小
 */
heapMin.prototype.size = function () {
  return this.set.length
}

/**
 * 堆是否爲空
 */
heapMin.prototype.empty = function () {
  return this.set.length === 0 ? true : false
}

// 定義不可達路徑值 -1
const INF = -1

// TSP解空間樹節點
function TSPNode (cost, index) {
  // 節點解向量
  this.x = []
  // 當前走過的路徑耗費值
  this.cost = cost
  // 當前節點在其解向量中的index
  this.index = index
}

/**
 * TSP對象
 * map: 地圖,採用鄰接矩陣的方式表示無向圖G
 */
function TSP (map) {
  // 檢查地圖數組合法性 n*n數組
  if (map === undefined || !Array.isArray(map) || map.length < 0 || !Array.isArray(map[0]) || map.length != map[0].length) {
    return console.error('map非法!')
  }
  // 賦值地圖數組
  this.map = map
  // 節點數量
  this.number = map.length
  // 當前最優路徑耗費
  this.costBest = INF
  // 最優解向量數組
  this.xBest = []
}


/**
 * 計算最佳路徑
 * 返回值:cost,最佳路徑耗費;routine,路徑
 */
TSP.prototype.getBestRoutine = function () {

  // 暫存地圖數組
  let map = this.map
  // 暫存節點數量
  let num = this.number
  // 建立一個最小堆
  let heap_min = new heapMin()
  
  // 建立初始節點,由於從節點0出發,因此 cost=0,index=1
  let startNode = new TSPNode(0, 1)
  // 初始化解向量,數組中存儲的是節點的序號,對應map中的索引,如:[0,1,...,n]
  for (let i = 0 ; i < num; i++) {
    startNode.x[i] = I
  }
  // 加入最小堆
  heap_min.push(startNode)
  
  // 開始搜索
  while (!heap_min.empty()) {
    // 取出當前節點
    var cNode = heap_min.pop()
    // 當前節點id
    var cIndex = cNode.index
    
    // 搜索至倒數第二個節點時中止
    if (cIndex === num) {
      // 當前點可到達,而且當前點能夠到達初始點
      if (map[cIndex-2][cNode.x[cIndex-1]] != INF && map[cNode.x[cIndex-1]][0] != INF) {
        // 找到一個最優解,進行記錄
        if (this.costBest === INF || cNode.cost + map[cNode.x[cIndex-1]][0] < this.costBest) {
          
          this.costBest = cNode.cost  + map[cNode.x[cIndex-1]][0]
          
          // 複製最優解向量
          for (let i=0; i < num; i++) {
            this.xBest[i] = cNode.x[I]
          }
        }
        
        continue
      }
    }
    
    // 判斷當前節點是否知足限界條件,若是不知足就不須要進行擴展
    if (this.costBest !== INF && cNode.cost >= this.costBest) {
      continue
    }
    
    // 沒有到達葉子節點,擴展子節點,對於那些路徑耗費大於當前最優路徑耗費的子節點,不須要擴展(也稱爲剪掉),即不加入最小堆中
    for(let j = cIndex; j < num; j++) {
      // 利用當前節點在其解向量中的索引得到上一個節點即cIndex-1的序號,若是x[cIndex-1]節點與x[j]節點有邊相連
      if (map[cNode.x[cIndex-1]][cNode.x[j]] != INF) {
        // 計算到達此節點的路徑耗費
        let cost = cNode.cost + map[cNode.x[cIndex-1]][cNode.x[j]]
        // 對於那些路徑耗費大於當前最優路徑耗費的子節點,不須要擴展(也稱爲剪掉),即不加入最小堆中。當前路徑耗費更小即cost < this.costBest,加入最小堆中
        if (this.costBest === INF || cost < this.costBest) {
          // 當前走過的路徑耗費, 節點層數+1
          let node = new TSPNode(cost, cIndex +1)
          // 複製以前的解向量
          for (let i = 0; i < num; i++) {
            node.x[i] = cNode.x[I]
          } 
          // 更新當前節點解向量
          let tmp = node.x[cIndex]
          node.x[cIndex] = node.x[j]
          node.x[j] = tmp
          // 加入最小堆中
          heap_min.push(node)
        }// end if
        
      }// end if
    
    }// end for 擴展子節點
    
  }// end while 搜索
  
  // 返回最優解向量
  return {
    cost: this.costBest,
    routine:this.xBest.join('-->')
  }
}

/**
 * map 地圖數組
 * -1 : 表示不可達INF
 **/
var map =  [
  [ -1, 8, 6,12,14,32],
  [ 8, -1, 8,30, 6,20],
  [ 6, 8, -1, 8, 5, 4],
  [12,30, 8, -1,20,12],
  [14, 6, 5,20, -1, 4],
  [32,20, 4,12, 4, -1],
]

// 建立一個TSP對象
var beginTime, endTime, res
var tsp = new TSP(map)

beginTime = new Date()
res = tsp.getBestRoutine()
endTime = new Date()

console.log("cost: "+ res.cost +"\r\nroutine: " + res.routine+"\r\nexecute time:" + (endTime-beginTime) + "ms")
複製代碼

最短距離的參考連接數組

最少找零問題

需求

其實這個需求是我假想的,由於現實中好比我在地鐵站購買飲料,不多投入大面額的一百去一瓶飲料,基本上比較接近,可是若是真的投入了一百,這時候機器給我吐出來90個硬幣我就崩潰了。因此,我更但願它以50,20,這樣的最佳相加結果給我,若是在這個機器已經有這個功能了,那是我沒用到,若是沒有我想應該加個,不過如今大部分人使用掃碼支付了。bash

核心代碼 貪心算法

function MinCoinChange(coins) {
    var coins = coins;
    this.makeChange = function (amount) {
        var change = [],total = 0;
        for(var i = coins.length; i >= 0; i--) {
            var coin = coins[i];
            while(total + coin <= amount) {
                change.push(coin);
                total += coin;
            }
        }
        return change;
    };
}
//機器可以輸出的錢幣面額種類[1,5,10,20,50,100]
let typeMoney = [1,5,10,20,50,100]
var minCoinChange = new MinCoinChange(typeMoney);
// 投入的錢面額
let maxMoney = 100
console.log(minCoinChange.makeChange(maxMoney))
//代碼核心思惟就是從最大的開始拿,而且對結果累加,去和剩下的小面額的相加,直到和maxMoney的數額相等爲止。
複製代碼

程序自己還須要一些完善,好比這段代碼裏還要對現有各類類型錢幣個數作個實時統計,因此錢幣類型也是動態的,若是沒有100的就從新從50開始計算.優化

相關文章
相關標籤/搜索