Time difference for random number generation implementation in Java vs. C++ - java

I'm writing a Monte Carlo simulation in Java that involves generating a lot of random integers. My thinking was that native code would be faster for random number generation, so I should write the code in C++ and return the output via JNI. But when I wrote the same method in C++, it actually takes longer to execute than the Java version. Here are the code samples:
Random rand = new Random();
int threshold = 5;
int[] composition = {10, 10, 10, 10, 10};
for (int j = 0; j < 100000000; j++) {
rand.setSeed(System.nanoTime());
double sum = 0;
for (int i = 0; i < composition[0]; i++) sum += carbon(rand);
for (int i = 0; i < composition[1]; i++) sum += hydrogen(rand);
for (int i = 0; i < composition[2]; i++) sum += nitrogen(rand);
for (int i = 0; i < composition[3]; i++) sum += oxygen(rand);
for (int i = 0; i < composition[4]; i++) sum += sulfur(rand);
if (sum < threshold) {}//execute some code
else {}//execute some other code
}
And the equivalent code in C++:
int threshold = 5;
int composition [5] = {10, 10, 10, 10, 10};
for (int i = 0; i < 100000000; i++)
{
srand(time(0));
double sum = 0;
for (int i = 0; i < composition[0]; i++) sum += carbon();
for (int i = 0; i < composition[1]; i++) sum += hydrogen();
for (int i = 0; i < composition[2]; i++) sum += nitrogen();
for (int i = 0; i < composition[3]; i++) sum += oxygen();
for (int i = 0; i < composition[4]; i++) sum += sulfur();
if (sum > threshold) {}
else {}
}
All of the element methods (carbon, hydrogen, etc) just generate a random number and return a double.
Runtimes are 77.471 sec for the Java code, and 121.777 sec for C++.
Admittedly I'm not very experienced in C++ so it's possible that the cause is just badly written code.

