I am writing a program that solves a sum of tenth powers problem and I need to have a fast algorithm to find n^10
as well as n^(1/10)
For natural n<1 000 000
. I am precomputing an array of powers, so n^10 (array lookup) takes O(1). For n^(1/10)
I am doing a binary search. Is there any way to accelerate extraction of a root beyond that? For example, making an array and filling elements with corresponding roots if the index is a perfect power or leaving zero otherwise would give O(1), but I will run out of memory. Is there a way to make root extraction faster than O(log(n))?
-
Do you need only the integer part of `n^(1/10)`? If no, you can use the stupid (exp(b*ln(a)) formula instead of precomputing, and probably cache the output. Also, if you can quickly log() of your n, you will need to only check adjacent potential roots log-wise, no need of binary search vs whole array if you can almost pinpoint the potential root. – Vesper Jun 30 '16 at 13:14
-
There are only 4 10th powers less than 1 million: 0, 1, 1024, and 59049, so discovering if a particular n is a tenth power should simply be a matter of testing if it's one of these numbers. Perhaps I misunderstand. – Paul Hankin Jun 30 '16 at 13:19
-
I don't need the fractional part, i am solving an equation in natural numbers. I have one million numbers to check, so the largest number will have one followed by 6*10=60 zeros. Precomputing will speed up search. – Waty Jun 30 '16 at 16:12
-
@paul-hankin the largest number is 1000000^10, not 3^10. – Waty Jun 30 '16 at 16:14
2 Answers
Why should the array of roots run out of memory? If it is the same size as the array of powers, it will fit using the same datatypes. However for the powers, (10^6)^10 = 10^60, which does not fit into a long variable so you need to use biginteger or bigdecimal types. In case your number n
is bigger than the biggest array size n_max your memory can afford, you can divide n
by n_m until it fits, i.e. split n = n_max^m*k, where m is a natural number and k < n_max:
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));
}
}

- 11,100
- 16
- 60
- 118
-
I am saying, create int array[100], place array[1]=1,array[4]=2,...array[100]=10, and filling rest with zeros gives O(1) search for square root, but requires N^2 memory. Making array[2]=4, array[3]=9 requires N memory, but isO(log(n)) complexity because relies on a binary search. While calculating square is still O(1). – Waty Jun 30 '16 at 14:42
-
Yes, i am using BigInteger. My question is - can I find a root faster than a binary search (which is log(n))? – Waty Jun 30 '16 at 14:44
Apart LUT You got two options to speed up I can think of:
use binary search without multiplication
If you are using bignums then 10th-root binary search search is not
O(log(n))
anymore as the basic operation used in it are no longerO(1)
!!! For example+,-,<<,>>,|,&,^,>=,<=,>,<,==,!=
will becameO(b)
and*
will beO(b^2)
orO(b.log(b))
whereb=log(n)
depending on algorithm used (or even operand magnitude). So naive binary search for root finding will be in the better caseO(log^2(n).log(log(n)))
To speedup it you can try not to use multiplication. Yes it is possible and the final complexity will bee
O(log^2(n))
Take a look at:To see how to achieve this. The difference is only in solving different equations:
x1 = x0+m x1^10 = f(x0,m)
If you obtain algebraically
x1=f(x0,m)
then each multiplication inside translate to bit-shifts and adds... For example10*x = x<<1 + x<<3
. The LUT table is not necessary as you can iterate it during binary search. I imagine thatf(x0,m)
will contain lesser powers ofx0
so analogically compute all the needed powers too ... so the final result will have no powering. Sorry too lazy to do that for you, you can use some math app for that like Derive for Windowsyou can use
pow(x,y) = x^y = exp2(y*log2(x))
So
x^0.1 = exp2(log2(x)/10)
But you would need bigdecimals for this (or fixed point) here see how I do it:
For more ideas see this: