completely testing an algorithm output (dynamic programming longest subsequence) - java

Before i start i just want to say this is for a homework assignment. An issue i have faced recently in this class is the ability to test my code effectively so that when i submit my solutions i can be confident all input combinations will be covered. I have been told that much of testing is subjective and requires actual knowledge of the implementation.. one size never fits all. That being said are there guidelines on how to effectively test?
One example i am currently struggling with is dynamic programing implementation of longest subsequence if elements. All of my tests work but when i submit to the grader i get stuck at what i assume is an edgecase ( we are not allowed to see the input or output of a failed test case after a certain test ).
import java.util.Arrays;
import java.util.Scanner;
public class LCS2 {
int [][] solutionMatrix;
public int lcs2(int[] a, int[] b) {
final int aARRAY_LENGTH = a.length + 1;
final int bARRAY_LENGTH = b.length + 1;
solutionMatrix = new int[aARRAY_LENGTH][bARRAY_LENGTH];
//set endge indexes equal to i
for (int i = 1; i < aARRAY_LENGTH; i++) {
solutionMatrix[i][0] = i;
}
for (int i = 1; i < bARRAY_LENGTH; i++) {
solutionMatrix[0][i] = i;
}
//fill in matrix to determine if each element is a insert, delete, match or mismatch
for (int i = 1; i < aARRAY_LENGTH; i++) {
for (int j = 1; j < bARRAY_LENGTH; j++) {
int insertion = solutionMatrix[i ][j - 1] + 1;
int deletion = solutionMatrix[i - 1][j ] + 1;
int match = solutionMatrix[i - 1][j - 1];
int mismatch = solutionMatrix[i - 1][j - 1] + 1;
//System.out.println("i: " + i + " j: " + j + " [" +insertion + " " + deletion + " " + match + " " + mismatch + "] ");
if (a[i - 1] == b[j - 1])
solutionMatrix[i][j] = Math.min(insertion, Math.min(deletion,match));
else
solutionMatrix[i][j] = Math.min(insertion, Math.min(deletion,mismatch));
}
}
//print out matrix for visualization
System.out.println(" " + Arrays.toString(b));
for (int i = 0; i < aARRAY_LENGTH; i++) {
if (i - 1 < 0)
System.out.println(" " + Arrays.toString(solutionMatrix[i]));
else
System.out.println("[" + a[i -1] + "] " + Arrays.toString(solutionMatrix[i]));
}
return outputAlignment(a.length, b.length, 0);
}
private int outputAlignment(int i, int j, int ret){
//recursive call.. if indexes are 0 then return
if (i == 0 && j == 0)
return ret;
//find pointer.. is this a insert, deletion, match or mismatch
int backtrack = backtrack(i, j);
//change current index based on result of backtrack
if (backtrack == 3)
//if matched then add one to the longest sequence
ret = outputAlignment(i - 1, j - 1, ret) + 1;
else if (backtrack == 2)
ret = outputAlignment(i - 1, j , ret);
else if (backtrack == 1)
ret = outputAlignment(i , j - 1, ret);
else
ret = outputAlignment(i - 1, j - 1, ret);
return ret;
}
private int backtrack(int i, int j){
//System.out.println("i: " + i + " j: " + j);
int currValue = solutionMatrix[i][j];
// System.out.println(currValue);
if ( currValue == solutionMatrix[i ][j - 1] + 1)
return 1; // insertion
else if (currValue == solutionMatrix[i - 1][j ] + 1)
return 2; //deletion
else if (currValue == solutionMatrix[i - 1][j - 1] )
return 3; //match
else if (currValue == solutionMatrix[i - 1][j - 1] + 1)
return 4; //mismatch
return 5;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = scanner.nextInt();
}
int m = scanner.nextInt();
int[] b = new int[m];
for (int i = 0; i < m; i++) {
b[i] = scanner.nextInt();
}
LCS2 lcs = new LCS2();
System.out.println(lcs.lcs2(a, b));
}
}
Here are some junit tests i have put together with no additional setup needed to run.
#Test
public void testLCS2(){
LCS2 lcs = new LCS2();
int[] a = {2,7,5};
int[] b = {2,5};
assertEquals("Testing Longest Common SubSequence for [2,7,5] --> [2,5]", 2,lcs.lcs2(a,b));
a = new int[] {2,3,9};
b = new int[] {2,9,7,8};
assertEquals("Testing Longest Common SubSequence for [2,3,9] --> [2,9,7,8]", 2,lcs.lcs2(a,b));
a = new int[] {1,2,3,4};
b = new int[] {1,2,3,4};
assertEquals("Testing Longest Common SubSequence for [1,2,3,4] --> [1,2,3,4]", 4,lcs.lcs2(a,b));
a = new int[] {7};
b = new int[] {1,2,3,4};
assertEquals("Testing Longest Common SubSequence for [7] --> [1,2,3,4]", 0,lcs.lcs2(a,b));
a = new int[] {2,7,8,3};
b = new int[] {5,2,8,7};
assertEquals("Testing Longest Common SubSequence for [2,7,8,3] --> [5,2,8,7]", 2,lcs.lcs2(a,b));
a = new int[] {1,1,1,1};
b = new int[] {1,2,3,4};
assertEquals("Testing Longest Common SubSequence for [2,7,8,3] --> [5,2,8,7]", 1,lcs.lcs2(a,b));
a = new int[] {1,1,1,1};
b = new int[] {1,2,3,4,1};
assertEquals("Testing Longest Common SubSequence for [2,7,8,3] --> [5,2,8,7]", 2,lcs.lcs2(a,b));
}

