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());
}
Related
I know similar questions have been asked before but I have found the answers confusing. I am trying to make a program that will find every combination of an array-list with no repetitions and only of the maximum size. If the list has 4 items it should print out only the combinations with all 4 items present. This is what I have so far:
public main(){
UI.initialise();
UI.addButton("Test", this::testCreate);
UI.addButton("Quit", UI::quit);
}
public void createCombinations(ArrayList<String> list, String s, int depth) {
if (depth == 0) {
return;
}
depth --;
for (int i = 0; i < list.size(); i++) {
if (this.constrain(s + "_" + list.get(i), list.size())) {
UI.println(s + "_" + list.get(i));
}
createCombinations(list, s + "_" + list.get(i), depth);
}
}
public void testCreate() {
ArrayList<String> n = new ArrayList<String>();
n.add("A"); n.add("B"); n.add("C"); n.add("D");
this.createCombinations(n , "", n.size());
}
public boolean constrain(String s, int size) {
// Constrain to only the maximum length
if ((s.length() != size*2)) {
return false;
}
// Constrain to only combinations without repeats
Scanner scan = new Scanner(s).useDelimiter("_");
ArrayList<String> usedTokens = new ArrayList<String>();
String token;
while (scan.hasNext()) {
token = scan.next();
if (usedTokens.contains(token)) {
return false;
} else {
usedTokens.add(token);
}
}
// If we fully iterate over the loop then there are no repitions
return true;
}
public static void main(String[] args){
main obj = new main();
}
This prints out the following which is correct:
_A_B_C_D
_A_B_D_C
_A_C_B_D
_A_C_D_B
_A_D_B_C
_A_D_C_B
_B_A_C_D
_B_A_D_C
_B_C_A_D
_B_C_D_A
_B_D_A_C
_B_D_C_A
_C_A_B_D
_C_A_D_B
_C_B_A_D
_C_B_D_A
_C_D_A_B
_C_D_B_A
_D_A_B_C
_D_A_C_B
_D_B_A_C
_D_B_C_A
_D_C_A_B
_D_C_B_A
This works for small lists but is very inefficient for larger ones. I am aware that what I have done is completely wrong but I want to learn the correct way. Any help is really appreciated. Thanks in advance.
P.S. This is not homework, just for interest although I am a new CS student (if it wasn't obvious).
Implementing Heap's algorithm in Java:
import java.util.Arrays;
public class Main {
public static void swap(final Object[] array, final int index1, final int index2) {
final Object tmp = array[index1];
array[index1] = array[index2];
array[index2] = tmp;
}
public static void printPermutations_HeapsAlgorithm(final int n, final Object[] array) {
final int[] c = new int[n];
for (int i = 0; i < c.length; ++i)
c[i] = 0;
System.out.println(Arrays.toString(array)); //Consume first permutation.
int i=0;
while (i < n) {
if (c[i] < i) {
if ((i & 1) == 0)
swap(array, 0, i);
else
swap(array, c[i], i);
System.out.println(Arrays.toString(array)); //Consume permutation.
++c[i];
i=0;
}
else
c[i++] = 0;
}
}
public static void main(final String[] args) {
printPermutations_HeapsAlgorithm(4, new Character[]{'A', 'B', 'C', 'D'});
}
}
Possible duplicate of this.
So this is my assignemt, I have to write a function that accepts a number less than 100 and returns the fibonacci element at that position. I don't understand what is wrong with my code. please note that I wrote only getFibonacciElementAt function code
import java.util.Scanner;
public class FibonacciNumber {
public long getFibonacciElementAt(int index) {
Scanner sc= new Scanner(System.in);
System.out.println("Please enter the number");
int n= sc.nextInt();
if(n<0)
return -1;
int a=0;
int b=1;
int i;
for(i=2; i<=n; i++)
{
int temp=a;
a=b;
b=temp;
}
return a;
}
public void printFibonacciElementAt(int index) {
System.out.println(getFibonacciElementAt(index));
}
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Exactly 1 inputs required.");
return;
}
try {
int num = Integer.parseInt(args[0]);
FibonacciNumber obj = new FibonacciNumber();
obj.printFibonacciElementAt(num);
} catch (NumberFormatException e) {
System.out.println("Only integers allowed.");
}
}
}
Okay, so re-posting a better recursive version using memoization so runtime of n<=100 is less than 1 second:
public static long getFibonacciElementAt(int n, long[] d) {
if (n == 0 || n == 1)
return n;
if (d[n] == 0)
d[n] = getFibonacciElementAt(n - 1, d) + getFibonacciElementAt(n - 2, d);
return d[n];
}
But once you call the method just pass a new array of the size n+1 as the following:
System.out.println(getFibonacciElementAt(n, new long[n+1]));
You are just exchanging, not adding the previous two numbers. So try following:
int a=0;
int b=1;
int temp;
for(int i=2; i<=n; i++)
{
temp=a+b;
a=b;
b=temp;
}
return b;
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.
i am trying to find lcm of n numbers and i want my recursion to run upto the last element, but i cant find an efficient method to limit it from going out of bounds at line 41.
please suggest any trick.
import java.io.*;
class lcm
{
static int x=1,k=1;
public static void main(String args[])throws IOException
{
BufferedReader br=new BufferedReader (new InputStreamReader(System.in));
System.out.println("enter the limit");
int n= Integer.parseInt(br.readLine());
System.out.println("enter the numbers");
int L;
int arr[]=new int[n];
for(int i=0;i<n;i++)
arr[i]=Integer.parseInt(br.readLine());
L=lc( arr[0],arr[1],arr,n);
System.out.println(L);
}
static int lc(int min, int max, int arr[], int n)
{
int fact;
if(x<=n-1 )
{
for(int i=1;i<=min;i++)
{
fact=max*i;
if(fact%min==0)
{
k=fact;
break;
}
}
//System.out.println("values of x"+x);
//System.out.println("values of k"+k);
x++;
return lc(arr[x],k,arr,n);
}
else
{
//System.out.println("vale of x"+x);
return k;
}
}
}
i found a solution though i still want to know if the code i wrote is efficient or not?
static int lc(int min, int max, int arr[], int n)
{
int fact;
if(x<n-1 )
{
for(int i=1;i<=min;i++)
{
fact=max*i;
if(fact%min==0)
{
k=fact;
break;
}
}
System.out.println("values of x"+x);
System.out.println("values of k"+k);
x++;
return lc(arr[x],k,arr,n);
}
else
{
min=arr[x];
max=k;
for(int i=1;i<=min;i++)
{
fact=max*i;
if(fact%min==0)
{
k=fact;
break;
}
}
return k;
}
}
}
I'm having some problems trying to figure out how to insert an integer using a scanner into an ArrayList. I'm not that great (actually not even really good) at java but I'm just trying to figure some things out and any help would be great.
package mySort;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
public class MergeInsert {
private int limit = 100;
//private int size = 0;
private ArrayList<Integer> ArrayToSort;
public MergeInsert(int x) {
ArrayToSort = new ArrayList<Integer>(x);
}
public MergeInsert(Scanner integerScan){
int j = 0;
while(integerScan.hasNextInt()){
this.insert(integerScan.hasNextInt());
if (j % 10000 == 0){
long time = System.nanoTime();
System.out.println(j + "," + time);
}
}
}
public void insert(int x){
for(int i=0; i<ArrayToSort.size(); i++){
ArrayToSort(size++) = x;
}
}
// public MergeInsert(int v){
// int val = v;
// }
// public void insertFile(){
// try {
// Scanner integerScan = new Scanner(new FileInputStream(""));
// while(integerScan.hasNextInt()){
// new MergeInsert(integerScan.nextInt());
// }
// }
// catch (FileNotFoundException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
public void sort(){
}
public void mergeSort(ArrayList<Integer> in, int low,int high){
int n = in.size();
int mid = (high+low)/2;
if (n<2){ //already sorted
return;
}
if ((high - low) < limit){
insertionSort(in);
}
ArrayList<Integer> in1 = new ArrayList<Integer>(); //helper
ArrayList<Integer> in2 = new ArrayList<Integer>(); //helper
int i=0;
while (i < n/2){ //moves the first half to the helper
in1.add(in.remove(0));
i++;
}
while (!in.isEmpty()) //moves the second half to the helper
in2.add(in.remove(0));
mergeSort(in1, low, mid); //breaks it down some more like mergesort should
mergeSort(in2, mid+1, high); //does it again
merge(in1,in2,in); //trying to build it up again
}
public void merge(ArrayList<Integer> in, ArrayList<Integer> in1, ArrayList<Integer> in2){
while (!in1.isEmpty() || !in2.isEmpty()) //as long as both helpers still have elements
if ((in1.get(0).compareTo(in2.get(0)) <= 0)) //comparison to rebuild
in.add(in1.remove(0)); //building it back up
else
in.add(in2.remove(0)); //still building
while(!in1.isEmpty()) //as long as the first helper isn't empty keep building
in.add(in1.remove(0));
while(!in2.isEmpty()) //as long as the second helper isn't empty keep building
in.add(in2.remove(0));
}
public ArrayList<Integer> insertionSort(ArrayList<Integer> in){
int index = 1;
while (index<in.size()){
insertSorted((int)(in.get(index)),in,index);
index = index +1;
}
return in;
}
public ArrayList<Integer> insertSorted(Integer s, ArrayList<Integer> in, int index){
int loc = index-1;
while((loc>=0) || s.compareTo(in.get(loc)) <= 0){
in.set(loc + 1, in.get(loc));
loc = loc -1;
}
in.set(loc+1, s);
return in;
}
/**
* #param args
* #throws FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException {
Scanner integerScan = new Scanner(new FileInputStream("src/myRandomNumbers.txt"));
MergeInsert myObject = new MergeInsert(integerScan);
myObject.sort();
}
}
It's not completely finished but the idea behind all of this is to try and improve on MergeSort. Basically once the elements get broken down to a certain point cut to InsertionSort because it is usually better on really small (really small being relative) sets of data.
public void insert(int x){
ArrayToSort.add(x); // add it to the end
}
The reason is... even if you go
ArrayToSort = new ArrayList<Integer>(100000);
It still has a size of 0. It just has a CAPACITY of 100000.
Use add to insert objects into the list.
Also, the way your code is structured now, you will get a NullPointerException when you attempt to invoke add because the constructor you invoke never initializes the list.
Given the quality of your code, I highly recommend reading Learning the Java Language.