Algorithm failing only for one test cases at hackerrank - java

I tried to attempt hackerrank problem of https://www.hackerrank.com/challenges/poisonous-plants and come up with below algorithm. I require some help as my solution is failing only 2 test cases and they are large data set so difficult to debug. I am including link to test case
http://ideone.com/B2WWaH its giving 204 answer but correct answer as per brute force method is 16. In simple words problem is that given a non empty array of positive integers in each iteration array element which is greater than it's previous is removed. After how many iteration there will be no removal.
Problem
There are N plants in a garden. Each of these plants has been added with some amount of pesticide. After each day, if any plant has more pesticide than the plant at its left, being weaker than the left one, it dies. You are given the initial values of the pesticide in each plant. Print the number of days after which no plant dies, i.e. the time after which there are no plants with more pesticide content than the plant to their left.
Input Format
The input consists of an integer N. The next line consists of N integers describing the array P where P[i] denotes the amount of pesticide in plant i.
Constraints
1≤N≤100000
0≤P[i]≤109
Output Format
Output a single value equal to the number of days after which no plants die.
I have made some observation
(1) First plant will always survive as there is no plant on its left side.
(2) In last longest decreasing sub-sequence starting from first plant will survive.
(3) We just have to find out how many days it will take for plant between elements of these sub-sequence to die.
(4) Came up with algorithm to track plant on which day it dies.
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class No {
public static void main(String[] args) throws FileNotFoundException {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
int a = 0 , no = 0 ;
Scanner scno = new Scanner(new File("D:\\no.txt"));
a = scno.nextInt();
int[] arr = new int[a];
int n[] = new int[a]; int n1 = 0;
for ( int i = 0 ; i < arr.length ; i++ )
{
arr[i] = scno.nextInt();
if ( 0 != i )
{
if ( arr[i] > arr[i-1] )
{
n[1] = arr[i];
no = Math.max(no, 1);
}
else if ( arr[i] > arr[a] )
{
for ( int j = no ; j >= 0 ; j-- )
{
if ( 0 == j )
{
n[++no] = arr[i];
break;
}
if ( arr[i] > n[j] )
{
n[j] = arr[i];
break;
}
}
}
else
{
a = i;
n1 = Math.max(n1, no);
no = 0;
Arrays.fill(n, 0);
}
}
else
{
a = i;
}
}
System.out.println(Math.max(n1, no));
}
}

