I'm trying to calculate e for my assignment using threads for the least amount of time possible, given that the user will pass a variable with the amount of threads that will be used for the calculation, but I can't get my multithreading working properly to achieve some kind of result. I was told that a good method for calculation will be using the following mathematical expression: e = sum( (3-4k^2)/((2k+1)!) ) , where k = (0;infinity). But so far I got only this basic method:
public class MainClass {
public static long fact(int x) {
long p = 1;
for (int i = 1; i <= x; i++)
p = p * i;
return p;
}
public static void main(String[] args) {
double e = 1;
for (int i = 1; i < 50; i++)
e = e + 1 / (double) (fact(i));
System.out.print("e = " + e);
}
}
You can use the Java 8 parrallel Streams it's easier and less error-prone than explcitly creating Threads.
import org.apache.commons.math.util.MathUtils;
...
public static double computeE(){
return IntStream.iterate(0,k->k+1)
.limit(100000)
.mapToDouble(k->(3-4*k*k)/MathUtils.factorialDouble(2*k+1))
.parallel()
.sum();
}
On my machine, it uses the two cores and find e=2.718281828459045 for 10000 iterations which is a value where every digits are correct.
Related
Suppose I am given integers a and b such that a range of interest is formed, of integers in [a,b]. The range can span well over 10^9 integers. I want to sum the values of a given function f : N -> N over all integers a <= n <= b. The range is very large so I want to do this using multithreading.
Less formaly, I want to parallelize the following code:
long sum = 0;
for (long n = a ; n <= b ; n++)
sum += f(n);
System.out.println(sum);
Ideally (at least in my mind), the range will divided equally across the available number of threads available by the processor (suppose f(n) has near-identical complexity and running time for each n in range). The values are completely independent, and f could really be any function. For example, it could output the sum of digits of the number, but it really could be anything, it's just an example.
Is there a general way to do exactly that in Java using multithreading?
This particular use-case fits very well for a parallel stream. See tutorial by Oracle. Use java.util.stream.LongStream class for a stream of 64-bit long integers.
You can implement it like this:
long sum = LongStream.rangeClosed(a, b)
.parallel()
.map(n -> f(n))
.sum();
System.out.println(sum);
You probably want to look into the fork/join framework; it's a generalized approach to this principle of splitting up a task into a great many little independent tasks and then combining them back together, and gives you all control you'd want about how to spin off threads.
Alternatively, you can use the parallel() method of the stream API, but note that this method doesn't explain or guarantee much about how it works. You have no control over which aspect is parallelized, and no control over the amount of threads that'll be involved. For trivial cases you might as well use it, but as a rule, if 'uhoh I better write this in parallel or it would be too slow' is relevant, then you need some guarantees and some control. Here's oracle's tutorial/explanation on parallel streams. For this specific case it does seem like it would more or less do what you want (it gets tricky if e.g. the stream you attempt to apply this to is e.g. what Files.lines gives you - where the parallelism gets potentially stymied by where it is applied vs. where the bottleneck is).
RecursiveTask is suitable for such a problem. Threads will be managed by the ForkJoinPool.
The general idea is to decompose the problem into shorter sub-problems, up to the point a single thread is able to manage the given sub-problem by itself.
class RecursiveSum extends RecursiveTask<Long> {
private final int THLD = 1_000;
private int low, high;
public RecursiveSum(int high) {
this(0,high);
}
public RecursiveSum(int low, int high) {
this.low = low; this.high = high;
}
private long sum(int l,int h) {
long sum = 0;
for (int i=l; i<h; i++) sum += i;
return sum;
}
#Override
protected Long compute() {
int len = high-low;
if (len<=THLD) return sum(low,high);
RecursiveSum rhalf = new RecursiveSum(low+len/2,high);
rhalf.fork();
RecursiveSum lhalf = new RecursiveSum(low,low+len/2);
high = low+len/2;
return lhalf.compute()+rhalf.join();
}
}
and use it like this:
long r = new RecursiveSum(1_000_000_000).invoke();
System.out.println("Sum="+r);
create your sum class (edit 'f' function to your function):
class sumFun implements Callable<Long> {
public long f(long num){
return num;
}
private long a;
private long b;
public sumFun(long a, long b) {
this.a = a;
this.b = b;
}
#Override
public Long call() {
long sum = 0;
for (long i = a; i <= b; i++) {
sum += f(i);
}
return sum;
}
}
in main :
public static void main(String[] args) throws InterruptedException, ExecutionException {
long start = 1;
long end = 10000000;
int numOfThreads = 5;
long step = (end - start + 1) / numOfThreads;
List<sumFun> tasks = new ArrayList<>();
for (long i = start; i <= end; i += step) {
long tmp = end;
if(i+step-1 < end){
tmp = i+step-1;
}
tasks.add(new sumFun(i, tmp));
}
ExecutorService executor = Executors.newFixedThreadPool(numOfThreads);
List<Future<Long>> results = executor.invokeAll(tasks);
long sum = 0;
for (Future<Long> result : results) {
sum += result.get();
}
System.out.println("sum = " + sum);
executor.shutdown();
}
So I have a task to calculate Euler's number using multiple threads, using this formula: sum( ((3k)^2 + 1) / ((3k)!) ), for k = 0...infinity.
import java.math.BigDecimal;
import java.math.BigInteger;
import java.io.FileWriter;
import java.io.IOException;
import java.math.RoundingMode;
class ECalculator {
private BigDecimal sum;
private BigDecimal[] series;
private int length;
public ECalculator(int threadCount) {
this.length = threadCount;
this.sum = new BigDecimal(0);
this.series = new BigDecimal[threadCount];
for (int i = 0; i < this.length; i++) {
this.series[i] = BigDecimal.ZERO;
}
}
public synchronized void addToSum(BigDecimal element) {
this.sum = this.sum.add(element);
}
public void addToSeries(int id, BigDecimal element) {
if (id - 1 < length) {
this.series[id - 1] = this.series[id - 1].add(element);
}
}
public synchronized BigDecimal getSum() {
return this.sum;
}
public BigDecimal getSeriesSum() {
BigDecimal result = BigDecimal.ZERO;
for (int i = 0; i < this.length; i++) {
result = result.add(this.series[i]);
}
return result;
}
}
class ERunnable implements Runnable {
private final int id;
private final int threadCount;
private final int threadRemainder;
private final int elements;
private final boolean quietFlag;
private ECalculator eCalc;
public ERunnable(int threadCount, int threadRemainder, int id, int elements, boolean quietFlag, ECalculator eCalc) {
this.id = id;
this.threadCount = threadCount;
this.threadRemainder = threadRemainder;
this.elements = elements;
this.quietFlag = quietFlag;
this.eCalc = eCalc;
}
#Override
public void run() {
if (!quietFlag) {
System.out.println(String.format("Thread-%d started.", this.id));
}
long start = System.currentTimeMillis();
int k = this.threadRemainder;
int iteration = 0;
BigInteger currentFactorial = BigInteger.valueOf(intFactorial(3 * k));
while (iteration < this.elements) {
if (iteration != 0) {
for (int i = 3 * (k - threadCount) + 1; i <= 3 * k; i++) {
currentFactorial = currentFactorial.multiply(BigInteger.valueOf(i));
}
}
this.eCalc.addToSeries(this.id, new BigDecimal(Math.pow(3 * k, 2) + 1).divide(new BigDecimal(currentFactorial), 100, RoundingMode.HALF_UP));
iteration += 1;
k += this.threadCount;
}
long stop = System.currentTimeMillis();
if (!quietFlag) {
System.out.println(String.format("Thread-%d stopped.", this.id));
System.out.println(String.format("Thread %d execution time: %d milliseconds", this.id, stop - start));
}
}
public int intFactorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
}
public class TaskRunner {
public static final String DEFAULT_FILE_NAME = "result.txt";
public static void main(String[] args) throws InterruptedException {
int threadCount = 2;
int precision = 10000;
int elementsPerTask = precision / threadCount;
int remainingElements = precision % threadCount;
boolean quietFlag = false;
calculate(threadCount, elementsPerTask, remainingElements, quietFlag, DEFAULT_FILE_NAME);
}
public static void writeResult(String filename, String result) {
try {
FileWriter writer = new FileWriter(filename);
writer.write(result);
writer.close();
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
public static void calculate(int threadCount, int elementsPerTask, int remainingElements, boolean quietFlag, String outputFile) throws InterruptedException {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[threadCount];
ECalculator eCalc = new ECalculator(threadCount);
for (int i = 0; i < threadCount; i++) {
if (i == 0) {
threads[i] = new Thread(new ERunnable(threadCount, i, i + 1, elementsPerTask + remainingElements, quietFlag, eCalc));
} else {
threads[i] = new Thread(new ERunnable(threadCount, i, i + 1, elementsPerTask, quietFlag, eCalc));
}
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
String result = eCalc.getSeriesSum().toString();
if (!quietFlag) {
System.out.println("E = " + result);
}
writeResult(outputFile, result);
long stop = System.currentTimeMillis();
System.out.println("Calculated in: " + (stop - start) + " milliseconds" );
}
}
I stripped out the prints, etc. in the code that have no effect. My problem is that the more threads I use the slower it gets. Currently the fastest run I have is for 1 thread. I am sure the factorial calculation is causing some issues. I tried using a thread pool but still got the same times.
How can I make it so that running it with more threads, up until some point, will speed up the calculation process?
How would one go about calculating this big factorials?
The precision parameter that is passed is the amount of elements in the sum that are used. Can I set the BigDecimal scale to be somehow dependent on that precision so I don't hard code it?
EDIT
I updated the code block to be in 1 file only and runnable without external libs.
EDIT 2
I found out that the factorial code messes up with the time. If I let the threads ramp up to some high precision without calculating factorials the time goes down with increasing threads. Yet I cannot implement the factorial calculating in any way while keeping the time decreasing.
EDIT 3
Adjusting code to address answers.
private static BigDecimal partialCalculator(int start, int threadCount, int id) {
BigDecimal nBD = BigDecimal.valueOf(start);
BigDecimal result = nBD.multiply(nBD).multiply(BigDecimal.valueOf(9)).add(BigDecimal.valueOf(1));
for (int i = start; i > 0; i -= threadCount) {
BigDecimal iBD = BigDecimal.valueOf(i);
BigDecimal iBD1 = BigDecimal.valueOf(i - 1);
BigDecimal iBD3 = BigDecimal.valueOf(3).multiply(iBD);
BigDecimal prevNumerator = iBD1.multiply(iBD1).multiply(BigDecimal.valueOf(9)).add(BigDecimal.valueOf(1));
// 3 * i * (3 * i - 1) * (3 * i - 2);
BigDecimal divisor = iBD3.multiply(iBD3.subtract(BigDecimal.valueOf(1))).multiply(iBD3.subtract(BigDecimal.valueOf(2)));
result = result.divide(divisor, 10000, RoundingMode.HALF_EVEN)
.add(prevNumerator);
}
return result;
}
public static void main(String[] args) {
int threadCount = 3;
int precision = 6;
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
ArrayList<Future<BigDecimal> > futures = new ArrayList<Future<BigDecimal> >();
for (int i = 0; i < threadCount; i++) {
int start = precision - i;
System.out.println(start);
final int id = i + 1;
futures.add(executorService.submit(() -> partialCalculator(start, threadCount, id)));
}
BigDecimal result = BigDecimal.ZERO;
try {
for (int i = 0; i < threadCount; i++) {
result = result.add(futures.get(i).get());
}
} catch (Exception e) {
e.printStackTrace();
}
executorService.shutdown();
System.out.println(result);
}
Seems to be working properly for 1 thread but messes up the calculation for multiple.
After a review of the updated code, I've made the following observations:
First of all, the program runs for a fraction of a second. That means that this is a micro benchmark. Several key features in Java make micro benchmarks difficult to implement reliably. See How do I write a correct micro-benchmark in Java? For example, if the program doesn't run enough repetitions, the "just in time" compiler doesn't have time to kick in to compile it to native code, and you end up benchmarking the intepreter. It seems possible that in your case the JIT compiler takes longer to kick in when there are multiple threads,
As an example, to make your program do more work, I changed the BigDecimal precision from 100 to 10,000 and added a loop around the main method. The execution times were measured as follows:
1 thread:
Calculated in: 2803 milliseconds
Calculated in: 1116 milliseconds
Calculated in: 1040 milliseconds
Calculated in: 1066 milliseconds
Calculated in: 1036 milliseconds
2 threads:
Calculated in: 2354 milliseconds
Calculated in: 856 milliseconds
Calculated in: 624 milliseconds
Calculated in: 659 milliseconds
Calculated in: 664 milliseconds
4 threads:
Calculated in: 1961 milliseconds
Calculated in: 797 milliseconds
Calculated in: 623 milliseconds
Calculated in: 536 milliseconds
Calculated in: 497 milliseconds
The second observation is that there is a significant part of the workload that does not benefit from multiple threads: every thread is computing every factorial. This means the speed-up cannot be linear - as described by Amdahl's law.
So how can we get the result without computing factorials? One way is with Horner's method. As an example, consider the simpler series sum(1/k!) which also conveges to e but a little slower than yours.
Let's say you want to compute sum(1/k!) up to k = 100. With Horner's method you start from the end and extract common factors:
sum(1/k!, k=0..n) = 1/100! + 1/99! + 1/98! + ... + 1/1! + 1/0!
= ((... (((1/100 + 1)/99 + 1)/98 + ...)/2 + 1)/1 + 1
See how you start with 1, divide by 100 and add 1, divide by 99 and add 1, divide by 98 and add 1, and so on? That makes a very simple program:
private static BigDecimal serialHornerMethod() {
BigDecimal accumulator = BigDecimal.ONE;
for (int k = 10000; k > 0; k--) {
BigDecimal divisor = new BigDecimal(k);
accumulator = accumulator.divide(divisor, 10000, RoundingMode.HALF_EVEN)
.add(BigDecimal.ONE);
}
return accumulator;
}
Ok that's a serial method, how do you make it use parallel? Here's an example for two threads: First split the series into even and odd terms:
1/100! + 1/99! + 1/98! + 1/97! + ... + 1/1! + 1/0! =
(1/100! + 1/98! + ... + 1/0!) + (1/99! + 1/97! + ... + 1/1!)
Then apply Horner's method to both the even and odd terms:
1/100! + 1/98! + 1/96! + ... + 1/2! + 1/0! =
((((1/(100*99) + 1)/(98*97) + 1)/(96*95) + ...)/(2*1) + 1
and:
1/99! + 1/97! + 1/95! + ... + 1/3! + 1/1! =
((((1/(99*98) + 1)/(97*96) + 1)/(95*94) + ...)/(3*2) + 1
This is just as easy to implement as the serial method, and you get pretty close to linear speedup going from 1 to 2 threads:
private static BigDecimal partialHornerMethod(int start) {
BigDecimal accumulator = BigDecimal.ONE;
for (int i = start; i > 0; i -= 2) {
int f = i * (i + 1);
BigDecimal divisor = new BigDecimal(f);
accumulator = accumulator.divide(divisor, 10000, RoundingMode.HALF_EVEN)
.add(BigDecimal.ONE);
}
return accumulator;
}
// Usage:
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<BigDecimal> submit = executorService.submit(() -> partialHornerMethod(10000));
Future<BigDecimal> submit1 = executorService.submit(() -> partialHornerMethod(9999));
BigDecimal result = submit1.get().add(submit.get());
There is a lot of contention between the threads: they all compete to get a lock on the ECalculator object after every little bit of computation, because of this method:
public synchronized void addToSum(BigDecimal element) {
this.sum = this.sum.add(element);
}
In general having threads compete for frequent access to a common resource leads to bad performance, because you're asking for the operating system to intervene and tell the program which thread can continue. I haven't tested your code to confirm that this is the issue because it's not self-contained.
To fix this, have the threads accumulate their results separately, and merge results after the threads have finished. That is, create a sum variable in ERunnable, and then change the methods:
// ERunnable.run:
this.sum = this.sum.add(new BigDecimal(Math.pow(3 * k, 2) + 1).divide(new BigDecimal(factorial(3 * k)), 100, RoundingMode.HALF_UP));
// TaskRunner.calculate:
for (int i = 0; i < threadCount; i++) {
threads[i].join();
eCalc.addToSum(/* recover the sum computed by thread */);
}
By the way would be easier if you used the higher level java.util.concurrent API instead of creating thread objects yourself. You could wrap the computation in a Callable which can return a result.
Q2 How would one go about calculating this big factorials?
Usually you don't. Instead, you reformulate the problem so that it does not involve the direct computation of factorials. One technique is Horner's method.
Q3 The precision parameter that is passed is the amount of elements in the sum that are used. Can I set the BigDecimal scale to be somehow dependent on that precision so I don't hard code it?
Sure why not. You can work out the error bound from the number of elements (it's proportional to the last term in the series) and set the BigDecimal scale to that.
I've been making my own class library in Java and I've run into a small annoyance. The library is centered around math. I started with the intention of not using the Java Math Class. Unfortunately, my lack of skill paired with my inability to find a resource online that tackles this problem has resulted in me falling back onto the Java Math Class. Is there a way I can do logarithms without using Math.log?
We could try to use a power series as per: https://math.stackexchange.com/a/61283
For example:
#Test
public void printMathCalculation() {
double libValue = Math.log(100);
double myValue = getLn(100);
System.out.println("Actual: \t" + libValue);
System.out.println("Approximate: \t" + myValue);
}
//Take double to an integer power
private double pow(double num, int power) {
double result = 1;
for(int i = 0; i < power; i++) {
result *= num;
}
return result;
}
//Get natural log
private double getLn(double num) {
int accuracy = 1000;
double sum = 0;
for(int n = 0; n < accuracy; n++) {
double num1 = (1.0/(2*n+1));
double num2 = (num-1)/(num+1);
sum += num1*pow(num2,2*n+1);
}
return 2 * sum;
}
The results I get are:
Actual: 4.605170185988092
Approximate: 4.605170185988078
You should use Math.log
Try this, using simple maths to calculate
public static int toLog2N(int num){
return num>1 ? 1 + toLog2N(num/2) : 0;
}
Its just an example, you should use Math class as it provides different methods like log(double a), log10(double a), log1p(double a). It will make your code more readable and easier to understand.
hope it helps!
I'm quite new to coding, as you all can see from the clumsy code below. However, looking at this code you can see what I'm getting at. The code basically does what its supposed to, but I would like to write it as a loop to make it more efficient. Could someone maybe point me in the right direction? I have done some digging and thought about recursion, but I haven't been able to figure out how to apply it here.
public static void main(String[] args) {
double a = 10;
double b = 2;
double c = 3;
double avg = (a + b + c)/3;
double avg1 = (avg + b + c)/3;
double avg2 = (avg1 + b + c)/3;
double avg3 = (avg2 + b + c)/3;
System.out.println(avg+ "\n" + avg1+ "\n"+ avg2 + "\n"+ avg3);
}
Functionally, this would be equivalent to what you have done:
public static void main(String[] args) {
double a = 10;
double b = 2;
double c = 3;
double avg = (a + b + c)/3;
System.out.println(avg);
for (int i=0; i<3; i++) {
avg = (avg + b + c)/3;
System.out.println(avg);
}
}
But also you should know that shorter code does not always mean efficient code. The solution may be more concise, but I doubt there will be any change in performance.
If you mean shorter code with efficieny you can do it like this.
public static void main(String[] args) {
double a = 10;
double b = 2;
double c = 3;
for (int i = 0; i < 4; i++) {
a = (a + b + c) / 3;
System.out.println(a);
}
}
I have no idea what this calculation represents (some sort of specialised weighted average?) but rather than use repetition and loops, you can reach the exact same calculation by using a bit of algebra and refactoring the terms:
public static double directlyCalculateWeightedAverage(double a, double b,
double c) {
return a / 81 + 40 * b / 81 + 40 * c / 81;
}
This reformulation is reached because the factor a appears just once in the mix and is then divided by 34 which is 81. Then each of b and c appear at various levels of division, so that b sums to this:
b/81 + b/27 + b/9 + b/3
== b/81 + 3b/81 + 9b/81 + 27b/81
== 40b/81
and c is treated exactly the same.
Which gives the direct calculation
a/81 + 40b/81 + 40c/81
Assuming your formula does not change, I'd recommend using this direct approach rather than resorting to repeated calculations and loops.
Your problem can be solved by 2 approaches: iterative (with a loop) or recursive (with a recursive function).
Iterative approach : for loop
The for loop allow you to repeat a group of instructions au given number of times.
In your case, you could write the following :
double a = 10, b = 2, c = 3;
double avg = a;
for (int i = 0; i < 4; i++) {
avg = (avg + b + c) / 3;
System.out.println(avg);
}
This will print the 4 first results of your calculation.
In my example, I overwrite the variable avg to only keep the last result, which might not be what you want. To keep the result of each loop iteration, you may store the result in an array.
Recursive approach
In Java, there is no such thing as a standalone function. In order to use recursion, you have to use static methods :
private static double recursiveAvg(double avg, int count) {
// Always check that the condition for your recursion is valid !
if (count == 0) {
return avg;
}
// Apply your formula
avg = (avg + 2 + 3) / 3;
// Call the same function with the new avg value, and decrease the iteration count.
return recursiveAvg(avg, count - 1);
}
public static void main(String[] args) {
// Start from a = 10, and repeat the operation 4 times.
double avg = recursiveAvg(10, 4);
System.out.println(avg);
}
Always check for a condition that will end the recursion. In our example, it's the number of times the operation should be performed.
Note that most programmers prefer the iterative approach : easier to write and read, and less error prone.
In my computer architecture class I just learned that running an algebraic expression involving multiplication through a multiplication circuit can be more costly than running it though an addition circuit, if the number of required multiplications are less than 3. Ex: 3x. If I'm doing this type of computation a few billion times, does it pay off to write it as: x + x + x or does the JIT optimizer optimize for this?
I wouldn't expect to be a huge difference on writing it one way or the other.
The compiler will probably take care of making all of those equivalent.
You can try each method and measure how long it takes, that could give you a good hint to answer your own question.
Here's some code that does the same calculations 10 million times using different approaches (x + x + x, 3*x, and a bit shift followed by a subtraction).
They seem to all take approx the same amount of time as measured by System.nanoTime.
Sample output for one run:
sum : 594599531
mult : 568783654
shift : 564081012
You can also take a look at this question that talks about how compiler's optimization can probably handle those and more complex cases: Is shifting bits faster than multiplying and dividing in Java? .NET?
Code:
import java.util.Random;
public class TestOptimization {
public static void main(String args[]) {
Random rn = new Random();
long l1 = 0, l2 = 0, l3 = 0;
long nano1 = System.nanoTime();
for (int i = 1; i < 10000000; i++) {
int num = rn.nextInt(100);
l1 += sum(num);
}
long nano2 = System.nanoTime();
for (int i = 1; i < 10000000; i++) {
int num = rn.nextInt(100);
l2 += mult(num);
}
long nano3 = System.nanoTime();
for (int i = 1; i < 10000000; i++) {
int num = rn.nextInt(100);
l3 += shift(num);
}
long nano4 = System.nanoTime();
System.out.println(l1);
System.out.println(l2);
System.out.println(l3);
System.out.println("sum : " + (nano2 - nano1));
System.out.println("mult : " + (nano3 - nano2));
System.out.println("shift : " + (nano4 - nano3));
}
private static long sum(long x) {
return x + x + x;
}
private static long mult(long x) {
return 3 * x;
}
private static long shift(long x) {
return (x << 2) - x;
}
}