I have applied the KNN algorithm for classifying handwritten digits. the digits are in vector format initially 8*8, and stretched to form a vector 1*64..
As it stands my code applies the kNN algorithm but only using k = 1. I'm not entirely sure how to alter the value k after attempting a couple of things I kept getting thrown errors. If anyone could help push me in the right direction it would be really appreciated. The training dataset can be found here and the validation set here.
ImageMatrix.java
import java.util.*;
public class ImageMatrix {
private int[] data;
private int classCode;
private int curData;
public ImageMatrix(int[] data, int classCode) {
assert data.length == 64; //maximum array length of 64
this.data = data;
this.classCode = classCode;
}
public String toString() {
return "Class Code: " + classCode + " Data :" + Arrays.toString(data) + "\n"; //outputs readable
}
public int[] getData() {
return data;
}
public int getClassCode() {
return classCode;
}
public int getCurData() {
return curData;
}
}
ImageMatrixDB.java
import java.util.*;
import java.io.*;
import java.util.ArrayList;
public class ImageMatrixDB implements Iterable<ImageMatrix> {
private List<ImageMatrix> list = new ArrayList<ImageMatrix>();
public ImageMatrixDB load(String f) throws IOException {
try (
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr)) {
String line = null;
while((line = br.readLine()) != null) {
int lastComma = line.lastIndexOf(',');
int classCode = Integer.parseInt(line.substring(1 + lastComma));
int[] data = Arrays.stream(line.substring(0, lastComma).split(","))
.mapToInt(Integer::parseInt)
.toArray();
ImageMatrix matrix = new ImageMatrix(data, classCode); // Classcode->100% when 0 -> 0% when 1 - 9..
list.add(matrix);
}
}
return this;
}
public void printResults(){ //output results
for(ImageMatrix matrix: list){
System.out.println(matrix);
}
}
public Iterator<ImageMatrix> iterator() {
return this.list.iterator();
}
/// kNN implementation ///
public static int distance(int[] a, int[] b) {
int sum = 0;
for(int i = 0; i < a.length; i++) {
sum += (a[i] - b[i]) * (a[i] - b[i]);
}
return (int)Math.sqrt(sum);
}
public static int classify(ImageMatrixDB trainingSet, int[] curData) {
int label = 0, bestDistance = Integer.MAX_VALUE;
for(ImageMatrix matrix: trainingSet) {
int dist = distance(matrix.getData(), curData);
if(dist < bestDistance) {
bestDistance = dist;
label = matrix.getClassCode();
}
}
return label;
}
public int size() {
return list.size(); //returns size of the list
}
public static void main(String[] argv) throws IOException {
ImageMatrixDB trainingSet = new ImageMatrixDB();
ImageMatrixDB validationSet = new ImageMatrixDB();
trainingSet.load("cw2DataSet1.csv");
validationSet.load("cw2DataSet2.csv");
int numCorrect = 0;
for(ImageMatrix matrix:validationSet) {
if(classify(trainingSet, matrix.getData()) == matrix.getClassCode()) numCorrect++;
} //285 correct
System.out.println("Accuracy: " + (double)numCorrect / validationSet.size() * 100 + "%");
System.out.println();
}
In the for loop of classify you are trying to find the training example that is closest to a test point. You need to switch that with a code that finds K of the training points that is the closest to the test data. Then you should call getClassCode for each of those K points and find the majority(i.e. the most frequent) of the class codes among them. classify will then return the major class code you found.
You may break the ties (i.e. having 2+ most frequent class codes assigned to equal number of training data) in any way that suits your need.
I am really inexperienced in Java, but just by looking around the language reference, I came up with the implementation below.
public static int classify(ImageMatrixDB trainingSet, int[] curData, int k) {
int label = 0, bestDistance = Integer.MAX_VALUE;
int[][] distances = new int[trainingSet.size()][2];
int i=0;
// Place distances in an array to be sorted
for(ImageMatrix matrix: trainingSet) {
distances[i][0] = distance(matrix.getData(), curData);
distances[i][1] = matrix.getClassCode();
i++;
}
Arrays.sort(distances, (int[] lhs, int[] rhs) -> lhs[0]-rhs[0]);
// Find frequencies of each class code
i = 0;
Map<Integer,Integer> majorityMap;
majorityMap = new HashMap<Integer,Integer>();
while(i < k) {
if( majorityMap.containsKey( distances[i][1] ) ) {
int currentValue = majorityMap.get(distances[i][1]);
majorityMap.put(distances[i][1], currentValue + 1);
}
else {
majorityMap.put(distances[i][1], 1);
}
++i;
}
// Find the class code with the highest frequency
int maxVal = -1;
for (Entry<Integer, Integer> entry: majorityMap.entrySet()) {
int entryVal = entry.getValue();
if(entryVal > maxVal) {
maxVal = entryVal;
label = entry.getKey();
}
}
return label;
}
All you need to do is adding K as a parameter. Keep in mind, however, that the code above does not handle ties in a particular way.
Related
I've searched up and down for the fix to my issue, but none seem to work. One particular reference-- this and this, and especially this. However, no matter how I implement them, I receive an OutOfBoundsError, which I can't understand.
The program is extra credit for a class. In truth, it is very simple--
Program Description: Use a two dimensional array to solve the following problem. A company has four sales persons (1 to 4) who sell five different products (1 to 5). Once a day, each salesperson passes in a slip for each different type of product sold. Each slip contains:
The sales persons numberThe product numberThe total dollar value of that product sold that day
Thus, each salesperson passes in between 0 and 5 sales slips per day. Assume that the information from all of the slips for last month is available. Each data line contains 3 numbers (the sales person number, product number, sales).
Write a program that will read all this information for last month’s sales, and summarize the total sales by salesperson by product.
The data provided:
1 2 121.77
1 4 253.66
1 5 184.22
1 1 97.55
2 1 152.44
2 2 104.53
2 4 189.97
2 5 247.88
3 5 235.87
3 4 301.33
3 3 122.15
3 2 301.00
3 1 97.55
4 1 125.66
4 2 315.88
4 4 200.10
4 3 231.45
The error only comes when it tries to calculate the columns. My rows work; no matter how I change the for-loop or any of the indeces in the row or column of the array, it doesn't work. I at first had my rows calculated separately, then my column sums, and it didn't work either. There is something that I'm missing that I'm clearly overlooking.
Here is my code:
import java.io.File;
import java.io.FileNotFoundException;
import java.text.DecimalFormat;
import java.util.Scanner;
public class prog480u {
static Scanner inFile = null;
public static void main(String[] args) {
try {
// create scanner to read file
inFile = new Scanner(new File ("prog480u.dat"));
} catch (FileNotFoundException e) {
System.out.println("File not found!");
System.exit(0);
}
// make the array
int x = 0;
int y = 0;
double[][] profits = new double[4][5];
while (inFile.hasNext()) {
x = inFile.nextInt(); // use sales numbers as coordinates
y = inFile.nextInt();
profits[x - 1][y - 1] = inFile.nextDouble();
}
// check if it's okay
System.out.println("");
double[][] columnProfits = sums(profits);
for (int a = 0; a < columnProfits.length; a++) {
System.out.print((a+1) + "\t");
for (int b = 0; b < columnProfits[a].length; b++) {
System.out.print(columnProfits[a][b] + "\t");
}
System.out.println("");
}
double[] bottomRow = columnSums(columnProfits);
for (int a = 0; a < bottomRow.length; a++) {
System.out.print("Total:" + bottomRow + "\t");
}
}
public static double[][] sums (double[][] q) {
double[][] array = new double[5][6];
array = q;
double sum = 0;
for (int a = 0; a < array.length; a++) {
for (int b = 0; b < array[0].length; b ++) {
sum += array[a][b]; // add everything in the row
}
array[a][4] = sum; // set that row to the last column
sum = 0; // reset sum to 0
}
return array;
}
public static double[] columnSums (double[][]q) {
double[][] array = new double[5][6];
array = q;
double sum2 = 0;
double[] columns = new double [5];
for (int a = 0; a < array.length; a++) {
for (int b = 0; b < array[0].length; b ++) {
sum2 += array[b][a];
columns[b] = sum2;
}
sum2 = 0; // reset sum to 0
}
return columns;
}
}
Thank you very much for your time. I have a feeling my program is close to working, but this small mistake is pushing me over the edge.
Here's the working code (I cleaned it up a bit):
You were very close, you just needed to swap your max indicies in the for loops. That's why you were getting a java.lang.ArrayIndexOutOfBoundsException
public static double[] columnSums(double[][] q)
{
double[][] array = q;
double[] columns = new double[5];
for (int a = 0; a < array[0].length; a++)
{
for (int b = 0; b < array.length; b++)
{
columns[a] += array[b][a];
}
}
return columns;
}
Just for fun, I wrote the object oriented version for that.
Easier to handle once the system requires additional functionalities:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
class Sale {
static public ArrayList<Sale> readSales(final String pSalesFileName) throws FileNotFoundException, IOException {
final ArrayList<Sale> ret = new ArrayList<>();
try (final BufferedReader br = new BufferedReader(new FileReader(pSalesFileName))) {
int lineIndex = 0;
while (true) {
++lineIndex;
final String line = br.readLine();
if (line == null) {
System.out.println("Line #" + lineIndex + " is empty, skipping...");
break;
}
try {
final String[] values = line.split("\\s");
final int salesPersonId = Integer.parseInt(values[0]);
final int productId = Integer.parseInt(values[1]);
final float sales = Float.parseFloat(values[2]);
final Sale sale = new Sale(salesPersonId, productId, sales);
ret.add(sale);
} catch (final ArrayIndexOutOfBoundsException e) {
System.err.println("Parse error in line #" + lineIndex + ": '" + line + "'");
}
}
}
return ret;
}
private final int mSalesPersonId;
private final int mProductId;
private final float mSales;
public Sale(final int pSalesPersonId, final int pProductId, final float pSales) {
mSalesPersonId = pSalesPersonId;
mProductId = pProductId;
mSales = pSales;
}
public Integer getSalesPersonId_R() {
return Integer.valueOf(mSalesPersonId);
}
public Integer getProductId_R() {
return Integer.valueOf(mProductId);
}
public float getSales() {
return mSales;
}
}
class SalesPerson {
private final HashMap<Integer, ArrayList<Sale>> mSalesMap = new HashMap<>();
private final int mId;
public SalesPerson(final int pId) {
mId = pId;
}
#Override public boolean equals(final Object pObj) {
if (!(pObj instanceof SalesPerson)) return false;
return ((SalesPerson) pObj).mId == mId;
}
#Override public int hashCode() {
return mId;
}
public void addSale(final Sale pSale) {
final Integer productId = pSale.getProductId_R();
ArrayList<Sale> salesList = mSalesMap.get(productId);
if (salesList == null) {
salesList = new ArrayList<>();
mSalesMap.put(productId, salesList);
}
salesList.add(pSale);
}
public Integer getId_R() {
return Integer.valueOf(mId);
}
public HashMap<Integer, ArrayList<Sale>> getSalesMap() {
return mSalesMap;
}
public float getSalesTotalByProductId(final Integer pProductId) {
final ArrayList<Sale> sales = mSalesMap.get(pProductId);
float accumulator = 0;
for (final Sale sale : sales) {
accumulator += sale.getSales();
}
return accumulator;
}
}
public class SalesFun {
public static void main(final String[] args) throws FileNotFoundException, IOException {
final ArrayList<Sale> sales = Sale.readSales("test/sales.txt");
final HashMap<Integer, SalesPerson> personMap = new HashMap<>();
for (final Sale sale : sales) {
// find right salesperson or create new, then add sale to it
final Integer salesPersonId = sale.getSalesPersonId_R();
SalesPerson person = personMap.get(salesPersonId);
if (person == null) {
person = new SalesPerson(salesPersonId.intValue());
personMap.put(salesPersonId, person);
}
person.addSale(sale);
}
printSales(personMap);
}
static private void printSales(final HashMap<Integer, SalesPerson> pPersonMap) {
for (final SalesPerson person : pPersonMap.values()) {
System.out.println("SalesMan ID: " + person.getId_R());
for (final Entry<Integer, ArrayList<Sale>> entry : person.getSalesMap().entrySet()) {
final Integer productId = entry.getKey();
final float sales = person.getSalesTotalByProductId(productId);
System.out.println("\tProduct ID: " + entry.getKey() + "\tSales: " + sales);
}
}
}
}
For example if the input is "name" and the minGram is 1 and maxGramSize is 2 output will consist of n,a,m,e,na,am,me. If the minGram=2, maxGram=4 inputWord=name, output = na,am,me,nam,ame,name.
Function signature can be something like this:
public List<String> generateNGrams(String input, int minGramSize, int maxGramSize)
Initially I tried doing it with for loops, but I was finding it hard to follow the indices. Then I tried solving it using recursion with pen and paper but I'm still struggling with it. Can someone help me with this?
One solution:
private static void addNgrams(final int size, final String input,
final List<String> list)
{
final int maxStartIndex = input.length() - size;
for (int i = 0; i < maxStartIndex; i++)
list.add(input.stubString(i, i + size));
}
public List<String> generateNGrams(final String input, final int minSize,
final int maxSize)
{
final List<String> ret = new ArrayList<>();
for (int size = minSize; size <= maxSize; size++)
addNgrams(size, input, ret);
return ret;
}
Note: lacks basic error checkings (for instance, maxSize greater than the size of input; minSize greater than maxSize; others); left as an exercise.
Here is a program that recursively generates nGrams: This code also handles the tail grams.
import java.util.ArrayList;
public class NGrams {
ArrayList<String> nGrams = new ArrayList<String>();
public void generateNGrams(String str, int n) {
if (str.length() == n ) {
int counter = 0;
while (counter < n) {
nGrams.add(str.substring(counter));
counter++;
}
return;
}
int counter = 0;
String gram = "";
while (counter < n) {
gram += str.charAt(counter);
counter++;
}
nGrams.add(gram);
generateNGrams(str.substring(1), n);
}
public void printNGrams() {
for (String str : nGrams) {
System.out.println(str);
}
}
public static void main(String[] args) {
NGrams ng = new NGrams();
ng.generateNGrams("hello world", 3);
ng.printNGrams();
}
}
Output:
hel
ell
llo
lo
o w
wo
wor
orl
rld
ld
d
I am trying to round an array of numbers to three decimal places in java, the reason being that I am running into an OutOfMemoryError (array is exceeding the VM's limit). I was curious if there was a way to do this without writing an entire new method or anything drastic like that.
EDIT: here is all the code
public class GuitarHero {
public static void main(String[] args) {
int index = 0;
double sample = 0.0;
String keyboard ="1234567890qwertyuiopasdfghjklzxcvbnm,";
GuitarString[] string = new GuitarString[keyboard.length()];
for(int i = 0; i < 37; i++) {
double concert = 110.0 * Math.pow(2,i-24);
string[i] = new GuitarString(concert);
}
while (true){
if (StdDraw.hasNextKeyTyped()) {
char key = StdDraw.nextKeyTyped();
index = keyboard.indexOf(key);
if (index >= 0 && index < 37){
string[index].pluck();
}
//sample = string[index].sample() + string[index+1].sample();
//StdAudio.play(sample);
}
for(int i=0; i<37; i++){
sample = string[i].sample();
StdAudio.play(sample);
}
for(int i = 0; i < 37; i++){
string[i].tic();
}
}
}
}
end of code 1
public class GuitarString {
private RingBuffer buffer; // ring buffer
// YOUR OTHER INSTANCE VARIABLES HERE
private int ticTimes = 0;
// create a guitar string of the given frequency
public GuitarString(double frequency) {
// YOUR CODE HERE
int N;
N = (int)(44100/frequency);
buffer = new RingBuffer(N);
for (int i=1; i <=N; i++ ){
buffer.enqueue(0.0);
}
}
// create a guitar string whose size and initial values are given by the array
public GuitarString(double[] init) {
// YOUR CODE HERE
buffer = new RingBuffer(init.length);
for (int i = 0; i < init.length; i++){
buffer.enqueue(init[i]);
}
}
// pluck the guitar string by setting the buffer to white noise
public void pluck() {
// YOUR CODE HERE
while(!buffer.isEmpty()) buffer.dequeue();
while(!buffer.isFull()){
buffer.enqueue(Math.random()-0.5);
}
}
// advance the simulation one time step
public void tic() {
// YOUR CODE HERE
double value1, value2;
value1 = buffer.dequeue();
value2 = buffer.peek();
buffer.enqueue(((value1+value2)/2)*0.996);
ticTimes++;
}
// return the current sample
public double sample() {
// YOUR CODE HERE
return buffer.peek();
}
// return number of times tic was called
public int time() {
// YOUR CODE HERE
return ticTimes;
}
public static void main(String[] args) {
int N = Integer.parseInt(args[0]);
double[] samples = { .2, .4, .5, .3, -.2, .4, .3, .0, -.1, -.3 };
GuitarString testString = new GuitarString(samples);
for (int i = 0; i < N; i++) {
int t = testString.time();
double sample = testString.sample();
System.out.printf("%6d %8.4f\n", t, sample);
testString.tic();
}
}
}
end of code 2
public class RingBuffer {
private int first; // index of first item in buffer
private int last; // index of last item in buffer
private int size; // current number of items of buffer
private double[] buffer;
// create an empty buffer, with given max capacity
public RingBuffer(int capacity) {
// YOUR CODE HERE
buffer = new double[capacity];
first =0;
last =capacity-1;
size =0;
}
// return number of items currently in the buffer
public int size() {
// YOUR CODE HERE
return size;
}
// is the buffer empty (size equals zero)?
public boolean isEmpty() {
// YOUR CODE HERE
if (size == 0)
return true;
else
return false;
}
// is the buffer full (size equals array capacity)?
public boolean isFull() {
// YOUR CODE HERE
if (size == buffer.length)
return true;
else
return false;
}
// add item x to the end
public void enqueue(double x) {
if (isFull()) { throw new RuntimeException("Ring buffer overflow"); }
// YOUR CODE HERE
last = (last+1)%buffer.length;
buffer[last]=x;
size++;
}
// delete and return item from the front
public double dequeue() {
if (isEmpty()) { throw new RuntimeException("Ring buffer underflow"); }
// YOUR CODE HERE
double temp = buffer[first];
first = (first+1)% buffer.length;
size--;
return temp;
}
// return (but do not delete) item from the front
public double peek() {
if (isEmpty()) { throw new RuntimeException("Ring buffer underflow"); }
// YOUR CODE HERE
return buffer[first];
}
// a simple test of the constructor and methods in RingBuffer
public static void main(String[] args) {
int N = Integer.parseInt(args[0]);
RingBuffer buffer = new RingBuffer(N);
for (int i = 1; i <= N; i++) {
buffer.enqueue(i);
}
double t = buffer.dequeue();
buffer.enqueue(t);
System.out.println("Size after wrap-around is " + buffer.size);
while (buffer.size() >= 2) {
double x = buffer.dequeue();
double y = buffer.dequeue();
buffer.enqueue(x + y);
}
System.out.println(buffer.peek());
}
}
Thanks!
Well in the first iteration of that loop the code is trying to allocate an array of 1,681,534,603 doubles (44100 / (110 * 2^-22)), which would require about 3GB of memory. I suggest you find a different solution.
I'm taking an online class on Algorithms and trying to implement a mergesort implementation of finding the number of inversions in a list of numbers. But, I cant figure what Im doing wrong with my implementation as the number of inversions returned is significantly lower than the number I get while doing a brute force approach. Ive placed my implementation of the mergesort approach below
/**
*
*/
package com.JavaReference;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFile {
public static void main(String args[]){
int count=0;
Integer n[];
int i=0;
try{
n=OpenFile();
int num[] = new int[n.length];
for (i=0;i<n.length;i++){
num[i]=n[i].intValue();
// System.out.println( "Num"+num[i]);
}
count=countInversions(num);
}
catch(IOException e){
e.printStackTrace();
}
System.out.println(" The number of inversions"+count);
}
public static Integer[] OpenFile()throws IOException{
FileReader fr=new FileReader("C:/IntegerArray.txt");// to put in file name.
BufferedReader textR= new BufferedReader(fr);
int nLines=readLines();
System.out.println("Number of lines"+nLines);
Integer[] nData=new Integer[nLines];
for (int i=0; i < nLines; i++) {
nData[ i ] = Integer.parseInt((textR.readLine()));
}
textR.close();
return nData;
}
public static int readLines() throws IOException{
FileReader fr=new FileReader("C:/IntegerArray.txt");
BufferedReader br=new BufferedReader(fr);
int numLines=0;
//String aLine;
while(br.readLine()!=null){
numLines++;
}
System.out.println("Number of lines readLines"+numLines);
return numLines;
}
public static int countInversions(int num[]){
int countLeft,countRight,countMerge;
int mid=num.length/2,k;
if (num.length<=1){
return 0;// Number of inversions will be zero for an array of this size.
}
int left[]=new int[mid];
int right[]=new int [num.length-mid];
for (k=0;k<mid;k++){
left[k]=num[k];
}
for (k=0;k<mid;k++){
right[k]=num[mid+k];
}
countLeft=countInversions(left);
countRight=countInversions(right);
int[] result=new int[num.length];
countMerge=mergeAndCount(left,right,result);
/*
* Assign it back to original array.
*/
for (k=0;k<num.length;k++){
num[k]=result[k];
}
return(countLeft+countRight+countMerge);
}
private static int mergeAndCount(int left[],int right[],int result[]){
int count=0;
int a=0,b=0,i,k=0;
while((a<left.length)&&(b<right.length)){
if(left[a]<right[b]){
result[k]=left[a++];// No inversions in this case.
}
else{// There are inversions.
result[k]=right[b++];
count+=left.length-a;
}
k++;
// When we finish iterating through a.
if(a==left.length){
for (i=b;i<right.length;i++){
result[k++]=right[b];
}
}
else{
for (i=a;i<left.length;i++){
}
}
}
return count;
}
}
I'm a beginner in Java and Algorithms so any insightful suggestions would be great!
I found two bugs:
In countInversions(), when num is split into left and right you assume right has m elements. When num.length is odd, however, it will be m + 1 elements. The solution is to use right.length instead of m.
In mergeAndCount(), handling of the bit where one subarray is empty and the other one still has some elements is not done correctly.
Side note:
There is absolutely no reason to use Integer in your program, except for the Integer.parseInt() method (which, by the way, returns an int).
Corrected code:
/**
*
*/
package com.JavaReference;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFile {
public static void main(String args[]){
int count=0;
Integer n[];
int i=0;
try{
n=OpenFile();
int num[] = new int[n.length];
for (i=0;i<n.length;i++){
num[i]=n[i].intValue();
// System.out.println( "Num"+num[i]);
}
count=countInversions(num);
}
catch(IOException e){
e.printStackTrace();
}
System.out.println(" The number of inversions"+count);
}
public static Integer[] OpenFile()throws IOException{
FileReader fr=new FileReader("C:/IntegerArray.txt");// to put in file name.
BufferedReader textR= new BufferedReader(fr);
int nLines=readLines();
System.out.println("Number of lines"+nLines);
Integer[] nData=new Integer[nLines];
for (int i=0; i < nLines; i++) {
nData[ i ] = Integer.parseInt((textR.readLine()));
}
textR.close();
return nData;
}
public static int readLines() throws IOException{
FileReader fr=new FileReader("C:/IntegerArray.txt");
BufferedReader br=new BufferedReader(fr);
int numLines=0;
//String aLine;
while(br.readLine()!=null){
numLines++;
}
System.out.println("Number of lines readLines"+numLines);
return numLines;
}
public static int countInversions(int num[]){
int countLeft,countRight,countMerge;
int mid=num.length/2,k;
if (num.length<=1){
return 0;// Number of inversions will be zero for an array of this size.
}
int left[]=new int[mid];
int right[]=new int [num.length-mid];
for (k=0;k<mid;k++){
left[k]=num[k];
}
// BUG 1: you can't assume right.length == m
for (k=0;k<right.length;k++){
right[k]=num[mid+k];
}
countLeft=countInversions(left);
countRight=countInversions(right);
int[] result=new int[num.length];
countMerge=mergeAndCount(left,right,result);
/*
* Assign it back to original array.
*/
for (k=0;k<num.length;k++){
num[k]=result[k];
}
return(countLeft+countRight+countMerge);
}
private static int mergeAndCount(int left[],int right[],int result[]){
int count=0;
int a=0,b=0,i,k=0;
while((a<left.length)&&(b<right.length)){
if(left[a]<right[b]){
result[k]=left[a++];// No inversions in this case.
}
else{// There are inversions.
result[k]=right[b++];
count+=left.length-a;
}
k++;
}
// BUG 2: Merging of leftovers should be done like this
while (a < left.length)
{
result[k++] = left[a++];
}
while (b < right.length)
{
result[k++] = right[b++];
}
return count;
}
}
The way I see it, counting the number of inversions in an array is finding a way to sort the array in an ascending order. Following that thought, here is my solution:
int countInversionArray(int[] A) {
if(A.length<=1) return 0;
int solution = 0;
for(int i=1;i<A.length;i++){
int j = i;
while(j+2<A.length && A[j] > A[j+1]){
invert2(j,j+1,A);
solution++;
j++;
}
j=i;
while(j>0 && A[j] < A[j-1]){
invert2(j,j-1,A);
solution++;
j--;
}
}
return solution;
}
private void invert2(int index1, int index2, int[] A){
int temp = A[index1];
A[index1] = A[index2];
A[index2] = temp;
}
I found a rigth solution in Robert Sedgewick book "Algorithms on java language"
Read here about merge
See java code for counting of inversion
You can try this In-Place Mergesort implemention on java. But minimum 1 temporary data container is needed (ArrayList in this case). Also counts inversions.
///
Sorter.java
public interface Sorter {
public void sort(Object[] data);
public void sort(Object[] data, int startIndex, int len);
}
MergeSorter implementation class (others like QuickSorter, BubbleSorter or InsertionSorter may be implemented on Sorter interface)
MergeSorter.java
import java.util.List;
import java.util.ArrayList;
public class MergeSorter implements Sorter {
private List<Comparable> dataList;
int num_inversion;
public MergeSorter() {
dataList = new ArrayList<Comparable> (500);
num_inversion = 0;
}
public void sort(Object[] data) {
sort(data, 0, data.length);
}
public int counting() {
return num_inversion;
}
public void sort(Object[] data, int start, int len) {
if (len <= 1) return;
else {
int midlen = len / 2;
sort(data, start, midlen);
sort(data, midlen + start, len - midlen);
merge(data, start, midlen, midlen + start, len - midlen);
}
}
private void merge(Object[] data, int start1, int len1, int start2, int len2) {
dataList.clear();
int len = len1 + len2;
// X is left array pointer
// Y is right array pointer
int x = start1, y = start2;
int end1 = len1 + start1 - 1;
int end2 = len2 + start2 - 1;
while (x <= end1 && y <= end2) {
Comparable obj1 = (Comparable) data[x];
Comparable obj2 = (Comparable) data[y];
Comparable<?> smallobject = null;
if (obj1.compareTo(obj2) < 0) {
smallobject = obj1;
x++;
}
else {
smallobject = obj2;
y++;
num_inversion += (end1 - x + 1);
}
dataList.add(smallobject);
}
while (x <= end1) {
dataList.add((Comparable)data[x++]);
}
while (y <= end2) {
dataList.add((Comparable)data[y++]);
}
for (int n = start1, i = 0; n <= end2; n++, i++) {
data[n] = dataList.get(i);
}
}
}
For testing, create a driver class and type the main method
public static void main(String[] args) {
Object[] data = ...............
Sorter sorter = new MergeSorter();
sorter.sort(data)
for (Object x : data) {
System.out.println(x);
}
System.out.println("Counting invertion: " + ((MergeSorter)sorter).counting());
}
i have a task where i need to find the mode of an array. which means i am looking for the int which is most frequent. i have kinda finished that, but the task also says if there are two modes which is the same, i should return the smallest int e.g {1,1,1,2,2,2} should give 1 (like in my file which i use that array and it gives 2)
public class theMode
{
public theMode()
{
int[] testingArray = new int[] {1,1,1,2,2,2,4};
int mode=findMode(testingArray);
System.out.println(mode);
}
public int findMode(int[] testingArray)
{
int modeWeAreLookingFor = 0;
int frequencyOfMode = 0;
for (int i = 0; i < testingArray.length; i++)
{
int currentIndexOfArray = testingArray[i];
int frequencyOfEachInArray = howMany(testingArray,currentIndexOfArray);
if (frequencyOfEachInArray > frequencyOfMode)
{
modeWeAreLookingFor = currentIndexOfArray;
frequencyOfMode = modeWeAreLookingFor;
}
}
return modeWeAreLookingFor;
}
public int howMany(int[] testingArray, int c)
{
int howManyOfThisInt=0;
for(int i=0; i < testingArray.length;i++)
{
if(testingArray[i]==c){
howManyOfThisInt++;
}
}
return howManyOfThisInt;
}
public static void main(String[] args)
{
new theMode();
}
}
as you see my algorithm returns the last found mode or how i should explain it.
I'd approach it differently. Using a map you could use each unique number as the key and then the count as the value. step through the array and for each number found, check the map to see if there is a key with that value. If one is found increment its value by 1, otherwise create a new entry with the value of 1.
Then you can check the value of each map entry to see which has the highest count. If the current key has a higher count than the previous key, then it is the "current" answer. But you have the possibility of keys with similar counts so you need to store each 'winnning' answer.
One way to approach this is to check each map each entry and remove each entry that is less than the current highest count. What you will be left with is a map of all "highest counts". If you map has only one entry, then it's key is the answer, otherwise you will need to compare the set of keys to determine the lowest.
Hint: You're updating ModeWeAreLookingFor when you find a integer with a strictly higher frequency. What if you find an integer that has the same frequency as ModeWeAreLookingFor ?
Extra exercice: In the first iteration of the main loop execution, you compute the frequency of '1'. On the second iteration (and the third, and the fourth), you re-compute this value. You may save some time if you store the result of the first computation. Could be done with a Map.
Java code convention states that method names and variable name should start with a lower case character. You would have a better syntax coloring and code easier to read if you follow this convention.
this might work with a little modification.
http://www.toves.org/books/java/ch19-array/index.html#fig2
if ((count > maxCount) || (count == maxCount && nums[i] < maxValue)) {
maxValue = nums[i];
maxCount = count;
}
since it seems there are no other way, i did a hashmap after all. i am stuck once again in the logics when it comes to comparing frequencys and and the same time picking lowest integer if equal frequencys.
public void theMode()
{
for (Integer number: intAndFrequencyMap.keySet())
{
int key = number;
int value = intAndFrequencyMap.get(number);
System.out.println("the integer: " +key + " exists " + value + " time(s).");
int lowestIntegerOfArray = 0;
int highestFrequencyOfArray = 0;
int theInteger = 0;
int theModeWanted = 0;
if (value > highestFrequencyOfArray)
{
highestFrequencyOfArray = value;
theInteger = number;
}
else if (value == highestFrequencyOfArray)
{
if (number < theInteger)
{
number = theInteger;
}
else if (number > theInteger)
{
}
else if (number == theInteger)
{
number = theInteger;
}
}
}
}
Completed:
import java.util.Arrays;
public class TheMode
{
//Probably not the most effective solution, but works without hashmap
//or any sorting algorithms
public TheMode()
{
int[] testingArray = new int[] {2,3,5,4,2,3,3,3};
int mode = findMode(testingArray);
System.out.println(Arrays.toString(testingArray));
System.out.println("The lowest mode is: " + mode);
int[] test2 = new int[] {3,3,2,2,1};
int mode2=findMode(test2);
System.out.println(Arrays.toString(test2));
System.out.println("The lowest mode is: " +mode2);
int[] test3 = new int[] {4,4,5,5,1};
int mode3 = findMode(test3);
System.out.println(Arrays.toString(test3));
System.out.println(The lowest mode is: " +mode3);
}
public int findMode(int[] testingArray)
{
int modeWeAreLookingFor = 0;
int frequencyOfMode = 0;
for (int i = 0; i < testingArray.length; i++)
{
int currentIndexOfArray = testingArray[i];
int countIntegerInArray = howMany(testingArray, currentIndexOfArray);
if (countIntegerInArray == frequencyOfMode)
{
if (modeWeAreLookingFor > currentIndexOfArray)
{
modeWeAreLookingFor = currentIndexOfArray;
}
}
else if (countIntegerInArray > frequencyOfMode)
{
modeWeAreLookingFor = currentIndexOfArray;
frequencyOfMode = countIntegerInArray;
}
}
return modeWeAreLookingFor;
}
public int howMany(int[] testingArray, int c)
{
int howManyOfThisInt=0;
for(int i=0; i < testingArray.length;i++)
{
if(testingArray[i]==c){
howManyOfThisInt++;
}
}
return howManyOfThisInt;
}
public static void main(String[] args)
{
new TheMode();
}
}
Glad you managed to solve it. As you will now see, there is more than one way to approach a problem. Here's what I meant by using a map
package util;
import java.util.HashMap;
import java.util.Map;
public class MathUtil {
public static void main(String[] args) {
MathUtil app = new MathUtil();
int[] numbers = {1, 1, 1, 2, 2, 2, 3, 4};
System.out.println(app.getMode(numbers));
}
public int getMode(int[] numbers) {
int mode = 0;
Map<Integer, Integer> numberMap = getFrequencyMap(numbers);
int highestCount = 0;
for (int number : numberMap.keySet()) {
int currentCount = numberMap.get(number);
if (currentCount > highestCount) {
highestCount = currentCount;
mode = number;
} else if (currentCount == highestCount && number < mode) {
mode = number;
}
}
return mode;
}
private Map<Integer,Integer> getFrequencyMap(int[] numbers){
Map<Integer, Integer> numberMap = new HashMap<Integer, Integer>();
for (int number : numbers) {
if (numberMap.containsKey(number)) {
int count = numberMap.get(number);
count++;
numberMap.put(number, count);
} else {
numberMap.put(number, 1);
}
}
return numberMap;
}
}