Before posting here, you should have done your research to answer your stated question: "are there guidelines on how to effectively test?" It's easy enough to find overviews on mind-numbing length, such as this very good one.
In your case, I suggest outlining "equivalence classes". Since exhaustive testing is impractical in most cases, and impossible when the input is of arbitrary length, you develop representative cases -- one for each class, to stand for all members of that class. For instance, lists with no common members might form one such class.
I can't comment well on your given tests, in part because you haven't explained why you tested these particular inputs, or why you decided that these were sufficient. Equivalence classes require you to analyze the problem to determine (or estimate) what processing differences there might be among various inputs.
Write out your rationale, and perhaps get a friend to brainstorm with you. Have you covered various ways in which the first iterations of your algorithm could go down the wrong path, and have to backtrack?

So in conjunction with all of your responses i ended up doing the following so thank you for your help.
bruce force algorithm to compare to
found the bug in my underlying algorithm
simplified the whole thing with only one method called from main or junit
int [][] solutionMatrix;
public int lcs2(int[] a, int[] b) {
final int aARRAY_LENGTH = a.length + 1;
final int bARRAY_LENGTH = b.length + 1;
solutionMatrix = new int[aARRAY_LENGTH][bARRAY_LENGTH];
//fill in matrix to determine if each element is a insert, delete, match or mismatch
for (int i=0; i<=a.length; i++) {
for (int j=0; j<=b.length; j++) {
if (i == 0 || j == 0)
solutionMatrix[i][j] = 0;
else if (a[i-1] == b[j-1])
solutionMatrix[i][j] = solutionMatrix[i-1][j-1] + 1;
else
solutionMatrix[i][j] = Math.max(solutionMatrix[i-1][j], solutionMatrix[i][j-1]);
}
}
return solutionMatrix[a.length][b.length];
}

Related

Trying to get my algorithm to O(n) runtime

