How to fix my data collection program? - java

This program is meant to collect data on the speed of three search algorithms (linear, binary, and random). I've extensively tested each search method called and they all work. I know the error exists in the for loop containing k, but I'm not sure what or why. I get the proper results for linear[0], binary[0], and random[0], but cannot get an answer for any other place in the arrays within the loop (which makes sense since I the other places have not happened yet). Outside of the loop though, I can't print out any part of any of the arrays.
import java.util.Random;
/*class to generate testing data*/
public class GenerateData{
public static void main(String[] args) {
/*creates and runs through each power of 2 array*/
for(int i=3; i < 5; i++) {
/*calculates a power of 2 starting at 8*/
int n = (int)Math.pow(2, i);
/*creates an array of a power of 2 starting at 8*/
int [] test = new int[n];
/*generates a starting testing point for the array*/
int start = 3;
/*fills the array with sorted testing data*/
for(int j=0; j < n; j++) {
test[j] = start;
start = start + 2;
}
/*creates an array to store linear testing times*/
long [] linear = new long[10];
/*creates an array to store binary testing times*/
long [] binary = new long[10];
/*creates an array to store random testing times*/
long [] random = new long[10];
/*runs through the search algorithms ten times*/
for(int k=0; k < 10; k++) {
/*generates a random number to test no larger than the largest
value in the array*/
int queryValue = (int)Math.floor(Math.random()*(start-1));
/*tests the array in each algorithm, keeping track of start and
end time*/
long linearStartTime = System.nanoTime();
int linearTest = LinearSearch.linearSearch(queryValue, test);
/*calculates the time for linear algorithm and adds it to
an array keeping track of linear algorithm run times*/
linear[k] = System.nanoTime() - linearStartTime;
long binaryStartTime = System.nanoTime();
int binaryTest = BinarySearch.binarySearch(queryValue, test);
/*calculates the time for binary algorithm and adds it to
an array keeping track of binary algorithm run times*/
binary[k] = System.nanoTime() - binaryStartTime;
long randomStartTime = System.nanoTime();
int randomTest = RandomSearch.randomSearch(queryValue, test);
/*calculates the time for random algorithm and adds it to
an array keeping track of random algorithm run times*/
random[k] = System.nanoTime() - randomStartTime;
}
/*placeholder initial values for mins, maxes, and avgs*/
long linMin = linear[1];
long binMin = binary[1];
long randMin = random[1];
long linMax = linear[1];
long binMax = binary[1];
long randMax = random[1];
long linAvg = 0;
long binAvg = 0;
long randAvg = 0;
/*cycles through the arrays calculating the min, max, and avg*/
for(int l=0; l < 9; l++) {
/*calculates the avg for each algorithm array*/
linAvg = linAvg + linear[l] / 2;
binAvg = binAvg + binary[l] / 2;
randAvg = randAvg + random[l] / 2;
/*calculates the min for each algorithm array*/
if(linear[l] < linMin) {
linMin = linear[l];
}
if(binary[l] < binMin) {
binMin = binary[l];
}
if(random[l] < randMin) {
randMin = random[l];
}
/*calculates the max for each algorithm array*/
if(linear[l] > linMax) {
linMax = linear[l];
}
if(binary[l] > binMax) {
binMax = linear[l];
}
if(random[l] > randMax) {
randMax = random[l];
}
/*prints the current power of 2, min, max, and avg
for each algorithm into a file*/
StdOut.println("linear" + "," + n + "," + linMin + "," + linMax + "," + linAvg);
StdOut.println("binary" + "," + n + "," + binMin + "," + binMax + "," + binAvg);
StdOut.println("random" + "," + n + "," + randMin + "," + randMax + "," + randAvg);
}
}
}
}
The binary search I'm using. Provided for testing purposes.
/*class containing binary search algorithm*/
public class BinarySearch {
/*conducts a binary search as specified by user*/
public static int binarySearch(int queryValue, int[] list) {
int length = list.length;
/*last point of list*/
int top = length-1;
/*first point of list*/
int bottom = 0;
/*starting midpoint of list*/
int mid = (int)Math.round((top + bottom)/2);
/*binary search*/
do {
if(queryValue > list[mid]) {
bottom = mid;
mid = (int)Math.ceil((top + bottom) / 2.0);
}
else {
top = mid;
mid = (int)Math.floor((top + bottom) / 2.0);
}
if(queryValue == list[mid]) {
return mid;
}
} while (mid < top || mid > bottom);
/*returns -1 if user value not found*/
return -1;
}
}
The linear search I'm using. Provided for testing purposes.
/*class containing linear search algorithm*/
public class LinearSearch {
public static int linearSearch(int queryValue, int[] list) {
int length = list.length;
/*conducts a linear search as specified by user*/
for(int i = 0; i < length; i++) {
if((int)queryValue == (int)list[i]) {
return i;
}
}
/*return -1 if user value not found*/
return -1;
}
}
The random search I'm using. Provided for testing purposes.
import java.util.Random;
/*class containing random search algorithm*/
public class RandomSearch {
public static int randomSearch(int queryValue, int[] list) {
/*conducts a random search as specified by user*/
/*trys 10,000,000 random combinations searching
for user value*/
int length = list.length;
for(int i=0; i < 10000000; i++) {
/*generates a random number from 0 to length*/
int randomNum = (int)Math.floor(Math.random()*(length));
if((int)queryValue == (int)list[randomNum]) {
return randomNum;
}
}
/*returns -2 if user value not found*/
return -2;
}
}