I suspect that the performance issue is in the bodies of your carbon(), hydrogen(), nitrogen(), oxygen(), and sulfur() functions. You should show how they produce the random data.
Or it could be in the if (sum < threshold) {} else {} code.
I wanted to keep setting the seed so the results would not be deterministic (closer to being truly random)
Since you're using the result of time(0) as a seed you're not getting particularly random results either way.
Instead of using srand() and rand() you should take a look at the <random> library and choose an engine with the performance/quality characteristics that meed your needs. If your implementation supports it you can even get non-deterministic random data from std::random_device (either to generate seeds or as an engine).
Additionally <random> provides pre-made distributions such as std::uniform_real_distribution<double> which is likely to be better than the average programmer's method of manually computing the distribution you want from the results of rand().
Okay, here's how you can eliminate the inner loops from your code and drastically speed it up (In Java or C++).
Your code:
double carbon() {
if (rand() % 10000 < 107)
return 13.0033548378;
else
return 12.0;
}
picks one of two values with a particular probability. Presumably you intended the first value to be picked about 107 times out of 10000 (although using % with rand() doesn't quite give you that). When you run this in a loop and sum the results as in:
for (int i = 0; i < composition[0]; i++) sum += carbon();
you'll essentially get sum += X*13.0033548378 + Y*12.0; where X is the number of times the random number stays under the threshold and Y is (trials-X). It just so happens that you can simulate running a bunch of trials and calculating the number of successes using a binomial distribution, and <random> happens to provide a binomial distribution.
Given a function sum_trials()
std::minstd_rand0 eng; // global random engine
double sum_trials(int trials, double probability, double A, double B) {
std::binomial_distribution<> dist(trials, probability);
int successes = dist(eng);
return successes*A + (trials-successes)*B;
}
You can replace your carbon() loop:
sum += sum_trials(composition[0], 107.0/10000.0, 13.003354378, 12.0); // carbon trials
I don't have the actual values you're using, but your whole loop will look something like:
for (int i = 0; i < 100000000; i++) {
double sum = 0;
sum += sum_trials(composition[0], 107.0/10000.0, 13.003354378, 12.0); // carbon trials
sum += sum_trials(composition[1], 107.0/10000.0, 13.003354378, 12.0); // hydrogen trials
sum += sum_trials(composition[2], 107.0/10000.0, 13.003354378, 12.0); // nitrogen trials
sum += sum_trials(composition[3], 107.0/10000.0, 13.003354378, 12.0); // oxygen trials
sum += sum_trials(composition[4], 107.0/10000.0, 13.003354378, 12.0); // sulfur trials
if (sum > threshold) {
} else {
}
}
Now one thing to note is that inside the function we're constructing distributions over and over with the same data. We can extract that by replacing the function sum_trials() with a function object, which we construct with the appropriate data once before the loop, and then just use the functor repeatedly:
struct sum_trials {
std::binomial_distribution<> dist;
double A; double B; int trials;
sum_trials(int t, double p, double a, double b) : dist{t, p}, A{a}, B{b}, trials{t} {}
double operator() () {
int successes = dist(eng);
return successes * A + (trials - successes) * B;
}
};
int main() {
int threshold = 5;
int composition[5] = { 10, 10, 10, 10, 10 };
sum_trials carbon = { composition[0], 107.0/10000.0, 13.003354378, 12.0};
sum_trials hydrogen = { composition[1], 107.0/10000.0, 13.003354378, 12.0};
sum_trials nitrogen = { composition[2], 107.0/10000.0, 13.003354378, 12.0};
sum_trials oxygen = { composition[3], 107.0/10000.0, 13.003354378, 12.0};
sum_trials sulfur = { composition[4], 107.0/10000.0, 13.003354378, 12.0};
for (int i = 0; i < 100000000; i++) {
double sum = 0;
sum += carbon();
sum += hydrogen();
sum += nitrogen();
sum += oxygen();
sum += sulfur();
if (sum > threshold) {
} else {
}
}
}
The original version of the code took my system about one minute 30 seconds. The last version here takes 11 seconds.
Here's a functor to generate the oxygen sums using two binomial_distributions. Maybe one of the other distributions can do this in one shot but I don't know.
struct sum_trials2 {
std::binomial_distribution<> d1;
std::binomial_distribution<> d2;
double A; double B; double C;
int trials;
double probabilty2;
sum_trials2(int t, double p1, double p2, double a, double b, double c)
: d1{t, p1}, A{a}, B{b}, C{c}, trials{t}, probability2{p2} {}
double operator() () {
int X = d1(eng);
d2.param(std::binomial_distribution<>{trials-X, p2}.param());
int Y = d2(eng);
return X*A + Y*B + (trials-X-Y)*C;
}
};
sum_trials2 oxygen{composition[3], 17.0/1000.0, (47.0-17.0)/(1000.0-17.0), 17.9999, 16.999, 15.999};
You can further speed this up if you can just calculate the probability that the sum is under your threshold:
int main() {
std::minstd_rand0 eng;
std::bernoulli_distribution dist(probability_sum_is_over_threshold);
for (int i=0; i< 100000000; ++i) {
if (dist(eng)) {
} else {
}
}
}
Unless the values for the other elements can be negative then the probability that the sum is greater than five is 100%. In that case you don't even need to generate random data; execute the 'if' branch of your code 100,000,000 times.
int main() {
for (int i=0; i< 100000000; ++i) {
//execute some code
}
}

Java (actually the JIT) is generally very good at detecting code which doesn't do anything useful. This is because the JIT can obtain information at runtime a static compiler cannot determine. For code which can be optimised away, Java can actually be faster than C++. In general however, a well tuned C++ program is faster than one in Java.
In short, given any amount of time, C++ will be faster for a well understand, well tuned program. However, given limited resources, changing requirements and teams of mixed ability Java can often outperform C++ by a significant margin.
All that said, it could be that the random in C++ is better, but more expensive.

Related

Java: Expression including binomial coefficient calculation

Task is to calculate expression for natural numbers entered.
I know I should calculate binominal coefficient here right?
Also I know that (-1)^p determines whether this array is decrementing or incrementing, but don't know how to use p in my code
I am not quite sure how to put it all together, this is what I came up with so far and it is really nothing special as I still can't grasp on the idea of how to write this in program.
public static int calculateExpression(int n, int k,int p) {
if(k<0 || n<k)
{
return 0;
}
// Find factorial of n
int n_fac = 1;
for (int j = 1; j <= n; j++) {
n_fac = n_fac * j;
}
// Find factorial of k
int k_fac = 1;
for(int i = 1; i<=k; i++) {
k_fac = k_fac * i;
}
// Find n-k fac
int n_k = n-k;
int n_k_fac = 1;
for(int l = 1; l<=n_k;l++) {
n_k_fac*=l;
}
// n/k = n!/k!(n-k)!
double resultOf_n_kDivision = n_fac/k_fac*n_k_fa;
System.out.println(resultOf_n_kDivision);
return n_k_fac;
}
The factorial function is a very fast-growing one, so calculating the numerator and denominator separately may not be a good idea, as it may lead to overflow for even relatively small values of n.
Let's look at an iterative method for calculating the coefficient:
We see that we can calculate the next coefficient of the row if we know the current one. Thus we can incrementally calculate each term in S, while being less concerned about overflow problems.
static int calculateExpression(int n, int k, int p)
{
// first term of the row is (n, 0) = 1
int binom = 1;
// Iteratively find (n, k)
for (int i = 0; i < k; i++)
binom = (binom * (n - i)) / (i + 1);
// sum the series
int S = binom;
for (int i = 0; i < p; i++) {
// "trick": multiply with a minus sign each time to flip the sign
binom = (-binom * (n - k - i)) / (k + i + 1);
S += binom;
}
return S;
}
UPDATE: Parallel numerical tests:
n k p | orig new
----------------------
5 3 2 | 6 6
10 4 1 | -42 -42
12 3 7 | 44 44
15 8 6 | 3433 8 // integer overflow occurred with the original method
As you can see the two functions were consistent until the last line with n = 15, as 15! = 1307674368000 is much bigger than the maximum positive value of int in most implementations of Java (32-bit).
Use abstraction for better tackling problems; define fac and over.
Then the problem becomes:
public static int calculateExpression(int n, int k,int p) {
int sum = 0;
int minus1toP = 1;
for (int i = 0; i <= p; i++) {
sum += minus1toP * over(n, ...);
minus1toP = -minus1toP;
}
return sum;
}
static int over(int n, int k) {
return fac(n) / fac(k) / fac(n - k);
}
static int fac(int n) {
int f = 1;
for(int i = 2; i <= n; i++) {
f *= i;
}
return f;
}
I did not give the entire solution (...), but maybe too much already.
I did not really get your question, but you can just use this.
public static double combination(int n, int k)
{
double nFactorial = getFactorialFromNToK(n, k);
double kFactorial = getFactorialFromNToK(k, 1);
return nFactorial / kFactorial;
}
public static double getFactorialFromNToK(double n, double k)
{
double factorial = 1;
for (; n - k + 1 > 0; n--)
{
factorial *= n;
}
return factorial;
}
This is the evaluation of nCk for the coef of a term in the binomial expansion.
If nCn is a term in the expansion, then it converges and if it does not exist as term in the expansion, then it will not converge. So if it is a natural number expansion, then it will always converge.
A better solution is to use the lngamma function instead of factorial. It's a more efficient way to calculate factorials. The natural log means that dividing large numbers will be less of a problem.

Sample without replacement in Java with probabilities

I have a list of 10 probabilities (assume these are sorted in descending order): <p1, p2, ..., p10>. I want to sample (without replacement) 10 elements such that the probability of selecting i-th index is p_i.
Is there a ready to use Java method in common libraries like Random, etc that I could use to do that?
Example: 5-element list: <0.4,0.3,0.2,0.1,0.0>
Select 5 indexes (no duplicates) such that their probability of selection is given by the probability at that index in the list above. So index 0 would be selected with probability 0.4, index 1 selected with prob 0.3 and so on.
I have written my own method to do that but feel that an existing method would be better to use. If you are aware of such a method, please let me know.
This is how this is typically done:
static int sample(double[] pdf) {
// Transform your probabilities into a cumulative distribution
double[] cdf = new double[pdf.length];
cdf[0] = pdf[0];
for(int i = 1; i < pdf.length; i++)
cdf[i] += pdf[i] + cdf[i-1];
// Let r be a probability [0,1]
double r = Math.random();
// Search the bin corresponding to that quantile
int k = Arrays.binarySearch(cdf, random.nextDouble());
k = k >= 0 ? k : (-k-1);
return k;
}
If you want to return a probability do:
return pdf[k];
EDIT: I just noticed you say in the title sampling without replacement. This is not so trivial to do fast (I can give you some code I have for that). Anyhow, your question does not make any sense in that case. You cannot sample without replacement from a probability distribution. You need absolute frequencies.
i.e. If I tell you that I have a box filled with two balls: orange and blue with the proportions 20% and 80%. If you do not tell me how many balls you have of each (in absolute terms), I cannot tell you how many balls you will have in a few turns.
EDIT2: A faster version. This is not how it is typically but I have found this suggestion on the web, and I have used it in projects of mine as well.
static int sample(double[] pdf) {
double r = random.nextDouble();
for(int i = 0; i < pdf.length; i++) {
if(r < pdf[i])
return i;
r -= pdf[i];
}
return pdf.length-1; // should not happen
}
To test this:
// javac Test.java && java Test
import java.util.Arrays;
import java.util.Random;
class Test
{
static Random random = new Random();
public static void sample(double[] pdf) {
...
}
public static void main(String[] args) {
double[] pdf = new double[] { 0.3, 0.4, 0.2, 0.1 };
int[] counts = new int[pdf.length];
final int tests = 1000000;
for(int i = 0; i < tests; i++)
counts[sample(pdf)]++;
for(int i = 0; i < counts.length; i++)
System.out.println(counts[i] / (double)tests);
}
}
You can see we get output very similar to the PDF that was used:
0.3001356
0.399643
0.2001143
0.1001071
This are the times I get when running each version:
1st version: 0m0.680s
2nd version: 0m0.296s
Use sample[i] as index of your values array.
public static int[] WithoutReplacement(int m, int n) {
int[] perm = new int[n];
for (int i = 0; i < n; i++) {
perm[i] = i;
}
//take sample
for (int i = 0; i < m; i++) {
int r = i + (int) (Math.random() * (n - 1));
int tmp = perm[i];
perm[i] = perm[r];
perm[r] = tmp;
}
int[] sample = new int[m];
for (int i = 0; i < m; i++) {
sample[i] = perm[i];
}
return sample;
}

Converting Complex to ArrayList<Float> in Java

I have an input signal that I want to store in an ArrayList then convert it into Complex, which goes something like this
-0.03480425839330703
0.07910192950176387
0.7233322451735928
0.1659819820667019
and this outputs its FFT like this
0.9336118983487516
-0.7581365035668999 + 0.08688005256493803i
0.44344407521182005
-0.7581365035668999 - 0.08688005256493803i
This is in a complex structure, I want to convert this into an ArrayList type. while dropping the + 0.08688005256493803i value.
So All I need are these values
0.9336118983487516
-0.7581365035668999
0.44344407521182005
-0.7581365035668999
What is the best way of going about this?
And this is the code that I am using
public static Complex[] fft(Complex[] x) {
int N = x.length;
// base case
if (N == 1) return new Complex[] { x[0] };
// radix 2 Cooley-Tukey FFT
if (N % 2 != 0) { throw new RuntimeException("N is not a power of 2"); }
// fft of even terms
Complex[] even = new Complex[N/2];
for (int k = 0; k < N/2; k++) {
even[k] = x[2*k];
}
Complex[] q = fft(even);
// fft of odd terms
Complex[] odd = even; // reuse the array
for (int k = 0; k < N/2; k++) {
odd[k] = x[2*k + 1];
}
Complex[] r = fft(odd);
// combine
Complex[] y = new Complex[N];
for (int k = 0; k < N/2; k++) {
double kth = -2 * k * Math.PI / N;
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
y[k] = q[k].plus(wk.times(r[k]));
y[k + N/2] = q[k].minus(wk.times(r[k]));
}
return y;
}
All you want to do is just drop imaginary part of your Complex data structure.
As you not show us Complex class assume it has member for real part (e.g double real;)
To drop imaginary part just call something like complex.getRealPart(), or access complex.real (substitute with your real member name).
To compose ArrayList<Double> use the following snippet:
ArrayList<Double> list = new ArrayList<Double>();
for (Complex c : complexes) { // complexes your array of complexes returned from for fft
list.add(c.getRealpart());
}
Note: Just in case, I can be wrong, but I assume that instead of real part you need absolute value of complex number. To calculate it use:
Math.sqrt(c.getRealPart() * c.getRealPart() + c.getImPart() * c.getImPart());
From what I understand you just want the real part of the complex value. If that's the case, presumably your Complex class also has getReal() and getImaginary() (or similar) methods - so just use getReal().

Numerically stable way to calculate normal log likelihood

I am trying to calculate the normal log likelihood which is given by:
L = l1+l2+l3+...+ln,
where
lk = log(1/(sqrt(2*PI)*sigma_k))-0.5*e_k*e_k
Sigmas are around 0.2, and e_k are normally distributed with mean 0 and unit variance, so most of them are between -2 and 2;
I tried the following java code (sigma_k mentioned above = sigmas.get(k)*Math.sqrt(dt)):
private double new1(List<Double> residuals, List<Double> sigmas, double dt) {
double a = 0;
for(int i=0; i<sigmas.size(); i++) {
a += Math.log(1.0/(Math.sqrt(2*Math.PI*dt)*sigmas.get(i)));
}
double b = 0;
for(int i=0; i<residuals.size(); i++) {
b += residuals.get(i)*residuals.get(i);
}
return a-0.5*b;
}
but the theoretical maximum is lower than the maximum I got by doing numerical optimisation, so I have some suspicions that my method is suboptimal.
Remark:
In some areas probability/statistics are calculated without taking the log, for instance in linguistic frequencies of combinations.
The following simplifies, becoming less stable, but afterwards one convert it back to a sum of logs or so.
double a = 0;
for(int i=0; i<sigmas.size(); i++) {
a += Math.log(1.0/(Math.sqrt(2*Math.PI*dt)*sigmas.get(i)));
}
log(x) + log(y) = log(x*y)
double a = 1.0;
for(int i=0; i<sigmas.size(); i++) {
a *= 1.0/(Math.sqrt(2*Math.PI*dt)*sigmas.get(i));
}
a = Math.log(a);
(1/x)*(1/y) = 1/(x*y)
double a = 1.0;
for(int i=0; i<sigmas.size(); i++) {
a *= Math.sqrt(2*Math.PI*dt)*sigmas.get(i);
}
a = Math.log(1.0/a);
sqrt(x)^n = (x^0.5)^n = x^(n/2)
static import Math.*;
double a = pow(2*PI*dt, sigmas.size() / 2.0);
for(int i=0; i<sigmas.size(); i++) {
a *= sigmas.get(i);
}
a = -log(a);
I'm not sure if it will greatly improve numerical stability, but your equations can be simplified using logarithm laws:
log(a*b) = log(a) + log(b)
log(1/a) = -log(a)
log(sqrt(a)) = log(a)/2
so you have:
lk = -log(2*pi)/2 - log(sigma_k) - 0.5*e_k*e_k
= -log(2*pi)/2 - log(dt)/2 - log(sigmas.get(k)) - 0.5*e_k*e_k
= -log(2*pi*dt)/2 - log(sigmas.get(k)) - 0.5*e_k*e_k
First is constant, so in the first loop you only need to do a -= log(sigmas.get(k)).
Also, it look suspicious, that first loop is to sigmas.size() and the second to residuals.size() while the equation suggests, that they should have the same length.

Translating equivalent formulas in to code isn't giving correct results

I'm trying to calculate the Mean Difference average of a set of data. I have two (supposedly equivalent) formulas which calculate this, with one being more efficient (O^n) than the other (O^n2).
The problem is that while the inefficient formula gives correct output, the efficient one does not. Just by looking at both formulas I had a hunch that they weren't equivalent, but wrote it off because the derivation was made by a statician in a scientific journal. So i'm assuming the problem is my translation. Can anyone help me translate the efficient function properly?
Inefficient formula:
Inefficient formula translation (Java):
public static double calculateMeanDifference(ArrayList<Integer> valuesArrayList)
{
int valuesArrayListSize = valuesArrayList.size();
int sum = 0;
for(int i = 0; i < valuesArrayListSize; i++)
{
for(int j = 0; j < valuesArrayListSize; j++)
sum += (i != j ? Math.abs(valuesArrayList.get(i) - valuesArrayList.get(j)) : 0);
}
return new Double( (sum * 1.0)/ (valuesArrayListSize * (valuesArrayListSize - 1)));
}
Efficient derived formula:
where (sorry, don't know how to use MathML on here):
x(subscript i) = the ith order statistic of the data set
x(bar) = the mean of the data set
Efficient derived formula translation (Java):
public static double calculateMean(ArrayList<Integer> valuesArrayList)
{
double sum = 0;
int valuesArrayListSize = valuesArrayList.size();
for(int i = 0; i < valuesArrayListSize; i++)
sum += valuesArrayList.get(i);
return sum / (valuesArrayListSize * 1.0);
}
public static double calculateMeanDifference(ArrayList<Integer> valuesArrayList)
{
double sum = 0;
double mean = calculateMean(valuesArrayList);
int size = valuesArrayList.size();
double rightHandTerm = mean * size * (size + 1);
double denominator = (size * (size - 1)) / 2.0;
Collections.sort(valuesArrayList);
for(int i = 0; i < size; i++)
sum += (i * valuesArrayList.get(i) - rightHandTerm);
double meanDifference = (2 * sum) / denominator;
return meanDifference;
}
My data set consists of a collection of integers each having a value bounded by the set [0,5].
Randomly generating such sets and using the two functions on them gives different results. The inefficient one seems to be the one producing results in line with what is being measured: the absolute average difference between any two values in the set.
Can anyone tell me what's wrong with my translation?
EDIT: I created a simpler implementation that is O(N) provided the all your data has values limited to a relatively small set.The formula sticks to the methodology of the first method and thus, gives identical results to it (unlike the derived formula). If it fits your use case, I suggest people use this instead of the derived efficient formula, especially since the latter seems to give negative values when N is small).
Efficient, non-derived translation (Java):
public static double calculateMeanDifference3(ArrayList<Integer> valuesArrayList)
{
HashMap<Integer, Double> valueCountsHashMap = new HashMap<Integer, Double>();
double size = valuesArrayList.size();
for(int i = 0; i < size; i++)
{
int currentValue = valuesArrayList.get(i);
if(!valueCountsHashMap.containsKey(currentValue))
valueCountsHashMap.put(currentValue, new Double(1));
else
valueCountsHashMap.put(currentValue, valueCountsHashMap.get(currentValue)+ 1);
}
double sum = 0;
for(Map.Entry<Integer, Double> valueCountKeyValuePair : valueCountsHashMap.entrySet())
{
int currentValue = valueCountKeyValuePair.getKey();
Double currentCount = valueCountKeyValuePair.getValue();
for(Map.Entry<Integer, Double> valueCountKeyValuePair1 : valueCountsHashMap.entrySet())
{
int loopValue = valueCountKeyValuePair1.getKey();
Double loopCount = valueCountKeyValuePair1.getValue();
sum += (currentValue != loopValue ? Math.abs(currentValue - loopValue) * loopCount * currentCount : 0);
}
}
return new Double( sum/ (size * (size - 1)));
}
Your interpretation of sum += (i * valuesArrayList.get(i) - rightHandTerm); is wrong, it should be sum += i * valuesArrayList.get(i);, then after your for, double meanDifference = ((2 * sum) - rightHandTerm) / denominator;
Both equations yields about the same value, but they are not equal. Still, this should help you a little.
You subtract rightHandTerm on each iteration, so it gets [over]multiplied to N.
The big Sigma in the nominator touches only (i x_i), not the right hand term.
One more note: mean * size == sum. You don't have to divide sum by N and then remultiply it back.

Categories

Resources