运行 Do...while 计算直到满足条件 说明Endothermic_Dragon:斯科特·索耶:Endothermic_Dragon:斯科特·索耶:Endothermic_Dragon:斯科特·索耶:

如何解决运行 Do...while 计算直到满足条件 说明Endothermic_Dragon:斯科特·索耶:Endothermic_Dragon:斯科特·索耶:Endothermic_Dragon:斯科特·索耶:

我正在使用 Javascript 编写一个简单的 do...while 语句,该语句旨在查看给定输入 num 的小数的长度,如果小数的长度大于或等于 1,取 num 并将其添加到自身上,直到小数长度等于 0。目前,它适用于长度为 1 的小数,但如果大于,则停止。

预期输出,例如,当 num 为 8.75 时,应该是 35,而不是 17.5。

该系列需要 4 个步骤才能达到 35。

8.75

17.5

26.25

35

这是我的代码:

const num = 8.75;
var decimalLength = num.toString().split(".")[1].length;
let result = "";
let i = num;

do {
  i = i + num;
  result = result + num;
  newLength = decimalLength;
} while (newLength < 0);

console.log(i);

解决方法

你可以使用一些花哨的数学来得到一个更明确的答案,而不是一遍又一遍地循环:

const num = 8.75;
var decimal = num % 1 //Decimal part
var decimalPlaces = num.toString().split(".")[1] ?.length || 0; //Get decimal places

//GCD function
const gcd = (x,y) => (!y ? x : gcd(y,x % y));

//GCD between decimal and 1,need to make it whole number to get multiplier,so multiply each with 10^[decimal places]
//Get what's remaining when divided by 10^[decimal places] as well,to see what to multiply by to get whole number
var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces,decimal * 10 ** decimalPlaces)

//Use log change of base property to get value in power of 2
var outputExponent = Math.log(multiplier) / Math.log(2)
//Multiply number by multiplier
var outputValue = num * multiplier

console.log(outputExponent) //Power of 2 to multiply by to get whole number
console.log(outputValue) //The whole number itself

,

此版本使用递归函数和容差来处理浮点舍入错误。

const firstIntMultiple = (n,tol=1e-6,c = 1) =>
  Math.abs(n * c - Math.floor(n * c)) < tol ? n * c : firstIntMultiple (n,tol,c + 1)

console .log (firstIntMultiple (8.75))               //  35/4
console .log (firstIntMultiple (8.333333333333334))  //  25/3
console .log (firstIntMultiple (14.058823529411764)) //  239/17

它通过乘以连续整数而不是连续加法来找到正确的版本,但这是相同的想法。

我们可以通过迭代方法轻松替换递归版本,这对于没有良好有理近似的数字可能很有用。 (现在,传递 Math.PI 会遇到递归限制。)

可能看起来像这样:

const firstIntMultiple = (n,tol=1e-6) => {
  let c = 1;
  while (Math.abs(n * c - Math.floor(n * c)) > tol) {
    c += 1
  }
  return n * c
}

对于 Math.PI,这将返回 4272943.0000005495,即整数 1e-6 内的第一个 pi 倍数。您可以根据需要调整容差。

更新——一种完全不同的技术

另一种技术将利用 the factcontinued fractions 提供了一种直接的方法来找到一个数字的最佳有理近似值。我们可以使用它来找到 pi 的十个最佳有理近似,例如:

bestApprox (Math.PI,1,10) .map (({n,d}) => `${n}/${d}`)
// => ["3/1","22/7","333/106","355/113","103993/33102","104348/33215",//     "208341/66317","312689/99532","833719/265381","1146408/364913"]

然后,使用这些近似值,我们可以找到第一个与我们的目标值相距很小的值。

代码可能如下所示:

const contFrac = (n,d = 1,count = Infinity) =>
  count < 1 || d == 0
    ? []
    : [Math .floor (n / d)] .concat (contFrac (d,n % d,count - 1))

