I have two arrays A & B (size of the array is 1 to 100,000) that can take values only 1,2,3,4,5,6.
Now my task is to make minimum numbers to be changed in arrays such that the sum of both the arrays is the same.
Example 2:
A=[5,4,1,2,6,6] & B=[2], we have to make A as [1,1,1,1,1,1] so we have to change A 5 times and then B=[6] once, so function should return 6.
Example 3:
A=[1,2,3,4,3,2,1] and B[6], function should return -1.
Method signature looks like this.
public int task(int[] A, int[] B) {
int diff = Math.abs(A.length - B.length);
if(diff >=6) return -1;
//
}
I am able to get answer for example 3 with simple condition. Bit not for first 2 examples.
What approach should I follow to solve this program? As we can turn each and every element in A & B and do a comparison, but that is more complex.
The alghorithm can be as following:
Prepare data part
Go through all items in array A and B and find out how many of each number (1,2,3,4,5,6) is in each array. Preferable have 2d array that stores the indexes of those numbers, so you can easily access it later.
i.e. array A=[1,1,1,4,6,4] will be translated into new 2d array as
2darr=
[]
[0,1,2]
[]
[]
[3,5]
[]
[4]
so when you i.e. want to see how many 1 are there you can see that 2darr[1].length is 3. And when you want to find out where it is i.e. the 2darr[1][0] will get you index in source array and A[0] is indeed 1
In process you can also count the sum, but even without it, the sum now can be easily found out just going through lengths of each subarray in 2darray.
Alghoritm
To find the minimum amount of changes, you will first find out which sum is smaller and which bigger. Then the best change is to start changing 1 values to 6 in smaller array or changing 6 values to 1 in bigger arrays. Then 2 to 6 in smaller array and 5 to 1 in bigger array. And then continue with other numbers.
In process you can changing the arrays based on indexes you already have and do it as long as needed to get both arrays to same sum. This is detailed alghoritm that will show you how actually both arrays will look like to satisfy your needs. Its O(n), so there is definitely no way how to make it faster as you have to go through all fields just to get the sum.
I suggest to do it so you can see the actual result. On the other hand, if you think more deeply, it can be done more easily, if you just seek the right answer - you just need to know how many times each number in each array is and then just find out how many changes are needed just by simple math. You already know, you are just changing up to all 1 to 6 in smaller array and up to all 6 to 1 in bigger array and it can be just counted easily how many of them you need to change and if it is sufficient or you change all of them and then you will continue with 5 to 1 and 2 to 6 etc.
Example
Imagine that A.length is 500 and B.length is 300. The sum of A=1000and B=700. You find out that A has 30 repetitions of number 6 and B has 20 repetitions of number 1. In A you change all those 6 to 1, therefore reducing it by 30*5=150 to total of A=850 and in B you change all those 1 to 6 and increasing the value 20*5=100 and therefore B=800. You did 50 changes in total.
Then you continue with higher number in smaller array and with lower number in bigger array. You find out that A has 100 numbers of 5. Reducing 5 to 1 decreases value by 4 for each. Right now you have only 50 value difference. 50/4=12.5, therefore you need to change 13 numbers and you are done. The answer is that minimum amount of changes is 63.
The impossibility-criteria is a simple one as you suspect, but it is different from what you guess: it depends on the length of the arrays, which determines their minimal and maximal sums. The shorter array can not produce a sum which is greater than 6 times its length (all elements are 6s) and the longer array can not produce a sum which is less than its length (all elements are 1s):
if( Math.min(A.length, B.length) * 6 < Math.max(A.length ,B.length) )
return -1;
Then you need the sums and the statistics what the other answer describes, but maybe there is place for a slightly different explanation. In order to have the two sums meet, the smaller one can be increased and the larger one can be decreased. For having the minimum amount of steps, you always want to make the largest steps possible, via starting to replace 1s with 6s in the smaller sum (each replacement increasing the sum by 5) and 6s with 1s in the larger sum (each replacement decreasing it by 5), and so on.
As you do not want to generate the steps (at least to my understanding), actually you can track the difference only and also count the pairs together (6s in the larger-sum-array and 1s in the smaller-sum-array, then the same with 5-2, etc.). And in fact you can do this pairing even at the beginning, without knowing which one is the larger/smaller sum, because the pairs will stay pairs, just their direction changes.
Example is JavaScript so it can run here, but I try to write it as Java as possible:
function task(A,B){
if( Math.min(A.length, B.length) * 6 < Math.max(A.length, B.length) )
return -1;
var diff=0;
var statistics=[0,0,0,0,0,0]; // would be a new int[6] in Java
for(var item of A){ // for(int item : A) in Java
// this loop guesses that A has the larger sum
diff+=item;
statistics[item-1]++; // 1s are counted in [0], 2s in [1], ... 6s in [5]
}
for(var item of B){ // for(int item : B) in Java
// this loop guesses that B has the smaller sum
diff-=item;
statistics[6-item]++; // 1s are counted in [5], 2s in [4], ... 6s in [0]
}
if(diff<0){
// the guess was wrong, swaps are needed
diff=-diff;
for(var i=0;i<3;i++){
var swap=statistics[i];
statistics[i]=statistics[5-i];
statistics[5-i]=swap;
}
}
var log=[A.join()," ",B.join()," ",diff," ",statistics.join()].join(); // <-- this one...
// at this point
// - array indices are conveniently denoting step sizes
// - diff is non-negative
// - and we know there is a solution (so we won't run out of array indices for example)
var changes=0;
var i=5;
while(diff>0){
var step = Math.min(statistics[i], Math.ceil(diff/i));
// would better be "int step = Math.min(statistics[i], (diff+i-1)/i);" in Java
// as Math.ceil() produces a double
changes += step;
diff -= i*step;
i--;
}
return [changes," ",log].join(); // <-- ... and this
// are only visuals
return changes;
}
console.log(task([1,2,3,4,3,2,1],[6]));
console.log(task([6],[1,2,3,4,3,2,1]));
console.log(task([2,3,1,1,2],[5,4,6]));
console.log(task([5,4,6],[2,3,1,1,2]));
console.log(task([5,4,1,2,6,6],[2]));
console.log(task([2],[5,4,1,2,6,6]));
At the end I've just thrown it together in Java too: https://ideone.com/mP3Sel
As others have noted, we can solve this with a greedy algorithm. Count the frequencies of numbers for each array, then iterate from the outside in. For the array with the larger sum, iterate over the negative multipliers; for the array with the smaller sum, the positive. Choose the greatest absolute multiplier, then the max frequency available (and needed) each time, and as soon as the sum difference is equal or reverses sign, stop.
2 3 1 1 2 = 9
mult 5 4 3 2 1 0
freq 2 2 1 0 0 0
^ -->
5 4 6 = 15
mult 0 -1 -2 -3 -4 -5
freq 0 0 0 1 1 1
<-- ^
function f(A, B){
let freqSm = [0, 0, 0, 0, 0, 0];
let freqLg = [0, 0, 0, 0, 0, 0];
let smSum = 0;
let lgSum = 0;
let sm = 'A';
let lg = 'B';
A.map(x => {
freqSm[x-1]++;
smSum += x;
});
B.map(x => {
freqLg[x-1]++;
lgSum += x;
});
if (lgSum < smSum){
sm = 'B';
lg = 'A';
let [_freq, _sum] = [freqSm, smSum];
freqSm = freqLg;
freqLg = _freq;
smSum = lgSum;
lgSum = _sum;
}
const smMult = [5, 4, 3, 2, 1, 0];
const lgMult = [0,-1,-2,-3,-4,-5];
const changes = [];
let diff = lgSum - smSum;
function numTxt(count, num){
const ws = [, 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
const countTxt = count < 10 ? ws[count] : count;
return `${ countTxt } ${ num }${ count > 1 ? 's' : '' }`;
}
function incSm(i){
const rem = diff % smMult[i];
const mult = Math.min(freqSm[i], Math.ceil(diff / smMult[i]));
diff -= mult * smMult[i];
let txt;
if (diff < 0 && rem){
if (mult > 1)
txt = `Change ${ numTxt(mult-1, i+1) } to 6 and one to ${ i + 1 + rem } in ${ sm }.`;
else
txt = `Change one ${ i + 1 } to ${ i + 1 + rem } in ${ sm }.`;
} else {
txt = `Change ${ numTxt(mult, i+1) } to 6 in ${ sm }.`;
}
changes.push(txt);
}
function decLg(j){
const rem = diff % -lgMult[j];
const mult = Math.min(freqLg[j], Math.ceil(-diff / lgMult[j]));
diff += mult * lgMult[j];
let txt;
if (diff < 0 && rem){
if (mult > 1)
txt = `Change ${ numTxt(mult-1, j+1) } to 1 and one to ${ j + 1 - rem } in ${ lg }.`;
else
txt = `Change one ${ j + 1 } to ${ j + 1 - rem } in ${ lg }.`;
} else {
txt = `Change ${ numTxt(mult, j+1) } to 1 in ${ lg }.`;
}
changes.push(txt);
}
for (let i=0; i<6; i++){
const j = 5 - i;
if (freqSm[i] >= freqLg[j]){
if (freqSm[i]){
incSm(i);
if (diff <= 0)
return changes.join('\n');
}
if (freqLg[j]){
decLg(j);
if (diff <= 0)
return changes.join('\n');
}
} else {
if (freqLg[j]){
decLg(j);
if (diff <= 0)
return changes.join('\n');
}
if (freqSm[i]){
incSm(i);
if (diff <= 0)
return changes.join('\n');
}
}
}
return -1;
}
var input = [
[[2,3,1,1,2], [5,4,6]],
[[5,4,1,2,6,6], [2]],
[[1,2,3,4,3,2,1], [6]]
];
for (let [A, B] of input){
console.log(`A: ${ A }`);
console.log(`B: ${ B }`);
console.log(f(A, B));
console.log('');
}
This code counts the number 7 of your input. This is the code method:
public static int count7(int n) {
if (n == 0) {
return 0;
}
else if (n % 10 == 7) {
return 1 + count7 (n/10);
}
else {
return count7(n/10);
}
}
What does the else-if statement and else do? Thank you so much.
if (n == 0) {
return 0;
There are no 7s in 0, so return 0.
} else if (n % 10 == 7) {
checks if the least significant digit of the number is 7. If that's the case, the total number of 7s is 1 + the number of 7s in the number you get by removing that digit (by dividing the number by 10) :
return 1 + count7 (n/10);
} else {
If the least significant digit is not 7, the number of 7s is the number of 7s in n/10 :
return count7(n/10);
}
if (n % 10 == 7) if the remainder is 7 lets take example n=17 so 17%10 you are going to get 7 so add 1 means count that you have found 7 if you have not found then go to the else part and this time call by dividing it suppose this time n=28 clearly there is no 7 in this number so it will go to else if condition and it is going to be fail it will go to the else part and it will call the method by dividing n to 10 for the next iteration.
This is a recursive method.
The first if is the base case, i.e, if the numbers is 0, then it returns 0.
The else if checks if the digit is 7. If it is, then get the answer to the remaining digits (whether they have any 7s) using the same method, add 1 to it, and return the value.
The last else just removes one digit, and calls the method for the remaining digits.
This method counts the total amount of the digit 7 in a given number.
n % 10 == 7 inspects the least significant digit, and if it equals 7, then add 1 to the sum.
In any case, the algorithm goes on with the other digits by taking n / 10, which actually removes the least significant digit, and then calls the method recursively. As the number is decreasing with 1 digit with each call and the stop trigger n == 0 makes the method eventually stop, the final returned number counts the sevens in the initial number.
Example
count7(172) = 0 + count7(17) = 0 + 1 + count7(1) = 0 + 1 + 0 = 1
The function simply counts the total number of appearances of the digit 7 in any given non-negative integer.
I am doing exercises with algebraic formulas to practice the use of recursion in Java.
I am trying to write a method that returns the result of n + (n - 3) + (n - 6) + (n - 9) ... + 0.
For example, when n = 7, the result should be 12.
When n = 10, the result should be 22.
So far, this is what I have:
public static int sumDownBy3(int n)
{
if(triSum <= 0)
{
return sum;
}
sum = n;
triVar += 3;
triSum = (n - triVar);
return sumDownBy3(n + triSum);
}
However, when I compile and run it, it does not return the expected result.
How may I fix this method to correctly apply the formula I am trying to emulate?
So, here are a few tips to hopefully get you going considering the comments by childofsoong and Jonny Henly.
What you are looking for is simply:
f(n) = n + f(n-3) for n > 0.
Your recursive function should just check if n is <= 0. If it is, return 0. Else return the variable n + another call to your function with n-3.
Hope this helps without giving too much away.
Since this isn't an assignment, just practice, then here is a working solution based off the information given in your question. This solution works for all n, however n <= 0 will always return 0.
public static int sumDownBy3(int n) {
if (n <= 0) {
return 0;
}
return n + sumDownBy3(n - 3);
}
Instead of having an extra parameter or global/class variables keeping track of the sum, this example just uses the stack to keep track of the sum. One downside to this approach is that for a very, very large number the stack could overflow and/or the program could lock up.
Output:
sumDownBy3(7) = 12
sumDownBy3(10) = 22
sumDownBy3(9) = 18
Breakdown for sumDownBy3(10):
sumDownBy3(10): 10 !<= 0 -> return 10 + sumDownBy3(10 - 3)
sumDownBy3( 7): 7 !<= 0 -> return 7 + sumDownBy3( 7 - 3)
sumDownBy3( 4): 4 !<= 0 -> return 4 + sumDownBy3( 4 - 3)
sumDownBy3( 1): 1 !<= 0 -> return 1 + sumDownBy3( 1 - 3)
sumDownBy3(-2): -2 <= 0 -> return 0
So 0 + 1 + 4 + 7 + 10 ='s 22
Full Disclosure: Homework.
Explanation: I cant understand my teacher.
Problem:
Write a method called printSquare that takes in two integer
parameters, a min and a max, and prints the numbers in the range from
min to max inclusive in a square pattern. The square pattern is
easier to understand by example than by explanation, so take a look at
the sample method calls and their resulting console output in the
table below. Each line of the square consists of a circular sequence
of increasing integers between min and max. Each line prints a
different permutation of this sequence. The first line begins with
min, the second line begins with min + 1, and so on. When the
sequence in any line reaches max, it wraps around back to min. You
may assume the caller of the method will pass a min and a max
parameter such that min is less than or equal to max
I cannot for the life of me figure out how to make the numbers stop at the 'max' value and start over in the middle of the line.
This is what I have so far, apologies but I have trouble with for loops.
for(int i = 0; i < row; i++)
{
for(int d = 0; d < row; d++)
{
System.out.print(d+1);
}
System.out.println(i);
}
I know I used row twice, but its the only way i can get the compiler to form a square shape with the loop. Does anyone even remotely understand what i'm trying to do? :/
This is actually a nice mathematical problem. Assume:
int side = to - from + 1; /// the size/width of the square.
the value at any point in the square (row, col) is:
from + ((row + col) % side)
you should be able to put that in your loops and "smoke it".
Edit based on comment asking for explanation.
The trick is to loop through all the positions in the 'matrix'. Given that the matrix is square, the loops are relatively simple, just two loops (nested) that traverse the system:
final int side = to - from + 1;
for (int row = 0; row < side; row++) {
for(int col = 0; col < side; col++) {
... magic goes here....
}
}
Now, in this loop, we have the variables row and col which represent the cell in the matrix we are interested in. The value in that cell needs to be proportional to the distance it is from the origin..... let me explain.... If the origin is the top left (which it is), then the distances from the origin are:
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
The distance is the sum of the row and the column...... (rows and columns start counting from 0).
The values we put in each matrix are limited to a fixed range. For the above example, with a square of size 5, it could have been specified as printSquare(1,5).
The value in each cell is the from value (1 in this example) plus the distance from the origin... naively, this would look like:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
here the values in the cell have exceeded the limit of 5, and we need to wrap them around... so, the trick is to 'wrap' the distances from the origin..... and the 'modulo' operator is great for that. First, consider the original 'origin distance' matrix:
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
if we instead populate this matrix with 'the remainder of the distance when dividing by 5' (the modulo 5, or %5) we get the matrix:
0 1 2 3 4
1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
Now, if we add this 'modulo' result to the from value (1), we get our final matrix:
1 2 3 4 5
2 3 4 5 1
3 4 5 1 2
4 5 1 2 3
5 1 2 3 4
in a sense, all you need to know is that the value at each cell is:
the from value plus the remainder when you divide the 'distance' by the width.
Here's the code I tested with:
public static final String buildSquare(final int from, final int to) {
final StringBuilder sb = new StringBuilder(side * side);
final int side = to - from + 1;
for (int row = 0; row < side; row++) {
for(int col = 0; col < side; col++) {
sb.append( from + ((row + col) % side) );
}
sb.append("\n");
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(buildSquare(1, 5));
System.out.println(buildSquare(3, 9));
System.out.println(buildSquare(5, 5));
System.out.println(buildSquare(0, 9));
System.out.println(buildSquare(0, 3));
}
Since this is homework, I'll just give a hint.
I cannot for the life of me figure out how to make the numbers stop at the 'max' value and start over in the middle of the line.
Here's one way to do it.
Create the first number twice in an array. Taking the printSquare(1, 5) example, create an int array of 1, 2, 3, 4, 5, 1, 2, 3, 4, 5.
Use a loop to loop through the array, starting with element zero and ending with element 4, and another loop to display 5 digits (max - min + 1).
try this
int i,j,k;
for(i=min;i<=max;i++) {
for(j=i;j<=max;j++) {
System.out.print(j);
}
for(k=min;k<i;k++){
System.out.print(k);
}
System.out.println();
}
you can try
loop from min value to max value and put all the numbers in an array
now loop again from min value to max value
each time print the array and do a circular shift (for circular shift you can find lot of example in SO)
I think #rolfl's solution is the cleanest. I'd recommend going with that.
You can find another simple solution by observing that each output in your "square" simply shifts the first element to the end the list of numbers. To imitate this, you can put all the numbers from min to max in a data structure like LinkedList or ArrayDeque where you can easily add/remove items from both ends, then you'd print the contents in order, and shift the first entry to the end. E.g., coll.addLast(coll.removeFirst()). If you repeat that process max - min + 1 times, you should get the desired output.
no array no problem you can easily solve.
it work with any range of number.
static void printSquare(int min, int max){
int len = max - min + 1;
int copy_min = min, permanent_min = min;
for(int i = 0; i < len; i++){
for(int j = 0; j< len; j++){
if(min > max)
if(min % len < permanent_min)
System.out.print((min % len )+ len);
else
System.out.print(min % len);
else
System.out.print(min);
min++;
}
min = ++copy_min;
System.out.println();
}
}
public static void printSquare(int min, int max) {
for (int i = min; i <= (max -min)+min; i++) {
for( int j =i; j <= max ; j++) {
System.out.print(j);
}
for (int j1= min; j1<= i * 1 - 1; j1++) {
System.out.print(j1);
}
System.out.println();
}
}