So in my advanced algorithms class, we are to write an algorithm for a program to find two numbers in two sorted arrays of integers. The format is A[i] + B[j] == x. The runtime of the algorithm needs to be O(n).
I thought i had it and wanted to check so I emailed my professor and she told me my runtime was O(n^2). Here is my code:
int[] A = {1,2,3,4,5};
int[] B = {1,2,3,4,5,6};
int x = 4;
int i = 0;
int j = 0;
for(int n = 0; n < (A.length*B.length); n++) {
if(i >= A.length)
i = 0;
if(n % B.length == 0)
j++;
if(A[i] + B[j] == x) {
System.out.println(A[i] + " + " + B[j] + " = " + x);
break;
}
i++;
}
EDIT
I do apologize if this is still incorrect. I never really grasped the concept of Big-Oh. Would this change the runtime to O(n)? I got rid of the A.length*B.length and tried something a little different.
int[] A = {1,2,3,4,5};
int[] B = {1,2,3,4,5};
int x = 5;
int i = 0;
int j = 0;
while(i < A.length) {
if(B[j] == x - A[i]) {
/* exit */ }
if(j >= B.length) {
j = 0;
i++; }
j++;
}
Solution 1:
Add all values in B to a Map with B value as the map key, and B-index as the map value.
Iterate A, and calculate desired B value as B = x - A. Look for it in the map, and if found, you then have the index.
You will only iterate A and B once each. Adding a single value to map is O(1), and looking up a value is O(1), assuming a HashMap, so overall is O(n).
Solution 2:
Iterate A ascending, and B descending.
For each value in A, look at current B value. Walk down B until A + B <= x (or you reach beginning of B).
You will only iterate A and B once each, so O(n).
Solution 2 requires less memory (no map), and is likely faster (no time spent building map).
UPDATE Here is code:
The above descriptions were based on need for index of values, and the code for each solution is:
Solution 1
private static void findSum(int[] a, int[] b, int x) {
Map<Integer, Integer> bIdx = new HashMap<>();
for (int j = 0; j < b.length; j++)
bIdx.put(b[j], j);
for (int i = 0; i < a.length; i++) {
Integer j = bIdx.get(x - a[i]);
if (j != null)
System.out.println("a[" + i + "] + b[" + j + "] = " + a[i] + " + " + b[j] + " = " + x);
}
}
Solution 2
private static void findSum(int[] a, int[] b, int x) {
for (int i = 0, j = b.length - 1, sum; i < a.length && j >= 0; i++) {
while (j >= 0 && (sum = a[i] + b[j]) >= x) {
if (sum == x)
System.out.println("a[" + i + "] + b[" + j + "] = " + a[i] + " + " + b[j] + " = " + x);
j--;
}
}
}
Test
int[] a = {1,2,3,4,5};
int[] b = {1,2,3,4,5,6};
findSum(a, b, 4);
Output (same from both)
a[0] + b[2] = 1 + 3 = 4
a[1] + b[1] = 2 + 2 = 4
a[2] + b[0] = 3 + 1 = 4
Solution 1 using Set
If you don't need index position, then a Set is better for solution 1:
private static void findSum(int[] aArr, int[] bArr, int x) {
Set<Integer> bSet = new HashSet<>();
for (int b : bArr)
bSet.add(b);
for (int a : aArr)
if (bSet.contains(x - a))
System.out.println(a + " + " + (x - a) + " = " + x);
}
Output
1 + 3 = 4
2 + 2 = 4
3 + 1 = 4
Here is an example of how you can measure your time, i've included another method to find the numbers you mentioned. See the difference in runtime:
int[] A = {1,2,3,4,5};
int[] B = {1,2,3,4,5,6};
int x = 4;
int i = 0;
int j = 0;
long t1 = System.nanoTime();
for(int n = 0; n < (A.length*B.length); n++) {
if(i >= A.length)
i = 0;
if(n % B.length == 0)
j++;
if(A[i] + B[j] == x) {
System.out.println(A[i] + " + " + B[j] + " = " + x);
break;
}
i++;
}
long t2 = System.nanoTime();
System.out.println("Time 1: "+(t2-t1));
//Here's the other method
long t3 = System.nanoTime();
for (int n = 0;n<B.length;n++){
for (int m =0;m<A.length;m++){
if(A[m]+B[n]==x){
System.out.println(A[m] +" + "+B[n] +" = "+ x);
}
}
}
long t4 = System.nanoTime();
System.out.println("Time 2: "+(t4-t3));
Here is the code, for Andreas's Solution 1, that I came up with:
int[] A = {2,3,4};
int[] B = {7,9};
Map<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
int x = 10;
int b;
for(int i = 0; i < B.length; i++) {
hashMap.put(B[i], i);
}
for (int n = 0; n < A.length; n++){
b = x - A[n];
if(hashMap.get(b) != null)
System.out.println(A[n] + " + " + b + " = " + x);
}

