Files
uni/year2/semester2/CT2109/Assignments/Assignment-03/code/Palindrome.java

321 lines
18 KiB
Java

import java.io.*;
public class Palindrome {
// global operations count for each method
public static long operations1 = 0;
public static long operations2 = 0;
public static long operations3 = 0;
public static long operations4 = 0;
// String objects used to hold the csv data (size of problem, number of operations) for each method
public static StringBuilder data1 = new StringBuilder("operations,size\n");
public static StringBuilder data2 = new StringBuilder("operations,size\n");
public static StringBuilder data3 = new StringBuilder("operations,size\n");
public static StringBuilder data4 = new StringBuilder("operations,size\n");
public static void main(String args[]) {
// generating all the String versions of the numbers so that they don't have to be generated for each method
// 0th column will be decimal, 1st column will be binary
String[][] strings = new String[1_000_001][2];
for (int i = 0; i <= 1_000_000; i++) {
strings[i][0] = Integer.toString(i, 10); // converting i to a String base 10
strings[i][1] = binary2string(strings[i][0]); // converting the decimal String to a binary String
}
// variables for method 1 - reversed order String vs original String
int decCount1 = 0; operations1++; // count of decimal palindromes for use by method 1
int binCount1 = 0; operations1++; // count of binary palindromes for use by method 1
int bothCount1 = 0; operations1++; // count of numbers that are palindromic in both binary & decimal for use by method 1
long startTime1 = System.currentTimeMillis(); operations1 += 1 + 1;
// testing method 1 - reversed order String vs original String
// incrementing the operations counter for the initialisation of i below
operations1++;
for (int i = 0; i <= 1_000_000; i++) {
// incrementing the operations count by 2, 1 for the loop condition check and 1 for incrementing i
operations1 += 2;
// converting the number to a decimal or binary String and checking if is a palindrome
boolean isDecPalindrome = reverseVSoriginal(strings[i][0]); operations1++;
boolean isBinPalindrome = reverseVSoriginal(strings[i][1]); operations1++;
// incrementing the appropriate counter if the number is a palindrome in that base
decCount1 = isDecPalindrome ? decCount1 + 1 : decCount1; operations1 += 1 + 1; // incremnting by 2, 1 for assignment, 1 for condition check
binCount1 = isBinPalindrome ? binCount1 + 1 : binCount1; operations1 += 1 + 1;
bothCount1 = isDecPalindrome && isBinPalindrome ? bothCount1 + 1 : bothCount1; operations1 += 1 + 1 +1; // 2 condition checks and one assignment, so incrementing by 3
// appending to the data StringBuilder at intervals of 50,000
if (i % 50_000 == 0) {
data1.append(operations1 + "," + i + "\n");
}
}
// calculating total time taken for method 1 and printing out the results
long totalTime1 = System.currentTimeMillis() - startTime1; operations1 += 1 + 1; // incrementing by 2, 1 for getting current time and subtracting start time, 1 for assignment
System.out.println("Number of decimal palindromes found using Method 1: " + decCount1);
System.out.println("Number of binary palindromes found using Method 1: " + binCount1);
System.out.println("Number of palindromes in both decimal & binary found using Method 1: " + bothCount1);
System.out.println("Number of primitive operations taken in Method 1: " + operations1);
System.out.println("Time taken for Method 1: " + totalTime1 + " milliseconds");
// variables for method 2 - comparing each element at index i to the element at n - i where n is the last index
int decCount2 = 0; operations2++; // count of decimal palindromes for use by method 2
int binCount2 = 0; operations2++; // count of binary palindromes for use by method 2
int bothCount2 = 0; operations2++; // count of numbers that are palindromic in both binary & decimal for use by method 2
long startTime2 = System.currentTimeMillis(); operations2 += 1 + 1;
// testingmethod 2 - comparing each element at index i to the element at n - i where n is the last index
operations2++;
for (int i = 0; i <= 1_000_000; i++) {
operations2 += 1 + 1;
// converting the number to a decimal or binary String and checking if is a palindrome
boolean isDecPalindrome = iVSiMinusn(strings[i][0]); operations2++;
boolean isBinPalindrome = iVSiMinusn(strings[i][1]); operations2++;
// incrementing the appropriate counter if the number is a palindrome in that base
decCount2 = isDecPalindrome ? decCount2 + 1 : decCount2; operations2 += 1 + 1;
binCount2 = isBinPalindrome ? binCount2 + 1 : binCount2; operations2 += 1 + 1;
bothCount2 = isDecPalindrome && isBinPalindrome ? bothCount2 + 1 : bothCount2; operations2 += 1 + 1 +1;
// appending to the data StringBuilder at intervals of 50,000
if (i % 50_000 == 0) {
data2.append(operations2 + "," + i + "\n");
}
}
// calculating total time taken for method 2 and printing out the results
long totalTime2 = System.currentTimeMillis() - startTime2; operations2 += 1 + 1;
System.out.println();
System.out.println("Number of decimal palindromes found using Method 2: " + decCount2);
System.out.println("Number of binary palindromes found using Method 2: " + binCount2);
System.out.println("Number of palindromes in both decimal & binary found using Method 2: " + bothCount2);
System.out.println("Number of primitive operations taken in Method 2: " + operations2);
System.out.println("Time taken for Method 2: " + totalTime2 + " milliseconds");
// variables for method 3 - comparing each element at index i to the element at n - i where n is the last index
int decCount3 = 0; operations3++; // count of decimal palindromes for use by method 3
int binCount3 = 0; operations3++; // count of binary palindromes for use by method 3
int bothCount3 = 0; operations3++; // count of numbers that are palindromic in both binary & decimal for use by method 3
long startTime3 = System.currentTimeMillis(); operations3 += 1 + 1;
// testingmethod 3 - comparing each element at index i to the element at n - i where n is the last index
operations3++;
for (int i = 0; i <= 1_000_000; i++) {
operations3 += 1 + 1;
// converting the number to a decimal or binary String and checking if is a palindrome
boolean isDecPalindrome = stackVsqueue(strings[i][0]); operations3++;
boolean isBinPalindrome = stackVsqueue(strings[i][1]); operations3++;
// incrementing the appropriate counter if the number is a palindrome in that base
decCount3 = isDecPalindrome ? decCount3 + 1 : decCount3; operations3 += 1 + 1;
binCount3 = isBinPalindrome ? binCount3 + 1 : binCount3; operations3 += 1 + 1;
bothCount3 = isDecPalindrome && isBinPalindrome ? bothCount3 + 1 : bothCount3; operations3 += 1 + 1 + 1;
// appending to the data StringBuilder at intervals of 50,000
if (i % 50_000 == 0) {
data3.append(operations3 + "," + i + "\n");
}
}
// calculating total time taken for method 3 and printing out the results
long totalTime3 = System.currentTimeMillis() - startTime3; operations3 += 1 + 1;
System.out.println();
System.out.println("Number of decimal palindromes found using Method 3: " + decCount3);
System.out.println("Number of binary palindromes found using Method 3: " + binCount3);
System.out.println("Number of palindromes in both decimal & binary found using Method 3: " + bothCount3);
System.out.println("Number of primitive operations taken in Method 3: " + operations3);
System.out.println("Time taken for Method 3: " + totalTime3 + " milliseconds");
// variables for method 4 - comparing each element at index i to the element at n - i where n is the last index
int decCount4 = 0; operations4++; // count of decimal palindromes for use by method 4
int binCount4 = 0; operations4++; // count of binary palindromes for use by method 4
int bothCount4 = 0; operations4++; // count of numbers that are palindromic in both binary & decimal for use by method 4
long startTime4 = System.currentTimeMillis(); operations4 += 1 + 1;
// testingmethod 4 - comparing each element at index i to the element at n - i where n is the last index
operations4++;
for (int i = 0; i <= 1_000_000; i++) {
operations4 += 2;
// converting the number to a decimal or binary String and checking if is a palindrome
boolean isDecPalindrome = recursiveReverseVSoriginal(strings[i][0]); operations4++;
boolean isBinPalindrome = recursiveReverseVSoriginal(strings[i][1]); operations4++;
// incrementing the appropriate counter if the number is a palindrome in that base
decCount4 = isDecPalindrome ? decCount4 + 1 : decCount4; operations4 += 1 + 1;
binCount4 = isBinPalindrome ? binCount4 + 1 : binCount4; operations4 += 1 + 1;
bothCount4 = isDecPalindrome && isBinPalindrome ? bothCount4 + 1 : bothCount4; operations4 += 1 + 1 + 1;
// appending to the data StringBuilder at intervals of 50,000
if (i % 50_000 == 0) {
data4.append(operations4 + "," + i + "\n");
}
}
// calculating total time taken for method 4 and printing out the results
long totalTime4 = System.currentTimeMillis() - startTime4; operations4 += 1 + 1;
System.out.println();
System.out.println("Number of decimal palindromes found using Method 4: " + decCount4);
System.out.println("Number of binary palindromes found using Method 4: " + binCount4);
System.out.println("Number of palindromes in both decimal & binary found using Method 4: " + bothCount4);
System.out.println("Number of primitive operations taken in Method 4: " + operations4);
System.out.println("Time taken for Method 4: " + totalTime4 + " milliseconds");
// outputting the data to separate csv files
try {
File csv1 = new File("method1.csv");
File csv2 = new File("method2.csv");
File csv3 = new File("method3.csv");
File csv4 = new File("method4.csv");
// creating files if they don't already exist
csv1.createNewFile();
csv2.createNewFile();
csv3.createNewFile();
csv4.createNewFile();
FileWriter writer1 = new FileWriter("method1.csv");
writer1.write(data1.toString());
writer1.close();
FileWriter writer2 = new FileWriter("method2.csv");
writer2.write(data2.toString());
writer2.close();
FileWriter writer3 = new FileWriter("method3.csv");
writer3.write(data3.toString());
writer3.close();
FileWriter writer4 = new FileWriter("method4.csv");
writer4.write(data4.toString());
writer4.close();
} catch (IOException e) {
System.out.println("IO Error occurred");
e.printStackTrace();
System.exit(1);
}
}
// method 1 - reversed order String vs original String
public static boolean reverseVSoriginal(String str) {
String reversedStr = ""; operations1++;
// looping through each character in the String, backwards
// incrementing operations counter by 2, 1 for initialisating i, 1 for getting str.length()
operations1 += 1 + 1;
for (int i = str.length(); i > 0; i--) {
operations1 += 1 + 1; // for loop condition check & incrementing i
reversedStr += str.charAt(i-1); operations1 += 1 + 1;
}
// returning true if the Strings are equal, false if not
operations1 += str.length(); // the equals method must loop through each character of the String to check that they are equal so it is O(n)
return str.equals(reversedStr);
}
// method 2 - comparing each element at index i to the element at n - i where n is the last index
public static boolean iVSiMinusn(String str) {
// looping through the first half of the String
operations2++;
for (int i = 0; i < Math.floor(str.length() / 2); i++) {
operations2 += 1 + 1 + 1 + 1; // 1 for the getting str.length(), 1 for Math,floor, 1 for checking condition, 1 for incrementing
// returning false if the digits don't match
operations2 += 1 + 1 + 1 + 1; // 1 for str.charAt(i), 1 for ((str.lenght() -1) - 1), 1 for the other str.charAt(), 1 for checking the condition
if (str.charAt(i) != str.charAt((str.length()-1) - i)) {
return false;
}
}
// returning true as default
return true;
}
// method 3 - using a stack and a queue to do, essentially, what method 2 does
public static boolean stackVsqueue(String str) {
ArrayStack stack = new ArrayStack(); operations3 += 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
ArrayQueue queue = new ArrayQueue(); operations3 += 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
// looping through each character in the String and adding the character to the stack & queue
operations3++;
for (int i = 0; i < str.length(); i++) {
operations3 += 1 + 1 + 1;
stack.push(str.charAt(i)); operations3 += 1 + 1 + 1 + 1;
queue.enqueue(str.charAt(i)); operations3 += 1 + 1 + 1 + 1;
}
// looping through each character on the stack & queue and comparing them, returning false if they're different
operations3++;
for (int i = 0; i < str.length(); i++) {
operations3 += 1 + 1 + 1;
operations3 += 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
if (!stack.pop().equals(queue.front())) {
return false;
}
// the complexity of ArrayQueue.dequeue() is 3n+2, where n is the number of items in the queue when dequeue() is called.
// we need to determine the number of items in the queue so that we can determine the number of primitive operations performed when queue.dequeue() is called.
// to do this, we'll loop through the queue, dequeuing each object and enqueueing it in another ArrayQueue. once complete, we'll reassign the variable queue to point to the new ArrayQueue containing all the objects
ArrayQueue newQueue = new ArrayQueue(); // not counting the operations for this as it's not part of the algorithm, it's part of the operations counting
int n = 0; // n is the number of items in the ArrayQueue when dequeue() is called
while (!queue.isEmpty()) {
newQueue.enqueue(queue.dequeue());
n++;
}
queue = newQueue; // setting queue to point to the newQueue, which is just the state that queue would have been in if we didn't do this to calculate the primitive operations
newQueue = null; // don't need the newQueue object reference anymore
operations3 += 3*n + 2; // complexity of dequeue is 3n+2
queue.dequeue();
}
return true;
}
// method 4 - comparing the String reversed using recursion to the original String (essentially method 1 but with recursion)
public static boolean recursiveReverseVSoriginal(String str) {
// returning true if the original String is equal to the reversed String, false if not
operations4++;
return str.equals(reverse(str));
}
// method to reverse the characters in a String using recursion
public static String reverse(String str) {
// base case - returning an empty String if there is no character left in the String
operations4++;
if (str.length() == 0) {
return "";
}
else {
char firstChar = str.charAt(0); operations4 += 1 + 1;
String remainder = str.substring(1); operations4 += 1 + 1; // selecting the rest of the String, excluding the 0th character
// recursing with what's left of the String
String reversedRemainder = reverse(remainder); operations4++;
// returning the reversed rest of String with the first character of the String appended
return reversedRemainder + firstChar;
}
}
// utility method to convert a decimal String to its equivalent binary String
public static String binary2string(String decimalStr) {
return Integer.toString(Integer.parseInt(decimalStr), 2); // parsing the String to an int and then parsing that int to a binary String
}
}