If this is Java, you want to use
System.out.println(....)
not your StdOut....

Related

Why is my random array sorting in 0.001 seconds?

I'm making a program that compares the times of 4 different types of sorts. Each sort uses a sorted array, a reverse sorted array, and a randomly filled array. The times for the sorted and reverse sorted look correct, but the randomly filled always returns <0.01, no matter the input size. My main looks like this:
System.out.println("---------LEFT PIVOT----------\n");
for (int i = 0; i < 4; i++) {
int[][] a = makearrays((int) (_SIZE * Math.pow(2, i)));
System.out.println("SIZE: " + _SIZE * Math.pow(2, i));
for (int k = 0; k < 3; k++) {
long start = System.currentTimeMillis();
Sort.quicksort(a[k], 0, a[k].length - 1);
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("Time for " + types[k] + ": " + (time / 1000.0) + " seconds.");
System.out.println();
}
}
Where a[0] is the sorted, a[1] is the reverse sorted, and a[2] is the random sorted. Here's a sample of the output:
Time for SORTED ARRAY: 1.212 seconds.
Time for REVERSE SORTED ARRAY: 5.226 seconds.
Time for RANDOM ARRAY: 0.008 seconds.
On another run, it outputs this:
Time for SORTED ARRAY: 1.19 seconds.
Time for REVERSE SORTED ARRAY: 4.053 seconds.
Time for RANDOM ARRAY: 0.009 seconds.
I printed the arrays before and after each sort, and can confirm the random array is completely random before the sort, and sorted after. Does anyone have any idea why it would be printing such a small time? Thanks.
Edit:
In that example, I used the following:
int _SIZE = 100000;
Here's how the arrays are generated:
public static int[][] makearrays(int size) {
Random r = new Random();
int[][] a = new int[3][size];
for (int i = 0; i < size; i++) {
a[0][i] = i;
a[1][i] = size - i - 1;
a[2][i] = r.nextInt(size);
}
return a;
}
And this is what my quicksort looks like:
public static void quicksort(int[] arr, int l, int r) {
if (l < r) {
int s = partition(arr, l, r);
quicksort(arr, l, s-1);
quicksort(arr, s+1, r);
}
}
public static int partition(int[] arr, int l, int r) {
int p=arr[l],i=l+1;
for (int j =l+1;j<=r;j++) {
if (arr[j]<p) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
}
}
int temp = arr[l];
arr[l] = arr[i-1];
arr[i-1] = temp;
return i-1;
}

UCF HSPT 2016 - Chomp Chomp