Need help to solve the hackerrank challenge

I'm trying to solve an "Almost Sorted" challenge in hackerrank the problem is:
Given an array with elements, can you sort this array in ascending order using only one of the following operations?
Swap two elements.
Reverse one sub-segment.
Input Format
The first line contains a single integer, , which indicates the size of the array.
The next line contains integers separated by spaces.
Sample Input #1
2
4 2
Sample Output #1
yes
swap 1 2
Sample Input #2
3
3 1 2
Sample Output #2
no
Sample Input #3
6
1 5 4 3 2 6
Sample Output #3
yes
reverse 2 5
I tried to solve the challenge and my code is working but it seems it's to slow for big arrays.
Kindly asking you to help me to find a better solution for mentioned problem.
Below is my code:
import java.util.*;
public class Solution
{
private static int[] arr;
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int N = in.nextInt();
arr = new int[N];
for (int i = 0; i < N; i++)
{
arr[i] = in.nextInt();
}
if (IsSorted(arr))
{
System.out.println("yes");
return;
}
if(CheckSingleSwap(arr))
return;
if(CheckSingleReverse(arr))
return;
System.out.println("no");
}
private static boolean CheckSingleReverse(int[] arr)
{
int length = arr.length;
int limit = length - 2;
int current = 1;
List<Integer> indexes = new ArrayList<Integer>();
while (current < limit)
{
for (int i = 0; i < length; i++)
{
int temp = current + i;
for (int j = i; j <= temp && temp < length; j++)
{
indexes.add(j);
}
if (IsSorted(ReverseArrayPart(arr, indexes)))
{
System.out.println("yes");
System.out.println("reverse " + (indexes.get(0) + 1) + " " + (indexes.get(indexes.size() - 1) + 1));
return true;
}
indexes.clear();
}
current++;
}
return false;
}
private static int[] ReverseArrayPart(int[] arr, List<Integer> indexes)
{
int[] result = new int[arr.length];
int[] arrayPart = new int[indexes.size()];
int j = 0;
for (int i = 0; i < arr.length; i++)
{
if (indexes.contains(i))
{
arrayPart[j] = arr[i];
j++;
}
result[i] = arr[i];
}
for(int i = 0; i < arrayPart.length / 2; i++)
{
int temp = arrayPart[i];
arrayPart[i] = arrayPart[arrayPart.length - i - 1];
arrayPart[arrayPart.length - i - 1] = temp;
}
j = 0;
for (int i = 0; i < result.length; i++)
{
if (indexes.contains(i))
{
result[i] = arrayPart[j];
j++;
}
}
return result;
}
private static boolean CheckSingleSwap(int[] arr)
{
int count = 0;
int[] B = Arrays.copyOf(arr, arr.length);
Arrays.sort(B);
List<Integer> indexes = new ArrayList<Integer>();
for(int i = 0; i < arr.length; i++)
{
if(arr[i] != B[i])
{
count++;
indexes.add(i+1);
}
}
if(count > 2)
return false;
System.out.println("yes");
System.out.println("swap " + indexes.get(0) + " " + indexes.get(1));
return true;
}
private static boolean IsSorted(int[] arr)
{
int length = arr.length;
for (int i = 0; i < length - 1; i++)
{
if (arr[i] > arr[i + 1])
{
return false;
}
}
return true;
}
}
For the following code, pass in A as the original array and B as the sorted array.
CheckSingleSwap:
Instead of adding the indices to another list, store the first swap you encounter, and keep going; if you find the corresponding other swap, then store it and record the finding; if you find a different swap, exit with false. At the end if you've recorded the finding, print the corresponding indices.
private static boolean CheckSingleSwap(int[] A, int[] B)
{
int L = A.length;
int firstSwap = -1, secondSwap = -1;
for(int i = 0; i < L; i++)
{
if(A[i] != B[i])
{
if (firstSwap == -1)
firstSwap = i;
else if (secondSwap == -1 && A[i] == B[firstSwap] && A[firstSwap] == B[i])
secondSwap = i;
else
return false;
}
}
if (firstSwap != -1 && secondSwap != -1)
{
System.out.println("yes");
System.out.println("swap " + (firstSwap + 1) + " " + (secondSwap + 1));
return true;
}
System.out.println("array is already sorted!");
return false; // or whatever you decide to do; maybe even an exception or enumerated type
}
CheckSingleReverse:
You are doing WAY too much here! You seem to be brute forcing every single possible case (at first glance).
What you can do instead is to find the region where all the numbers are different. If there are more than two of these, or two which are separated by more than one element, then return false immediately.
The reason for the "more than one" thing above is because of odd-number length regions - the middle element would be the same. If you find such two regions, treat them as one. Then you can proceed to find out if the region is reversed.
private static boolean CheckSingleReverse(int[] A, int[] B)
{
// find region
int L = A.length;
int diffStart = -1, diffEnd = -1; boolean mid = false, found = false;
for (int i = 0; i < L; i++)
{
if (A[i] != B[i])
{
if (found)
{
if (i - diffEnd == 2 && !mid)
{
mid = true;
found = false;
diffEnd = -1;
}
else
return false;
}
else if (diffStart == -1)
diffStart = i;
}
else
if (diffStart != -1 && diffEnd == -1)
{
found = true;
diffEnd = i - 1;
}
}
if (diffEnd == -1)
{
if (A[L - 1] != B[L - 1])
diffEnd = L - 1;
else if (!found)
{
System.out.println("array is already sorted!");
return false;
}
}
// find out if it's reversed
int count = (diffEnd - diffStart + 1) / 2;
for (int i = 0; i < count; i++)
{
int oneEnd = diffStart + i, otherEnd = diffEnd - i;
if (!(A[oneEnd] == B[otherEnd] && A[otherEnd] == B[oneEnd]))
return false;
}
System.out.println("yes");
System.out.println("reverse " + (diffStart + 1) + " " + (diffEnd + 1));
return true;
}
Just to give you an idea of the performance boost, on ideone.com, with an array length of 150, the original implementation of CheckSingleReverse took 1.83 seconds, whereas the new one took just 0.1 seconds. With a length of 250, the original actually exceeded the computational time limit (5 seconds), whereas the new one still took just 0.12 seconds.
From this it would seem that your implementation takes exponential time, whereas mine is linear time (ignoring the sorting).
Funnily enough, with an array size of 3 million I'm still getting around 0.26 seconds (ideone's execution time fluctuates a bit as well, probs due to demand)

