4

我知道Math.sin()andMath.cos()函数,但我想知道是否有一种方法可以创建(或使用已经存在的)更快的函数,因为我不关心精确度。我正在寻找执行基本的 sin 或 cos 计算,并让它尽可能快地执行。简单地迭代 sigma 几次会比 sigma 快Math.sin()吗?

4

4 回答 4

7

由于您不太关心准确性,因此将其存储在预先计算或仅计算一次的表中,这就是我想要避免调用Math时所做的事情,这些调用在大量完成时可能会很昂贵。

大致

public class CosSineTable {
double[] cos = new double[361];
double[] sin = new double[361];
private static CosSineTable table = new CosSineTable();

private CosSineTable() {
    for (int i = 0; i <= 360; i++) {
        cos[i] = Math.cos(Math.toRadians(i));
        sin[i] = Math.sin(Math.toRadians(i));
    }
}

public double getSine(int angle) {
    int angleCircle = angle % 360;
    return sin[angleCircle];
}

public double getCos(int angle) {
    int angleCircle = angle % 360;
    return cos[angleCircle];
}

public static CosSineTable getTable() {
    return table;
}
}

我把循环和方法的优化留给你。

于 2013-06-05T02:35:30.573 回答
7

预先计算好的表格是要走的路。这是一个实现:

static final int precision = 100; // gradations per degree, adjust to suit

static final int modulus = 360*precision;
static final float[] sin = new float[modulus]; // lookup table
static { 
    // a static initializer fills the table
    // in this implementation, units are in degrees
    for (int i = 0; i<sin.length; i++) {
        sin[i]=(float)Math.sin((i*Math.PI)/(precision*180));
    }
}
// Private function for table lookup
private static float sinLookup(int a) {
    return a>=0 ? sin[a%(modulus)] : -sin[-a%(modulus)];
}

// These are your working functions:
public static float sin(float a) {
    return sinLookup((int)(a * precision + 0.5f));
}
public static float cos(float a) {
    return sinLookup((int)((a+90f) * precision + 0.5f));
}

在我的笔记本电脑上,这些速度比Math.sin.

我只使用了一张表——将余弦转换为正弦的成本并不是很明显。

我使用浮点数,假设您可能会在计算中使用浮点数,因为您更喜欢性能而不是精度。这里没有太大区别,因为瓶颈实际上只是数组查找。

这是我的基准:

public static void main(String[] args) {
    int reps = 1<<23;
    int sets = 4;

    Q.pl("  Trial  sinTab  cosTab  sinLib");
    for(int i = 0; i<sets; i++) {
        Q.pf("%7d\t%7.2f\t%7.2f\t%7.2f\n", i, testSinTab(reps), testCosTab(reps), testSinLib(reps));
    }
}

private static float[] sample(int n) {
    Random rand = new Random();
    float[] values = new float[n];
    for (int i=0; i<n; i++) {
        values[i] = 400*(rand.nextFloat()*2-1);
    }
    return values;
}
private static float testSinTab(int n) {
    float[] sample = sample(n);
    long time = -System.nanoTime();
    for (int i=0; i<n; i++) {
        sample[i] = sin(sample[i]);
    }
    time += System.nanoTime();
    return (time/1e6f);
}
private static float testCosTab(int n) {
    float[] sample = sample(n);
    long time = -System.nanoTime();
    for (int i=0; i<n; i++) {
        sample[i] = cos(sample[i]);
    }
    time += System.nanoTime();
    return time/1e6f;
}
private static float testSinLib(int n) {
    float[] sample = sample(n);
    long time = -System.nanoTime();
    for (int i=0; i<n; i++) {
        sample[i] = (float) Math.sin(sample[i]);
    }
    time += System.nanoTime();
    return time/1e6f;
}

输出:

  Trial  sinTab  cosTab  sinLib
      0  102.51  111.19  596.57
      1   93.72   92.20  578.22
      2  100.06  107.20  600.68
      3  103.65  102.67  629.86
于 2013-06-05T07:46:54.083 回答
2

你可以试试 http://sourceforge.net/projects/jafama/

它使用查找表,因此它实际上可能比 Math 慢,特别是如果表经常从 CPU 缓存中逐出,但对于数千次连续调用,它可能会更快。

在类加载期间它似乎也更慢(也许 JIT 那时还没有启动),所以你可能希望在那个特定的用例中避​​免它。

于 2013-06-06T19:01:56.067 回答
-1

我知道这个问题很老,但我认为它是最快的 java 实现,可精确到 65536 个元素。

public class MathHelper {

    private static double[] a = new double[65536];

    public static final double sin(float f) {
        return a[(int) (f * 10430.378F) & '\uffff'];
    }

    public static final double cos(float f) {
        return a[(int) (f * 10430.378F + 16384.0F) & '\uffff'];
    }

    static {
        for (int i = 0; i < 65536; ++i) {
            a[i] = Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D);
        }
    }
}

来源:https ://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/MathHelper.java

于 2017-08-27T17:47:24.943 回答