I am trying to find the k nearest neighbors with the Knn classifier in OpenCV.
I found this C++ Code:
class atsKNN{
public :
void knn(cv::Mat& trainingData, cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses, int K)
{
cv::KNearest knn(trainingData, trainingClasses, cv::Mat(), false, K);
cv::Mat predicted(testClasses.rows, 1, CV_32F);
for(int i = 0; i < testData.rows; i++) {
const cv::Mat sample = testData.row(i);
predicted.at<float>(i,0) = knn.find_nearest(sample, K);
}
float percentage = evaluate(predicted, testClasses) * 100;
cout << "K Nearest Neighbor Evaluated Accuracy = " << percentage << "%" << endl;
prediction = predicted;
}
void showplot(cv::Mat testData)
{
plot_binary(testData, prediction, "Predictions Backpropagation");
}
private:
cv::Mat prediction;
};
The comments mention it works really good but i am having problems Converting it to Java. There is no Documentation for Java. I tried using a C++ to Java Converter but the resulting Code does not work.
here is the code it produced:
public class atsKNN
{
public final void knn(cv.Mat trainingData, cv.Mat trainingClasses, cv.Mat testData, cv.Mat testClasses, int K)
{
cv.KNearest knn = new cv.KNearest(trainingData, trainingClasses, cv.Mat(), false, K);
cv.Mat predicted = new cv.Mat(testClasses.rows, 1, CV_32F);
for (int i = 0; i < testData.rows; i++)
{
final cv.Mat sample = testData.row(i);
predicted.<Float>at(i,0) = knn.find_nearest(sample, K);
}
float percentage = evaluate(predicted, testClasses) * 100;
System.out.print("K Nearest Neighbor Evaluated Accuracy = ");
System.out.print(percentage);
System.out.print("%");
System.out.print("\n");
prediction = predicted;
}
public final void showplot(cv.Mat testData)
{
plot_binary(testData, prediction, "Predictions Backpropagation");
}
private cv.Mat prediction = new cv.Mat();
}
edit:
The line predicted.at(i,0) = knn.find_nearest(sample, K); has most definitely to be wrong.
There is now function at in object Mat.
Also there is no "evaluate function".
Another thing is where does the prediction Mat belong to?In java you can not just put it in the end of the class.
Thanks=)
The following code is for finding the digits
here's some code to try:
import org.opencv.core.*;
import org.opencv.imgproc.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.ml.*;
import org.opencv.utils.*;
import java.util.*;
class SimpleSample {
static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }
public static void main(String[] args) {
// samples/data/digits.png, have a look at it.
Mat digits = Imgcodecs.imread("digits.png", 0);
// setup train/test data:
Mat trainData = new Mat(),
testData = new Mat();
List<Integer> trainLabs = new ArrayList<Integer>(),
testLabs = new ArrayList<Integer>();
// 10 digits a 5 rows:
for (int r=0; r<50; r++) {
// 100 digits per row:
for (int c=0; c<100; c++) {
// crop out 1 digit:
Mat num = digits.submat(new Rect(c*20,r*20,20,20));
// we need float data for knn:
num.convertTo(num, CvType.CV_32F);
// 50/50 train/test split:
if (c % 2 == 0) {
// for opencv ml, each feature has to be a single row:
trainData.push_back(num.reshape(1,1));
// add a label for that feature (the digit number):
trainLabs.add(r/5);
} else {
testData.push_back(num.reshape(1,1));
testLabs.add(r/5);
}
}
}
// make a Mat of the train labels, and train knn:
KNearest knn = KNearest.create();
knn.train(trainData, Ml.ROW_SAMPLE, Converters.vector_int_to_Mat(trainLabs));
// now test predictions:
for (int i=0; i<testData.rows(); i++)
{
Mat one_feature = testData.row(i);
int testLabel = testLabs.get(i);
Mat res = new Mat();
float p = knn.findNearest(one_feature, 1, res);
System.out.println(testLabel + " " + p + " " + res.dump());
}
//// hmm, the 'real world' test case probably looks more like this:
//// make sure, you follow the very same preprocessing steps used in the train phase:
// Mat one_feature = Imgcodecs.imread("one_digit.png", 0);
// Mat feature; one_feature.convertTo(feature, CvTypes.CV_32F);
// Imgproc.resize(feature, feature, Size(20,20));
// int predicted = knn.findNearest(feature.reshape(1,1), 1);
}
}
Related
After researching and a lot of trials-and-errors, I have come to a point that I can construct a spectrogram which I think it has element of rights and wrongs.
1. First, I read .wav file into a byte array and extract only the data part.
2. I convert the byte array into a double array which takes the average of right and left channels. I also notice that 1 sample of 1 channel consists of 2 bytes. So, 4 bytes into 1 double.
3. For a certain window size of power of 2, I apply FFT from here and get the amplitude in frequency domain. This is a vertical strip of the spectrogram image.
4. I do this repeatedly with the same window size and overlapping for the whole data and obtain the spectrogram.
The following is the code for read .wav into double array
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
public class readWAV2Array {
private byte[] entireFileData;
//SR = sampling rate
public double getSR(){
ByteBuffer wrapped = ByteBuffer.wrap(Arrays.copyOfRange(entireFileData, 24, 28)); // big-endian by default
double SR = wrapped.order(java.nio.ByteOrder.LITTLE_ENDIAN).getInt();
return SR;
}
public readWAV2Array(String filepath, boolean print_info) throws IOException{
Path path = Paths.get(filepath);
this.entireFileData = Files.readAllBytes(path);
if (print_info){
//extract format
String format = new String(Arrays.copyOfRange(entireFileData, 8, 12), "UTF-8");
//extract number of channels
int noOfChannels = entireFileData[22];
String noOfChannels_str;
if (noOfChannels == 2)
noOfChannels_str = "2 (stereo)";
else if (noOfChannels == 1)
noOfChannels_str = "1 (mono)";
else
noOfChannels_str = noOfChannels + "(more than 2 channels)";
//extract sampling rate (SR)
int SR = (int) this.getSR();
//extract Bit Per Second (BPS/Bit depth)
int BPS = entireFileData[34];
System.out.println("---------------------------------------------------");
System.out.println("File path: " + filepath);
System.out.println("File format: " + format);
System.out.println("Number of channels: " + noOfChannels_str);
System.out.println("Sampling rate: " + SR);
System.out.println("Bit depth: " + BPS);
System.out.println("---------------------------------------------------");
}
}
public double[] getByteArray (){
byte[] data_raw = Arrays.copyOfRange(entireFileData, 44, entireFileData.length);
int totalLength = data_raw.length;
//declare double array for mono
int new_length = totalLength/4;
double[] data_mono = new double[new_length];
double left, right;
for (int i = 0; i < new_length; i++){
left = ((data_raw[i] & 0xff) << 8) | (data_raw[i+1] & 0xff);
right = ((data_raw[i+2] & 0xff) << 8) | (data_raw[i+3] & 0xff);
data_mono[i] = (left+right)/2.0;
}
return data_mono;
}
}
The following code is the main program to run
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import javax.imageio.ImageIO;
public class App {
public static Color getColor(double power) {
double H = power * 0.4; // Hue (note 0.4 = Green, see huge chart below)
double S = 1.0; // Saturation
double B = 1.0; // Brightness
return Color.getHSBColor((float)H, (float)S, (float)B);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String filepath = "audio_work/Sine_Sweep_Full_Spectrum_20_Hz_20_kHz_audiocheck.wav";
try {
//get raw double array containing .WAV data
readWAV2Array audioTest = new readWAV2Array(filepath, true);
double[] rawData = audioTest.getByteArray();
int length = rawData.length;
//initialize parameters for FFT
int WS = 2048; //WS = window size
int OF = 8; //OF = overlap factor
int windowStep = WS/OF;
//calculate FFT parameters
double SR = audioTest.getSR();
double time_resolution = WS/SR;
double frequency_resolution = SR/WS;
double highest_detectable_frequency = SR/2.0;
double lowest_detectable_frequency = 5.0*SR/WS;
System.out.println("time_resolution: " + time_resolution*1000 + " ms");
System.out.println("frequency_resolution: " + frequency_resolution + " Hz");
System.out.println("highest_detectable_frequency: " + highest_detectable_frequency + " Hz");
System.out.println("lowest_detectable_frequency: " + lowest_detectable_frequency + " Hz");
//initialize plotData array
int nX = (length-WS)/windowStep;
int nY = WS;
double[][] plotData = new double[nX][nY];
//apply FFT and find MAX and MIN amplitudes
double maxAmp = Double.MIN_VALUE;
double minAmp = Double.MAX_VALUE;
double amp_square;
double[] inputImag = new double[length];
for (int i = 0; i < nX; i++){
Arrays.fill(inputImag, 0.0);
double[] WS_array = FFT.fft(Arrays.copyOfRange(rawData, i*windowStep, i*windowStep+WS), inputImag, true);
for (int j = 0; j < nY; j++){
amp_square = (WS_array[2*j]*WS_array[2*j]) + (WS_array[2*j+1]*WS_array[2*j+1]);
if (amp_square == 0.0){
plotData[i][j] = amp_square;
}
else{
plotData[i][j] = 10 * Math.log10(amp_square);
}
//find MAX and MIN amplitude
if (plotData[i][j] > maxAmp)
maxAmp = plotData[i][j];
else if (plotData[i][j] < minAmp)
minAmp = plotData[i][j];
}
}
System.out.println("---------------------------------------------------");
System.out.println("Maximum amplitude: " + maxAmp);
System.out.println("Minimum amplitude: " + minAmp);
System.out.println("---------------------------------------------------");
//Normalization
double diff = maxAmp - minAmp;
for (int i = 0; i < nX; i++){
for (int j = 0; j < nY; j++){
plotData[i][j] = (plotData[i][j]-minAmp)/diff;
}
}
//plot image
BufferedImage theImage = new BufferedImage(nX, nY, BufferedImage.TYPE_INT_RGB);
double ratio;
for(int x = 0; x<nX; x++){
for(int y = 0; y<nY; y++){
ratio = plotData[x][y];
//theImage.setRGB(x, y, new Color(red, green, 0).getRGB());
Color newColor = getColor(1.0-ratio);
theImage.setRGB(x, y, newColor.getRGB());
}
}
File outputfile = new File("saved.png");
ImageIO.write(theImage, "png", outputfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
However, the image I obtain from .wav playing sweeping sound from 20-20kHz is like this:
The color show the intensity of sound red(High)-->green(Low)
By right, it should look something like the picture below:
I would really appreciate if I can get any correct/improvement/suggest on my project. Thank you in advance for commenting on my question.
Fortunately it seems you have more rights than wrongs.
The first and main issue which results in the extra red lines is due to how you decode the data in readWAV2Array.getByteArray. Since the samples span 4 bytes, you must index in multiples of 4 (e.g. bytes 0,1,2,3 for sample 0, bytes 4,5,6,7 for sample 1) otherwise you would be reading overlapping blocks of 4 bytes (e.g. bytes 0,1,2,3 for sample 0, bytes 1,2,3,4 for sample 1). The other thing with this conversion is that you must explicitly cast the result to the signed short type before it can be assigned to left and right (which are of type double) in order to get a signed 16 bit result out of unsigned bytes. This should give you a conversion loop which looks like:
for (int i = 0; 4*i+3 < totalLength; i++){
left = (short)((data_raw[4*i+1] & 0xff) << 8) | (data_raw[4*i] & 0xff);
right = (short)((data_raw[4*i+3] & 0xff) << 8) | (data_raw[4*i+2] & 0xff);
data_mono[i] = (left+right)/2.0;
}
At this point you should start to get a plot that has strong lines representing your 20Hz-20kHz chirp:
But you should notice that you actually get 2 lines. This is because for real-valued signal, the frequency spectrum has Hermitian symmetry. The magnitude of the spectrum above the Nyquist frequency (half the sampling rate, in this case 44100Hz/2) is thus a redundant reflection of the spectrum below the Nyquist frequency. Only plotting the non-redundant part below the Nyquist frequency can be achieved by changing the definition of nY in main to:
int nY = WS/2 + 1;
and would give you:
Almost what we're looking for, but the sweep with increasing frequency generates a figure with a line that's decreasing. That's because your indexing make the 0Hz frequency at index 0 which is the top of the figure, and the 22050Hz frequency at index nY-1 which is the bottom of the figure. To flip the figure around and get the more usual 0Hz at the bottom and 22050Hz at the top, you can change the indexing to use:
plotData[i][nY-j-1] = 10 * Math.log10(amp_square);
Now you should have a plot which looks like the one you were expecting (although with a different color map):
A final note: while I understand your intention to avoid taking the log of 0 in your conversion to decibels, setting the output to the linear scale amplitude in this specific case could produce unexpected results. Instead I would select a cutoff threshold amplitude for the protection:
// select threshold based on the expected spectrum amplitudes
// e.g. 80dB below your signal's spectrum peak amplitude
double threshold = 1.0;
// limit values and convert to dB
plotData[i][nY-j-1] = 10 * Math.log10(Math.max(amp_square,threshold));
I have ranges let's say
1-10
20-40
30-50
55-65
65-80
75-90
95-100
As in this example 20-40 and 30-50 intersects instead of storing both I need to store it as 20-50.
Then instead of 55-65,65-80 and 75-90 I want to store 55-90 alone.
So the result set would be like this
1-10
20-50
55-90
95-100
I have these values in redis and the Structure which I store them in Java are arrays a start array and end array.
My solution :
for int i =0; i< length-1 ; i++
for int j=i+1;j<length; j++
if start[i] <= start[j] && end[i] >= start[j]
store the min max in start and end array and remove the other two entries and proceed
I found this as O(n log n) is there any better algorithm to do this?
Any suggestions in the data structure both in Java and redis and the approach or algorithm for processing this would be great.
Thanks
If the intervals are sorted by the start position, there is a very simple linear algorithm to merge the intervals. Sorting takes O(nlogn), so the overall time complexity is the same. If the input is not sorted, I believe general algorithms still take O(nlogn). Sorting is usually faster because it is associated with a small constant. It is the more efficient solution.
Here is an implementation in javascript, just to give you an idea. You can translate to java or can run it with node.js:
function merge_intervals(a)
{ // this function save the result IN PLACE
if (a.length == 0) return;
var st = a[0][0], en = a[0][1], k = 0;
for (var i = 1; i < a.length; ++i) {
if (a[i][0] > en) { // a new interval
a[k++] = [st, en];
st = a[i][0], en = a[i][1];
} else en = a[i][1] > en? a[i][1] : en;
}
a[k++] = [st, en]; // add the last interval
a.length = k; // discard the rest
}
// intervals are half-close-half-open, like C arrays
var a = [[1,10], [20,40], [30,50], [55,65], [65,80], [75,90], [95,100]];
// sort the intervals based on start positions
a.sort(function(x,y) { return x[0]-y[0] });
merge_intverals(a);
for (var i = 0; i < a.length; ++i)
console.log(a[i].join("\t"));
Use a RangeSet from Guava.
From the documentation:
Implementations that choose to support the add(Range) operation are required to ignore empty ranges and coalesce connected ranges.
Applied to your example:
public static void main(String args[]) {
final RangeSet<Integer> ranges = TreeRangeSet.create();
ranges.add(Range.closed(1, 10));
ranges.add(Range.closed(20, 40));
ranges.add(Range.closed(30, 50));
ranges.add(Range.closed(55, 65));
ranges.add(Range.closed(65, 80));
ranges.add(Range.closed(75, 90));
ranges.add(Range.closed(95, 100));
System.out.println(ranges);
}
Output:
[[1‥10], [20‥50], [55‥90], [95‥100]]
As Range and TreeRangeSet both implements Serializable you can persist them to Redis as is.
I think the ranges may not be always in order. Of course, the code may not be best but it's functional
import java.util.*;
class Interval {
int lo;
int hi;
Interval() {
lo = 0;
hi = 0;
}
Interval(int lo, int hi) {
this.lo = lo;
this.hi = hi;
}
#Override
public String toString() {
return "[" + lo + "," + hi + "]";
}
}
public class Demo {
public static ArrayList<Interval> merge(ArrayList<Interval> list) {
Collections.sort(list, new Comparator<Interval>() {
public int compare(Interval i1, Interval i2) {
if (i1.lo == i2.lo) {
return i1.hi - i2.hi;
}
return i1.lo - i2.lo;
}
});
System.out.println("Sorted Input: " + list);
ArrayList<Interval> result = new ArrayList<Interval>();
Interval prev = list.get(0);
result.add(prev);
for (int i = 1; i < list.size(); i++) {
Interval current = list.get(i);
if (prev.hi >= current.lo) {
Interval Interval = new Interval(prev.lo, Math.max(prev.hi, current.hi));
prev = Interval;
} else {
prev = current;
}
removeIfExist(result, prev);
result.add(prev);
}
return result;
}
private static void removeIfExist(ArrayList<Interval> result, Interval prev) {
if (result.size() > 0) {
Interval existing = result.get(result.size() - 1);
if (existing.lo == prev.lo) {
result.remove(result.size() - 1);
}
}
}
public static void main(String[] args) {
ArrayList<Interval> list = new ArrayList<Interval>();
System.out.println("--------------------------------------------------------------------------------");
list.add(new Interval(30, 50));
list.add(new Interval(20, 40));
list.add(new Interval(75, 90));
list.add(new Interval(1, 10));
list.add(new Interval(95, 100));
list.add(new Interval(65, 80));
list.add(new Interval(55, 65));
System.out.println("Input: " + list);
System.out.println("merged Interval: " + merge(list));
System.out.println("--------------------------------------------------------------------------------");
}
}
I am working on solving a linear programming problem using joptimizer.
My problem is:
Maximize (x1*f1 + x2*f2 + x3*f3)
such that (x1*v1 + x2*v2 + x3*v3) <= h
I need to find x1, x2 and x3.
I do not know how to create a joptimizer input from the above equation.
Java doc is available here
http://www.joptimizer.com/apidocs/index.html
simple example
minimize 3x+4y such that 2x+3y >= 8, 5x+2y >= 12, x >= 0, y >= 0
My sample code for solving simple linear programming question is below:
package test_joptimizer;
import com.joptimizer.functions.ConvexMultivariateRealFunction;
import com.joptimizer.functions.LinearMultivariateRealFunction;
import com.joptimizer.optimizers.JOptimizer;
import com.joptimizer.optimizers.OptimizationRequest;
import org.apache.log4j.BasicConfigurator;
/**
* #author K.P.L.Kanchana
*/
public class Main {
public static void main(String[] args) throws Exception {
// Objective function (plane)
LinearMultivariateRealFunction objectiveFunction = new LinearMultivariateRealFunction(new double[] {3.0, 4.0}, 0); //minimize 3x+4y
//inequalities (polyhedral feasible set G.X<H )
ConvexMultivariateRealFunction[] inequalities = new ConvexMultivariateRealFunction[4];
// x >= 0
inequalities[0] = new LinearMultivariateRealFunction(new double[]{-1.0, 0.00}, 0.0); // focus: -x+0 <= 0
// y >= 0
inequalities[1] = new LinearMultivariateRealFunction(new double[]{0.0, -1.00}, 0.0); // focus: -y+0 <= 0
// 2x+3y >= 8
inequalities[2] = new LinearMultivariateRealFunction(new double[]{-2.0, -3.00}, 8.0); // focus: -2x-3y+8 <= 0
// 5x+2y >= 12
inequalities[3] = new LinearMultivariateRealFunction(new double[]{-5.0, -2.00}, 12.0);// focus: -5x-2y+12 <= 0
//optimization problem
OptimizationRequest or = new OptimizationRequest();
or.setF0(objectiveFunction);
or.setFi(inequalities);
//or.setInitialPoint(new double[] {0.0, 0.0});//initial feasible point, not mandatory
or.setToleranceFeas(1.E-9);
or.setTolerance(1.E-9);
//optimization
JOptimizer opt = new JOptimizer();
opt.setOptimizationRequest(or);
int returnCode = opt.optimize();
double[] sol = opt.getOptimizationResponse().getSolution();
System.out.println("Length: " + sol.length);
for (int i=0; i<sol.length/2; i++){
System.out.println( "X" + (i+1) + ": " + Math.round(sol[i]) + "\ty" + (i+1) + ": " + Math.round(sol[i+1]) );
}
}
}
Maybe you would like to have a look at the following sample code too.
Don't forget to import the dependecies from http://www.joptimizer.com/downloadWithAdd.html.
You should download and import external three jar files ( /joptimizer-4.0.0.jar, /joptimizer-4.0.0-dependencies.zip, /joptimizer-4.0.0-sources.jar) into your project manually. The .zip file demands unzipping too.
import java.util.Arrays;
import com.joptimizer.optimizers.*;
public class LCLP {
// min (x) for J = C'*X;
// s.t.
// A*X = b;
// X <= 0;
// X = [x1;x2;x3;...xN]
public static void main(String[] args) throws Exception {
// Example from http://www.joptimizer.com/linearProgramming.html
//Objective function
double[] c = new double[] { -1., -1. };
//Inequalities constraints
double[][] G = new double[][] {{4./3., -1}, {-1./2., 1.}, {-2., -1.}, {1./3., 1.}};
double[] h = new double[] {2., 1./2., 2., 1./2.};
//Bounds on variables
double[] lb = new double[] {0 , 0};
double[] ub = new double[] {10, 10};
//optimization problem
LPOptimizationRequest or = new LPOptimizationRequest();
or.setC(c);
or.setG(G);
or.setH(h);
or.setLb(lb);
or.setUb(ub);
or.setDumpProblem(true);
//optimization
LPPrimalDualMethod opt = new LPPrimalDualMethod();
opt.setLPOptimizationRequest(or);
opt.optimize();
double[] sol = opt.getOptimizationResponse().getSolution();
System.out.println("Solution = " + Arrays.toString(sol));
}
}
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;
}
I wrote this piece of code that should make what is described here:
Conjugate Gradient from wikipedia
but after some iterations the variable denomAlpha goes to zero and so I get a NAN on alpha. So what is wrong with my algorithm?
import Jama.Matrix;
public class ConjugateGrad {
private static final int MAX_IT = 20;
private static final int MAX_SIZE = 50;
public static void main(String[] args) {
Matrix A = Matrix.random(MAX_SIZE, MAX_SIZE);
Matrix b = Matrix.random(MAX_SIZE, 1);
double[][] d = new double[MAX_SIZE][1];
for(int ii=0;ii<MAX_SIZE;ii++) {
d[ii][0] =0;
}
Matrix x = Matrix.constructWithCopy(d);
Matrix r = b.minus(A.times(x));
Matrix p = r;
Matrix rTrasp_r = r.transpose().times(p);
for (int i = 0; i < MAX_IT; i++) {
Matrix denomAlpha = p.transpose().times(A.times(p));
double numeratorAlpha = rTrasp_r.getArray()[0][0];
double Alpha = numeratorAlpha / denomAlpha.getArray()[0][0];
x = x.plus(p.times(Alpha));
r = r.minus(A.times(p));
Matrix rNew = r.transpose().times(r);
if (Math.sqrt(rNew.getArray()[0][0]) <1.0e-6) {
break;
}
double Beta = rNew.getArray()[0][0] / rTrasp_r.getArray()[0][0];
p = r.plus(p.times(Beta));
rTrasp_r = rNew;
}
}
}
it same that with those parameters :
double[][] matrixA = {{4,1},{1,3}};
Matrix A = Matrix.constructWithCopy(matrixA);
double[][] vectorb = {{1},{2}};
Matrix b = Matrix.constructWithCopy(vectorb);
double[][] d = {{2},{1}};
Matrix x = Matrix.constructWithCopy(d);
at first step of the algorithm things are good
but at second step not...
r: -8.0, -3.0
Alpha: 0.22054380664652568
Beta: 12.67123287671233
x: 0.2356495468277946, 0.33836858006042303,
Second step :
Alpha: 0.0337280177221555
Beta: 159.11259655226627
x: -2.2726985108925097, -0.47156587291133856,
Ok, I have found one Error:
r = r.minus(A.times(p).times(Alpha));
Now it works:
r: -8.0, -3.0,
Alpha: 0.22054380664652568
rNew: 0.6403099643121183,
Beta: 0.008771369374138607
p: -0.3511377223647101, 0.7229306048685207,
x: 0.2356495468277946, 0.33836858006042303,
Sorry for the hack answer but... using the numerical example params from the Wikipedia article and outputting the matrices to terminal at each step could find the discrepancy.