Making merge sort to use only (n/2 + 1) extra space in java

I'm trying to make the merge sort to use only (n/2 + 1) extra space and still O(n log n) time. This is my homework.
The original quesetion:
Write the non-recursive version of merge sort. Your program should run
in O(n log n) time and use n/2 + O(1) extra spaces.
The program will split an array in to two like normal merge sort. The left part will be in another array, which is ceil(n/2) long, so it will fit the requirement.
The right part will be in the original array. So it will be half in-place sorting
Sorry, I don't know how to explain further.
I think this is basically correct. But I kept on facing OutOfBounds error.
I know the code is quite long and messy. But can anyone help me about that?
I spent about 5 hours to implement this. Please help me.
package comp2011.lec6;
import java.util.Arrays;
public class MergeSort {
public static void printArr(int[] arr){
for(int i = 0; i < arr.length; i++){
System.out.printf("%d ", arr[i]);
}
}
public static void mergeSort(int[] arr){
if(arr.length<2) {
return;
}
int n, lBegin, rBegin;
n = 1;
int[] leftArr = new int[arr.length - (arr.length/2)];
while(n<arr.length) {
lBegin = 0;
rBegin = n;
while(rBegin + n <= arr.length) {
mergeArrays(arr, lBegin, lBegin+n, rBegin, rBegin+n, leftArr);
lBegin = rBegin+n;
rBegin = lBegin+n;
}
if(rBegin < arr.length) {
mergeArrays(arr, lBegin, lBegin+n, rBegin, arr.length, leftArr);
}
n = n*2;
}
}
public static void mergeArrays(int[] array, int startL, int stopL, int startR, int stopR, int[] leftArr) {
// int[] right = new int[stopR - startR + 1];
// int[] left = new int[stopL - startL + 1];
// for(int i = 0, k = startR; i < (right.length - 1); ++i, ++k) {
// right[i] = array[k];
// }
System.out.println("==============");
System.out.println("stopL: " + stopL +" startL: " + startL);
for(int i = 0, k = startL; i <= (stopL - startL); ++i, ++k) {
System.out.println(leftArr[i]);
leftArr[i] = array[k];
}
// right[right.length-1] = Integer.MAX_VALUE;
leftArr[stopL - startL] = Integer.MAX_VALUE;
System.out.println("leftArr: " + Arrays.toString(leftArr));
System.out.println("RightArr: " + Arrays.toString(Arrays.copyOfRange(array, startR, stopR)));
System.out.println("before: " + Arrays.toString(array));
// for(int k = startL, m = 0, n = startR; k < stopR; ++k) {
System.out.println("StartL: " + startL + " StartR: " + stopR);
for(int k = startL, m = 0, n = startR; ( (k < stopR) ); ++k) {
System.out.println("k: " + k);
System.out.println("Left: " + leftArr[m]);
System.out.println("Right: " + array[n]);
System.out.println("Array[k] before: " + array[k]);
// if(leftArr[m] == Integer.MAX_VALUE){
// System.out.println("YES");
// }
if( (leftArr[m] <= array[n]) || (n >= stopR) ) {
System.out.println("Left is smaller than right");
array[k] = leftArr[m];
m++;
}
else {
System.out.println("Right is smaller than left");
array[k] = array[n];
System.out.println("right: " + array[k]);
n++;
}
System.out.println("Array[k] after: " + array[k]+"\n");
}
System.out.println("after " + Arrays.toString(array));
}
public static void main(String[] args) {
int[] array = new int[] { 5, 2, 4, 12, 2, 10, 13, 1, 7 };
mergeSort(array);
printArr(array);
}
}

