循环中的求和对不能按预期工作[重复]
Posted
技术标签:
【中文标题】循环中的求和对不能按预期工作[重复]【英文标题】:Summing pairs in loop does not work as expected [duplicate] 【发布时间】:2021-10-25 11:52:17 【问题描述】:下面是任务描述和我写函数的两次尝试(都输出错误的结果,例如用这些参数检查我的第二个函数:
sumPairs([10, 5, 2, 3, 7, 5], 10);
*outputs [5, 5], should be [3, 7]*
sumPairs([1, 2, 3, 4, 5, 8, 6, 13, 9], 10);
*outputs [1, 9], should be [2, 8]*
请指出我解决此问题的方式有什么问题。 下面我解释一下我尝试做的事情。
.......................
任务描述:
对的总和。
给定一个整数列表和一个总和值,返回前两个 值(请从左侧解析)按出现的顺序加起来 形成总和。
sum_pairs([11, 3, 7, 5], 10)。
结果:3 + 7 = 10
sum_pairs([4, 3, 2, 3, 4], 6).
结果:4 + 2 = 6,因为最早的索引:0、2,因为整个 对更早。
sum_pairs([0, 0, -2, 3], 2).
结果:未定义。
sum_pairs([10, 5, 2, 3, 7, 5], 10).
结果:3 + 7 = 10,索引:3、4,因为整对更早, 因此是正确答案。
负数和重复数可以并且将会出现。
注意:还将测试长度超过 10,000,000 的列表 元素。确保您的代码不会超时。 ......................................
我的计划是:循环遍历 ints 数组的每个元素,将每个元素添加到 ints 数组的其他元素(除了将相同的元素添加到自身),直到找到 1 或 2 对元素加起来 s总和:*
function sumPairs(ints, s)
let arrResults = [];
let sumOfTwo;
for (i = 0; i < ints.length; i++)
for (j = 0; j < ints.length; j++)
if (j !== i)
sumOfTwo = ints[i] + ints[j];
if (sumOfTwo === s)
return [ints[i], ints[j]];
return undefined;
console.log(
sumPairs([10, 5, 2, 3, 7, 5], 10),
// *outputs [5, 5], should be [3, 7]*
sumPairs([1, 2, 3, 4, 5, 8, 6, 13, 9], 10)
// *outputs [1, 9], should be [2, 8]*
)
一旦找到前 1 或 2 对,我就使用 break 语句停止循环。找到 2 对元素后,选择 ints 数组中“较早”(具有较早索引)的对。当然 indexOf 重复的问题再次出现。所以我尝试在每个循环中创建一个单独的计数器。然后将结果推送到单独的 arrResults 中并选择最佳对:
function sumPairs(ints, s)
let arrResults = [];
let sumOfTwo;
let iIndexCount = 0;
let jIndexCount = 0;
for (i = 0; i < ints.length; i++)
for (j = 0; j < ints.length; j++)
if (j !== i)
sumOfTwo = ints[i] + ints[j];
if (sumOfTwo === s)
arrResults.push(ints[i], ints[j], jIndexCount - iIndexCount);
jIndexCount++;
if (arrResults.length === 9)
break;
iIndexCount++;
if (arrResults.length === 9)
break;
if (arrResults.length === 9)
if (arrResults[2] <= arrResults[8])
return [arrResults[0], arrResults[1]];
else
return [arrResults[6], arrResults[7]];
else if (arrResults.length === 6)
return [arrResults[0], arrResults[1]];
else
return undefined;
console.log(
sumPairs([10, 5, 2, 3, 7, 5], 10),
// *outputs [5, 5], should be [3, 7]*
sumPairs([1, 2, 3, 4, 5, 8, 6, 13, 9], 10)
// *outputs [1, 9], should be [2, 8]*
)
【问题讨论】:
我将您的问题编辑成一组minimal reproducible example 并修正了您的标题。请看一看。它现在可读且可重现 yesterday 的问题呢? 【参考方案1】:这很有趣 - 有点过度设计,但提供了标记和距离
按距离排序(如果需要,还可以使用 idx1)
function sumPair(list, s)
var pairs = []
for (var i = 0; i < list.length; i++)
for (var j = i + 1; j < list.length; j++)
const sum = list[i] + list[j]
if (sum === s) pairs.push(
idx1: i,
first: list[i],
idx2: j,
second: list[j],
distance: j - i
);
if (pairs.length) return pairs.sort((a,b) => a.distance - b.distance)[0]
return undefined;
;
let first, second = sumPair([1, 2, 3, 4, 5, 8, 6, 13, 9], 10)
console.log(first,second)
// similar for the rest
console.log(sumPair([10, 5, 2, 3, 7, 5], 10));
console.log(sumPair([1, 2, 3, 4, 5, 8, 6, 13, 9], 10))
console.log(sumPair([3, 1, 4, 1, 2, 6, 5], 8))
console.log(sumPair([1, 2, 3, 4, 5, 8, 6, 13, 9], 21))
【讨论】:
【参考方案2】:按照 cmets 到上一个(错误)答案,在这里您可以看到一个修改(和评论)的版本:
function sumPairs(arr, sum)
const pairs = [];
for (let i = 0; i < arr.length; i++)
let first = arr[i];
if (first < sum) // no point in checking anything, if the number itself is already equal to or bigger than the expected sum
/*
* As a + b === b + a, it is pointless to compare the sum of an item
* with any preceding item because that would have been already checked
* therefore each number will be compared only with the following ones,
* in order to avoid unnecessary iterations
*/
const rest = arr.slice(i + 1);
for (let j = 0; j < rest.length; j++)
const second = rest[j];
if (second < sum && first + second === sum) pairs.push(
pair: [first, second],
lastIndex: j // storing the last index will make easier to sort the pairs later
);
return pairs.length === 0
// no pair found
?
undefined :
pairs.length === 1
// only one pair found
?
pairs[0].pair
// more then one pair found: order pairs by the index (lowest to highest index)
// of the second number and pick the first one
:
pairs.sort((a, b) => a.lastIndex - b.lastIndex)[0].pair
// test
console.log('#testing sumPairs([11, 3, 7, 5], 10):');
console.log(sumPairs([11, 3, 7, 5], 10));
console.log('#testing sumPairs([4, 3, 2, 3, 4], 6):');
console.log(sumPairs([4, 3, 2, 3, 4], 6));
console.log('#testing sumPairs([0, 0, -2, 3], 2):');
console.log(sumPairs([0, 0, -2, 3], 2));
console.log('#testing sumPairs([10, 5, 2, 3, 7, 5], 10):');
console.log(sumPairs([10, 5, 2, 3, 7, 5], 10));
【讨论】:
我刚刚检查了你的第一个函数,它也给出了错误的结果:你的 sum_pairs([1, 2, 3, 4, 5, 8, 6, 13, 9], 10) 输出[1, 9],应该是 [2, 8] 因为这对彼此更接近(看任务描述);您的 sumpairs([10, 5, 2, 3, 7, 5], 10) 输出 [5, 5] 应该是 [3, 7] 因为这对彼此更接近。 此外,在您的第一个函数中:如果在每次迭代中对数组进行切片,则在下一次迭代中,您将丢失数组的开始部分(要与之比较的第一个元素)。换句话说,每次迭代都会缩小 j 的“其余部分”以与之前的 i 进行比较。对吗? 您的第二个函数 sum_pairs2 也给出了错误的结果。 ([1, 2, 3, 4, 5, 8, 6, 13, 9], 10) 输出 [1, 9],应该是 [2, 8] ([10, 5, 2, 3, 7, 5 ], 10) 输出 [5, 5],应该是 [3, 7]。谢谢你的帮助,但这个任务比这更复杂。它是输出具有最低索引的和对,同时该对的元素彼此最接近。 @NataliaGrzywacz,你是对的;我误解了要求中“较早/最早”的含义;感谢您指出了这一点。我将在几分钟后删除答案,只是为了给你时间阅读这个答案。对于slice()
而言,这是一种预期行为,因为它可以节省一些不必要的迭代(如果我已经检查了第一个元素与第二个元素的总和,那么检查第二个元素的总和是没有意义的第一个或在以下迭代中与自身一起使用)。
这个新版本应该符合要求【参考方案3】:
这种方法使用Set()
来存储数字,因此只需要一个循环。
灵感来自 NinaScholz 对您之前的 question 的回答:
function sumPairs(ints, s)
const set = new Set();
for(let n of ints)
if (set.has(s - n))
return ([s - n, n]);
else
set.add(n);
console.log(sumPairs([10, 5, 2, 3, 7, 5], 10))
console.log(sumPairs([1, 2, 3, 4, 5, 8, 6, 13, 9], 10))
console.log(sumPairs([11, 3, 7, 5], 10))
console.log(sumPairs([4, 3, 2, 3, 4], 6))
console.log(sumPairs([11, 1, 7, 5], 10));
【讨论】:
谢谢,看起来它适用于大多数输入,但是使用此输入: sumPairs([11, 1, 7, 5], 10);它显示“未捕获的类型错误:无法解构‘ints.reduce(...)’的属性‘对’,因为它为空。” 啊哈,谢谢你的检查。我会看看我能用它做什么。我相信我现在已经修好了.. 出色的解决方案。试图解释: ints.reduce((acc,n,i - 具有3个参数的reducer函数:累加和,当前数,当前索引)。内部for循环,检查:当前n数加上当前j值是否等于s AND ...从这里我不明白...并且如果先前返回的累积总和或当前 j 小于 acc.loc 则我们分配给 acc 具有两个键的对象: loc:当前 j 数字对:数组当前的 n 个数字和当前的 j。最后我们返回 acc 对象。然后 null)?.pair;我也不明白:我们访问 acc 对象的 pair 属性? 这有点难以理解好吧。我把它换成了我认为更简单更好的东西。但是稍微解释一下,我们基本上使用 reduce() 遍历 ints 数组。然后在该循环中,我们查看比当前数字更远的每个数字。如果两个数字总和为 s 并且 具有较低的 loc(即它们是满足条件的第一对),我们将 acc 替换为它们。这 ?。运算符只是说如果 acc 为空(例如,我们还没有找到匹配项)不要抛出错误,只需返回 undefined。 谢谢,这个解决方案如此简短而精彩。为了弄清楚,我必须了解 new Set() 是什么,现在我明白了。这是有人提出的另一个解决方案,也很短:var sum_pairs=function(ints, s) var seen = for (var i = 0; i < ints.length; ++i) if (seen[s - ints[i]]) return [s - ints[i], ints[i]]; seen[ints[i]] = true
以上是关于循环中的求和对不能按预期工作[重复]的主要内容,如果未能解决你的问题,请参考以下文章
使用 Javascript 使用 for 循环对数组中的数字求和 [重复]