https://www.codewars.com/kata/is-my-friend-cheating/train/javascript
我的目标是设计一个函数,找到满足等式(a, b)
的数字对a * b == sum(1, 2, 3 ..., n-2, n-1, n) - a - b
。
以下代码查找所有对,但速度太慢且超时。我在评论中看到了这个挑战,算法需要通过O(n)复杂度。这是怎么做到的?
function removeNb (n) {
if(n===1) return null;
let sum = (n * (n+1))/2;
let retArr = [];
let a = n;
while( a !== 0){
let b = n;
while( b !== 0){
if(b != a && a*b == ((sum - b) - a) ){
retArr.push([a,b]);
}
b--;
}
a--;
}
retArr.sort( (a,b) => a[0] - b[0]);
return retArr;
}
感谢大家的帮助!这是我的最终解决方案:
function removeNb (n) {
let retArr = [];
let a = 1;
let b = 0;
let sumN = (n * (n+1))/2;
while( a <= n){
b = parseInt((sumN - a) / (a + 1));
if( b < n && a*b == ((sumN - b) - a) )
retArr.push([a,b]);
a++;
}
return retArr;
}
当我尝试解决b
时,我认为我的主要问题是我的代数出现了(令人尴尬的)错误。以下是任何想知道的正确步骤:
a*b = sum(1 to n) - a - b
ab + b = sumN - a
b(a + 1) = sumN - a
b = (sumN - a) / (a + 1)
答案 0 :(得分:1)
你可以求解b并得到:b = (sum - a)/(a + 1)
(给定一个!= -1)
现在迭代a
一次 - &gt;为O(n)
答案 1 :(得分:0)
这是一个实现:
function removeNb(n){
var sum = (1 + n) * n / 2;
var candidates = [];
// O(n)
for(var y = n; y >= 1; y--){
x = (-y + sum) / (y + 1);
/*
* Since there are infinite real solutions,
* we only record the integer solutions that
* are 1 <= x <= n.
*/
if(x % 1 == 0 && 1 <= x && x <= n)
// Assuming .push is O(1)
candidates.push([x, y]);
}
// Output is guaranteed to be sorted because
// y is iterated from large to small.
return candidates;
}
console.log(removeNb(26));
console.log(removeNb(100));
https://jsfiddle.net/DerekL/anx2ox49/
根据您的问题,它还说明了
在该序列中,他选择两个数字a和b。
但是,它没有提及a
和b
是唯一的数字,因此代码中不包含检查。
答案 2 :(得分:0)
如在其他答案中所解释的,可以对b求解O(n)算法。此外,考虑到解的对称性 - 如果(a,b)是解,同样(b,a)是 - 也可以保存一些迭代,一次添加几个解。要知道需要多少次迭代,让我们注意b> a当且仅当a&lt; -1 + SQRT(1个+总和)。为了证明这一点:
(sum-a)/(a+1) > a ; sum-a > a^2+a ; sum > a^2+2a ; a^2+2a-sum < 0 ; a_1 < a < a_2
其中a_1和a_2来自2度方程解:
a_1 = -1-sqrt(1+sum) ; a_2 = -1+sqrt(1+sum)
因为a_1&lt; 0和a&gt; 0,最后我们证明了b> a当且仅当a&lt; A_2。
因此我们可以避免在-1 + sqrt(1 + sum)之后的迭代。
一个工作示例:
function removeNb (n) {
if(n===1) return null;
let sum = (n * (n+1))/2;
let retArr = [];
for(let a=1;a<Math.round(Math.sqrt(1+sum));++a) {
if((sum-a)%(a+1)===0) {
let b=(sum-a)/(a+1);
if(a!==b && b<=n) retArr.push([a,b],[b,a]);
}
}
retArr.sort( (a,b) => a[0] - b[0]);
return retArr;
}
然而,通过这种实现,我们仍然需要最终的排序。为了避免它,我们可以注意到b =(sum-a)/(a + 1)是a的递减函数(推导它来证明)。因此,我们可以构建retArr连接两个数组,一个向末尾添加元素(push),一个在开头添加元素(unshift)。一个工作的例子如下:
function removeNb (n) {
if(n===1) return null;
let sum = (n * (n+1))/2;
let retArr = [];
let retArr2 = [];
for(let a=1;a<Math.round(Math.sqrt(1+sum));++a) {
if((sum-a)%(a+1)===0) {
let b=(sum-a)/(a+1);
if(a!==b && b<=n) {
retArr.push([a,b]);
retArr2.unshift([b,a]); // b>a and b decreases with a
}
}
}
retArr=retArr.concat(retArr2); // the final array is ordered in the 1st component
return retArr;
}
作为一个非母语的人,我会说从参考文献“all(a,b)中可能删除的序列1到n中的数字”的短语意味着!= b, 所以我添加了这个约束。