【问题标题】:Make loop and code more efficient to handle large numbers with loops使循环和代码更有效地处理带有循环的大量数字
【发布时间】:2018-05-18 13:06:27
【问题描述】:

这是一个关于 Codewars 的作业,换句话说,就像一个家庭作业。 :) 我必须编写一个函数来返回 1 和 n(含)之间的数字数量,可以表示为两个完美正方形的差。例如,20 = 6² - 4²21 = 5² - 2²。许多数字都可以这样写,但不是全部。

我写了一个函数,它运行良好,但它需要能够处理n 的值,最高可达45000。基本上,我的代码在分析数千个数字时会崩溃。为了使代码更高效,我尝试反转初始循环,从n0,而不是从0n。我试图将n 除以二,直到它变得足够小,然后再次将最终结果乘以 2,但没有奏效。我也使用了while 循环,但后来我意识到我根本不知道如何解决这个问题,经过 3 天毫无意义的暴力尝试解决它后,我正在寻求帮助,因为我没有想干脆放弃吧。这是我的代码

function countSquareable(n){
  var y = []
  var c = []

  for (var i = 0; i <= n; i++) { // all numbers powered in range
  y.push(Math.pow(i,2))
  }

for(i = 0; i < y.length; i++) {
    c.push(y.map(a => y[i]-a)) //  all subtractions' combos

}

var d = c.toString().split(",").sort(function(a, b){return a-b}).filter(function(a) {return a>0 && a<=n}) // only the combos I need in the range

var a = [], b = [], prev; // remove duplicates
d.sort();
    for ( var i = 0; i < d.length; i++ ) {
        if ( d[i] !== prev ) {
            a.push(d[i]);
            b.push(1);
        } else {
            b[b.length-1]++;
        }
        prev = d[i];
    }

return console.log(a.length) // end result



};
countSquareable(500)

countSquareable(4) // should return 3 and it works
countSquareable(5) // should return 4 and it works
countSquareable(40) // should return 30 and it works
countSquareable(45000) // should return 33750 but crashes
countSquareable(6427), // should return 4820 but crashes

如何让代码更有效地解决问题? 卡塔是here。 谢谢!!

【问题讨论】:

  • 第一:不要使用Math.Pow(x, 2),而是使用x*x
  • 不知道这背后的数学原理,但是 n = a²-b² 和 a,b > n 是一个有效的解决方案吗?
  • 也许可以,但我需要返回满足该条件的确切数字数量
  • @RegisPortalez:数学不支持它。 a^2 - b^2 = (a - b) * (a + b),所以如果任一(正)项是&gt; n,那么(a + b) &gt; n,整个表达式太大了。

标签: javascript loops for-loop while-loop coding-efficiency


【解决方案1】:

这可以用少量的数学来完成。

如果你可以列出它们而不是计算值,比如 30 个 40,你会得到

[1, 3, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20, 21,
 23, 24, 25, 27, 28, 29, 31, 32, 33, 35, 36, 37, 39, 40]

如果难以看到那里的模式,请尝试大声朗读。关键是这些群体很容易成为

[        1,
 3,  4,  5,
 7,  8,  9,
 11, 12, 13,
 15, 16, 17,
 19, 20, 21,
 23, 24, 25,
 27, 28, 29,
 31, 32, 33,
 35, 36, 37,
 39, 40, (41)]

换句话说,从 2 开始,每四个数字都丢失了。下面是遵循该模式的代码:

const range = (lo, hi) => Array(...Array(hi - lo + 1)).map((_, n) => lo + n)

const countSquareable = (n) => range(1, n).filter(n => n % 4 !== 2).length

console.log(countSquareable(4))
console.log(countSquareable(5))
console.log(countSquareable(40))
console.log(countSquareable(45000))

所以它符合预期。但是我们现在处于数学领域,所以我们需要证明一些事情。我们可以在三种情况下做到这一点:

n可以表示为平方差吗?

案例一:n 是奇数

a = (n + 1) / 2,让b = (n - 1) / 2

