This commit is contained in:
gabe venberg 2021-04-26 00:32:50 -05:00
parent b1fe8d6ea1
commit 29c576c988
9 changed files with 577 additions and 0 deletions

1
.gitignore vendored
View file

@ -46,3 +46,4 @@ hs_err_pid*
/Lab108-VenbergGE/nbproject/private/
/Lab108-VenbergGE/build/
/Lab108-VenbergGE/dist/
/Lab109/build/

181
Lab109/src/ASCIITable.java Normal file
View file

@ -0,0 +1,181 @@
/*
* Copyright (C) 2021 Gabriel Venberg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* utility library for nicely formatted ascii tables.
* @author Gabriel Venberg
*/
public class ASCIITable {
/**
* generates an ASCII table based on a 2d data array. the top level array is an array of rows.
* @param data 2d array containing data to put in table
* @param padding how much padding to put on each side of entries
* @param tableHeader string to put in the table header (may cause problems if extremely long)
* @param columnHeaders array of strings to put at the top of each column.
* @return
*/
public static String render(Object data[][], int padding, String tableHeader, String[] columnHeaders) throws IllegalArgumentException {
int cols = calcNoCols(data);
if(cols!=columnHeaders.length){throw new IllegalArgumentException("must have equal number of column headers as columns!");}
int[] colWidths = calcColumnWidth(cols, data, columnHeaders);
//colWidths does not count padding or the | chars betwwen tables.
int width = sumOfArray(colWidths)+padding*cols*2+(cols-1);
String horizontalSpacer = assembleHorizontalSpacers(colWidths, padding, cols);
/*ok, so each cell will have the colwidth for the data, then padding for padding,
* then a | at the end. (plus 1 at the begginning of the table.
there will be 2 rows for each row of data (horizontal sep) plus a horizontal sep
at the end.
*/
String string = horizontalSpacer+'\n';
//print table header
string=string+tableHeader(tableHeader, width)+"\n";
string = string+horizontalSpacer+"\n";
//print coumn headers
string=string+columnHeaderString(colWidths, padding, columnHeaders)+'\n';
//got everything set up, build the table row by row.
for(int i=0; i<data.length; i++){
string = string+horizontalSpacer+"\n";
string = string+dataString(colWidths, padding, data[i])+'\n';
}
string = string+horizontalSpacer;
return string;
}
private static String tableHeader(String header, int width){
String string="|";
int halfPadding=(width-header.length())/2;
//front padding
for(int i=0; i<halfPadding; i++){string=string+" ";}
//if the total padding we need is odd, put it in front of the header
if((width-header.length())%2==1){string=string+" ";}
string=string+header;
//rear padding
for(int i=0; i<halfPadding; i++){string=string+" ";}
string=string+"|";
return string;
}
/**
* calcs the sum of all elements in an int array
* @param array array to be summed
* @return sum of array
*/
private static int sumOfArray(int[] array){
int sum=0;
for(int i=0; i<array.length; i++){
sum += array[i];
}
return sum;
}
/**
* calculates the maximum number of entries the rows in the data set have
* @param data 2D array of data
* @return needed number of rows in the final table.
*/
private static int calcNoCols(Object data[][]){
int rows = 0;
for(int i=0; i<data.length; i++){
rows = Math.max(rows, data[i].length);
}
return rows;
}
/**
* calculates the needed column widths for a data array without padding
* @param data the array of data
* @return an array of integers representing the needed width of each column
*/
private static int[] calcColumnWidth(int cols, Object data[][], String[] headers){
int[] maxWidths = new int[cols];
for(int i=0; i<cols; i++){
maxWidths[i]=headers[i].length();
for(int j=0; j<data.length; j++){
maxWidths[i]=Math.max(maxWidths[i], data[j][i].toString().length());
}
}
return maxWidths;
}
/**
* gives the horizontal spacer needed for the table
* @param colWidth width of each column;
* @param padding padding on each side of data.
* @param noOfCols number of columns;
* @return a string suitable to use as the horizontal spacer for the table.
*/
private static String assembleHorizontalSpacers(int[] colWidth, int padding, int noOfCols){
String string = "+";
for(int i=0; i<noOfCols; i++){
for(int j=0; j<colWidth[i]+2*padding; j++){
string = string+'-';
}
string = string+'+';
}
return string;
}
/**
* takes a single row of the data array and returns a row. Make sure your colWidth is accurate.
* @param colWidth width of each column
* @param padding min padding to have around each entry
* @param data 1D array of data to print
* @return a string containing the data
*/
private static String dataString(int[] colWidth, int padding, Object data[]){
String string ="|";
//for each entry in the row
for(int i=0; i<data.length; i++){
//only calc this once.
int length=data[i].toString().length();
// front padding. Also, I wish java had string multiplication.
for(int p=0; p<padding+(colWidth[i]-length); p++){string = string+" ";}
string = string+data[i].toString();
//rear padding
for(int p=0; p<padding; p++){string = string+" ";}
string = string+"|";
}
return string;
}
/**
* takes an array of strings (column headers) and outputs a single row of the column, center justified.
* @param colWidth width of each column
* @param padding min padding around each entry
* @param columnHeaders 1d array of strings containing col headers
* @return
*/
private static String columnHeaderString(int[] colWidth, int padding, String columnHeaders[]){
String string="|";
for(int i=0; i<columnHeaders.length; i++){
//calc this once.
int length=columnHeaders[i].length();
int sidePadding=(colWidth[i]-length+padding*2)/2;
//front padding
for(int p=0; p<sidePadding; p++){string=string+" ";}
//if we need an odd number of total padding, add the spare on the front
if((colWidth[i]-length)%2==1){string=string+" ";}
string=string+columnHeaders[i];
//rear padding
for(int p=0; p<sidePadding; p++){string=string+" ";}
string=string+"|";
}
return string;
}
}

