假设我有一个设备及其统计数据的矩阵。帽子,衬衫,裤子,靴子。每个内部数组的大小可以不同,但总是会有一定数量的内部数组——在本例中为4。
var array = [
[1,9,2,8,3], // hat
[2,8,3,6], // shirt
[1,3], // pants
[9,3,2,6,8,2,1,5,2] // boots
]
我想通过矩阵找到最佳路径,然后以这样一种方式删除一个项目,即确定的下一个路径(以及路径的总和)是第一个路径之后的次优。在这个例子中,[9, 8, 3, 9]
是最好的,对吗?因此,我们可以在[0]中移除一个9
,以达到仅给出1的下降的8
。
我可以汇总所有可能的路线并从中确定,但内部阵列的大小可能比显示的要大得多。
我花了一些时间思考,并四处研究。我唯一能看到的是匈牙利算法,但它已经超过了我现在的数学/compsci知识。在这种情况下,这是最适用的知识吗?这似乎是为了满足一条路线的最低"成本",但我需要相反的成本。
我的想法,至少作为一种想法:
- 提取每个内部数组中的最高数字,然后从中创建新数组。对此进行排名[0]
- 将每个内部数组中最高的数字与下一个最低的数字进行比较。排序每个之间的差异
- 从内部数组中删除最高的数字,并在#2中找到最小的差值
- 重复#1到#3
在上面的例子中,我期望得到如下的结果。
[9, 8, 3, 9]
[8, 8, 3, 9]
[8, 8, 3, 8]
[8, 6, 3, 8]
[8, 6, 3, 6]
[8, 6, 3, 5]
编辑:我想我把这个问题的解释搞砸了,让我稍微修正一下。
第2版:本质上是集合间的最小损失,只从一个内部数组中删除一个项目。
更新:我最初误解了你的问题。你随后澄清了这个问题,我在这里提供了一个全新的解决方案。
我使用的策略是将所有子数组中的所有元素扁平化为单个数组,但这样做的方式要记住它们来自哪个原始子数组。然后,我对扁平数组进行排序,首先按值降序排列,然后按子数组编号升序排列。例如,排序后的扁平数组的前3个元素将是[[9,0]、[9,3]、[8,0]、…]表示来自子阵列0的值9,然后是来自子阵列3的值9、然后是来自于子阵列0中的值8,等等。然后,我继续浏览列表,根据需要取尽可能多的值,直到达到n,但为我尚未选择值的任何子阵列留有空间。
请注意,此解决方案适用于任意数量的子阵列,每个子阵列具有任意数量的元素。
const array = [
[1,9,2,8,3], // hat
[2,8,3,6], // shirt
[1,3], // pants
[9,3,2,6,8,2,1,5,2] // boots
];
for (let n = 0; n < 17; n += 1) {
const valuesChosen = getTopNBest(array, n);
console.log(`n = ${n}: ${JSON.stringify(valuesChosen)}`);
}
function getTopNBest(array, n) {
const numTypes = array.length;
const allElmtsRanked = [];
array.forEach((sub, typeNum) => {
sub.forEach(elmt => {allElmtsRanked.push([elmt, typeNum]);});
});
allElmtsRanked.sort((a,b) => b[0] - a[0] !== 0 ? b[0] - a[0] : a[1] - b[1]);
const valuesChosen = array.map(() => null);
let totalNumValuesExamined = 0;
let numSecondaryValuesChosen = 0;
let numUnrepresentedTypes = numTypes;
let currPair, currValue, currTypeNum;
while (numUnrepresentedTypes !== 0 || numSecondaryValuesChosen < n) {
currPair = allElmtsRanked[totalNumValuesExamined];
currValue = currPair[0];
currTypeNum = currPair[1];
totalNumValuesExamined += 1;
if (valuesChosen[currTypeNum] === null) {
valuesChosen[currTypeNum] = currValue;
numUnrepresentedTypes -= 1;
} else if (numSecondaryValuesChosen < n) {
numSecondaryValuesChosen += 1;
valuesChosen[currTypeNum] = currValue;
}
}
return valuesChosen;
}
您随后问我为什么先按值排序,然后按子数组编号排序。比较以下两种情况:
var array = [
[8],
[9,9,9,9,9],
[8],
[8]
]
对
var array = [
[9,8],
[9,9],
[9,8],
[9,8]
]
如果你简单地将这些值展平并对展平的数组进行排序,而不保留关于值来自哪个子数组的任何信息,那么在这两种情况下,你最终都会得到相同的排序展平数组,即
var sortedFlattenedArray = [9,9,9,9,9,8,8,8]
假设你想要最好的/最优化的路径。现在,无论哪种情况,您都可以获得[9,9,9,9]
,而对于第一种情况,则确实需要[8,9,8,8]
。只有当你记住了子数组/类型编号时,你才能得到这个,例如:
var sortedFlattenedArray = [[9,1],[9,1],[9,1],[9,1],[9,1],[8,0],[8,2],[8,3]]
因此,当您不再需要任何类型的次优值,但仍在为特定类型寻找可能的最高值时,实际策略允许您忽略已经采样的类型中相当高但次优的值。
这是另一种看待它的方法。这里的策略允许您对所有原始数组进行扁平化和排序,但请记住每个元素来自。这反过来又让你在选择途径时有选择性,"啊,下一个值很高,这很好,但等一下,它是一顶帽子(你只能通过读取它的相关子数组/类型号才能知道),我已经检索到了我的‘次优值’配额,所以我会忽略这个帽子值。但是,我会继续在排序后的扁平数组中移动,找到衬衫的最高剩余值(同样,你只能通过读取其关联的子数组/类型号来发现),我仍然没有找到任何值。">