【问题标题】:Using an array of precomputed powers to extract roots使用一组预先计算的幂来提取根
【发布时间】:2016-06-30 12:47:46
【问题描述】:

我正在编写一个解决十次方总和问题的程序,我需要一个快速算法来找到n^10 以及n^(1/10) 自然n<1 000 000。我正在预先计算一个幂数组​​,所以 n^10(数组查找)需要 O(1)。对于n^(1/10),我正在进行二分搜索。有没有什么办法可以加速根的提取?例如,如果索引是完美的幂,则创建一个数组并使用相应的根填充元素,否则将给出 O(1),但我会用完内存。有没有办法让根提取比 O(log(n)) 更快?

【问题讨论】:

  • 你只需要n^(1/10)的整数部分吗?如果不是,您可以使用愚蠢的 (exp(b*ln(a)) 公式而不是预先计算,并且可能会缓存输出。此外,如果您可以快速 log() 您的 n,您将只需要检查相邻的潜力对数根,如果您几乎可以查明潜在的根,则不需要二进制搜索与整个数组。
  • 只有 4 个 10 次方小于 100 万:0、1、1024 和 59049,因此发现特定 n 是否是 10 次方应该只是测试它是否是其中之一的问题数字。也许我理解错了。
  • 我不需要小数部分,我正在求解一个自然数方程。我有一百万个数字要检查,所以最大的数字将有一个后跟 6*10=60 个零。预计算将加快搜索速度。
  • @paul-hankin 最大的数字是 1000000^10,而不是 3^10。

标签: java algorithm math optimization


【解决方案1】:

为什么根数组会耗尽内存?如果它与幂数组的大小相同,它将适合使用相同的数据类型。但是对于幂,(10^6)^10 = 10^60,它不适合长变量,因此您需要使用 biginteger 或 bigdecimal 类型。如果您的数字 n 大于您的内存可以承受的最大数组大小 n_max,您可以将 n 除以 n_m 直到合适,即拆分 n = n_max^m*k,其中 m 是自然数,k

public class Roots
{
    static final int N_MAX = 1_000_000;

    double[] roots = new double[N_MAX+1];

    Roots() {for (int i = 0; i <= N_MAX; i++) {roots[i] = Math.pow(i, 0.1);}}

    double root(long n)
    {
        int m = 0;
        while (n > N_MAX)
        {
            n /= N_MAX;
            m++;
        }
        return (Math.pow(roots[N_MAX],m)*roots[(int)n]); // in a real case you would precompute pow(roots[N_MAX],m) as well
    }

    static public void main(String[] args)
    {
        Roots root = new Roots();
        System.out.println(root.root(1000));
        System.out.println(root.root(100_000_000_000_000l));
    }
}

【讨论】:

  • 我是说,创建 int array[100],放置 array[1]=1,array[4]=2,...array[100]=10,然后用零填充其余部分O(1) 搜索平方根,但需要 N^2 内存。使 array[2]=4, array[3]=9 需要 N 内存,但是 isO(log(n)) 复杂度,因为依赖于二分查找。虽然计算平方仍然是 O(1)。
  • 是的,我正在使用 BigInteger。我的问题是 - 我能比二分搜索(即 log(n))更快地找到根吗?
【解决方案2】:

除了 LUT 我能想到的加快速度有两种选择:

  1. 使用不乘法的二分查找

    如果您使用的是 bignums,那么第 10 根二分搜索不再是 O(log(n)),因为其中使用的基本操作不再是 O(1) !!!例如+,-,&lt;&lt;,&gt;&gt;,|,&amp;,^,&gt;=,&lt;=,&gt;,&lt;,==,!= 将变为O(b)* 将变为O(b^2)O(b.log(b)) 其中b=log(n) 取决于所使用的算法(甚至操作数大小)。因此,用于根查找的天真的二进制搜索将在更好的情况下O(log^2(n).log(log(n)))

    为了加快速度,您可以尝试不使用乘法。是的,这是可能的,最终的复杂性将是 O(log^2(n)) 看看:

    看看如何实现这一点。区别仅在于求解不同的方程:

    x1 = x0+m
    x1^10 = f(x0,m)
    

    如果您以代数方式获得x1=f(x0,m),则内部的每个乘法都会转换为位移位并添加...例如10*x = x&lt;&lt;1 + x&lt;&lt;3LUT 表不是必需的,因为您可以在二进制搜索期间对其进行迭代。 我想f(x0,m) 将包含x0 的较小幂,因此也可以类比地计算所有需要的幂......所以最终结果将没有幂。抱歉懒得为你做这些,你可以使用一些数学应用程序,比如 Windows 的 Derive

  2. 你可以使用pow(x,y) = x^y = exp2(y*log2(x))

    所以x^0.1 = exp2(log2(x)/10) 但是你需要大小数点(或固定点)在这里看看我是怎么做的:

有关更多想法,请参阅:

【讨论】:

    猜你喜欢
    • 2011-09-18
    • 1970-01-01
    • 2015-09-03
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多