const bestApprox = (n,c) =>
  contFrac(n,d,c)
    .reduce ((a,n,i) => [
      ...a,{
        n: a [i] .n + n * a [i + 1] .n,d: a [i] .d + n * a [i + 1] .d
      }
    ],[{n: 0,d: 1},{n: 1,d: 0}])
    .slice (2)

const firstIntMultiple = (x,ε = 1e-6) => 
  bestApprox (x,1) 
    .find (({n,d},i,a) => i == a.length - 1 || Math.abs (n / d - x) < ε) 
    .n


console .log (firstIntMultiple (8.75))               //  35/4
console .log (firstIntMultiple (8.333333333333334))  //  25/3
console .log (firstIntMultiple (14.058823529411764)) //  239/17
console .log (firstIntMultiple (Math.PI))            //  353/113
console .log (firstIntMultiple (Math.PI,1e-8))      //  103993/33102

我没有测试过性能,但这应该是相当不错的,特别是对于那些连分数早期包含大整数的数字。 (例如,pi<3; 7,15,292,...> 292 意味着 3 + (1 / (7 + (1 / (15 + 1 / 1))))355 / 113pi 的极好近似值,事实上,它很好保留到小数点后六位。

我不知道这对 OP 有多大帮助,但它表明古老的数学课程有一天可能会派上用场!。 ;-)

更新 2 - 现在有更多解释!

此版本通过不检查测试值是否在原始值的 epsilon 范围内,而是检查测试值与原始值的比率,用小值解决了第二种方法中的问题值在 1 的 epsilon 内。它也有一些小的清理和默认 epsilon 的较小值:

const contFrac = (n,count = Infinity) =>
  contFrac(n,count)
    .reduce ((a,d: 0}])
    .slice (2)

const isClose = (x,y,ε) =>
  y == 0 ? x < ε : (x / y > 1 - ε && x / y < 1 + ε)

const firstIntMultiple = (x,ε = 1e-8) => 
  bestApprox (x,a) => i == a.length - 1 || isClose (n / d,x,ε)) 
    .n


console .log (firstIntMultiple (8.75))               //  35/4
console .log (firstIntMultiple (8.333333333333334))  //  25/3
console .log (firstIntMultiple (14.058823529411764)) //  239/17
console .log (firstIntMultiple (Math.PI))            //  103993/33102
console .log (firstIntMultiple (Math.PI,1e-6))      //  353/113
console .log (firstIntMultiple (13.000000221))       //  58823532/4524887
console .log (firstIntMultiple (1.0000003333))       //  3000301/3000300
console .log (firstIntMultiple (1234/987654321))     //  6/4802209
.as-console-wrapper {min-height: 100% !important; top: 0}

说明

这里的主要函数仍然是firstIntMultiple,它相当简单,只是搜索bestApprox的结果以获得足够接近我们目标数的有理近似值,然后返回该结果的分子. “足够接近”由isClose决定,它检查两个数字的比率是否在1 - ε1 + ε之间,其中ε是一个可选参数,默认为{ {1}}。

所以问题是 1e-8 是如何工作的。为此,我们需要讨论 Continued Fractions。我不能在这里公正地对待他们,但希望我能描述足够多的人来感受他们。

这是一个重复的无限简单连分数:

bestApprox

这是一个连分数,因为我们将分数嵌套在其他分数的分母中。它是无限的,因为……嗯,因为它无限地继续——以一种明显的方式。这很简单,因为所有 分子是 1 1 + --------------------------------- 1 2 + ---------------------------- 1 2 + --------------------- 1 2 + --------------- 1 2 + --------- 2 + ...

用一点代数不难证明这代表 1 的平方根。

这通常会用如下符号缩写:

2

这里所有的值都是整数,在第一个之后,都是正数。

这些有一些优点。所有有理数都具有作为连分数的有限表示,并且所有二次数都具有重复的无限模式。但更重要的是,这些连分数的前缀包含对一个数的最佳有理近似。 (证明不是特别难,应该是非数学家可以遵循的。但我不会在这里尝试。)