First of all, you have to follow some steps to solve any problem. In this type of problem, you have to:
Read all the data first, this means an independent loop to read all the plant data.
You apply your evolution model if there is one. This is another independent loop.
The assumptions you have made are not sufficient. I do not know if it will be possible to get it in one shot, you will easily define those characteristics if you first write a solution that works. The solution that will work for sure is to iterate and kill plants. Iterate from the right and kill the plant that satisfies the dying condition. And repeat until one loop doesn't kill any plant. The number of those days is the nomber of loops you have made minus one (the last that did not kill any plant). ArrayList will make your life simple.
in short, your can declare your data array as
ArraList<int> arr = new ArraList(a)
fill it as (after reading the size a of the vector
for ( int i = 0 ; i < a ; i++ ) arr.add(scno.nextInt());
and use a subroutine like this to do the job (this is just an indication, I did not test it)
private static int numberDaysNoD(ArraList<int> arr){
int nDays = 0;
boolean haveKilled = true;
while(haveKilled){
haveKilled = false;
for(int i = arr.size()-1; i>0; i--){
if(arr.get(i)>arr.get(i-1)){
arr.remove(i);
haveKilled = true;
}
}
nDays++;
}
return nDays--
}
Second there is one thing missing in the statement of your problem. Does the pesticide level in plants evolves? What is the evolution model? Or simply there is no evolution and each day, we simply kill those that have more that their left neighbor (this automatically change the left neighbor if one dies) and wait for the next day to repeat?

Related

Reducing the time of execution of the following code [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 5 years ago.
Improve this question
I am working on a problem. Out of 17 test cases 10 works fine and gives the result in less than a second but in 7 cases it is taking 2 seconds which are beyond the time limit. Following is the code
import java.util.*;
import java.io.*;
class TestClass
{
static PrintWriter wr = new PrintWriter(System.out);
public static void func1(int arr[], int n)
{
int temp = arr[0];
for (int jj = 0; jj < n; jj++)
{
if (jj == (n - 1))
arr[jj] = temp;
else
arr[jj] = arr[jj + 1];
}
}
public static void func2(int arr[], int n, int rt)
{
int count = 0;
for (int a = 0; a < n; a++)
{
for (int b = a; b < n; b++)
{
if (arr[a] > arr[b])
count++;
}
}
if (rt == (n - 1))
wr.print(count);
else
wr.print(count + " ");
}
public static void main(String args[]) throws Exception
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine().trim();
StringTokenizer st = new StringTokenizer(str);
int t = Integer.parseInt(st.nextToken());
for (int i = 0; i < t; i++) //for test cases
{
str = br.readLine().trim();
st = new StringTokenizer(str);
int n = Integer.parseInt(st.nextToken());
int arr[] = new int[n];
str = br.readLine().trim();
st = new StringTokenizer(str);
for (int j = 0; j < n; j++) //to take input of array for each test case
{
arr[j] = Integer.parseInt(st.nextToken());
}
for (int rt = 0; rt < n; rt++) //for number of times circular shifting of array is done
{
func1(arr, n); //circularly shifts the array by one position
func2(arr, n, rt); //prints the number of inversion counts
}
if (i != (t - 1))
wr.println();
}
wr.close();
br.close();
}
}
Can someone suggest how to optimize the code so that it takes less time in execution.
I know BufferReader and PrintWriter takes less time as compared to Scanner and System.out.print. I was using scanner and System.out.print earlier but changed it later in hope of getting less time but it didn't help. Also I did it earlier without the use of func1 and func2 and did all the operations in main only. The time in both the cases remains the same.
I am getting the currect output in all the cases so code is correct, I just need help in optimizing it.
The website you are using acquires questions from past programming competitions. I recognize this as a familiar problem
Like most optimization questions, the preferred steps are:
Do less.
Do the same in fewer instructions.
Don't use functions.
Use faster instructions.
In your case, you have an array, and you wish to rotate it a number of times, and then to process it from the rotated position.
Rotating an array is an incredibly expensive operation, because you typically need to copy every element in the array into a new location. What is worse for you is that you are doing it the simplest way, you are rotating the array one step for every step needing rotation.
So, if you have a 100 element array that needs rotated 45 steps, you would then have (3 copies per element swap) 100 * 45 * 3 copies to perform your rotation.
In the above example, a better approach would be to figure out a routine that rotates an array 45 elements at a time. There are a number of ways to do this. The easiest is to double the RAM requirements and just have two arrays
b[x] = a[(mod(x+45), a.length)]
An even faster "do less" would be to never rotate the array, but to perform the calculation in reverse. This is conceptually the function of the desired index in the rotated array to the actual index in the pre-rotated array. This avoids all copying, and the index numbers (by virtue of being heavily manipulated in the math processing unit) will already be stored in the CPU registers, which is the fastest RAM a computer has.
Note that once you have the starting index in the original array, you can then calculate the next index without going through the calculation again.
I might have read this problem a bit wrong; because, it is not written to highlight the problem being solved. However, the core principles above apply, and it will be up to you to apply them to the exact specifics of your programming challenge.
An example of a faster rotate that does less
public static void func1(int arr[], int shift) {
int offset = shift % arr.length;
int [] rotated = new int[arr.length];
// (arr.length - 1) is the last index, walk up till we need to copy from the head of arr
for (int index = 0; index < (arr.length - 1) - offset; index++) {
rotated[index] = arr[index+offset];
}
// copy the end of the array back into the beginning
for ( int index = (arr.length - 1) - offset; index < arr.length; index++ ) {
rotated[index] = (offset - ((arr.length - 1) - index) - 1);
}
System.arraycopy(rotated, 0, arr, 0, arr.length);
}
This copies the array into its rotated position in one pass, instead of doing a pass per index to be rotated.
The first rule of optimisation (having decided it is necessary) is to use a profiler. This counts how many times methods are invoked, and measures the accumulated time within each method, and gives you a report.
It doesn't matter if a method is slow if you only run it a few times. If you run it hundreds of thousands of times, you need to either make it faster, or run it fewer times.
If you're using a mainstream IDE, you already have a profiler. Read its documentation and use it.
The other first rule of optimisation is, if there's already literature about the problem you're trying to solve, read it. Most of us might have invented bubble-sort independently. Fewer of us would have come up with QuickSort, but it's a better solution.
It looks as if you're counting inversions in the array. Your implementation is about as efficient as you can get, given that naive approach.
for(int i=0; i< array.length; i++) {
int n1 = array[i];
for(int j=i+1; j< array.length; j++) {
n2 = array[j];
if(n1 > n2) {
count++;
}
}
}
For an array of length l this will take ( l - 1) + ( l - 2 ) ... 1 -- that's a triangular number, and grows proportionally to the square of l.
So for l=1000 you're doing ~500,000 comparisons. Then since you're repeating the count for all 1000 rotations of the array, that would be 500,000,000 comparisons, which is definitely the sort of number where things start taking a noticeable amount of time.
Googling for inversion count reveals a more sophisticated approach, which is to perform a merge sort, counting inversions as they are encountered.
Otherwise, we need to look for opportunities for huge numbers of loop iterations. A loop inside a loop makes for big numbers. A loop inside a loop inside another loop makes for even bigger numbers.
You have:
for (int i = 0; i < t; i++) {
// stuff removed
for (int rt = 0; rt < n; rt++) {
// snip
func2(arr, n, rt); //prints the number of inversion counts
}
// snip
}
public static void func2(int arr[], int n, int rt) {
// snip
for (int a = 0; a < n; a++) {
for (int b = a; b < n; b++) {
// stuff
}
}
// snip
}
That's four levels of looping. Look at the input values for your slow tests, and work out what n * n * n * t is -- that an indicator of how many times it'll do the work in the inner block.
We don't know what your algorithm is supposed to achieve. But think about whether you're doing the same thing twice in any of these loops.
It looks as if func1() is supposed to rotate an array. Have a look at System.arrayCopy() for moving whole chunks of array at a time. Most CPUs will do this in a single operation.

Algorithm for removing duplicates from ArrayList

I have an ArrayList<String> that contains the following:
2#3#1#0
1#0#4#1
9#2#5#0
4#2#3#2
1#1#2#1
Output: 6 different numbers.
I'm trying to write an algorithm that removes duplicates of the highlighted numbers so I can then use a counter to see how many different numbers in total in all of those locations are.
I've tried many things including some of the following: [Java remove duplicates from array using loops][1], [Java - Removing duplicates in an ArrayList][2], the first option in [How to find duplicates in Java array?][3] and many more. I've spent at least 5-10h just trying to figure what I'm doing wrong, but I can not, so I've turned to you.
Most of the time the solutions I find online seem to work on simple stuff, but not in my case. In it, when I try to print the different characters, it always returns the wrong int numbers.
I've also tried, also tried separating each line of numbers into a different int Array[] and then comparing, but it just won't catch all the different values.
In another example where I had 5 different numbers in total, I kept getting "4 different" as a result, so I even tried long n = ArrayList.stream().distinct().count(); just to see if I was doing something wrong, but even this thing returned "4 different" numbers.
I know the easiest way is using Set and Map, but I don't want that. I'd like to have an algorithm.
EDIT:
One of the many things I've tried is the following:
for (int m = 0; m < (size-1); m++){
for (int j = m + 1; j < size; j++){
if (ArrayList.get(j).charAt(0) != ArrayList.get(m).charAt(0)){
continue;
}
current++;
ArrayList.remove(j).charAt(0);
j--;
size--;
}
}
With this one, I'd have to use another one for the ArrayList.get().charAt(4).
EDIT2:
I've found the following code [here][1], but how would it be implemented in this case?
public static <T> ArrayList<T> uniquefy(ArrayList<T> myList) {
ArrayList <T> uniqueArrayList = new ArrayList<T>();
for (int i = 0; i < myList.size(); i++){
if (!uniqueArrayList.contains(myList.get(i))){
uniqueArrayList.add(myList.get(i));
}
}
return uniqueArrayList;
}
EDIT3:
I've found a possible solution, but it gives me an IndexOutOfBoundsException.
I've put the numbers 2, 1, 9, 4, 1 into Array1 and 1, 4, 5, 3, 2 into Array2, but when I try to compare them, I get the mentioned error.
boolean stopSequence = false;
for (int i = 0; i < Array1.length; i++){
for (int a = 0; a < Array2.length && !stopSequence;){
if (Array1[i] != Array2[a]){
Array1[i] = 0;
a++;
}
if (Array1[i] == Array2[a]){
Array1[i] = 0;
stopSequence = true;
}
}
stopSequence = false;
}
[1]: https://stackoverflow.com/questions/26998156/java-remove-duplicates-from-array-using-loops
[2]: https://stackoverflow.com/questions/2435156/java-removing-duplicates-in-an-arraylist
[3]: http://javarevisited.blogspot.com.es/2015/06/3-ways-to-find-duplicate-elements-in-array-java.html
[4]: https://stackoverflo
w.com/questions/203984/how-do-i-remove-repeated-elements-from-arraylist?rq=1
The algorithm is much simpler than what you think it is:
transform every string into a pair of characters
putting all the characters into a collection or stream that removes duplicates
counting the number of characters.
Here is a complete example:
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class Duplicates {
public static void main(String[] args) {
List<String> list = Arrays.asList("2#3#1#0",
"1#0#4#1",
"9#2#5#0",
"4#2#3#2",
"1#1#2#1");
System.out.println(
list.stream()
.flatMapToInt(s -> IntStream.of(s.charAt(0), s.charAt(4)))
.distinct()
.count());
}
}
EDIT: You seem to want to obey absurd restrictions, and thus neither use a Stream nor a Set, where these completely make sense. Here's code only using lists, but doing basically the same thing as above, but in a much less efficient way:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Duplicates {
public static void main(String[] args) {
List<String> list = Arrays.asList("2#3#1#0",
"1#0#4#1",
"9#2#5#0",
"4#2#3#2",
"1#1#2#1");
List<Character> uniqueChars = new ArrayList<>();
for (String s : list) {
Character c0 = s.charAt(0);
Character c4 = s.charAt(4);
if (!uniqueChars.contains(c0)) {
uniqueChars.add(c0);
}
if (!uniqueChars.contains(c4)) {
uniqueChars.add(c4);
}
}
System.out.println(uniqueChars.size());
}
}
It's not that difficult to count different numbers of the highlighted locations.you can use helper array called frequency array to get the expected result.
Try this simple algorithm using frequency array I think it worked perfectly for your case:
ArrayList<String> numlist=new ArrayList<String>();
int freq[] = new int [10];
numlist.add("2#3#1#0");
numlist.add("1#0#4#1");
numlist.add("9#2#5#0");
numlist.add("4#2#3#2");
numlist.add("1#1#2#1");
for(int i = 0; i < numlist.size(); i++){
String row = numlist.get(i);
int numValue1 = Character.getNumericValue(row.charAt(0));
int numValue2 = Character.getNumericValue(row.charAt(4));
freq[numValue1]++;
freq[numValue2]++;
}
int count = 0;
for(int i = 0; i < 10; i++){
if(freq[i] > 0){
count++;
}
}
System.out.println(count + " different numbers");
Output:
6 different numbers
Another option with bit masks:
public static void main(String[] args) {
List<String> arrayList = Arrays.asList("2#3#1#0", "1#0#4#1", "9#2#5#0", "4#2#3#2", "1#1#2#1");
int mask = 0;
for(String s : arrayList) { // Place the bits
mask = mask | (1 << Character.getNumericValue(s.charAt(0))) | (1 << Character.getNumericValue(s.charAt(4)));
}
int counter = 0;
for(int i = 0; i < 32; i++) { // count the bits
counter += (mask & (1 << i)) == 1 << i ? 1 : 0;
}
System.out.println(counter);
}
Output:
6
This relies on the bit mask which is at the end of the execution of the code:
1000111110
Possibly this is faster than most solutions, since it does not rely on conventional data structures.
Well, a good practice is always to divide the problem into smaller parts:
For example, a good design would be a class with these members:
digits: This is an instance variable of array of ints to contain the number of times each digit was repeated. It must be pre-sized to the maximum allowed digit (I guess that is 9).
differentDigits: The is an instance variable to contain the number of different digits.
processList: This method shall receive the list to browse it and call processItem for each item.
processItem: This method shall receive an item String and parse the digits according to the specified format (through StringTokenizer, for example), and call storeDigit for each required digit.
storeDigit: This method shall receive an int and use it to index the instance array digits, and increment the indexed position. If the indexed position was 0, it should also increment differentDigits.

