Base Negation given an inDigits - java

I have the following problem:
Given an input in base (the input is given as an array of its digits in that base), write the negation of the number in "base's"-complement notatation in outDigits.
The "base's complement" notation of a number is a generalization of "two's complement": if we treat (-x) as an unsigned number in base and add it to x, we should get 0 (modulo base^digit-size). I cannot call other function (even Math.pow)
I keep getting an error with my tests. My code:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
outDigits[0] = 1;
for (int i = outDigits.length - 1; i >= 0; i--) {
outDigits[i] += base - (1 + inDigits[i]);
if (i < outDigits.length - 1) {
outDigits[i + 1] = outDigits[i] / base;
}
outDigits[i] %= base;
}
}
I cannot find the error in my calculations, please help.
my test:
------------------------------------ Negate number 365 in base 10 ------------------------------------
Test case have FAILED.
Base: 10
Input number: [5, 6, 3]
Expected: [5, 3, 6]
Output: [5, 0, 0]
-------------------------------- Negate number b1010110011 in base 2 --------------------------------
Test case have FAILED.
Base: 2
Input number: [1, 1, 0, 0, 1, 1, 0, 1, 0, 1]
Expected: [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
Output: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-------------------------------------- Negate 0x7AF0 in base 16 --------------------------------------
Test case have FAILED.
Base: 16
Input number: [0, 15, 10, 7]
Expected: [0, 1, 5, 8]
Output: [0, 1, 0, 0]

Your problem is that you may seem to be trying to do the negation of the complement while calculating the complement and it is complicating your solution.
You could try to simplify your solution by splitting it into two phases:
First compute the complement.
Second add the +1 to the computed complement.
The following method is a working version of this:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
// Compute the complement of the digits
for (int i = outDigits.length - 1; i >= 0; i--)
outDigits[i] = base - (1 + inDigits[i]);
// Negate the complement by adding +1 to the computed number (collection of digits)
for (int i = 0; i < outDigits.length; i++) {
if (outDigits[i] == base - 1) {
// Max out digit. Set it to zero and try with the higher order next.
outDigits[i] = 0;
} else {
// Digit that has room for +1. Finally add the 1 and DONE!
outDigits[i]++;
break;
}
}
}
This approach is clearer, better performing and the code is self explanatory; but I added comments in the code to follow the logic used.
Complete code on GitHub
Hope this helps.