View file

@ -0,0 +1,60 @@
import java.util.Comparator;
/*
* Data Structures & Algorithms 6th Edition
* Goodrich, Tamassia, Goldwasser
* Code Fragments 9.6
*
* An implementation of the AbstractPriorityQueue class
* */
/**
*
* @author Gabriel Venberg
*/
public abstract class AbstractPriorityQueue<K,V> implements PriorityQueue<K,V> {
//nested PQEntry class
protected static class PQEntry<K,V> implements Entry<K,V> {
private K k;
private V v;
public PQEntry(K key, V value){
k=key;
v=value;
}
//methods of entry interface
public K getKey(){return k;}
public V getValue(){return v;}
//utilites not exposed
protected void setKey(K key){k=key;}
protected void setValue(V value){v=value;}
}
//end of nested class
/** the comparator defining ordering of keys into the priority queue.*/
private Comparator<K> comp;
/** Creates an empty priority queue using the given comparator to order keys. */
protected AbstractPriorityQueue(Comparator<K> c){comp=c;}
/** creates an empty priority queue based on the natrual ordering of its keys */
protected AbstractPriorityQueue(){this(new DefaultComparator<K>());}
/** method for comparing two entries according to the key*/
protected int compare(Entry<K,V> a, Entry<K,V> b){
return comp.compare(a.getKey(), b.getKey());
}
/** determines whether a key is valid*/
protected boolean checkKey(K key) throws IllegalArgumentException {
try{
return (comp.compare(key,key)==0); //see if the key can be compared
}catch(ClassCastException e){
throw new IllegalArgumentException("incompatable key");
}
}
/**tests whether the priority queue is empty*/
public boolean isEmpty(){return size()==0;}
}

122
Lab109/src/Client.java Normal file
View file

