首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Math.sin() .cos()与自己的疯狂实现的成本

Math.sin() .cos()与自己的疯狂实现的成本
EN

Stack Overflow用户
提问于 2016-06-13 10:25:34
回答 2查看 1.5K关注 0票数 1

大约一个小时前,我经历了一个美好而令人尴尬的时刻,在很长一段时间后,一些东西发出咔嚓声。快30岁了,我终于得到了鼻窦和眼窝。经过一段只能被描述为基本上重新实现车轮的插曲之后。

为了让WASD控件在两个轴上移动,考虑到播放器的外观方向,我决定使用一个简单的开关:

代码语言:javascript
复制
switch (key) {
case 'up':
    movement.z -= 1 * modulation.z;
    movement.x += 1 * modulation.x;
    break;
case 'down':
    movement.z += 1 * modulation.z;
    movement.x -= 1 * modulation.x;
    break;
case 'left':
    movement.x -= 1 * modulation.z;
    movement.z -= 1 * modulation.x;
    break;
case 'right':
    movement.x += 1 * modulation.z;
    movement.z += 1 * modulation.x;
    break;

...
}

..。其中的调制,z和.x将基于球员面临的方向。调制值需要从-1到+1。(这应该是我的第一条线索,是的!)于是,我自食其力地拿出了一些信得过的纸笔,幸亏我对自己的9年级教育一无所知,于是我想到了:

代码语言:javascript
复制
function setModulation(rotation) {
    var rotationTemp = 0;
    var modulation = {};

    rotationTemp = rotation;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.z = 1 - rotationTemp * 2;

    rotationTemp = rotation + 90;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.x = 1 - rotationTemp * 2;
    modulation.x *= -1;

    return modulation;
};

在学习和/或记忆的同时,当我重温这段代码时,我意识到:这是cos和sin的一项工作。狡猾。

代码语言:javascript
复制
function setModulationMath(rotation) {
    var modulation = {};

    modulation.z = Math.cos(rotation * Math.PI / 180);
    modulation.x = Math.sin(rotation * Math.PI / 180);

    return modulation;
};

到目前为止还很开心!使用数学--数学是为了什么!但后来我检查了表演。而我自己的“实现”在FF、Chrome和IE中要快得多。使用1.000.000次迭代,在我的本地设置中它的增益在35%到45%之间。

有人知道为什么吗?它只是Math.cos() / .sin()吗?一个可测量的增长远远超出了人们的预期,不是吗?有经验的人愿意和她分享他的见解吗?

代码语言:javascript
复制
function setModulationMath(rotation) {
    var modulation = {};

    modulation.z = Math.cos(rotation * Math.PI / 180);
    modulation.x = Math.sin(rotation * Math.PI / 180);

    return modulation;
};

function setModulation(rotation) {
    var rotationTemp = 0;
    var modulation = {};

    rotationTemp = rotation;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.z = 1 - rotationTemp * 2;

    rotationTemp = rotation + 90;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.x = 1 - rotationTemp * 2;
    modulation.x *= -1;

    return modulation;
};

var iterations = 1000000;
console.time('setModulation');
for(var i = 0; i < iterations; i++ ){
    setModulation(i, i / 2);
};
console.timeEnd('setModulation')

console.time('setModulationMath');
for(var i = 0; i < iterations; i++ ){
    setModulationMath(i, i / 2);
};
console.timeEnd('setModulationMath')

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-13 10:53:38

你的功能可能会更快。但是,这种比较没有任何意义,因为函数产生的结果完全不同。让我们对错误进行基准测试:

代码语言:javascript
复制
var err_z = 0;
var err_x = 0;
for (var i = 0; i < 360; ++i){
    var mm = setModulationMath(i);
    var m = setModulation(i);
    err_z += Math.abs(mm.z - m.z);
    err_x += Math.abs(mm.x - m.x);
}

和…

代码语言:javascript
复制
err_z == 49.17730025861921
err_x == 113.58865012930961

那太糟了。如果两者是完全相似的,您将期望值接近于0。

虽然我的第一个假设是,我一定是以某种方式伪造了它:它是完美的。这两个函数给出相同的调制值。话虽如此:我很高兴被告知这是一次黑客攻击,只因为x.:)

证明,z轴上的平均误差为0.137,x轴上为0.316。这可能对您来说已经足够好了,但是肯定有一些值的差异更大。你可能只是检查正交方向吗?在这种情况下,您需要的是一个switch,而不是三角学。

编辑:哦,这是z部分的图表。曲线是余弦,直线是你的近似值。

但我离题了。

(尝试)滚动您自己实现的trig函数、数学函数等并不一定是坏事。它确实取决于应用程序的具体情况,有时您会发现您不需要这样的精度,或者您的角度只表示为整数度…。在这些情况下,使用更简单的近似或查找表可能是有益的。

实际上,Javascript (和许多其他标准库)中的正弦和余弦是非常优化和非常精确的。

票数 5
EN

Stack Overflow用户

发布于 2016-06-13 11:03:47

由于许多原因,数学运算比较昂贵,但主要是因为它们处理一般情况和边缘情况,而不像简单的乘法和加法运算。

此外,对象查找和函数调用非常昂贵,因此缓存PI始终是一条经验法则。我将setModulationMath操作更改为以下操作。我使用的是performance.now()而不是timeEnd,因为它更准确。在这里阅读更多信息:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

我的研究结果:

  • setModulation: 50.264999999999999999986ms
  • setModulationMath: 68.464999999999999997

代码语言:javascript
复制
var PI180 = Math.PI/180;

function setModulationMath(rotation) {
  var rotationC = rotation * PI180;
  return {
    z: Math.cos(rotationC),
    x: Math.sin(rotationC)
  };
};


function setModulation(rotation) {
  var rotationTemp = 0;
  var modulation = {};
  rotationTemp = rotation;
  if (rotationTemp > 180) {
    rotationTemp = 360 - rotationTemp;
  }
  rotationTemp /= 180;
  modulation.z = 1 - rotationTemp * 2;
  rotationTemp = rotation + 90;
  if (rotationTemp > 180) {
    rotationTemp = 360 - rotationTemp; 
  }
  rotationTemp /= 180;
  modulation.x = 1 - rotationTemp * 2;
  modulation.x *= -1;
  return modulation;
};


var iterations = 1000000;
var t0 = performance.now();
for (var i = 0; i < iterations; i++){
  setModulation(i);
}
var t1=performance.now();
console.log("setModulation: "+(t1-t0)+"ms");


var t2 = performance.now();
for (var l = 0; l < iterations; l++){
  setModulationMath(l);
}
var t3=performance.now();
console.log("setModulationMath: "+(t3-t2)+"ms");

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37787302

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档