我的意思是这些数字:

<1; 2,2,...>

是对 <1;> //=> 1 <1; 2> //=> 3/2 <1; 2,2> //=> 7/5 <1; 2,2> //=> 17/12 <1; 2,2> //=> 41/29 ... 的连续更好的近似,除了更高的分母,没有更好的近似可用。也就是说,例如,在大于 sqrt(2) 且小于 12 的分母中,没有比 29 更好的近似值。

因此,通过计算一个数的部分连分数,我们可以找到所有最好的近似值,并最终找到一个能得到我们正在寻找的倍数的近似值。

现在我们需要知道如何计算这些连分数,然后如何将它们的部分转换为有理数。幸运的是,两者都非常简单。

要找到连分数的元素,我们需要做的就是找到数字的下限,将其添加到我们的列表中,然后继续求余数的倒数。

如果我们从 sqrt(2) 开始,那么第一个元素将是 27/11floor(27/11);余数为2,其倒数为5/11,下一位数为11/5的底数,余数为2,其倒数是 1/5 没有余数。所以5可以写成27/11

如果我们从 <2; 2,5> 开始,那么我们的第一个元素将是 pi,然后我们将继续使用 3 的倒数,即 0.14159265358979312,并且下一个元素是 7.062513305931052。取余数的倒数,我们得到7,下一个元素是15.996594406684103。余数的倒数是 15,所以下一个元素是 1.0034172310150002。然后余数的倒数变得更大,在 1。我们可以继续得到如下结果:

292.63459087501246

没有明显的模式。但是 <3; 7,3,14,...> 的高值告诉我们,292<3; 7,1>355/113 的极好近似值。

函数 pi 按照描述执行此算法。

现在,要将部分转换为有理数,我们可以使用简单的递归。在 contFrac 中,第一个近似值是 <a_0; a_1,a_2,a_3,...>,我们将其写为 a_0。第二个是 a_0/1a_0 + (1 / a_1)。之后,我们可以通过这个简单的公式找到 (a_0 * a_1) / a_1nd 值:(k + 2)n_(k + 2) = a_(k + 2) * n_(k + 1) + n_k

所以对于 d_(k + 2) = a_(k + 2) * d_(k + 1) + d_k,我们从 <3; 7,1> 开始,然后是 3/1,然后我们的下一个值是 22/7(15 * 22 + 3) / (15 * 7 + 1),然后是 333/106(1 * 333 + 22) / (1 * 106 + 7)。代码使用了一个小技巧,将分子和分母扩展回两步,以便我们可以对每一步使用我们的递归,然后简单地将这两个值从最终结果中切掉。

我们有了。通过使用一个数的最佳有理近似,我们可以快速找到最小的整数,在很小的容差范围内,它是该数的倍数。

,

这是所有答案的组合。这样做的好处是它对每种输入都有一个“后备”。如果有终止小数点,Endothermic_Dragon 的第一种方法就足够了。如果它是一个重复的小数,那么找到明确答案的新方法就足够了。如果不够,则连分数用作后备。

请注意,我最后包含连分数的唯一原因是它有时会导致小数点较小的错误。我只想“修复”这个方法,但我不明白它是如何工作的,所以我用它作为后备。

//At the top so it is viewable
console.log(findMultiple(54.46333333333333)) //16339/300
console.log(findMultiple(8.333333333333333)) //25/3
console.log(findMultiple(14.05882352941176)) //Irrational
console.log(findMultiple(94.03820382038203)) //Irrational
console.log(findMultiple(623.0549383482724)) //1009349/1620
console.log(findMultiple(Math.PI)) //Irrational
console.log(findMultiple(13.000000221)) //1829587379722249/140737488355328
console.log(findMultiple(1.0000003333)) //1125900282105063/1125899906842624