@ -0,0 +1,122 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JOptionPane;
/*
* Copyright (C) 2021 Gabriel Venberg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received p copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
* @author Gabriel Venberg
*/
public class Client {
public static void main(String[] args){
String filePath = (String) JOptionPane.showInputDialog(null, "Enter the path of the file", "/home/toric/Downloads/words/words.txt");
File file = new File(filePath);
Scanner fileContents = null;
try {
fileContents = new Scanner(file);
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog(null, "file does not exist.");
System.exit(1);
}
ArrayList<String> words = new ArrayList<>();
while(fileContents.hasNext()){
words.add(fileContents.next());
}
String[][] hashData = new String[15][3];
for(int a=30; a<45; a++){
//tried using this using an array of buckets, it got... messy
HeapPriorityQueue<Integer,Integer> hashes = new HeapPriorityQueue<>();
for(int i=0; i< words.size(); i++){
//the value doesnt matter
hashes.insert(Hashing.polynomialHashCode(words.get(i), a), a);
}
int totalColisions=0, maxColisions=0, currentColisions=0;
//used to detect 'boundries' between groups of hash codes.
int prevHash=hashes.removeMin().getKey();
while(!hashes.isEmpty()){
//if its the same as the previous, it is p collision.
int currentHash=hashes.removeMin().getKey();
if(currentHash==prevHash){
currentColisions++;
totalColisions++;
}
//we have reached the end of that 'block', store our max collisions (if applicable), and reset current colisions.
else{
maxColisions=Math.max(maxColisions, currentColisions);
currentColisions=0;
prevHash=currentHash;
}
}
//write the table for value of a
hashData[a-30][0]=String.format("%,d", a);
hashData[a-30][1]=String.format("%,d", maxColisions);
hashData[a-30][2]=String.format("%,d", totalColisions);
}
String[] colHeaders={"a", "max collisions", "total collisions"};
System.out.println(ASCIITable.render(hashData, 2, "Polynomial hash code", colHeaders));
//lets have p be 92000. (for appropriate load factor). first prime number after is 92003
String[][] compressionData = new String[10][3];
for(int p=91998; p<92008; p++){
//tried using this using an array of buckets, it got... messy
HeapPriorityQueue<Integer,Integer> compressions = new HeapPriorityQueue<>();
for(int i=0; i< words.size(); i++){
//the value doesnt matter
compressions.insert(Hashing.madCompression(Hashing.polynomialHashCode(words.get(i), 33), 92000, p, 6578, 75245), p);
}
int totalColisions=0, maxColisions=0, currentColisions=0;
//used to detect 'boundries' between groups of hash codes.
int prevCompression=compressions.removeMin().getKey();
while(!compressions.isEmpty()){
//if its the same as the previous, it is p collision.
int currentCompression=compressions.removeMin().getKey();
if(currentCompression==prevCompression){
currentColisions++;
totalColisions++;
}
//we have reached the end of that 'block', store our max collisions (if applicable), and reset current colisions.
else{
maxColisions=Math.max(maxColisions, currentColisions);
currentColisions=0;
prevCompression=currentCompression;
}
}
//write the table for value of p
compressionData[p-91998][0]=String.format("%,d", p);
compressionData[p-91998][1]=String.format("%,d", maxColisions);
compressionData[p-91998][2]=String.format("%,d", totalColisions);
}
String[] colHeaders2={"p", "max collisions", "total collisions"};
System.out.println(ASCIITable.render(compressionData, 2, "MAD compression", colHeaders2));
}
}

View file

@ -0,0 +1,20 @@
import java.util.Comparator;
/*
* Data Structures & Algorithms 6th Edition
* Goodrich, Tamassia, Goldwasser
* Code Fragments 9.4
*
* An implementation of the DefaultComparator class
* */
/**
*
* @author Gabriel Venberg
*/
public class DefaultComparator<E> implements Comparator<E> {
public int compare(E a, E b) throws ClassCastException{
return((Comparable<E>)a).compareTo(b);
}
}

16
Lab109/src/Entry.java Normal file
View file

@ -0,0 +1,16 @@
/*
* Data Structures & Algorithms 6th Edition
* Goodrich, Tamassia, Goldwasser
* Code Fragments 9.1
*
* An implementation of the Entry Interface
* */
/**
*
* @author Gabriel Venberg
*/
public interface Entry <K,V>{
K getKey();
V getValue();
}

57
Lab109/src/Hashing.java Normal file
View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2021 Gabriel Venberg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* an implementation of polynomial hash code and MAD compression functions
* @author Gabriel Venberg
*/
public class Hashing {
/**
* hashes a given string (x0,x1,...,xn-1) by x0a^n-1+x1a^n-2+...+xn-2a+xn-1. using a given value of a
* @param value the string to hash
* @param a the value of a to use
* @return an int representing the resulting hash.
*/
public static int polynomialHashCode(String value, int a){
//due to twos compliment math, we dont actually need to use longs to detect rollover, and can just use a mask to ignore the sign bit!
//also, wasted 2 hours because I had set this to MIN_VALUE....
final int mask = Integer.MAX_VALUE;
int hash=0;
//using horners rule iteratively
for(int i=0; i<value.length(); i++){
hash = (hash * a) + (int) value.charAt(i);
}
// im *pretty sure* we only need to zero the sign bit once... will test
return hash&mask;
}
/**
* compresses a hash code with by multiply add divide method with given constants
* @param value the hash code to compress
* @param N the given value of N (should be the size of your bucket array if using to make hash table)
* @param p should be a prime number larger than N
* @param a random value between 1 and p-1
* @param b random value between 0 and p-1
* @return the compressed hash.
*/
public static int madCompression(int value, int N, int p, int a, int b) throws IllegalArgumentException{
if(0>b||b>p-1){throw new IllegalArgumentException("b needs to be between 0 and p-1, inclusive.");}
if(0>=a||a>p-1){throw new IllegalArgumentException("a needs to be betwwen 1 and p-1, inclusive.");}
return ((a*value+b)%p)%N;
}
}

