团队一个小伙伴儿提了个问题,二维数组交叉组合分组算法怎么实现?一听这怎么着复杂度都是一个O(n^n)的算法实现了,什么业务场景需要这么折腾?
了解了下,背景是这样:一个用户可能有N个标签组,每个标签组有不同的标签属性;需求是,需要根据这N个标签组的属性组合生成用户组。
把业务抽象之后,也可以这么描述:
给定一个不定长的二维数组,例如:
[['A1', 'A2', 'A3'], ['B1', 'B2']]
实现一个方法输出:[['A1', 'B1'],['A1', 'B2'],['A2', 'B1'],['A2', 'B2'],['A3', 'B1'],['A3', 'B2']]
。
这确实就不可避免的需要n的n方算法实现了。
场景一:矩阵的行列式展开(又叫笛卡尔乘积)
以上场景类似矩阵的行列式展开算法实现,实现细节如下;但涉及核心线性代数基础(比如矩阵的连乘实现)需再探究。
/**
* 二维数组交叉组合分组算法实现
*
* @param {Array} arr
*/
function determinant(arr){
const result = [];
const totalLength = arr.length;
const currentFlag = new Array(totalLength).fill(0)
getItems(0);
return result;
function getItems(curIndex){
const nextIndex = curIndex + 1;
// 必须在根节点执行回调逻辑
if (curIndex === totalLength) {
result.push(currentFlag.reduce((sum, item, index)=>{
sum.push(arr[index][item]);
return sum;
}, []));
}
if (nextIndex > totalLength) return;
for (let i = 0; i < arr[curIndex].length; i++){
currentFlag[curIndex] = i;
// 递归调用
getItems(nextIndex);
}
}
}
场景二:单一数组全排列
联想到另外一个场景,题设是这样:
假设现在有N张发票,总面额是11,000元。
实现一个算法:将这N张发票尽可能分给10个人,使每个人的发票总面额大于1000元,而且余下来的发票总面额尽可能最大
这是一个可以动态规划解决的场景之一,这里仅实现了一个数组全排列的方案:
/**
* 排列组合算法:传入一个数组,计算所有可能的排列组合,在回调中逐一执行
* @param {Array} arr 默认数组
* @param {Function}
*/
function Permutation(arr, callback) {
var result = [];
times(arr, result);
function times(arr, result) {
var length = arr.length;
if (length == 0) {
callback && callback(result)
return;
}
for (var i = 0; i < length; i++) {
var newArr = arr.slice();
newArr.splice(i, 1);
var newResult = [].concat(result, arr[i])
times(newArr, newResult)
}
}
}
具体的实现可以移步:http://wilee.me/demo/grouping.html 。
[TODO]
- 矩阵连乘实现
- 动态规划算法优化策略
2 comments