//Main control
function findMultiple(num,interpretLiteral = false,epsilon = 1e-6) {
  if (num.toString().length < 17 || num % 1 == 0 || interpretLiteral) {
    return EndothermicDragonFirstMethod(num) //Terminating decimal
  }
  var checkRepeatingNum = CheckRepeating(num)
  if (checkRepeatingNum != false) {
    return Math.round(EndothermicDragonSecondMethod(num,checkRepeatingNum) * num) //Repeating decimal
  } else {
    return ScottSauyetFirstMethod(num,epsilon) //Continued fraction
  }
}


//Predifined functions

//GCD
function gcd(x,y){return !y ? x : gcd(y,x % y)};

//Check if digits repeat,if they do,return repeat period
function CheckRepeating(num) {
  var totalSearchLength = (num % 1).toString().split('.')[1].slice(0,-1).length
  var numTemp1 = (num % 1).toString().split('.')[1].slice(0,-1).split('').reverse().join('')
  for (var i = 1; i < Math.floor(totalSearchLength / 3); i++) {
    var numTemp2 = numTemp1.slice(0,3 * i)
    var searchLength = i
    bool = numTemp2.slice(0,searchLength) == numTemp2.slice(searchLength,2 * searchLength) && numTemp2.slice(0,searchLength) == numTemp2.slice(2 * searchLength,3 * searchLength)
    if (bool) {
      return searchLength
    }
  }
  return false
}

//Terminating decimal
function EndothermicDragonFirstMethod(num) {
  var decimal = num % 1;
  var decimalPlaces = num.toString().split(".")[1]?.length || 0;
  var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces,decimal * (10 ** decimalPlaces));
  return num * multiplier;
}

//Repeating decimal
function EndothermicDragonSecondMethod(num,repeatInterval) {
  var numArray = num.toString().split('.')[1].slice(-repeatInterval).split('').reverse()
  var restOfNum = num.toString().split('.')[1].slice(0,-repeatInterval * 3).split('').reverse()
  var counter = 0;
  var extraRepeat = 0;
  restOfNum.every(el => {
    if (el == numArray[counter]) {
      extraRepeat++;
      counter++;
      if (counter == numArray.length) {
        counter = 0
      }
      return true
    }
    return false
  })
  var repeatingPart = num.toString().split('.')[1].slice(-repeatInterval * 3 - extraRepeat,-repeatInterval * 2 - extraRepeat)
  var notRepeatingPart = num.toString().split('.')[1].slice(0,-repeatInterval * 3 - extraRepeat)
  var numerator = (parseInt(notRepeatingPart) * (parseInt("9".repeat(repeatingPart.length)))) + parseInt(repeatingPart)
  var denominator = (parseInt("9".repeat(repeatingPart.length)) * (10 ** notRepeatingPart.length))
  return denominator / gcd(numerator,denominator)
}

//Otherwise (irrational numbers or other)
function ScottSauyetFirstMethod(num,epsilon = 1e-6) {

  const contFrac = (n,count = Infinity) =>
    count < 1 || d == 0 ? [] : [Math.floor(n / d)].concat(contFrac(d,count - 1))

  const bestApprox = (n,c) =>
    contFrac(n,c)
    .reduce((a,{
        n: a[i].n + n * a[i + 1].n,d: a[i].d + n * a[i + 1].d
      }
    ],[{
      n: 0,d: 1
    },{
      n: 1,d: 0
    }])
    .slice(2)

  const firstIntMultiple = (x,epsilon) =>
    bestApprox(x,1)
    .find(({
      n,d
    },a) => i == a.length - 1 || Math.abs(n / d - x) < epsilon)
    .n

  return firstIntMultiple(num,epsilon)
}

这会产生准确的答案,无论输入是什么(即使它是一个小数)!

,

两个相互竞争的答案。哪个更好?在你测试之前没有办法知道。所以,这就是这个新答案试图做的。

这些“测试”(如果您愿意)运行每个函数 50 次,在清除控制台之前为每次迭代计时并记录时间的平均值。

8 十进制精度

Endothermic_Dragon:

const num = 3.14159265;