View file

@ -0,0 +1,101 @@
import java.util.ArrayList;
import java.util.Comparator;
/*
* Data Structures & Algorithms 6th Edition
* Goodrich, Tamassia, Goldwasser
* Code Fragments 9.8-9.9
*
* An implementation of the heapPriortyQueue class
* */
/**
*
* @author Gabriel Venberg
*/
public class HeapPriorityQueue<K,V> extends AbstractPriorityQueue<K,V> {
/** primary collection of priorty queue entries*/
protected ArrayList<Entry<K,V>> heap = new ArrayList<>();
/**creates an empty priority queue based on the natrual ordering of its keys*/
public HeapPriorityQueue(){super();}
/** creates an empty priorty queue using the given comparator to order keys.*/
public HeapPriorityQueue(Comparator<K> comp){super(comp);}
//protected utilities
protected int parent(int j){return(j-1)/2;}
protected int left(int j){return 2*j+1;}
protected int right(int j){return 2*j+2;}
protected boolean hasLeft(int j){return left(j)<heap.size();}
protected boolean hasRight(int j){return right(j)<heap.size();}
protected void swap(int i, int j){
Entry<K,V> temp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, temp);
}
/** moves the entry at index j higher, if necessary, to restore heap property*/
protected void upheap(int j){
//continue till reaching root or break statemetnt
while(j>0){
int p=parent(j);
//heap verified
if(compare(heap.get(j), heap.get(p))>=0){break;}
swap(j,p);
//continue from the parents location.
j=p;
}
}
/** moves the entry at index j lower, if necessayr, to restore the heap property */
protected void downheap(int j){
while(hasLeft(j)) { //continue to bottom or break statement.
int leftIndex=left(j);
//although right may be smaller
int smallChildIndex = leftIndex;
if(hasRight(j)){
int rightIndex=right(j);
if(compare(heap.get(leftIndex), heap.get(rightIndex))>0){
smallChildIndex=rightIndex;
}
}
if(compare(heap.get(smallChildIndex), heap.get(j))>=0){break;}
swap(j, smallChildIndex);
j=smallChildIndex;
}
}
//public methods
/**returns the number of items in the priority queue*/
public int size(){return heap.size();}
/**returns but does not remove an entry with minimal key (if any)*/
public Entry<K,V> min(){
if(heap.isEmpty()){return null;}
return heap.get(0);
}
/**inserts a key value pair and returns the entry created*/
public Entry<K,V> insert(K key, V value) throws IllegalArgumentException{
checkKey(key); //could throw exceptions.
Entry<K,V> newest = new PQEntry<>(key,value);
//add to end of the list
heap.add(newest);
//and upheap
upheap(heap.size()-1);
return newest;
}
/**removes and returns an entry with minimal key(if any)*/
public Entry<K,V> removeMin(){
if(heap.isEmpty()){return null;}
Entry<K,V> answer = heap.get(0);
//put minimum item at the end
swap(0, heap.size()-1);
//and remove from list
heap.remove(heap.size()-1);
//then fix the new root.
downheap(0);
return answer;
}
}

View file

@ -0,0 +1,19 @@
/*
* Data Structures & Algorithms 6th Edition
* Goodrich, Tamassia, Goldwasser
* Code Fragments 9.2
*
* An implementation of the PriorityQueue interface
* */
/**
*
* @author Gabriel Venberg
*/
public interface PriorityQueue <K,V> {
int size();
boolean isEmpty();
Entry<K,V> insert(K key, V value) throws IllegalArgumentException;
Entry<K,V> min();
Entry<K,V> removeMin();
}