一個關於JS解決陣列相乘問題

coldday發表於2019-04-10

陣列相乘,顧名思義就是將多個陣列的每一元素乘(組合)起來。它的結果以幾何數級增長,初次遇到此類問題時,常常使人頭皮發麻,我們現在以js的角度來解決這個問題。

從例項出發

眾所周知,女孩出門前一般需要經過精心打扮,那麼假設你有一個女朋友,她有著3頂帽子,5件衣服,5條褲子,3雙鞋子,2只口紅,4個包包,2副墨鏡,且心情可能會影響穿著,她可能因為心情不好而選擇不帶一些物品,但是她會告訴你她會穿戴什麼,要求列舉所有方案,看到題目後……

一個關於JS解決陣列相乘問題
不說了,先將實際問題轉化成語言問題吧。
七個陣列,分別表示七種穿戴,陣列中存值為該穿戴的代號,傳值為字串是她告訴你她需要的穿著,例"clothes trousers",中間以空格隔開

const hat = ['a','b','c'];
const clothes = ['0','1','2','3','4'];
const trousers = ['q','w','e','r','t'];
const shoes = ['A','B','C'];
const lipstick = ['$1','$2'];
const bag = ['¥1','¥2','¥3','¥4'];
const sunglasses = ['^1','^2'];
function getComb(str){
    return arr;
}
複製程式碼

解決思路

秉著不管什麼問題,看到陣列我就用迴圈的思路,去解題,難免會遇上很多問題。如果,題目上明確指出女朋友心情必須max每次出門都裝備拉滿,那ok沒問題,7次迴圈解決。但是女人心海底針吶,你只能通過她告訴的穿著來列舉(如果無論什麼情況,你都用7次迴圈,那當我沒說)。

隨機數法

我們可以讓電腦自己隨機組合,並判斷如果已經新增過這個結果,就不加進陣列就ok了。

const arr = str.split(' ')
    .map(name => {
        switch (name) {
            case 'hat': return hat;
            case 'clothes': return clothes;
            case 'trousers': return trousers;
            case 'shoes': return shoes;
            case 'lipstick': return lipstick;
            case 'bag': return bag;
            case 'sunglasses': return sunglasses;
        }
    })
複製程式碼

先將傳入的字串轉化一個與它傳值相關的二維陣列,例:傳入"hat clothes"
arr為[ [ 'a', 'b', 'c' ], [ '0', '1', '2', '3', '4' ] ],這樣我們就得知了穿戴的數目,以及該穿戴的種類了。我們不難得出,這種情況下最多能列出15種結果,定義一個total變數並修改一下上面程式碼。

let total = 1;
const arr = str.split(' ')
    .map(name => {
        switch (name) {
            case 'hat': total *= hat.length ; return hat;
            case 'clothes': total *= clothes.length ; return clothes;
            case 'trousers': total *= trousers.length ; return trousers;
            case 'shoes': total *= shoes.length ; return shoes;
            case 'lipstick': total *= lipstick.length ; return lipstick;
            case 'bag': total *= bag.length ; return bag;
            case 'sunglasses': total *= sunglasses.length ; return sunglasses;
        }
    })
複製程式碼

如果使用隨機數我們就能將一個多次迴圈轉化為一個while判斷的for迴圈。我們new一個陣列result,由於我們知道一共有幾種結果,所以while迴圈的條件就是:result.length < total.

let result = [], sum = '';
    while (result.length < total) {
        for (let i = 0; i < arr.length; i++) {
            sum += arr[i][parseInt(Math.random() * arr[i].length)];
        }
        if (result.indexOf(sum) == -1)
            result.push(sum);
        sum = '';
    }
    return result;
複製程式碼

注意:Math.random()是產生一個[0,1)的隨機浮點數,陣列的索引是整數,所以我們需要轉換型別;sum是陣列的組合,所以我們在新增完後需要了給它還原.

隨機數法的缺點:
演算法過於暴力,由於Math.random值返回的不確定性,導致很多時間會浪費在生成一個陣列已經新增過的值上。

reduce方法

我們也可以使用reduce方法非常簡便的完成陣列的乘法.很多初學者可能會對reduce比較陌生,因為這是一個不常見的方法,但是使用reduce進行很方便,先簡單介紹一下reduce方法。

reduce方法接收一個函式作為累加器,陣列中的每個值(從左到右)開始縮減,最終為一個值

reduce 為陣列中的每一個元素依次執行回撥函式,不包括陣列中被刪除或從未被賦值的元素,接受四個引數:初始值(或者上一次回撥函式的返回值),當前元素值,當前索引,呼叫 reduce 的陣列。

arr.reduce(callback,[initialValue])
callback (執行陣列中每個值的函式,包含四個引數)
previousValue (上一次呼叫回撥返回的值,或者是提供的初始值(initialValue))
currentValue (陣列中當前被處理的元素)
index (當前元素在陣列中的索引)
array (呼叫 reduce 的陣列)
initialValue (作為第一次呼叫 callback 的第一個引數。)
複製程式碼

基礎用法

 const a = [1,2,3,4,5,6];
    const rst = a.reduce((pre,cur)=>{
        return pre + cur;
    })
    console.log(rst); // rst = 1+2+3+4+5+6
複製程式碼

pre為第一個值或上次計算的結果,這裡沒有傳初值,pre初始預設為0,cur為陣列的每一個值。這裡解析過程:

=> pre = 0 ; cur = a[0];
=> pre = 0 + a[0] ; cur = a[1];
=> pre = 0 + a[0] + a[1] ; cur = a[2];
...
rst = 0 + 1 + 2 + 3 + 4 + 5 + 6 // 21
複製程式碼

用reduce解決陣列相乘

return arr.reduce((pre,cur)=>{
        return [].concat(...pre.map(e=>cur.map(ele=>ele+e)))
    }) 
複製程式碼

例:

str = 'clothes hat bag';
=> arr :
[ [ '0', '1', '2', '3', '4' ],
  [ 'a', 'b', 'c' ],
  [ '¥1', '¥2', '¥3', '¥4' ] ]
複製程式碼

首先將一個複雜組合問題,轉化為疊加組合問題。如果能將arr[0],arr[1]兩個組合的結果返回給pre,那麼我們就能通過兩兩組合來完成複雜組合。接下來使用map完成兩兩組合:

pre : [ '0', '1', '2', '3', '4' ]
cur : [ 'a', 'b', 'c' ]
pre.map(e => cur.map(ele => e + ele));
/*第一次*/
e : '0' , ele: 'a' , e + ele ='0a', pre[['0a']]
e : '0' , ele: 'b' , e + ele = '0b', pre[['0a','0b']]
...
/*結束*/
pre[['0a','0b','0c'],['1a','1b','1c'],['2a','2b','2c'],['3a','3b','3c'],['4a','4b','4c' ]]
cur : [ '¥1', '¥2', '¥3', '¥4' ]
複製程式碼

接下來我們只需要將二維陣列pre轉化為一維陣列,然後函式就會隨著reduce一步一步將陣列相乘起來了

[].concat[...pre.map(e => cur.map(ele => e + ele))]
複製程式碼

擴充套件運算子( spread )是三個點(...)。它好比 rest 引數的逆運算,將一個陣列轉為用逗號分隔的引數序列,一般在傳參時使用。

到這裡我們就解決這個女朋友出門的問題了。

相關文章