由于n 是奇数,n - 1n + 1 是偶数,所以ab 都是整数。

a^2 = (n^2 + 2n + 1) / 4
b^2 = (n^2 - 2n + 1) / 4

所以

a^2 - b^2 = 4n / 4 = n

因此,奇数可以表示为平方差。

案例 2:n 可以被 4 整除

a = (n / 4 + 1),让b = (n / 4 - 1)

由于n 可以被4 整除,所以(n / 4) is an integer and thusaandb` 是整数。

现在

a^2 = (n^2/16 + 2n/4 + 1)
b^2 = (n^2/16 - 2n/4 + 1)

a^2 - b^2 = 4n/4 = n

因此,4的倍数可以表示为平方差。

案例 3:2 的倍数不是 4 的倍数

我们可以这样除整数:(4n), (4n + 1), (4n + 2), (4n + 3)

对每一个进行平方,然后选择合适的k,我们得到:

(4n)^2 = 16n^2 = 4 * 4n^2                             = (4k)
(4n + 1)^2 = (16n^2 + 8n + 1) = 4(4n^2 + 2n) + 1,     = (4k + 1)
(4n + 2)^2 = (16n^2 + 16n + 4) = 4(4n^2 + 4n + 1)     = (4k)
(4n + 3)^2 = (16n^2 + 24n + 9) = 4(4n^2 + 6n + 2) + 1 = (4k + 1)

因此,将一个正方形除以 4 时唯一可能的余数是 0 和 1。

减去这些,我们得到(1 - 0) = 1(1 - 1) = 0(0 - 0) = 0(0 - 1) = -1(最后一个与 3 的余数相同:4k - 1 = 4(k -1) + 3。)

所以我们可以得到013 的余数。但我们无法获得2

因此,不是 4 的倍数的 2 的倍数不能表示为平方差。

Q.E.D.我们已经证明我们的直觉是正确的:任何整数都可以写成平方的差,除了那些是 2 的倍数但不是 4 的整数。


更新

我的原始代码是一种蛮力方法,注意到 a^2 - b^2 = (a - b) * (a + b) 以及因此较小的因子 ((a - b)) 必须小于我们最大数字的平方根,如果产品小于 @987654355 @。然后我尝试了b 的所有可能值,如果小于n,则保存(a^2 - b^2)。这很有效,并且对于45000 的情况似乎足够有效。但它错过了上面的分析。

无论如何,这是那个版本:

const countSquareable = (n) => {
  const found = new Set()
   // a^2 - b^2 = (a - b)(a + b), so (a - b) can be no larger than sqrt(n)
  const topDiff = Math.sqrt(n)
  for (let diff = 1; diff <= topDiff; diff++) {
    const topB = n / 2 // can we get a tighter bound here?
    for (let b = 0; b < topB; b++) {
      let a = b +  diff
      const val = (a * a) - (b * b)
      if (val <= n) {
        found.add(val)
      }
    }
  }
  //console.log([...found].sort((a, b) => a - b))
  return found.size
}

console.log(countSquareable(45000))

【讨论】:

  • 伙计,非常感谢你写了这么多这么详细的文章。我完全没有尝试用蛮力解决这个问题。惊人的工作!我已经说不出话来感谢你了,更多的是解释而不是解决方案本身。
  • 嗯,我从蛮力方法开始——比你的效率高一点,也许是因为我注意到a^2 - b^2 = (a - b) * (a + b)。只有在我完成这项工作之后,我才注意到这种模式,并简化为这个。也许我会发布那个版本。但也要注意,我最好用一个非常简单的公式重写:countSquareable = n =&gt; Math.floor((n + 1) / 2) + Math.floor(n / 4)
  • 我写的所有那些庞大、笨重、低效的代码都可以用这简单的一行来简化,这让我很困惑
  • 这是找到所有解决方案和计算它们(或识别它们)之间的区别。
猜你喜欢
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
  • 2020-12-29
  • 1970-01-01
  • 2016-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多