I am having a lot of trouble finding an efficient solution to Problem #9 in the UCF HSPT programming competition. The whole pdf can we viewed here, and the problem is called "Chomp Chomp!".
Essentially the problem involves taking 2 "chomps" out of an array, where each chomp is a continuous chain of elements from the array and the 2 chomps have to have at least element between them that's not "chomped." Once the two "chomps" are determined, the sum of all the elements in both "chomps" has to be a multiple of the number given in the input. My solution essentially is a brute-force that goes through every possible "chomp" and I tried to improve the speed of it by storing previously calculated sums of chomps.
My code:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;
public class chomp {
static long[] arr;
public static long sum(int start, int end) {
long ret = 0;
for(int i = start; i < end; i++) {
ret+=arr[i];
}
return ret;
}
public static int sumArray(int[] arr) {
int sum = 0;
for(int i = 0; i < arr.length; i++) {
sum+=arr[i];
}
return sum;
}
public static long numChomps(long[] arr, long divide) {
HashMap<String, Long> map = new HashMap<>();
int k = 1;
long numchomps = 0;
while(true) {
if (k > arr.length-2) break;
for (int i = 0; i < arr.length -2; i++) {
if ((i+k)>arr.length-2) break;
String one = i + "";
String two = (i+k) + "";
String key1 = one + " " + two;
long first = 0;
if(map.containsKey(key1)) {
//System.out.println("Key 1 found!");
first = map.get(key1).longValue();
} else {
first = sum(i, i+k);
map.put(key1, new Long(first));
}
int kk = 1;
while(true){
if (kk > arr.length-2) break;
for (int j = i+k+1; j < arr.length; j++) {
if((j+kk) > arr.length) break;
String o = j + "";
String t = (j+kk) + "";
String key2 = o + " " + t;
long last = 0;
if(map.containsKey(key2)) {
//System.out.println("Key 2 found!");
last = map.get(key2).longValue();
} else {
last = sum(j, j+kk);
map.put(key2, new Long(last));
}
if (((first+last) % divide) == 0) {
numchomps++;
}
}
kk++;
}
}
k++;
}
return numchomps;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
int n = Integer.parseInt(in.nextLine());
for(int i = 1; i <= n; i++) {
int length = in.nextInt();
long divide = in.nextLong();
in.nextLine();
arr = new long[length];
for(int j = 0; j < length; j++) {
arr[j] = (in.nextLong());
}
//System.out.println(arr);
in.nextLine();
long blah = numChomps(arr, divide);
System.out.println("Plate #"+i + ": " + blah);
}
}
}
My code gets the right answer, but seems to take too long, especially for large inputs when the size of the array is 1000 or greater. I tried to improve the speed of my algorithm my storing previous sums calculated in a HashMap, but that didn't improve the speed of my program considerably. What can I do to improve the speed so it runs under 10 seconds?
The first source of inefficiency is constant recalculation of sums. You should make an auxiliary array of partial sums long [n] partial;, then instead of calling sum(i, i + k) you may simply do partial[i + k] - partial[i].
Now the problem reduces to finding indices i<j<k<m such that
(partial[j] - partial[i] + partial[m] - partial[k]) % divide == 0
or, rearranging terms,
(partial[j] + partial[m]) % divide == (partial[i] + partial[k]) % divide
To find them you may consider an array of triples (s, i, j) where s = (partial[j] - partial[i]) % divide, stable sort it by s, and inspect equal ranges for non-overlapping "chomps".
This approach improves performance from O(n4) to O(n2 log n). Now you shall be able to improve it to O(n log n).

Checking if a value is equal to another within a loop(java)

I need to do find values 10 different times or until the value of difference is found twice. Im not really sure how to break the loop if found.
Here goes the code:
public class Algorithm {
private static int small; //global field so it is usable in zeros method
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
int number = 0;
// object array to use collections class
Integer [] digit = new Integer [4];
// loop for handling 10 different numbers
for (int index=0; index<10; index++){
number = random();
String smaller =""; String bigger=""; // strings used to display zeros/ easier processing
for (int i = 3; i >= 0; i--) {
digit[i] = number % 10;
number /= 10;
}
// prints initial random number
System.out.println(digit[0] + " " +digit[1] + " " +
digit[2]+ " "+ digit[3] + " Random Number");
// sorts the digits increasingly
Arrays.sort(digit);
// adds the numbers to the smaller string
for (int i=0; i <digit.length; i++){
smaller += digit[i];
}
// convert string to int
int small = Integer.parseInt(smaller);
String zerosNr = null;
zerosNr = zeros();
System.out.printf(" smaller " +zerosNr, small );
// Reverse sort order and adds results to bigger for displaying
Arrays.sort(digit, Collections.reverseOrder());
for (int i=0; i < digit.length; i++){
bigger += digit[i];
}
int big = Integer.parseInt(bigger);
System.out.println("bigger " + big);
int difference = 0;
int [] copy;
copy = new int[11];
difference = big - small;
copy[index] = big - small;
// here i tried to do it
System.out.println( index + " Difference "+ difference);
if (difference == copy[index+1])
break;
}
}
//method that creates random numbers
public static int random(){
final int n = 9999;
Random rad = new Random();
int number = rad.nextInt(n);
return number;
}
//method that adds zeros where necesarry to smaller
public static String zeros(){
String zerosNr = null;
if (small < 1000)
return " %04d\n ";
else if (small < 100)
return " %03d\n ";
else
return " %02d\n ";
}
}
A problem with your approach is that you're trying to break based on "future" values, i.e. when you compare difference == copy[index+1] you have not yet populated copy[index+1]. You just haven't gotten that far yet.
Instead, you should be comparing difference to past values of copy. And you'll have to compare each difference to all preceding values of copy. So something like:
boolean iShouldBreak = false;
for (int j = 0; j < index; j++) {
if (difference == copy[j]) {
iShouldBreak = true;
}
}
if (iShouldBreak)
{
break;
]
You'll want to use a label:
For example:
OUTERFOR: for(Object1 object1: objects){
for(Object2 object2: object1.getMoreObjects()){
if( object2.isWhatever()) {
break OUTERFOR;
}
}// inner for loop ends here
}// outer for loop ends here