Finding a 2D array inside a bigger 2D array

I am able to solve the problem for those test cases where there is only 1 string per row.But i fail if there are multiple strings in a single row.
For example:
Test Case Type 1:
Big matrix:
7283455864
6731158619
8988242643
3830589324
2229505813
5633845374
6473530293
7053106601
0834282956
4607924137
Small Matrix:
9505
3845
3530
I pass these kinds of test cases as 9505 occurs at most 1 time inside Big Matrix.
Test Case Type:2
Big Matrix:
7652157548860692421022503
9283597467877865303553675
4160389485250089289309493
2583470721457150497569300
3220130778636571709490905
3588873017660047694725749
9288991387848870159567061
4840101673383478700737237
8430916536880190158229898
8986106490042260460547150
2591460395957631878779378
1816190871689680423501920
0704047294563387014281341
8544774664056811258209321
9609294756392563447060526
0170173859593369054590795
6088985673796975810221577
7738800757919472437622349
5474120045253009653348388
3930491401877849249410013
1486477041403746396925337
2955579022827592919878713
2625547961868100985291514
3673299809851325174555652
4533398973801647859680907
Small Matrix:
5250
1457
8636
7660
7848
I fail these kinds of test cases where 5250(or any other row in small matrix) occurs more than once inside same row of bigger matrix:
Below is the code i have written:
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
public static void main(String[] args) {
HashMap<Integer,Integer> rowCol=new HashMap();
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for(int a0 = 0; a0 < t; a0++){
//bigger array
int R = in.nextInt();
int C = in.nextInt();
String G[] = new String[R];
for(int G_i=0; G_i < R; G_i++){
G[G_i] = in.next();
}
//smaller array
int r = in.nextInt();
int c = in.nextInt();
String P[] = new String[r];
for(int P_i=0; P_i < r; P_i++){
P[P_i] = in.next();
}
for(int i = 0;i<R-r;i++)//obvious
{
for(int j = 0; j<r;j++ )//obvious
{
//if string found put in map(row at which found,column at whch found)
if(G[i].indexOf(P[j])>=0)//string found
{
rowCol.put(i,G[i].indexOf(P[j]));
}
}
}
//now check if rows are consecutive(1,2,3) and columns are equal(1,1,1)
HashSet<Integer> mc = new HashSet<Integer>(rowCol.values());//if size==1 then same column
ArrayList<Integer> mr = new ArrayList<Integer>(rowCol.keySet());
int count = 0;
for(int m = 0 ;m<mr.size()-1;m++)//checking if keys are consecutive
{
if(mr.get(m)+1==mr.get(m+1))
{
count++;//how many values are same ,hw mny strings found at same index
}
}
//System.out.println(count+"+++++"+(mr.size()-1));
//System.out.println( rowCol.values().size()+"==="+r);
if(mc.size()==1 && count==(mr.size()-1) && rowCol.keySet().size()==r)//all column same && all rows are consecutive &&
{
System.out.println("YES");
}
else
{
System.out.println("NO");
}
}
}
}
Your logic is faulty.
for(int i = 0;i<R-r;i++)//obvious
{
for(int j = 0; j<r;j++ )//obvious
{
//if string found put in map(row at which found,column at whch found)
if(G[i].indexOf(P[j])>=0)//string found
{
rowCol.put(i,G[i].indexOf(P[j]));
}
}
}
This goes through all the lines in G, and all the lines of P. If that line of P exists in that line of G, it will be placed in the map.
But first, it only tells you that some line from P is in that line of G, it doesn't tell you which line. This will mean it also fails when you are looking for a small matrix which is just like an existing matrix but different order of lines.
Second, if there is more than one line of the small matrix in the line of G, it will keep the index of the lower line from P. That is, if you have both 5250 and 7660 in the same line in G, it will keep just the index of the 7660 and ignore the 5250.
ArrayList<Integer> mr = new ArrayList<Integer>(rowCol.keySet());
You are using mr to check for consecutive line, but you are filling it with the key set from a HashMap. This means that the order is not guaranteed - you could get the line numbers in the order 5,3,1,2,4 or whatever.
You should write your program with different logic. Don't try to collect all the locations of all the lines. Find the location of the first line of P, and immediately check if the rest are in place. Say you found the first line of P in line 30 at index 15. Then check if line 2 is in line 31 at index 15, and if line 3 is in line 32 at index 15, and so on.
If all matched, print "YES", and return from the method. Do not continue the loop. But if not all matched, keep looking for P[0].
Note that there may be more than one occurrence of P[0] in the same line in G. So if matching failed, continue to search in the same line of G until no more occurrences of P[0] occur, and only then move to the next line of G.
If you didn't find any matches, then at the end of the loop you can print "NO".