How do I solve this StackOverflowError?

In this section of my MergeSort program, I am recursively dividing a unsorted array called "arr". To do this I create two subarrays, "leftArr" and "rightArr", then I fill "leftArr" and "rightArr" with the first half of "arr" and the second half of "arr" respectively. Afterwards I will use recursion to divde / sort leftArr and rightArr.
Just wanted clarify: mid = arr.length;
To initialise the rightArr I do the following:
double halfLength = arr.length * 0.5;
if((!(halfLength < 0)) && (!(0 < halfLength))){
// if right array is an even num, length of right array is mid
rightArr = new int [mid];
} else
{
// else right arrays length is mid + 1
rightArr = new int[mid + 1];
}
When I do this I get no errors:
if(arr.length % 2 == 0){
// if right array is an even num, length of right array is mid
rightArr = new int [mid];
} else
{
// else right arrays length is mid + 1
rightArr = new int[mid + 1];
}
But my project doesn't allow me to use the modulus operator "%" and the "==" operator.
Im not getting any syntax error. All i see in the console window is:
" Exception in thread "main" java.lang.StackOverflowError ".
The Complete recursive method looks like this:
public int[] mergeSort(int[] arr) {
if (arr.length < 2){
return arr; // if array has only one element, its already sorted
}
int mid = arr.length / 2; // find midpoint of array
int leftArr[] = new int [mid]; // create left subarray of length mid
int rightArr[]; // create right subarray
/* if(arr.length % 2 == 0){
// if right array is an even num, length of right array is mid
rightArr = new int [mid];
} else
{
// else right arrays length is mid + 1
rightArr = new int[mid + 1];
}*/
double halfLength = arr.length * 0.5;
if((!(halfLength < 0)) && (!(0 < halfLength))){
// if right array is an even num, length of right array is mid
rightArr = new int [mid];
} else
{
// else right arrays length is mid + 1
rightArr = new int[mid + 1];
}
// create a resultArr of size arr, to store the sorted array
int resultArr[] = new int [arr.length];
int i = 0;
// Copy first half of arr[] into leftArr[]
while(i < mid){
leftArr[i] = arr[i];
i = i + 1;
}
int j = mid;
int indexOfRight = 0;
// Copy second half of arr into rightArr
while(j < arr.length){
rightArr[indexOfRight] = arr[j];
indexOfRight = indexOfRight + 1;
j = j + 1;
}
// Recursively call mergeSort to sort leftArr and rightArr
leftArr = mergeSort(leftArr);
rightArr = mergeSort(rightArr);
// merge leftArr and rightArr into a resultant Array, and then return the resultArr
return resultArr = merge(leftArr, rightArr);
}
This is how I merge:
public int[] merge(int[] a1, int[] a2) {
// TO BE COMPLETED
int lengthOfRes = a1.length + a2.length;
int resArr[] = new int [lengthOfRes]; // create resultant array of size a1 + a2
int a1Index = 0;
int a2Index = 0;
int resIndex = 0;
while((a1Index < a1.length) || (a2Index < a2.length))
{
if((a1Index < a1.length) && (a2Index < a2.length)){
// if a1's element is <= a2's element, then insert a1's elem in resArr
if(a1[a1Index] < a2[a2Index]){
resArr[resIndex] = a1[a1Index];
a1Index = a1Index + 1;
resIndex = resIndex + 1;
} else
// else, insert a2's elem in resArr
{
resArr[resIndex] = a2[a2Index];
a2Index = a2Index + 1;
resIndex = resIndex + 1;
}
}
// Here, if there are any of a1's elements left over, then insert them into resArr
else if(a1Index < a1.length){
resArr[resIndex] = a1[a1Index];
a1Index = a1Index + 1;
resIndex = resIndex + 1;
}
// Here, if there are any of a2's elements left over, then insert them into resArr
else
{
resArr[resIndex] = a2[a2Index];
a2Index = a2Index + 1;
resIndex = resIndex + 1;
}
}
return resArr; // return the resulting array
}
How can I fix this problem?
Thanks in advance!
This algorithm is not sorting anything. You are only breaking the array recursively, but there isn't any comparison.
This site have a good explanation about merge sorting algorithm: http://algs4.cs.princeton.edu/22mergesort/
http://algs4.cs.princeton.edu/22mergesort/Merge.java.html
It's worth studying it.
The problem is that this code
double halfLength = arr.length * 0.5;
if((!(halfLength < 0)) && (!(0 < halfLength)))
do not determine if the arr.length is even. Try this:
public boolean isEven(int number) {
// return (number - (number / 2) * 2) == 0;
return (!((number - (number / 2) * 2) > 0)) && (!((number - (number / 2) * 2) < 0));
}
Here is another method without division, mod or equals operations
public boolean isEven(int number) {
number = number < 0 ? number * -1 : number;
if (number < 1) {
return true;
}
if (number > 0 && number < 2) {
return false;
}
return isEven(number - 2);
}