Since the "expected" values show that index 0 is the lowest order digit, it means that for number 123₁₀ the array would be [3, 2, 1], i.e. the digits are in reverse order of what you'd expect as a human. To a computer, it makes sense that value at index i is the value that must be multiplied by baseⁱ.
That means you need the i loop to iterate up, not down, so you can track the carry-over. Otherwise you code works fine:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
outDigits[0] = 1;
for (int i = 0; i < outDigits.length; i++) { // <== reversed iteration
outDigits[i] += base - (1 + inDigits[i]);
if (i < outDigits.length - 1) {
outDigits[i + 1] = outDigits[i] / base;
}
outDigits[i] %= base;
}
}
Personally, writing it like this makes more sense, especially since it doesn't rely on outDigits array to be pre-initialized to all 0's:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
int carry = 0;
for (int i = 0; i < outDigits.length; i++) {
outDigits[i] = (base - inDigits[i] - carry) % base;
carry = (inDigits[i] + outDigits[i] + carry) / base;
}
}
For better performance, you don't want to use % and /, so something like this might be better:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
boolean carry = false;
for (int i = 0; i < outDigits.length; i++) {
if (carry) {
outDigits[i] = base - inDigits[i] - 1;
} else if (inDigits[i] != 0) {
outDigits[i] = base - inDigits[i];
carry = true;
}
}
}
Test
All 3 will give the same result:
public static void main(String[] args) {
test(10, 5,6,3);
test(2, 1,1,0,0,1,1,0,1,0,1);
test(16, 0,15,10,7);
test(8, 0,0,0); // 0 -> 0 (000)
test(8, 1,0,0); // 1 -> -1 (777)
test(8, 7,7,3); // 255 -> -255 (104)
test(8, 0,0,4); // -256 -> -256 (004)
}
static void test(int base, int... inDigits) {
int[] outDigits = new int[inDigits.length];
baseNegate(base, inDigits, outDigits);
System.out.printf("%d: %s -> %s%n", base, Arrays.toString(inDigits),
Arrays.toString(outDigits));
}
Output
10: [5, 6, 3] -> [5, 3, 6]
2: [1, 1, 0, 0, 1, 1, 0, 1, 0, 1] -> [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
16: [0, 15, 10, 7] -> [0, 1, 5, 8]
8: [0, 0, 0] -> [0, 0, 0]
8: [1, 0, 0] -> [7, 7, 7]
8: [7, 7, 3] -> [1, 0, 4]
8: [0, 0, 4] -> [0, 0, 4]

I think there's a problem around here:
if (i < outDigits.length - 1) {
outDigits[i + 1] = outDigits[i] / base;
}
Let's say you're using base 10. Since a digit can only be 0 through 9, dividing by 10 would mean the result of this calculation is always 0. I don't think you intended this.

Related

Coin change problem with a condition to use exactly m coints

I have coins for 10, 30 and 50. But I want to use only M coins to get a given sum.
I have this code (from this as reference) that just find all possible ways to get the total sum without applying the condition of using only M coins.
static long countWays(int coins[], int n, int sum)
{
// Time complexity of this function: O(n*sum)
// Space Complexity of this function: O(sum)
// table[i] will be storing the number of solutions
// for value i. We need sum+1 rows as the table is
// constructed in bottom up manner using the base
// case (sum = 0)
long[] table = new long[sum + 1];
// Initialize all table values as 0
Arrays.fill(table, 0);
// Base case (If given value is 0)
table[0] = 1;
// Pick all coins one by one and update the table[]
// values after the index greater than or equal to
// the value of the picked coin
for (int i = 0; i < n; i++)
for (int j = coins[i]; j <= sum; j++)
table[j] += table[j - coins[i]];
return table[sum];
}
// Driver Function to test above function
public static void main(String args[])
{
int coins[] = { 10, 30, 50 };
int n = coins.length;
int sum = 80;
System.out.println(countWays(coins, n, sum));
}
How can we add that condition for this problem or is there any alternate approach for this.
For example:
M=4 and sum = 80
Output 2.
Explanation:
case 1 : 10*2 + 30*2 = 80 ( used 4 coins i.e. M coins)
case 2 : 10*3 + 50*1 = 80 ( used 4 coins i.e. M coins)
Constraints:
M reaches up to 5000
sum reaches up to 250000
One way to think about this problem is to count in a different base system. You use the number of unique coins as the base. So for your example of 10, 30, and 50, the base would be 3.
Now you need numbers in that base system that have the correct number of digits, which is 4 for your example. Since each digit can be only one of 3 values in base 3 (0, 1, or 2), the total number of possibilites is 3 raised to the power of 4, or 81.
Thus we can count from 0 to 80 in decimal, and convert that decimal number to a four digit base 3 number using stacked repeated division.
Here's what those four digit base 3 numbers would look like:
0 in base 3: [0, 0, 0, 0]
1 in base 3: [0, 0, 0, 1]
2 in base 3: [0, 0, 0, 2]
3 in base 3: [0, 0, 1, 0]
4 in base 3: [0, 0, 1, 1]
5 in base 3: [0, 0, 1, 2]
6 in base 3: [0, 0, 2, 0]
7 in base 3: [0, 0, 2, 1]
8 in base 3: [0, 0, 2, 2]
9 in base 3: [0, 1, 0, 0]
10 in base 3: [0, 1, 0, 1]
11 in base 3: [0, 1, 0, 2]
12 in base 3: [0, 1, 1, 0]
13 in base 3: [0, 1, 1, 1]
14 in base 3: [0, 1, 1, 2]
15 in base 3: [0, 1, 2, 0]
16 in base 3: [0, 1, 2, 1]
17 in base 3: [0, 1, 2, 2]
18 in base 3: [0, 2, 0, 0]
19 in base 3: [0, 2, 0, 1]
20 in base 3: [0, 2, 0, 2]
21 in base 3: [0, 2, 1, 0]
22 in base 3: [0, 2, 1, 1]
23 in base 3: [0, 2, 1, 2]
24 in base 3: [0, 2, 2, 0]
25 in base 3: [0, 2, 2, 1]
26 in base 3: [0, 2, 2, 2]
27 in base 3: [1, 0, 0, 0]
28 in base 3: [1, 0, 0, 1]
29 in base 3: [1, 0, 0, 2]
30 in base 3: [1, 0, 1, 0]
31 in base 3: [1, 0, 1, 1]
32 in base 3: [1, 0, 1, 2]
33 in base 3: [1, 0, 2, 0]
34 in base 3: [1, 0, 2, 1]
35 in base 3: [1, 0, 2, 2]
36 in base 3: [1, 1, 0, 0]
37 in base 3: [1, 1, 0, 1]
38 in base 3: [1, 1, 0, 2]
39 in base 3: [1, 1, 1, 0]
40 in base 3: [1, 1, 1, 1]
41 in base 3: [1, 1, 1, 2]
42 in base 3: [1, 1, 2, 0]
43 in base 3: [1, 1, 2, 1]
44 in base 3: [1, 1, 2, 2]
45 in base 3: [1, 2, 0, 0]
46 in base 3: [1, 2, 0, 1]
47 in base 3: [1, 2, 0, 2]
48 in base 3: [1, 2, 1, 0]
49 in base 3: [1, 2, 1, 1]
50 in base 3: [1, 2, 1, 2]
51 in base 3: [1, 2, 2, 0]
52 in base 3: [1, 2, 2, 1]
53 in base 3: [1, 2, 2, 2]
54 in base 3: [2, 0, 0, 0]
55 in base 3: [2, 0, 0, 1]
56 in base 3: [2, 0, 0, 2]
57 in base 3: [2, 0, 1, 0]
58 in base 3: [2, 0, 1, 1]
59 in base 3: [2, 0, 1, 2]
60 in base 3: [2, 0, 2, 0]
61 in base 3: [2, 0, 2, 1]
62 in base 3: [2, 0, 2, 2]
63 in base 3: [2, 1, 0, 0]
64 in base 3: [2, 1, 0, 1]
65 in base 3: [2, 1, 0, 2]
66 in base 3: [2, 1, 1, 0]
67 in base 3: [2, 1, 1, 1]
68 in base 3: [2, 1, 1, 2]
69 in base 3: [2, 1, 2, 0]
70 in base 3: [2, 1, 2, 1]
71 in base 3: [2, 1, 2, 2]
72 in base 3: [2, 2, 0, 0]
73 in base 3: [2, 2, 0, 1]
74 in base 3: [2, 2, 0, 2]
75 in base 3: [2, 2, 1, 0]
76 in base 3: [2, 2, 1, 1]
77 in base 3: [2, 2, 1, 2]
78 in base 3: [2, 2, 2, 0]
79 in base 3: [2, 2, 2, 1]
80 in base 3: [2, 2, 2, 2]
The integer in each resulting array (the base 3 number) represents which coin from the original coin values should go in that spot (0 = 10, 1 = 30, 2 = 50).
For example, the number 61 in decimal is 2021 in base 3:
61 in base 3: [2, 0, 2, 1]
The resulting coin combination for that number would be:
50, 10, 50, 30
Here's the code that generated the list of base 3 numbers above:
import java.util.Arrays;
class Main {
public static void main(String[] args) {
int sum = 80;
int numCoins = 4;
int[] coins = new int[]{10, 30, 50};
int base = coins.length;
int combos = (int)Math.pow(base, numCoins);
int[][] combinations = new int[combos][];
for(int d=0; d<combos; d++) {
combinations[d] = convertToBase(d, base, numCoins);
System.out.println(d + " in base " + base + ": " + Arrays.toString(combinations[d]));
}
}
public static int[] convertToBase(int decimalNumber, int base, int numDigits) {
int[] digits = new int[numDigits];
int index = digits.length - 1;
int quotient = decimalNumber;
while (quotient > 0) {
digits[index] = quotient % base;
index--;
quotient = quotient / base;
}
return digits;
}
}
Now that you have all possible combinations of four coins, you need to add up the values from each combo and see if they add up to 80.
Here's a new main() to do just that:
public static void main(String[] args) {
int sum = 80;
int numCoins = 4;
int[] coins = new int[]{10, 30, 50};
int base = coins.length;
int combos = (int)Math.pow(base, numCoins);
int[][] combinations = new int[combos][];
for(int d=0; d<combos; d++) {
combinations[d] = convertToBase(d, base, numCoins);
String combo = "";
int curSum = 0;
for(int coinChoice : combinations[d]) {
combo = combo + coins[coinChoice] + " ";
curSum = curSum + coins[coinChoice];
}
if (curSum == sum) {
System.out.println("Coins: " + combo + " = " + curSum);
}
}
}
Producing the following output:
Coins: 10 10 10 50 = 80
Coins: 10 10 30 30 = 80
Coins: 10 10 50 10 = 80
Coins: 10 30 10 30 = 80
Coins: 10 30 30 10 = 80
Coins: 10 50 10 10 = 80
Coins: 30 10 10 30 = 80
Coins: 30 10 30 10 = 80
Coins: 30 30 10 10 = 80
Coins: 50 10 10 10 = 80
Notice that there are repeats because the same combination of coin denominations could be put into different positions of the four slots.
If you want to get rid of duplicates, you could SORT the resulting combos and add them to a Hashmap if they don't already exist (add import java.util.HashMap;):
public static void main(String[] args) {
int sum = 80;
int numCoins = 4;
int[] coins = new int[]{10, 30, 50};
int base = coins.length;
int combos = (int)Math.pow(base, numCoins);
int[][] combinations = new int[combos][];
HashMap<String, String> uniqueCombos = new HashMap<String, String>();
for(int d=0; d<combos; d++) {
combinations[d] = convertToBase(d, base, numCoins);
String combo = "";
int curSum = 0;
for(int coinChoice : combinations[d]) {
combo = combo + coins[coinChoice] + " ";
curSum = curSum + coins[coinChoice];
}
if (curSum == sum) {
Arrays.sort(combinations[d]);
String key = Arrays.toString(combinations[d]);
if (!uniqueCombos.containsKey(key)) {
uniqueCombos.put(key, combo);
System.out.println("Coins: " + combo + " = " + curSum);
}
}
}
}
Now we only get the two unique combinations in our output:
Coins: 10 10 10 50 = 80
Coins: 10 10 30 30 = 80
Here is the final version of the entire program:
import java.util.Arrays;
import java.util.HashMap;
class Main {
public static void main(String[] args) {
int sum = 80;
int numCoins = 4;
int[] coins = new int[]{10, 30, 50};
int base = coins.length;
int combos = (int)Math.pow(base, numCoins);
int[][] combinations = new int[combos][];
HashMap<String, String> uniqueCombos = new HashMap<String, String>();
for(int d=0; d<combos; d++) {
combinations[d] = convertToBase(d, base, numCoins);
String combo = "";
int curSum = 0;
for(int coinChoice : combinations[d]) {
combo = combo + coins[coinChoice] + " ";
curSum = curSum + coins[coinChoice];
}
if (curSum == sum) {
Arrays.sort(combinations[d]);
String key = Arrays.toString(combinations[d]);
if (!uniqueCombos.containsKey(key)) {
uniqueCombos.put(key, combo);
System.out.println("Coins: " + combo + " = " + curSum);
}
}
}
}
public static int[] convertToBase(int decimalNumber, int base, int numDigits) {
int[] digits = new int[numDigits];
int index = digits.length - 1;
int quotient = decimalNumber;
while (quotient > 0) {
digits[index] = quotient % base;
index--;
quotient = quotient / base;
}
return digits;
}
}
You want to find all i>=0, j>=0, k>=0 such that:
10i + 30j + 50k = S
i + j + k = M
For any particular k, there's at most one solution for i and j. Solving:
10i + 30j = S - 50k
i + j = M - k
gives:
j = M - i - k
10i + 30j = S - 50k
10i + 30(M - i - k) = S - 50k
10i + 30M - 30i - 30k = S - 50k
-20i = S - 50k - 30M + 30k
-20i = S - 20k - 30M
20i = -S + 20k + 30M
i = (30M + 20k - S) / 20
Now, i is an integer if 30M+20k-S is divisible by 20, which is when 30M-S is divisible by 20. If i is an integer, then j is also an integer.
Now we just need to find the range of k for which i and j are both non-negative.
Well, i>=0 when 30M+20k-S>=0, ie. when 20k >= S-30M, or k >= (S-30M)/20.
And j>=0, when M-i-k>=0, ie. when M-(30M+20k-S)/20 - k >= 0, or 20M-30M-20k+S - 20k >= 0, or 40k <= S-10M, or k <= (S-10M)/40.
So without writing a program, we can solve this problem: if 30M-S is not divisible by 20, then there's no solutions. Otherwise, we find the upper and lower bounds for k so that i and j are non-negative.
For example, when M=4 and sum=80, 30M-S=40 which is divisible by 20, we have i>=0 when k>=(S-30M)/20=(80-120)/20=-2 and j>=0 when k<=(S-10M)/40=(80-40)/40=1. We also need 0<=k<=M, so k=0 and k=1 are the two solutions.
An example program (in Python, but easy to convert to whatever you like):
def ways(M, S):
if (30*M-S) % 20 != 0:
return 0
top = min(M, (S-10*M)//40)
bot = max(0, (S-30*M)//20)
if top < bot:
return 0
return top - bot + 1
// Pick all coins one by one and update the table[]
// values after the index greater than or equal to
// the value of the picked coin
for (int i = 0; i < n; i++)
for (int j = coins[i]; j <= sum; j++)
table[j] += table[j - coins[i]];
The overall direction is correct, Dynamic Programming is the right tool for this problem.
But there's a couple of serious mistakes in your solution:
The outer for-loop should iterate over the coins, but in the snippet above, the outer loop iterates over the amounts (index i represents a particular amount). As a consequence, as the end result you'll get not a number of distinct Combinations, but a number of Permutations because in the approach you've taken two identical sets of coins arranged differently would be taken into account. For example, based on the logic of the code permutations like 10 + 10 + 50 + 10 and 10 + 10 + 10 + 50 would contribute the amount of 80, which not correct according to the problem description.
The second issue is much easier to spot, but nevertheless it's important: you're not checking if the array index to which you're referring to exists, which would lead to an ArrayIndexOutOfBoundsException.
So, the main takeaway - we number the number of Combinations, and for that we need to consider each coin separately from other coins, i.e. the outer loop should perform iteration over the given array of coins.
To make the solution more comprehensible, I'll represent each combination of coins and each group of coin-combinations having the same total amount as objects Combination and Combinations respectively.
The array that would accumulate the result would be of type Combinations[]. Each Combination has two properties: limit and coinCount representing the required and current number of coins respectively. Combinations which coinCount exceeds the limit would be discarded on the fly, the rest would filtered out while producing the resulting value.
public static long countWays(int[] coins, int n, int sum) {
Combinations[] table = new Combinations[sum + 1];
table[0] = Combinations.withOneNoCoinsComb(n);
for (int coin : coins) {
for (int i = 0; i < table.length; i++) {
// no combinations representing this amount or i + coin exceeds the sum
if (table[i] == null || i + coin > sum) continue;
if (table[i + coin] == null) {
table[i + coin] = Combinations.getEmpty(); // creating an empty Combinations instance
}
table[i + coin].addCombinations(table[i]); // adding the combinations from the previous array position representing amount i
}
}
return table[sum].getValidCombCount();
}
Custom classes Combinations and Combination:
public class Combinations {
private List<Combination> comb = new ArrayList<>();
private Combinations() {}
public void addCombinations(Combinations other) {
for (Combination c : other.comb) {
if (c.canAdd()) this.comb.add(Combination.copyOf(c).addCoin());
}
}
public int getValidCombCount() {
return (int) comb.stream().filter(Combination::isValid).count();
}
private static Combinations withOneNoCoinsComb(int limit) {
Combinations oneComb = new Combinations();
oneComb.addCombination(new Combination(limit));
return oneComb;
}
private static Combinations getEmpty() {
return new Combinations();
}
public void addCombination(Combination c) {
comb.add(c);
}
// getters
}
public class Combination {
private final int limit; // m - max number of coins
private int coinCount;
public Combination(int limit) {
this.limit = limit;
}
public Combination(int limit, int coinCount) {
this.limit = limit;
this.coinCount = coinCount;
}
public Combination addCoin() {
coinCount++;
return this;
}
public boolean canAdd() {
return coinCount <= limit;
}
public boolean isValid() {
return coinCount == limit;
}
public static Combination copyOf(Combination c) {
return new Combination(c.getLimit(), c.getCoinCount());
}
// getters
}
main()
public static void main(String[] args) {
System.out.println(countWays(new int[]{10, 30, 50}, 4, 80));
}
Output:
2

Behaviour of Arraylist passed as parameter in Recursion

I am trying to solve a DSA problem for Minimum coin change,For that trying to print all the combinations of coins needed to make a certain amount. Below are two programs one uses String as a param and other one uses ArrayList. String one seems to pring the correct output but ArrayList doesn't.
Input to question -
wt[] = {1,2,3}
cap : 5
n : wt.length
coin = ""
Below are the code samples -
For String as param :
private static int findMaxProfit(int[] wt, int cap, int n, String coin) {
if (cap == 0) {
System.out.println(coin);
return 1;
}
if (n == 0 || cap < 0) {
return 0;
}
if (wt[n - 1] <= cap) {
//Don't reduce n, when we are including the item in knapsack.
return findMaxProfit(wt, cap - wt[n - 1], n, coin + wt[n - 1] + ",") + findMaxProfit(wt, cap, n - 1, coin);
} else {
return findMaxProfit(wt, cap, n - 1, coin);
}
}
For Arraylist as param :
private static int findMinCoins(int[] wt, int cap, int n, List<Integer> list, boolean val) {
if (cap == 0) {
System.out.println(list);
return 1;
}
if (n == 0 || cap < 0) {
return 0;
}
if (wt[n - 1] <= cap) {
//Don't reduce n, when we are including the item in knapsack.
return findMinCoins(wt, cap - wt[n - 1], n, list, list.add(wt[n - 1])) + findMinCoins(wt, cap, n - 1, list, true);
} else {
return findMinCoins(wt, cap, n - 1, list, true);
}
}
Above code samples can be executed by passing the input for reference, Adding output of both the code samples below -
String output : 3,2, ArrayList output : [3, 2]
3,1,1, [3, 2, 1, 1]
2,2,1, [3, 2, 1, 1, 2, 2, 1]
2,1,1,1, [3, 2, 1, 1, 2, 2, 1, 1, 1, 1]
1,1,1,1,1, [3, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Longest Plateau Solution: the length and location of the longest continuous sequence of equal values

Here is a full program description with test cases and below it is my solution:
Given an array of integers int A[], find the length and location of the longest contiguous sequence of equal values for which the values of the elements just before and just after this sequence are smaller.
You should just print these two numbers (first is the length and second is the starting index of the plateau).
To complete the definition, we can consider there are imaginary index positions at A[-1] and A[A.length] where A[-1] < A[0] and A[A.length] < A[A.length-1]. Therefore, the plateau can start/end at both ends of array A. This condition guarantees the existence of a plateau. A plateau can be of length 1.
Example 1:
java LongestPlateau 1 2 2 2 2 1
With these command-line arguments, the program should print:
4
1
Example 2:
java LongestPlateau 1 2 2 2 2 3
With these command-line arguments, the program should print:
1
5
Example 3:
java LongestPlateau 3 2 2 2 1 2 1 1 1 2 2 0 1 1 1 1 0
With these command-line arguments, the program should print:
4
12
Here is my Solution:
public class LongestPlateau {
private static int[] parseInputArray(String[] args) {
int[] value = new int[args.length+1];
for(int i = 0 ; i < args.length; i++){
if (i == args.length-1) value[i] = 0; // this imaginary last value of the array ensures that if the plateau is the last value of the array, then it outputs the correct answer
value[i] = Integer.parseInt(args[i]);
}
return value;
}
public static void printLargestPlateau(int[] values) {
int biggestStartIndex = -1;
int biggestLength = 0;
int currentIndex = 1;
int currentPlateauStartIndex = 1;
int currentLength = 1;
boolean plateauStarted = false;
while (currentIndex < values.length) {
if(isStartOfPlateau(currentIndex, values)){
currentLength = 1;
plateauStarted = true;
currentPlateauStartIndex = currentIndex;
} else if (isEndOfPlateau(currentIndex, values)) {
if(plateauStarted && currentLength > biggestLength){
biggestLength = currentLength;
biggestStartIndex = currentPlateauStartIndex;
}
plateauStarted = false;
currentLength = 1;
} else {
currentLength++;
}
currentIndex++;
}
System.out.println(biggestLength +"\n"+biggestStartIndex);
}
private static boolean isStartOfPlateau(int index, int[] values){
if(index <= 0){
return false;
}
return values[index-1] < values[index];
}
private static boolean isEndOfPlateau(int index, int[] values){
if(index <= 0){
return false;
}
return values[index - 1] > values[index];
}
public static void main(String[] args) {
int[] values = parseInputArray(args);
printLargestPlateau(values);
}
}
As I mentioned in the comments, existing code fails to detect plateaus at the start and the end of the input data, and the following implementation fixes this issue.
static void printLargestPlateau(int ... arr) {
int start = -1, maxStart = -1;
int length = 0, maxLength = -1;
boolean onPlateau = false;
if (arr.length > 0) {
start = 0;
length = 1;
onPlateau = true;
for (int i = 1; i < arr.length; i++) {
if (arr[i] == arr[i - 1]) {
if (onPlateau) {
length++;
}
} else if (arr[i] < arr[i - 1]) {
if (length > maxLength) {
maxLength = length;
maxStart = start;
}
onPlateau = false;
} else { // possible start of new plateau
onPlateau = true;
start = i;
length = 1;
}
}
// check possible plateau at the end
if (length > maxLength) {
maxLength = length;
maxStart = start;
}
}
System.out.println(maxLength);
System.out.println(maxStart);
}
Tests:
int[][] tests = {
{},
{1},
{1, 1},
{1, 2},
{1, 1, 2},
{1, 2, 2},
{1, 2, 1},
{1, 2, 3},
{1, 2, 2, 2, 3, 3, 1, 1, 1, 1},
{1, 2, 2, 2, 2, 1},
{1, 2, 2, 2, 2, 3},
{3, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 1, 1, 1, 1, 0},
{3, 3, 3, 3, 1, 1, 0, 2, 2, 2, 2, 2, 2}
};
for (int[] arr : tests) {
System.out.println(Arrays.toString(arr));
printLargestPlateau(arr);
System.out.println("-".repeat(arr.length * 3));
}
Output:
[]
-1
-1
[1]
1
0
---
[1, 1]
2
0
------
[1, 2]
1
1
------
[1, 1, 2]
1
2
---------
[1, 2, 2]
2
1
---------
[1, 2, 1]
1
1
---------
[1, 2, 3]
1
2
---------
[1, 2, 2, 2, 3, 3, 1, 1, 1, 1]
2
4
------------------------------
[1, 2, 2, 2, 2, 1]
4
1
------------------
[1, 2, 2, 2, 2, 3]
1
5
------------------
[3, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 1, 1, 1, 1, 0]
4
12
---------------------------------------------------
[3, 3, 3, 3, 1, 1, 0, 2, 2, 2, 2, 2, 2]
6
7
---------------------------------------

Combinations with replacement Java

For the last couple of days, I am trying to solve this problem of combinations with replacement in java. I checked other languages as well maybe it was done in them and I could translate to java but with no luck, so any help greatly appreciated.
So here is the problem(mock interview question) I came upon:
Combine from range 0 - 6(n)
In an array of size r (let's say 3)
So the formula for combinations with replacement is C(n,r)=(n+r−1)! / r!(n−1)!. In this case the combinations will be 84
000
010,
011,
...,
025,
055,
...,
666.
However, I can't get my head around this algorithm WITH REPLACEMENT which is an entirely different story from without replacement.
Thank you again in advance for your help.
Retrieved first version of answer:
You can use nn=(n+1) variants of digit at every of r places, so overall number of combinations is P = nn^r. Note that every combination corresponds to the number in range 0..P-1.
So you can walk through all integer values in range 0..P-1 and represent loop counter in nn-ary system.
Java code
public static void main (String[] args) throws java.lang.Exception
{
int n = 2;
int r = 3;
int nn = n + 1;
int p = 1;
for (int i=0; i<r; i++)
p *= nn;
for (int i=0; i<p; i++){
int t = i;
String comb = "(";
for (int j=0; j<r; j++){
comb = comb + String.format("%2d, ", t % nn);
t = t / nn;
}
comb = comb.substring(0, comb.length()-2) + ')';
System.out.println(comb);
}
}
Python code:
n = 2
r = 3
nn = n + 1
p = nn**r
for V in range(p):
t = V
comb = []
for i in range(r):
d = t % nn
comb.append(d)
t = t // nn
print(comb)
[0, 0, 0]
[1, 0, 0]
[2, 0, 0]
[0, 1, 0]
[1, 1, 0]
[2, 1, 0]
[0, 2, 0]
[1, 2, 0]
[2, 2, 0]
[0, 0, 1]
[1, 0, 1]
[2, 0, 1]
[0, 1, 1]
[1, 1, 1]
[2, 1, 1]
[0, 2, 1]
[1, 2, 1]
[2, 2, 1]
[0, 0, 2]
[1, 0, 2]
[2, 0, 2]
[0, 1, 2]
[1, 1, 2]
[2, 1, 2]
[0, 2, 2]
[1, 2, 2]
[2, 2, 2]
The second version: for combinations with replacement.
Recursive (the simplest way) generation in Python.
def cwrreq(maxlen, maxx, s):
if len(s)== maxlen:
print(s)
else:
for i in range(maxx + 1):
cwrreq(maxlen, i, s + str(i))
def combwithrepl(maxlen, maxval):
cwrreq(maxlen, maxval, "")
combwithrepl(3, 6)
generates 84 combinations
000
100
110
111
200
...
663
664
665
666
Full list for (3,3).
Meaning: there are three indistinguishable boxes and three kinds of paints (say red, green, blue).
000 all boxes are hollow
100 one box is red
110
111 all boxes are red
200
210 one box is green, another is red
211
220
221
222
300
310
311
320
321 all boxes have distinct colors
322
330
331
332
333 all boxes are blue
private static List<int[]> samples(int n, int k) {
if (k < 0 || n < 0) throw new IllegalArgumentException();
if (k == 0) return Collections.emptyList();
List<Integer> set = new ArrayList<>();
for (int i = 0; i < n; i++) set.add(i);
if (k == 1) return set.stream().map(i -> new int[]{i}).collect(Collectors.toList());
List<int[]> previous = samples(n, k - 1);
List<int[]> out = new ArrayList<>();
for (int i : set) for (int[] array : previous) out.add(glue(i, array));
return out;
}
private static int[] glue(int i, int[] array) {
int[] out = new int[array.length + 1];
out[0] = i;
System.arraycopy(array, 0, out, 1, array.length);
return out;
}
e.g.,
for (int[] sample : samples(2, 3)) {
System.out.println(Arrays.toString(sample));
}
yields
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]
and
for (int[] sample : samples(4, 2)) {
System.out.println(Arrays.toString(sample));
}
yields
[0, 0]
[0, 1]
[0, 2]
[0, 3]
[1, 0]
[1, 1]
[1, 2]
[1, 3]
[2, 0]
[2, 1]
[2, 2]
[2, 3]
[3, 0]
[3, 1]
[3, 2]
[3, 3]

How to separate range of integers into two equal parts

I have an array of integers like this one
int [] num = {5, 8, 1, 1, 2, 3, 2}
and I want to divide it into 2 parts, so that the total of 2 sets will be as equal as possible: (output)
SET 1: 5 Piece(s)
1
1
2
2
5
Total: 11
SET 2: 2 Piece(s)
8
3
Total: 11
another example is
int [] num = {4, 6, 1, 2, 3, 3, 4}
with output like this:
SET 1: 3 Piece(s)
3
4
4
Total: 11
SET 2: 4 Piece(s)
6
1
2
3
Total: 12
any help? =) thanks
If the array is not too long, try a lazy solution: brute-force. Since all you need is to separate it into two sets, you'll need to check 2^n possibilities, because a number is either in the first set or not (i.e. in the second set).
Here's a sample code I just wrote:
public static int[][] bruteForce(int[] input) {
int n = input.length;
int[][] res = new int[2][n];
int minVal = Integer.MAX_VALUE;
int iMinVal = 0;
int limit = (int) Math.pow(2, n);
for (int i = 0; i < limit; i++) {
int v = i;
int diff = 0;
for (int j = 0; j < n; j++) {
diff = diff + ((v & 1) == 0 ? +1 : -1) * input[j];
v = v >> 1;
}
if (Math.abs(diff) < minVal) {
iMinVal = i;
minVal = Math.abs(diff);
}
}
int a = 0, b = 0;
for (int i = 0; i < n; i++) {
if ((iMinVal & 1) == 0) {
res[0][a++] = input[i];
} else {
res[1][b++] = input[i];
}
iMinVal = iMinVal >> 1;
}
return res;
}
public static void main(String[] args) {
int[] num = {5 ,8 ,1 ,1 ,2 ,3 ,2};
int[] num2 = {4, 6, 1, 2, 3, 3, 4};
int[][] r = bruteForce(num);
System.out.println("First example:");
System.out.println(Arrays.toString(r[0])+ ", sum = "+Arrays.stream(r[0]).sum());
System.out.println(Arrays.toString(r[1])+ ", sum = "+Arrays.stream(r[1]).sum());
r = bruteForce(num2);
System.out.println("Second example:");
System.out.println(Arrays.toString(r[0])+ ", sum = "+Arrays.stream(r[0]).sum());
System.out.println(Arrays.toString(r[1])+ ", sum = "+Arrays.stream(r[1]).sum());
}
output:
First example:
[5, 1, 3, 2, 0, 0, 0], sum = 11
[8, 1, 2, 0, 0, 0, 0], sum = 11
Second example:
[2, 3, 3, 4, 0, 0, 0], sum = 12
[4, 6, 1, 0, 0, 0, 0], sum = 11
If the length of the array is big, then I guess try some smart brute-force, or other methods. Like, sorting the array into non-ascending order, then starting from the biggest value, put each value into the array with less sum, which in the current case give the right answer:
public static int[][] alternative(int[] input) {
int n = input.length;
int[][] res = new int[2][n];
int[] input0 = Arrays.copyOf(input, n);
Arrays.sort(input0);
System.out.println("Input: "+Arrays.toString(input)+", ordered: "+Arrays.toString(input0));
int sum1 = 0, sum2 = 0;
int a = 0, b = 0;
for (int i = n-1; i >= 0; i--) {
if (sum1 <= sum2) {
res[0][a++] = input0[i];
sum1 = sum1 + input0[i];
System.out.println("Adding "+input0[i]+" into set 1 ==> Sum1 = "+sum1);
} else {
res[1][b++] = input0[i];
sum2 = sum2 + input0[i];
System.out.println("Adding "+input0[i]+" into set 2 ==> Sum2 = "+sum2);
}
}
return res;
}
output:
First example:
Input: [5, 8, 1, 1, 2, 3, 2], ordered: [1, 1, 2, 2, 3, 5, 8]
Adding 8 into set 1 ==> Sum1 = 8
Adding 5 into set 2 ==> Sum2 = 5
Adding 3 into set 2 ==> Sum2 = 8
Adding 2 into set 1 ==> Sum1 = 10
Adding 2 into set 2 ==> Sum2 = 10
Adding 1 into set 1 ==> Sum1 = 11
Adding 1 into set 2 ==> Sum2 = 11
[8, 2, 1, 0, 0, 0, 0], sum = 11
[5, 3, 2, 1, 0, 0, 0], sum = 11
Second example:
Input: [4, 6, 1, 2, 3, 3, 4], ordered: [1, 2, 3, 3, 4, 4, 6]
Adding 6 into set 1 ==> Sum1 = 6
Adding 4 into set 2 ==> Sum2 = 4
Adding 4 into set 2 ==> Sum2 = 8
Adding 3 into set 1 ==> Sum1 = 9
Adding 3 into set 2 ==> Sum2 = 11
Adding 2 into set 1 ==> Sum1 = 11
Adding 1 into set 1 ==> Sum1 = 12
[6, 3, 2, 1, 0, 0, 0], sum = 12
[4, 4, 3, 0, 0, 0, 0], sum = 11
For the 0s in the output, you can just write a simple function that create a new arrays without the 0s.
Have a for loop to loop like:
int sum =0:
for(int I=0; I<num.length/2; I++){
System.out.println(num[i]);
sum=sum+num[i];
}
System.out.println(sum);
(Written on ipad excuse any mistakes plz.
We should try with all combinations. For example, if the array is (1,1,100,100), then answer is (100,1) (100,1). If the array is (1,1,50,100) then answer is (1,1,50) (100). I don't think there is any short cut to this. If the array has N elements, then we shall try all combinations (Nc1, NcN01), (Nc2, NcN-2) .. and so on. Then find which of their difference is least.

Categories