//Function start
const firstIntMultiple = () => {
  var decimal = num % 1;
  var decimalPlaces = num.toString().split(".")[1] ?.length || 0;
  const gcd = (x,x % y));
  var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces,decimal * (10 ** decimalPlaces));
  return num * multiplier;
}
//Function end


var times = []

for (var i = 0; i < 50; i++) {
  var startTime = new Date()
  console.log(firstIntMultiple().toString())
  times.push((new Date() - startTime) / 1000)
}

console.clear()
console.log((times.reduce((a,b) => a + b,0) / times.length) + " seconds on average")

斯科特·索耶:

const num = 3.14159265;


//Function start
const firstIntMultiple = (tol = 1e-8) => {
  let c = 1;
  while (Math.abs(num * c - Math.floor(num * c)) > tol) {
    c += 1
  }
  return num * c
}
//Function end


var times = []
for (var i = 0; i < 50; i++) {
  var startTime = new Date()
  console.log(firstIntMultiple().toString())
  times.push((new Date() - startTime) / 1000)
}

console.clear()
console.log((times.reduce((a,0) / times.length) + " seconds on average")

10 十进制精度

Endothermic_Dragon:

const num = 3.1415926535;


//Function start
const firstIntMultiple = () => {
  var decimal = num % 1;
  var decimalPlaces = num.toString().split(".")[1] ?.length || 0;
  const gcd = (x,0) / times.length) + " seconds on average")

斯科特·索耶:

const num = 3.1415926535;


//Function start
const firstIntMultiple = (tol = 1e-10) => {
  let c = 1;
  while (Math.abs(num * c - Math.floor(num * c)) > tol) {
    c += 1
  }
  return num * c
}
//Function end


var times = []
for (var i = 0; i < 50; i++) {
  var startTime = new Date()
  console.log(firstIntMultiple().toString())
  times.push((new Date() - startTime) / 1000)
}

console.clear()
console.log((times.reduce((a,0) / times.length) + " seconds on average")

请注意,由于内部机制略有不同,这些函数将返回略有不同的结果。 Endothermic_Dragon 的答案试图找到将返回精确整数的倍数,而 Scott Sauyet 的答案试图找到使数字在整数的指定容差范围内的乘数。

这些示例可以通过在每个示例上添加更多 pi 数字并降低对 Scott Sauyet 答案的容忍度来进一步扩展。

我最初打算在测试这个 (https://mikemcl.github.io/decimal.js/) 时使用 decimal.js,但是,结果证明在 Scott 的方法上非常慢 - 计算每次迭代需要 5.1243 秒,而在 Endothermic_Dragon 的方法上每次迭代需要 0.0002 秒。请注意,这是在 w3schools 上通过 10 次迭代完成的。

所有这些分析可以得出两个结论:

  1. Endothermic_Dragon 的答案在终止小数方面具有可扩展性和准确性。
  2. Scott Sauyet 的回答耗费了一些时间,但提供了准确的答案。这在处理不终止的小数时特别有用,无论它们是无理数还是重复。

另外,这是一个额外的测试,因为为什么不呢。

15 十进制精度

Endothermic_Dragon:

const num = 3.141592653589793;


//Function start
const firstIntMultiple = () => {
  var decimal = num % 1;
  var decimalPlaces = num.toString().split(".")[1] ?.length || 0;
  const gcd = (x,0) / times.length) + " seconds on average")

斯科特·索耶:

const num = 3.141592653589793;


//Function start
const firstIntMultiple = (tol = 1e-15) => {
  let c = 1;
  while (Math.abs(num * c - Math.floor(num * c)) > tol) {
    c += 1
  }
  return num * c
}
//Function end


var times = []
for (var i = 0; i < 50; i++) {
  var startTime = new Date()
  console.log(firstIntMultiple().toString())
  times.push((new Date() - startTime) / 1000)
}

console.clear()
console.log((times.reduce((a,0) / times.length) + " seconds on average")

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-