Reputation: 23
I am given a number k and I have to find every (2k) factorial from [0;k]; for example 0, 2!, 4!, 6!, etc. I have attempted a solution to save the values in a map and for each kth value to use the (k-1)th result like this:
private Map<Long, BigInteger> cache;
private FactorialCache(int k) {//
cache = new HashMap<>();
calculate(k);
System.out.println("last item " + k);
}
private void calculate(int k) {
BigInteger result = BigInteger.ONE;
cache.put(0l, result);
cache.put(1l, result);
for (long i = 2; i <= k; i += 1) {
BigInteger currentRes = cache.get(i - 1).multiply(BigInteger.valueOf(i));
cache.put(i, currentRes);
}
}
However, I was curious if there was a faster approach to find and save these specific factorials?
Upvotes: 1
Views: 177
Reputation:
n! = (n - 2)! * n * (n - 1)
Try this.
private void FactorialCache(int k) {
cache = new HashMap<>();
BigInteger result = BigInteger.ONE;
cache.put(0L, result);
for (long i = 2; i <= k; i += 2)
cache.put(i, result = result.multiply(BigInteger.valueOf(i * (i - 1))));
}
Or you can use an array instead of Map.
private BigInteger[] Factorials(int k) {
BigInteger[] results = new BigInteger[k + 1];
BigInteger result = BigInteger.ONE;
results[0] = result;
for (int i = 2; i <= k; i += 2)
results[i] = result = result.multiply(BigInteger.valueOf(i * (i - 1)));
return results;
}
and
BigInteger[] results = Factorials(40000);
System.out.println("10! = " + results[10]);
// -> 10! = 3628800
Upvotes: 0
Reputation: 966
So one possible solution to this is using dynamic programming and saving the data in an array rather than using a map.
I went ahead and tried to recreate your code using this approach (you said 2k! in your question, but didn't have that in your code so I did implement that), and I got about a ~25% improvement in speed for larger k. The main drawback of using this approach is you have to define the size of the array beforehand.
The Code:
public static BigInteger fact(int k, BigInteger[] arr) {
if (k > 0) {
arr[0] = BigInteger.valueOf(1);
k *= 2;
for (int i = 1; i <= k; ++i) {
arr[i] = BigInteger.valueOf(i).multiply(arr[i - 1]);
}
} else {
System.out.println("A negative k was entered.");
return null;
}
return arr[k];
}
Using this approach with a k value of 2500 (5000 with your code) and calculating the runtime using nanoTime()
using my approach I got a time to compute of 20787300 whereas with your code I got a time to compute of 27325500.
You can read more about dynamic programming here.
Upvotes: 1
Reputation: 7923
This is not an answer, but rather an extended comment.
If your goal is to compute factorials, you cannot get seriously faster. However, I assume that this question is asked in the context of your previous question, regarding summation of a certain series,
∑(2k+1)/(2k)! , k =0,... ,∞
If I am correct, the answer is to not compute factorials at all. Use a Horner schedule instead. Your sum can be expressed as
1 + 1/(1*2)(3 + 1/(3*4)(5 + 1/(5*6)(7 + 1/(7*8)(9 + ...)))...)))
See how factorials disappear. Now fix a number of terms you want to add, and work this expression inside out.
Finding a number of terms to achieve a desired precision is a totally different topic. As I mentioned in the comment once, a Taylor theorem is very helpful.
Upvotes: 5