Substrings of a string without nested loops [duplicate]

Given a string s, what is the fastest method to generate a set of all its unique substrings?
Example: for str = "aba" we would get substrs={"a", "b", "ab", "ba", "aba"}.
The naive algorithm would be to traverse the entire string generating substrings in length 1..n in each iteration, yielding an O(n^2) upper bound.
Is a better bound possible?
(this is technically homework, so pointers-only are welcome as well)
As other posters have said, there are potentially O(n^2) substrings for a given string, so printing them out cannot be done faster than that. However there exists an efficient representation of the set that can be constructed in linear time: the suffix tree.
There is no way to do this faster than O(n2) because there are a total of O(n2) substrings in a string, so if you have to generate them all, their number will be n(n + 1) / 2 in the worst case, hence the upper lower bound of O(n2) Ω(n2).
First one is brute force which has complexity O(N^3) which could be brought down to O(N^2 log(N))
Second One using HashSet which has Complexity O(N^2)
Third One using LCP by initially finding all the suffix of a given string which has the worst case O(N^2) and best case O(N Log(N)).
First Solution:-
import java.util.Scanner;
public class DistinctSubString {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter The string");
String s = in.nextLine();
long startTime = System.currentTimeMillis();
int L = s.length();
int N = L * (L + 1) / 2;
String[] Comb = new String[N];
for (int i = 0, p = 0; i < L; ++i) {
for (int j = 0; j < (L - i); ++j) {
Comb[p++] = s.substring(j, i + j + 1);
}
}
/*
* for(int j=0;j<N;++j) { System.out.println(Comb[j]); }
*/
boolean[] val = new boolean[N];
for (int i = 0; i < N; ++i)
val[i] = true;
int counter = N;
int p = 0, start = 0;
for (int i = 0, j; i < L; ++i) {
p = L - i;
for (j = start; j < (start + p); ++j) {
if (val[j]) {
//System.out.println(Comb[j]);
for (int k = j + 1; k < start + p; ++k) {
if (Comb[j].equals(Comb[k])) {
counter--;
val[k] = false;
}
}
}
}
start = j;
}
System.out.println("Substrings are " + N
+ " of which unique substrings are " + counter);
long endTime = System.currentTimeMillis();
System.out.println("It took " + (endTime - startTime) + " milliseconds");
}
}
Second Solution:-
import java.util.*;
public class DistictSubstrings_usingHashTable {
public static void main(String args[]) {
// create a hash set
Scanner in = new Scanner(System.in);
System.out.print("Enter The string");
String s = in.nextLine();
int L = s.length();
long startTime = System.currentTimeMillis();
Set<String> hs = new HashSet<String>();
// add elements to the hash set
for (int i = 0; i < L; ++i) {
for (int j = 0; j < (L - i); ++j) {
hs.add(s.substring(j, i + j + 1));
}
}
System.out.println(hs.size());
long endTime = System.currentTimeMillis();
System.out.println("It took " + (endTime - startTime) + " milliseconds");
}
}
Third Solution:-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class LCPsolnFroDistinctSubString {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter Desired String ");
String string = br.readLine();
int length = string.length();
String[] arrayString = new String[length];
for (int i = 0; i < length; ++i) {
arrayString[i] = string.substring(length - 1 - i, length);
}
Arrays.sort(arrayString);
for (int i = 0; i < length; ++i)
System.out.println(arrayString[i]);
long num_substring = arrayString[0].length();
for (int i = 0; i < length - 1; ++i) {
int j = 0;
for (; j < arrayString[i].length(); ++j) {
if (!((arrayString[i].substring(0, j + 1)).equals((arrayString)[i + 1]
.substring(0, j + 1)))) {
break;
}
}
num_substring += arrayString[i + 1].length() - j;
}
System.out.println("unique substrings = " + num_substring);
}
}
Fourth Solution:-
public static void printAllCombinations(String soFar, String rest) {
if(rest.isEmpty()) {
System.out.println(soFar);
} else {
printAllCombinations(soFar + rest.substring(0,1), rest.substring(1));
printAllCombinations(soFar , rest.substring(1));
}
}
Test case:- printAllCombinations("", "abcd");
For big oh ... Best you could do would be O(n^2)
No need to reinvent the wheel, its not based on a strings, but on a sets, so you will have to take the concepts and apply them to your own situation.
Algorithms
Really Good White Paper from MS
In depth PowerPoint
Blog on string perms
well, since there is potentially n*(n+1)/2 different substrings (+1 for the empty substring), I doubt you can be better than O(n*2) (worst case). the easiest thing is to generate them and use some nice O(1) lookup table (such as a hashmap) for excluding duplicates right when you find them.
class SubstringsOfAString {
public static void main(String args[]) {
String string = "Hello", sub = null;
System.out.println("Substrings of \"" + string + "\" are :-");
for (int i = 0; i < string.length(); i++) {
for (int j = 1; j <= string.length() - i; j++) {
sub = string.substring(i, j + i);
System.out.println(sub);
}
}
}
}
class program
{
List<String> lst = new List<String>();
String str = "abc";
public void func()
{
subset(0, "");
lst.Sort();
lst = lst.Distinct().ToList();
foreach (String item in lst)
{
Console.WriteLine(item);
}
}
void subset(int n, String s)
{
for (int i = n; i < str.Length; i++)
{
lst.Add(s + str[i].ToString());
subset(i + 1, s + str[i].ToString());
}
}
}
This prints unique substrings.
https://ideone.com/QVWOh0
def uniq_substring(test):
lista=[]
[lista.append(test[i:i+k+1]) for i in range(len(test)) for k in
range(len(test)-i) if test[i:i+k+1] not in lista and
test[i:i+k+1][::-1] not in lista]
print lista
uniq_substring('rohit')
uniq_substring('abab')
['r', 'ro', 'roh', 'rohi', 'rohit', 'o', 'oh', 'ohi', 'ohit', 'h',
'hi', 'hit', 'i', 'it', 't']
['a', 'ab', 'aba', 'abab', 'b', 'bab']
Many answers that include 2 for loops and a .substring() call claim O(N^2) time complexity. However, it is important to note that the worst case for a .substring() call in Java (post update 6 in Java 7) is O(N). So by adding a .substring() call in your code, the order of N has increased by one.
Therefore, 2 for loops and a .substring() call within those loops equals an O(N^3) time complexity.
It can only be done in o(n^2) time as total number of unique substrings of a string would be n(n+1)/2.
Example:
string s = "abcd"
pass 0: (all the strings are of length 1)
a, b, c, d = 4 strings
pass 1: (all the strings are of length 2)
ab, bc, cd = 3 strings
pass 2: (all the strings are of length 3)
abc, bcd = 2 strings
pass 3: (all the strings are of length 4)
abcd = 1 strings
Using this analogy, we can write solution with o(n^2) time complexity and constant space complexity.
The source code is as below:
#include<stdio.h>
void print(char arr[], int start, int end)
{
int i;
for(i=start;i<=end;i++)
{
printf("%c",arr[i]);
}
printf("\n");
}
void substrings(char arr[], int n)
{
int pass,j,start,end;
int no_of_strings = n-1;
for(pass=0;pass<n;pass++)
{
start = 0;
end = start+pass;
for(j=no_of_strings;j>=0;j--)
{
print(arr,start, end);
start++;
end = start+pass;
}
no_of_strings--;
}
}
int main()
{
char str[] = "abcd";
substrings(str,4);
return 0;
}
Naive algorithm takes O(n^3) time instead of O(n^2) time.
There are O(n^2) number of substrings.
And if you put O(n^2) number of substrings, for example, set,
then set compares O(lgn) comparisons for each string to check if it alrady exists in the set or not.
Besides it takes O(n) time for string comparison.
Therefore, it takes O(n^3 lgn) time if you use set. and you can reduce it O(n^3) time if you use hashtable instead of set.
The point is it is string comparisons not number comparisons.
So one of the best algorithm let's say if you use suffix array and longest common prefix (LCP) algorithm, it reduces O(n^2) time for this problem.
Building a suffix array using O(n) time algorithm.
Time for LCP = O(n) time.
Since for each pair of strings in suffix array, do LCP so total time is O(n^2) time to find the length of distinct subtrings.
Besides if you want to print all distinct substrings, it takes O(n^2) time.
Try this code using a suffix array and longest common prefix. It can also give you the total number of unique substrings. The code might give a stack overflow in visual studio but runs fine in Eclipse C++. That's because it returns vectors for functions. Haven't tested it against extremely long strings. Will do so and report back.
// C++ program for building LCP array for given text
#include <bits/stdc++.h>
#include <vector>
#include <string>
using namespace std;
#define MAX 100000
int cum[MAX];
// Structure to store information of a suffix
struct suffix
{
int index; // To store original index
int rank[2]; // To store ranks and next rank pair
};
// A comparison function used by sort() to compare two suffixes
// Compares two pairs, returns 1 if first pair is smaller
int cmp(struct suffix a, struct suffix b)
{
return (a.rank[0] == b.rank[0])? (a.rank[1] < b.rank[1] ?1: 0):
(a.rank[0] < b.rank[0] ?1: 0);
}
// This is the main function that takes a string 'txt' of size n as an
// argument, builds and return the suffix array for the given string
vector<int> buildSuffixArray(string txt, int n)
{
// A structure to store suffixes and their indexes
struct suffix suffixes[n];
// Store suffixes and their indexes in an array of structures.
// The structure is needed to sort the suffixes alphabatically
// and maintain their old indexes while sorting
for (int i = 0; i < n; i++)
{
suffixes[i].index = i;
suffixes[i].rank[0] = txt[i] - 'a';
suffixes[i].rank[1] = ((i+1) < n)? (txt[i + 1] - 'a'): -1;
}
// Sort the suffixes using the comparison function
// defined above.
sort(suffixes, suffixes+n, cmp);
// At his point, all suffixes are sorted according to first
// 2 characters. Let us sort suffixes according to first 4
// characters, then first 8 and so on
int ind[n]; // This array is needed to get the index in suffixes[]
// from original index. This mapping is needed to get
// next suffix.
for (int k = 4; k < 2*n; k = k*2)
{
// Assigning rank and index values to first suffix
int rank = 0;
int prev_rank = suffixes[0].rank[0];
suffixes[0].rank[0] = rank;
ind[suffixes[0].index] = 0;
// Assigning rank to suffixes
for (int i = 1; i < n; i++)
{
// If first rank and next ranks are same as that of previous
// suffix in array, assign the same new rank to this suffix
if (suffixes[i].rank[0] == prev_rank &&
suffixes[i].rank[1] == suffixes[i-1].rank[1])
{
prev_rank = suffixes[i].rank[0];
suffixes[i].rank[0] = rank;
}
else // Otherwise increment rank and assign
{
prev_rank = suffixes[i].rank[0];
suffixes[i].rank[0] = ++rank;
}
ind[suffixes[i].index] = i;
}
// Assign next rank to every suffix
for (int i = 0; i < n; i++)
{
int nextindex = suffixes[i].index + k/2;
suffixes[i].rank[1] = (nextindex < n)?
suffixes[ind[nextindex]].rank[0]: -1;
}
// Sort the suffixes according to first k characters
sort(suffixes, suffixes+n, cmp);
}
// Store indexes of all sorted suffixes in the suffix array
vector<int>suffixArr;
for (int i = 0; i < n; i++)
suffixArr.push_back(suffixes[i].index);
// Return the suffix array
return suffixArr;
}
/* To construct and return LCP */
vector<int> kasai(string txt, vector<int> suffixArr)
{
int n = suffixArr.size();
// To store LCP array
vector<int> lcp(n, 0);
// An auxiliary array to store inverse of suffix array
// elements. For example if suffixArr[0] is 5, the
// invSuff[5] would store 0. This is used to get next
// suffix string from suffix array.
vector<int> invSuff(n, 0);
// Fill values in invSuff[]
for (int i=0; i < n; i++)
invSuff[suffixArr[i]] = i;
// Initialize length of previous LCP
int k = 0;
// Process all suffixes one by one starting from
// first suffix in txt[]
for (int i=0; i<n; i++)
{
/* If the current suffix is at n-1, then we don’t
have next substring to consider. So lcp is not
defined for this substring, we put zero. */
if (invSuff[i] == n-1)
{
k = 0;
continue;
}
/* j contains index of the next substring to
be considered to compare with the present
substring, i.e., next string in suffix array */
int j = suffixArr[invSuff[i]+1];
// Directly start matching from k'th index as
// at-least k-1 characters will match
while (i+k<n && j+k<n && txt[i+k]==txt[j+k])
k++;
lcp[invSuff[i]] = k; // lcp for the present suffix.
// Deleting the starting character from the string.
if (k>0)
k--;
}
// return the constructed lcp array
return lcp;
}
// Utility function to print an array
void printArr(vector<int>arr, int n)
{
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
cout << endl;
}
// Driver program
int main()
{
int t;
cin >> t;
//t = 1;
while (t > 0) {
//string str = "banana";
string str;
cin >> str; // >> k;
vector<int>suffixArr = buildSuffixArray(str, str.length());
int n = suffixArr.size();
cout << "Suffix Array : \n";
printArr(suffixArr, n);
vector<int>lcp = kasai(str, suffixArr);
cout << "\nLCP Array : \n";
printArr(lcp, n);
// cum will hold number of substrings if that'a what you want (total = cum[n-1]
cum[0] = n - suffixArr[0];
// vector <pair<int,int>> substrs[n];
int count = 1;
for (int i = 1; i <= n-suffixArr[0]; i++) {
//substrs[0].push_back({suffixArr[0],i});
string sub_str = str.substr(suffixArr[0],i);
cout << count << " " << sub_str << endl;
count++;
}
for(int i = 1;i < n;i++) {
cum[i] = cum[i-1] + (n - suffixArr[i] - lcp[i - 1]);
int end = n - suffixArr[i];
int begin = lcp[i-1] + 1;
int begin_suffix = suffixArr[i];
for (int j = begin, k = 1; j <= end; j++, k++) {
//substrs[i].push_back({begin_suffix, lcp[i-1] + k});
// cout << "i push " << i << " " << begin_suffix << " " << k << endl;
string sub_str = str.substr(begin_suffix, lcp[i-1] +k);
cout << count << " " << sub_str << endl;
count++;
}
}
/*int count = 1;
cout << endl;
for(int i = 0; i < n; i++){
for (auto it = substrs[i].begin(); it != substrs[i].end(); ++it ) {
string sub_str = str.substr(it->first, it->second);
cout << count << " " << sub_str << endl;
count++;
}
}*/
t--;
}
return 0;
}
And here's a simpler algorithm:
#include <iostream>
#include <string.h>
#include <vector>
#include <string>
#include <algorithm>
#include <time.h>
using namespace std;
char txt[100000], *p[100000];
int m, n;
int cmp(const void *p, const void *q) {
int rc = memcmp(*(char **)p, *(char **)q, m);
return rc;
}
int main() {
std::cin >> txt;
int start_s = clock();
n = strlen(txt);
int k; int i;
int count = 1;
for (m = 1; m <= n; m++) {
for (k = 0; k+m <= n; k++)
p[k] = txt+k;
qsort(p, k, sizeof(p[0]), &cmp);
for (i = 0; i < k; i++) {
if (i != 0 && cmp(&p[i-1], &p[i]) == 0){
continue;
}
char cur_txt[100000];
memcpy(cur_txt, p[i],m);
cur_txt[m] = '\0';
std::cout << count << " " << cur_txt << std::endl;
count++;
}
}
cout << --count << endl;
int stop_s = clock();
float run_time = (stop_s - start_s) / double(CLOCKS_PER_SEC);
cout << endl << "distinct substrings \t\tExecution time = " << run_time << " seconds" << endl;
return 0;
}
Both algorithms listed a simply too slow for extremely long strings though. I tested the algorithms against a string of length over 47,000 and the algorithms took over 20 minutes to complete, with the first one taking 1200 seconds, and the second one taking 1360 seconds, and that's just counting the unique substrings without outputting to the terminal. So for probably strings of length up to 1000 you might get a working solution. Both solutions did compute the same total number of unique substrings though. I did test both algorithms against string lengths of 2000 and 10,000. The times were for the first algorithm: 0.33 s and 12 s; for the second algorithm it was 0.535 s and 20 s. So it looks like in general the first algorithm is faster.
Here is my code in Python. It generates all possible substrings of any given string.
def find_substring(str_in):
substrs = []
if len(str_in) <= 1:
return [str_in]
s1 = find_substring(str_in[:1])
s2 = find_substring(str_in[1:])
substrs.append(s1)
substrs.append(s2)
for s11 in s1:
substrs.append(s11)
for s21 in s2:
substrs.append("%s%s" %(s11, s21))
for s21 in s2:
substrs.append(s21)
return set(substrs)
If you pass str_ = "abcdef" to the function, it generates the following results:
a, ab, abc, abcd, abcde, abcdef, abcdf, abce, abcef, abcf, abd, abde, abdef, abdf, abe, abef, abf, ac, acd, acde, acdef, acdf, ace, acef, acf, ad, ade, adef, adf, ae, aef, af, b, bc, bcd, bcde, bcdef, bcdf, bce, bcef, bcf, bd, bde, bdef, bdf, be, bef, bf, c, cd, cde, cdef, cdf, ce, cef, cf, d, de, def, df, e, ef, f