divide bags of candies among three children evenly

I have n bags of candies such that no two bags have the same number of candies inside (i.e. it's a set A[] = {a0,a1,a2,...,ai,...,aj} where ai != aj).
I know how many candies is in each bag and the total number M of candies I have.
I need to divide the bags among three children so that the candies are distributed as fairly as possible (i.e. each child gets as close to M/3 as possible).
Needless to say, I may not tear into the bags to even out the counts -- then the question would be trivial.
Does anyone have any thoughts how to solve this -- preferably in Java?
EDIT:
the interviewer wanted me to use a 2-D array to solve the problem: the first kid gets x, the second kid y, the third gets the rest: S[x][y].
This after I tried following:
1] sort array n lg n
2] starting with largest remaining bag, give bag to kid with fewest candy.
Here is my solution for partitioning to two children (it is the correct answer). Maybe it will help with getting the 3-way partition.
int evenlyForTwo(int[] A, int M) {
boolean[] S = new boolean[M+1];
S[0]=true;//empty set
for(int i=0; i<A.length; i++)
for(int x=M; x >= A[i]; x--)
if(!S[x])
S[x]=S[x-A[i]];
int k = (int) M/2;
while(!S[k])
k--;
return k;//one kid gets k the other the rest.
}//
The problem you describe is known as the 3-Partition problem and is known to be NP-hard. The problem is discussed a bit on MathOverflow. You might find some of the pointers there of some value.
Here is a little solution, crude but gives correct results. And you can even change the number of children, bags, etc.
public class BagOfCandies {
static public void main(String...args) {
int repeat = 10;
int childCount = 3;
int bagsCount = childCount + (int) (Math.random() * 10);
for (int k=0; k<repeat; k++) {
int candyCount = 0, n=0;
int[] bags = new int[bagsCount];
for (int i=0; i<bags.length; i++) {
n += 1 + (int) (Math.random() * 2);
bags[i] = n;
candyCount += n;
}
shuffle(bags); // completely optional! It works regardless
boolean[][] dist = divideBags(bags, childCount);
System.out.println("Bags of candy : " + Arrays.toString(bags) + " = " + bags.length);
System.out.println("Total calculated candies is " + candyCount);
int childCandySum = 0;
for (int c=0; c<childCount; c++) {
int childCandies = countSumBags(bags, dist[c]);
System.out.println("Child " + (c+1) + " = " + childCandies + " --> " + Arrays.toString(dist[c]));
childCandySum += childCandies;
}
System.out.println("For a total of " + childCandySum + " candies");
System.out.println("----------------");
}
}
static private void shuffle(int[] bags) {
for (int i=0, len=bags.length; i<len; i++) {
int a = (int)Math.floor(Math.random()*len);
int b = (int)Math.floor(Math.random()*len);
int v = bags[a];
bags[a] = bags[b];
bags[b] = v;
}
}
static private boolean[][] divideBags(int[] bags, int childCount) {
int bagCount = bags.length;
boolean[][] dist = new boolean[childCount][bagCount];
for (int c=0; c<childCount; c++)
Arrays.fill(dist[c], false);
for (int i=0; i<bagCount; i+=childCount)
for (int j=i, c=0; c<childCount && j<bagCount; j++, c++)
dist[c][j] = true;
if (childCount == 1) return dist; // shortcut here
int sumDiff = 1;
int oldDiff = 0;
while (sumDiff != oldDiff) {
oldDiff = sumDiff;
sumDiff = 0;
// start comparing children in pair
for (int child1=0; child1<childCount-1; child1++) {
for (int child2=child1+1; child2<childCount; child2++) {
int count1 = countSumBags(bags, dist[child1]);
int count2 = countSumBags(bags, dist[child2]);
int diff = Math.abs(count1 - count2);
// a difference less than 2 is negligeable
if (diff > 1) {
// find some bags with can swap to even their difference
int c1=-1, c2=-1, cdiff;
boolean swap = false;
for (int i=0; i<bagCount-1; i++) {
for (int j=i; j<bagCount; j++) {
if (dist[child1][i] && dist[child2][j]) {
cdiff = Math.abs((count1 - bags[i] + bags[j]) - (count2 + bags[i] - bags[j]));
if (cdiff < diff) {
c1 = i; c2 = j;
diff = cdiff;
swap = true;
}
}
if (dist[child1][j] && dist[child2][i]) {
cdiff = Math.abs((count1 - bags[j] + bags[i]) - (count2 + bags[j] - bags[i]));
if (cdiff < diff) {
c1 = j; c2 = i;
diff = cdiff;
swap = true;
}
}
}
}
if (swap) {
//System.out.println("Swaping " + c1 + " with " + c2);
dist[child1][c1] = false; dist[child1][c2] = true;
dist[child2][c1] = true; dist[child2][c2] = false;
}
}
//System.out.println("Diff between " + child1 + "(" + countSumBags(bags, dist[child1]) + ") and " + child2 + "(" + countSumBags(bags, dist[child2]) + ") is " + diff);
sumDiff += diff;
}
}
//System.out.println("oldDiff="+oldDiff+", sumDiff="+sumDiff);
}
return dist;
}
static private int countSumBags(int[] bags, boolean[] t) {
int count = 0;
for (int i=0; i<t.length; i++) {
if (t[i]) {
count+=bags[i];
}
}
return count;
}
}
I don't know if this the result you were looking for, but it seems to be, from my understanding of the question.

Categories