Why does this merge sort give incorrect results?

My assignment is to merge two arrays using int arrays that the user fills and we have to assume that there will be a maximum of 10000 inputs from the user, and the user inputs a negative number to stop. Then sort the array from least to greatest and print it out. Initially i thought that this would be quite easy but when i finished, i began getting outputs such as:
Enter the values for the first array, up to 10000 values, enter a negative number to quit: 1
3
5
-1
Enter the values for the second array, up to 10000 values, enter a negative number to quit
2
4
6
-1
First Array:
1
3
5
Second Array:
2
4
6
Merged Array:
6 1 2 3 4 5
as you can see, the six is out of place and i have no idea how to fix it. Here is the source code, i have included copious comments because I really want you guys to help me out to the best of your abilities. IF it's possible to use the same exact technique without implement new techniques and methods into the code please do so. I know there are methods in java that can do all of this in one line but it's for an assignment at a more basic level.
import java.util.Scanner;
public class Merge
{
public static void main(String [] args)
{
Scanner scan = new Scanner(System.in);
int [] first = new int[10000]; //first array, assume 10k inputs max
int [] second = new int[10000]; //first array, assume 10k inputs max
boolean legal = true; //WILL IMPLIMENT LATER
int end = 0; // set how many elements to put in my "both" array
int end2 = 0;// set how many elements to put in my "both" array
System.out.print("Enter the values for the first array, up to 10000 values, enter a negative number to quit");
//get values
for(int i = 0; i<first.length; i++)
{
first[i] = scan.nextInt(); //fill first with user input
if(first[i] <0) //if negative number, stop loop
{
end = i; //get position of end of user input
break;
}
}
System.out.println("Enter the values for the second array, up to 10000 values, enter a negative number to quit");
for(int i = 0; i<second.length; i++) //exact same as the first get values loop
{
second[i] = scan.nextInt();
if(second[i] <0)
{
end2 = i;
break;
}
}
System.out.print("First Array:\n");
for(int i = 0; i<first.length; i++) //print first array
{
if(i == end) //this prevents from printing thousands of zeros, only prints values that user inputed
break;
System.out.println(first[i] + " ");
}
System.out.print("Second Array:\n");
for(int i = 0; i<second.length; i++) //same as printing first array
{
if(i == end2)
break;
System.out.println(second[i] + " ");
}
int [] both = new int[(end)+(end2)]; //instanciate an int array to hold only inputted values from first[] and second[]
int [] bothF = new int[(end)+(end2)]; //this is for my simple sorter algotithm loop
for(int i = 0; i<both.length; i++) //fill both with the first array that was filled
{
both[i] = first[i];
}
int temp = end; // see below
for(int i = 0;i<both.length; i++) //fill array with the second array that was filled(starting from the end of the first array so that the first set is not overwritten
{
if(temp<both.length){ //this prevents an out of bounds
both[temp] = second[i];
temp++;}
}
//simple sorting algorithm
for(int d = both.length -1;d>=0;d--)
{
for(int i = 0; i<both.length; i++)
{
if(both[d]<both[i])
{
bothF[d] = both[d];
both[d] = both[i];
both[i] = bothF[d];
}
}
}
System.out.println("Merged Array:"); //print the results
for(int i = 0; i<both.length; i++)
{
System.out.print(both[i] + " ");
}
//System.out.println("ERROR: Array not in correct order");
}
Your sorting algorithm is faulty.
It's similar to selection sort, in that you take two elements and swap them if they're out of place. However, you don't stop the comparisons when you should: when the index d is less than the index i, the comparison-and-swap based on arr[d] > arr[i] is no longer valid.
The inner loop should terminate with i=d.
The logic of your sort goes something like this:
On the d-th loop, the elements at d+1 and to the right are correctly sorted (the larger numbers). This is true at the beginning, because there are 0 elements correctly sorted to the right of the right-most element.
On each of the outer loops (with the d counter), compare the d-th largest element slot with every unsorted element, and swap if the other element is larger.
This is sufficient to sort the array, but if you begin to compare the d-th largest element slot with already-sorted elements to its right, you'll end up with a larger number in the slot than should be. Therefore, the inner loop should terminate when it reaches d.
Sure, you can do it like this
for (int i = 0; i < end; i++) {
both[i] = first[i];
}
for (int i = 0; i < end2; i++) {
both[i + end] = second[i];
}
// simple sorting algorithm
for (int d = both.length - 1; d >= 0; d--) {
for (int i = 0; i < d; i++) {
if (both[i] > both[d]) {
int t = both[d];
both[d] = both[i];
both[i] = t;
}
}
}
Output(s) -
Enter the values for the first array, up to 10000 values, enter a negative number to quit3
5
-1
Enter the values for the second array, up to 10000 values, enter a negative number to quit
2
4
6
-1
First Array:
3
5
Second Array:
2
4
6
-1
Merged Array:
2 3 4 5 6
First I will start with some recommendations:
1.Give end1 and end2 the initial value as the array lengths.
The printing part - instead of breaking the loop - loop till i == end(if its not changed by the first part it will stay the array length).
One suggestion is to use a "while" statement on the user input to do the reading part (it seems cleaner then breaking the loop- but its OK to do it like you have done too).
Try to use more functions.
now to the main thing- why not to insert the numbers from both arrays to the join array keeping them sorted?
Guiding:
Keep a marker for each array.
Iterate over the new join array If arr1[marker1]> arr2[marker2]
insert arr2[marker2] to the joint array in the current position.
and add 1 to marker2. and the opposite.
(don't forget to choose what happens if the are equal).
This can be achieved because the arrays were sorted in the first place.
Have fun practicing!
I guess you have sort of a reverse "selection sort"-algorithm going on there. I made an class that run your code and printed out the output after every swap. Here is the code which is the same as you got in your application with the addition of print.
for(int d = both.length -1;d>=0;d--)
{
for(int i = 0; i<both.length; i++)
{
if(both[d]<both[i])
{
int temp = both[d];
both[d] = both[i];
both[i] = temp;
printArray(both);
}
}
}
and when we run this on an example array we get this output
[9, 8, 7, 6]=
-> 6879
-> 6789
-> 6798
-> 6978
-> 9678
The algorithm actually had the correct answer after two swaps but then it started shuffling them into wrong order. The issue is the inner for loops end parameter. When you have run the outer loop once, you can be certain that the biggest number is in the end. 'd' is here 3 and it will swap out a bigger number every time it encounters it. the if clause comparisions in the first loop is 6-9 (swap), 9-8, 9-7, 9-9. All good so far.
Potential problems comes in the second iteration with 'd' as 2. Array is now [6,8,7,9] and comparisons are 7-6, 7-8 (swap with result [6,7,8,9]), 8-8, 8-9 (swap!!) resulting in [6,7,9,8]. the last swap was the problematic one. We knew that the biggest number was already in the last spot, but we still compare against it. with every gotrough of the whole inner loop it will always find the biggest number (and all other bigger than both[d] that is already in place) and swap it to some wrong position.
As we know that the biggest number will be last after one iteration of the outer loop, we shouldn't compare against it in the second iteration. You sort of lock the 9 in the array and only try to sort the rest, being in this case [6,8,7] where d = 3, value 7. hence, your inner loop for(int i = 0; i<both.length; i++) becomes for(int i = 0; i<=d; i++). As an added bonus, you know that in the last iteration i==d, and thus the code inside it, if(both[d]<both[i]) will never be true, and you can further enhance the loop into for(int i = 0; i<d; i++).
In your algorithm you always do four comparisons in the inner loop over four iterations of the outer loop, which means there is a total of 16 comparisons. if we use the i<d we'll just do three comparisons in the inner loop on the first iteration of the outer loop, then two, then one. This brings it to a total of six comparisons.
Sorry if too rambling, just wanted to be thorough.

How to find indicies of longest run of consecutive values in ordered list?

I'm building an AI player for a yahtzee game and what I'm trying to do at the moment is have it evaluate the 5 dice given to it (in a sorted ArrayList), to decide the probability of attaining a low/high straight from the current position (e.g. with 1 or 2 more rolls); and which dice to hold as a result. At the moment I acheive the probability by finding the longest streak of consecutive numbers and calculating probabilities from there. What I'm having issues with is telling the computer which dice to hold. Basically I can't find the start and end of the streak.
My method for getting the longest streak is like this:
public Class checkStraight{
private HashMap<Integer, Integer> dieFreq = new HashMap<Integer, Integer>();
private ArrayList<Die> dice = new ArrayList<Die>(); //Die has a method "getValue()"
// which returns the face value and a method roll() which assigns a random value.
public checkStraight(){
for(Die d : dice){
d.roll();
}
for(int i = 1; i<7; i++){
dieFreq.put(i, 0);
}
buildMap();
}
public void buildMap(){
for(int i = 0; i<5; i++){
dieFreq.put(dice.get(i).getValue(), dieFreq.get(dice.get(i).getValue()) + 1);
}
}
public int longestStreak(){
int count = 1;
int highCount = 1;
for(int i = 1; i<6; i++){
if(dieFreq.get(i) != 0 && dieFreq.get(i+1) != 0){
count++;
}
else{
if(count>highCount){
highCount = count;
}
count = 1;
}
return highCount;
}
}
Obviously if you're going for a straight you should hold one of each consecutive die and reroll the others, but I can't see a way to find which dice comprise the longest streak in the ArrayList of Die due to potentially having more than one die of the same value.
I want to pass integers into a new ArrayList to determine which dice to hold (1 for hold 0 for reroll). Can any of you think of a way I can do that, either within the method for calculating the longest streak or some other way?
Thanks
You have these bugs in your longestStreak() method
1) counts should start at (and be reset to) zero, not one.
2) your loop does not include 6
for(int i=1; i<6; i++) {
should be
for(int i=1; i<=6; i++) {
Strategically, even a correct longest streak method will not help you with the problem you are trying to solve, because 1, 2, 4, 5 yields a 33% chance per reroll of getting a straight, while the longest streak is only two.

Categories

Resources