Number of Comparisons between selection sort and bubble sort keep coming up the same

I have written this program to compare the number of operations needed to sort a random numbers using both selection and bubble sort. However, these numbers keep coming up the same and I can't figure out where my code went wrong.
static int num_comps;
public static void main(String[] args)
{
Random rnd = new Random();
// max size of array
// number of N inputs
int array_size = 32768;
int num_datasets = 12;
// allocate array once to the max size
int[] vals = new int[array_size];
// temp array with allocated array to max size
int[] tvals = new int[array_size];
// array to hold operation counts
int[] op_counts = new int[num_datasets];
int[] op_counts2 = new int[num_datasets];
// array to hold the size of each array
//
int[] arraySizes = new int[num_datasets];
int i;
int j;
int sz;
for (i = 0, sz = 16; i < num_datasets; i++, sz *= 2)
arraySizes[i] = sz;
for (int iter = 0; iter < num_datasets; iter++)
{
int curr_size = arraySizes[iter];
// load array with random values
//
for (i = 0; i < curr_size; i++)
vals[i] = rnd.nextInt(4999);
for (i = 0; i < curr_size; i++)
tvals[i] = vals[i];
// run the bubble sort algorithm
//
num_comps = 0;
bubbleSort(tvals, curr_size);
op_counts[iter] = num_comps;
//System.out.println("Num comps at " + iter + " is " + num_comps);
// run the selection-sort algorithm
num_comps = 0;
selectionSort(tvals, curr_size);
op_counts2[iter] = num_comps;
//System.out.println("Num comps at " + iter + " is " + num_comps);
}
System.out.println("Operation Counts (N vs. op Count): ");
for (int k = 0; k < num_datasets; k++)
System.out.println(arraySizes[k] + "\t\t" + op_counts[k] + "\t\t" + op_counts2[k]);
}
static void bubbleSort(int vals[], int curr_size)
{
int temp;
for (int i = 0; i < curr_size - 1; i++)
{
for (int j = 0; j < curr_size - i - 1; j++)
{
// swap
num_comps = num_comps + 1;
if (vals[j+1] < vals[j])
{
temp = vals[j];
vals[j] = vals[j+1];
vals[j+1] = temp;
}
}
}
}
static void selectionSort(int vals[], int curr_size)
{
int temp;
for(int i=0; i<curr_size - 1; i++)
{
for(int j=i+1; j<curr_size; j++)
{
num_comps = num_comps + 1;
if(vals[i] > vals[j] )
{
temp = vals[j];
vals[j] = vals[i];
vals[i] = temp;
}
}
}
}
Your selection sort algorithm does not search for the lowest value in the list. And swapping it afterwards with the index of the outer loop.
You should do something like this:
static void selectionSort(int vals[], int curr_size)
{
int temp;
for(int i=0; i<curr_size - 1; i++)
{
int lowest = i;
for(int j=i+1; j<curr_size; j++)
{
num_comps = num_comps + 1;
if(vals[lowest] > vals[j] )
{
lowest = j;
}
}
// swap lowest with current index
temp = vals[lowest];
vals[lowest] = vals[i];
vals[i] = temp;
}
}
(of course this can be optimized further)
The strength of this algorithm is not the amount of of comparisons, but this amount of swaps (which is at a minimum, I suppose).
Your bubble sort algorithm seems ok to me.
Both have the same two loops, so comparing the counts of the current implementations indeed result in the same values. But, I think you can optimize the bubble sort, to stop earlier (when no swaps were found). Again, the strength of sorting algorithms depends on the used ones, and are not necessarily the least amount of comparisons. So Using the correct algorithm for your specific task, and thereby circumventing the task-specific high cost operations, is important!

Categories