Algorithms: Ruby solution timing out but Java isn't - java

I have the following two solutions for the following algo problem:
Suppose you have N integers from 1 to N. We define a beautiful
arrangement as an array that is constructed by these N numbers
successfully if one of the following is true for the ith position (1 <=
i <= N) in this array:
The number at the ith position is divisible by i.
i is divisible by the number at the ith position. Now given N, how many beautiful
arrangements can you construct? (N will be <= 15)
Here's the provided solution in Java:
public class Solution {
int count = 0;
public int countArrangement(int N) {
boolean[] visited = new boolean[N + 1];
calculate(N, 1, visited);
return count;
}
public void calculate(int N, int pos, boolean[] visited) {
if (pos > N)
count++;
for (int i = 1; i <= N; i++) {
if (!visited[i] && (pos % i == 0 || i % pos == 0)) {
visited[i] = true;
calculate(N, pos + 1, visited);
visited[i] = false;
}
}
}
}
And here's my Ruby solution:
def count_arrangement(n)
visited = Array.new(n + 1) # zero index left blank
count_arrangements(n, visited, 1)[:arrangements]
end
def count_arrangements(n, visited, i)
return { arrangements: 1, recursed: true } if i == n + 1 # this is end of one arrangement
max_arrangements = 0
recursed = false
1.upto(n) do |num|
next if visited[num]
if num % i == 0 || i % num == 0
recursed = true
this_visited = visited.dup
this_visited[num] = true
arrangements_obj = count_arrangements(n, this_visited, i + 1) # recursive depth only goes up to O(N)
if arrangements_obj[:recursed]
max_arrangements += arrangements_obj[:arrangements]
end
end
end
recursed ? { arrangements: max_arrangements, recursed: true } : { arrangements: -1, recursed: false }
end
Seems like the algorithmic complexity is the same (AFAIK), and yet my Ruby solution times out where the Java one doesn't. Does anyone know why this is?

Related

Shortest path algorithm on an integer matrix grid does not return the expected value

I have an assignment to submit and I need help I will appreciate it a lot!!
I need to write a method that gets an integer matrix grid with numbers from 0 and above. 1 value in the matrix is different and contains -1. the function also gets the starting x and y in the matrix.
I need to write a recursive function that finds the shortest path from the starting x, y to the -1 value.
you can "jump" to the left' right' up and down. the condition to "jump" from one index to another is if the absolute value between the subtraction of the the 2 pairing is either 0, 1 or 2
The shortest path is 4
I cannot use while and for loops, and global variables
The function signature should be: public static int shortestPath(int[][] drm, int i, int j)
Thanks a lot!
Here is my try:
package assignment1;
import java.util.*;
public class run {
static Scanner reader = new Scanner(System.in);
public static int shortestPath(int[][] drm, int i, int j) {
if (drm.length == 0 || i >= drm.length || j >= drm.length || i < 0 || j < 0)
return 0;
if (i > 0 && j > 0 && i < drm.length - 1 && j < drm[i].length - 1) {
if (drm[i][j - 1] == -1) {
System.out.print("Got it! the target is on the left");
return 2;
}
if (drm[i][j + 1] == -1) {
System.out.print("Got it! the target is on the right");
return 2;
}
if (drm[i - 1][j] == -1) {
System.out.print("Got it! the target is up");
return 2;
}
if (drm[i + 1][j] == -1) {
System.out.print("Got it! the target is down");
return 2;
}
}
int temp = drm[i][j];
int left = Integer.MAX_VALUE, right = Integer.MAX_VALUE, up = Integer.MAX_VALUE, down = Integer.MAX_VALUE;
if (isValidJump(drm, i, j, i + 1, j)) {
System.out.print("down ");
drm[i][j] = Integer.MIN_VALUE;
down = shortestPath(drm, i + 1, j) + 1;
}
if (isValidJump(drm, i, j, i, j + 1)) {
System.out.print("right ");
drm[i][j] = Integer.MIN_VALUE;
right = shortestPath(drm, i, j + 1) + 1;
}
if (isValidJump(drm, i, j, i, j - 1)) {
System.out.print("left ");
drm[i][j] = Integer.MIN_VALUE;
left = shortestPath(drm, i, j - 1) + 1;
}
if (isValidJump(drm, i, j, i - 1, j)) {
System.out.print("up ");
drm[i][j] = Integer.MIN_VALUE;
up = shortestPath(drm, i - 1, j) + 1;
}
drm[i][j] = temp;
return Math.min(Math.min(Math.min(up, down), left), right);
}
public static boolean isValidJump(int[][] drm, int i, int j, int m, int n) {
if (m < drm.length && m >= 0 && n < drm.length && n >= 0 && drm[m][n] != Integer.MIN_VALUE) {
int jump = drm[m][n] - drm[i][j];
if (jump == 0 || jump == 1 || jump == -1 || jump == -2) {
return true;
}
}
return false;
}
public static void main(String[] args) {
int[][] drm = { { 2, 0, 1, 2, 3 }, { 2, 3, 5, 5, 4 }, { 8, -1, 6, 8, 7 }, { 3, 4, 7, 2, 4 },
{ 2, 4, 3, 1, 2 } };
System.out.println(shortestPath(drm, 0, 0));
}
}
It supposed to return 4 (shortest path)
Given this is for a class I advise you to notify your professor that you received assistance from this post on stack overflow. Neglecting to do this would be considered academic dishonestly at most universities.
Second thing is as David suggested this is a good opportunity for you to learn how to use a debugger. This is a skill that will be incredibly valuable in your academic career and in a engineering role.
Your Code
Now looking at your code it does give the solution "4" for the case you presented which is correct. Problem is if you change the inputs the output may not give the correct answer.
This is because your code as written gives the FIRST path it finds and not the SHORTEST path.
Your logic as far as the recursion is sound and based on this code it looks like you understand the basics of recursion. Your problem is a minor logical flaw with how your are masking your data when you call your function recursively.
You should have everything you need to solve this. If you are still having problems please try to use a debugger and examine the area where you make your recursive calls.
Solution
I advise you to try to figure this out yourself before looking at the spoilers below.
In the code where you make your recursive calls you mask by setting drm[i][j] = Integer.MIN_VALUE. The problem is after each of your recursive calls return you do not set it back to the previous value with drm[i][j] = temp before doing the tests for your next recursive call.
What is happening is when you next call isValidJump() it will always return false because drm[i][j] will always be Integer.MIN_VALUE after your have made your first recursive call on this iteration.
How to fix:
Put drm[i][j] = temp immediately after each recursive call to shortestPath().

Find maximum product using recursion

There's a question I saw and I'm wondering if it's possible to solve it using recursion. It goes as follow:
Write an algorithm that, when given an array of input, finds the maximum product from those inputs. For example:
Input: [1, 2, 3]
Output: 6 (1*2*3)
Input: [-1, 1, 2, 3]
Output: 6 (1*2*3)
Input: [-2, -1, 1, 2, 3]
Output: 12 (-2*-1*1*2*3)
I'm trying to find a way of using recursion to solve it, but the algorithm I tried doesn't work. My algorithm, written in Java is as follow
Integer[] array;
public int maximumProduct(int[] nums) {
array=new Integer[nums.length];
return multiply(nums, 0);
}
public int multiply(int[] nums, int i){
if (array[i]!=null){
return array[i];
}
if (i==(nums.length-1)){
return nums[i];
}
int returnval=Math.max(nums[i]*multiply(nums, i+1), multiply(nums, i+1));
array[i]=returnval;
return returnval;
}
The problem with this algorithm is that it doesn't work well if there's an even number of negative numbers. For example, if nums[0]=-2, nums[1]=-1 and nums[2]=1, then multiply(nums, 1) will always return 1 instead of -1, and thus it will always see 1 as bigger than 1*-2 at multiply(nums, 0). I'm not sure how to solve this problem, however. Is there any way of solving this using recursion or dynamic programming?
If there is only one non-zero element in the array, and it happens to be a negative number, then then answer is either 0, if there is a 0 present in the input, or if the array contains only that single negative element, the answer is that element itself.
In all other cases, the final answer is going to be positive.
We first make a linear scan to find the number of negative integers. If this number is even, then the answer is the product of all the non-zero elements. If there are an odd number of negative elements, we need to leave out one negative element from the answer, so that the answer is positive. As we want the maximum possible answer, the number we want to leave out should have as small an absolute value as possible. So among all the negative numbers, find the one with the minimum absolute value, and find the product of the remaining non-zero elements, which should be the answer.
All this requires only two linear scans of the array, and hence runs in O(n) time.
What is the maximum product of integers?
To obtain the maximum sum, you will want to multiply all the positive integers with the product of the largest negative integers, with the number of negative integers included in the product being even to obtain a positive final result.
In an algorithm for a single traversal
I am going to treat the positive integers and the negative integers in the input separately. You will want to keep a running product of positive integers, a running product of negative integers and the largest negative integer (ie. the negative integer with the smallest absolute value) found so far.
Let us ignore the edge cases where the final answer is <= 0. That can be handled easily.
//Initialization
int [] nums // Input
int posProduct = 1;
int negProduct = 1;
int smallestNeg = 1;
//Input Traversal
for (int i : nums) {
if ( i == 0 ) {
// ignore
} else if ( i < 0 ) {
if (smallestNeg == 1) {
smallestNeg = i;
} else if ( i > smallestNeg ) {
negProduct *= smallestNeg; //Integrate the old smallest into the running product
smallestNeg = i; // i is the new smallest
} else {
negProduct *= i;
}
} else {
// i is strictly positive
posProduct *= i;
}
}
//Result Computation
int result = posProduct;
if ( negProduct < 0 ) {
// The running product of negative number numbers is negative
// We use the smallestNeg to turn it back up to a positive product
result *= smallestNeg;
result *= negProduct;
} else {
result *= negProduct
}
edit: In a recursive traversal
I personally find that writing the array traversal in a recursive manner to be clumsy but it can be done.
For the beauty of the exercise and to actually answer the question of the OP, here is how I would do it.
public class RecursiveSolver {
public static int findMaxProduct (int [] nums) {
return recursiveArrayTraversal(1, 1, 1, nums, 0);
}
private static int recursiveArrayTraversal(int posProduct, int negProduct,
int smallestNeg, int [] nums, int index) {
if (index == nums.length) {
// End of the recursion, we traversed the whole array
posProduct *= negProduct;
if (posProduct < 0) {
posProduct *= smallestNeg;
}
return posProduct;
}
// Processing the "index" element of the array
int i = nums[index];
if ( i == 0 ) {
// ignore
} else if ( i < 0 ) {
if (smallestNeg == 1) {
smallestNeg = i;
} else if ( i > smallestNeg ) {
negProduct *= smallestNeg;
smallestNeg = i;
} else {
negProduct *= i;
}
} else {
// i is strictly positive
posProduct *= i;
}
//Recursive call here!
//Notice the index+1 for the index parameter which carries the progress
//in the array traversal
return recursiveArrayTraversal(posProduct, negProduct,
smallestNeg, nums, index+1);
}
}
First, break the array in subproblems always you find a 0 in the list:
1 -2 4 -1 8 0 4 1 0 -3 -4 0 1 3 -5
|_____________| |____| |____| |_______|
p1 p2 p3 p4
Then, for each problem pi, count how many negative numbers are there.
If pi has an even number of negatives (or no negatives at all), the answer of pi is the product of all its elements.
If pi has only 1 negative number (say n), the answer will be the maximum between the product of all the elements in n's right and the product of all elements in n's left.
If pi has an odd number (bigger than only 1) of negative numbers, call the index of the leftmost negative number l and the index of the rightmost negative number r. Supposing pi has n elements, the answer will be:
max(
pi[ 0 ] * pi[ 1 ] * ... * pi[r - 1],
pi[l + 1] * pi[l + 2] * ... * pi[ n ]
)
Knowing that, it's easy to write a recursion for each step of the solution of this problem: a recursion to divide problems at zeros, another to count negatives and another to find answers, in O(n).
Linear version
List<Integer> vals = new ArrayList<>(List.of(5,1,-2,1,2,3,-4,-1));
int prod = 0;
int min = 1;
for (int v : vals) {
if (v == 0) {
// ignore zero values
continue;
}
if (prod == 0) {
prod = 1;
}
prod *= v;
// compute min to be the largest negative value in the list.
if (v < 0 && min < Math.abs(v)) {
min = v;
}
}
if (prod < 0) {
prod /= min;
}
System.out.println("Maximum product = " + prod);
}
Recursive version
int prod = prod(vals, new int[] {0} , vals.size());
System.out.println("Maximum product = " + prod);
public static int prod(List<Integer> vals, int[]min, int size) {
int prod = 0;
if(vals.size() > 0) {
int t = vals.get(0);
if (t < 0 && min[0] < Math.abs(t)) {
min[0] = t;
}
prod = prod(vals.subList(1,vals.size()), min, vals.size());
}
if (vals.isEmpty() || vals.get(0) == 0) {
return prod;
}
if (prod == 0) {
prod = 1;
}
prod *= t;
if (vals.size() == size && prod < 0) {
prod/=min[0];
}
return prod;
}
This is my solution - leaving it open for optimization and to figure out the runtime. This is a general purpose solution that finds the products of all the combinations of integers in a list. Of course, there is a O(n) solution but I present this solution as well.
import java.util.ArrayList;
import java.util.List;
public class MaxProd {
int[] input = {1, 2, 3};
// int[] input = {-2, -1, 1, 2, 3};
public static void main(String[] args) {
MaxProd m = new MaxProd();
List<Integer> ll = m.max(0);
for (int i : ll) {
System.out.println(i);
}
ll.sort((x,y) -> Integer.compare(x, y));
System.out.println("The max: " + ll.get(ll.size() -1 ));
}
private List<Integer> max(int index) {
if (index < input.length){
List<Integer> l = new ArrayList<>();
List<Integer> retList = max(index + 1);
for (int j : retList){
l.add(input[index] * j);
}
l.add(input[index]);
l.addAll(retList);
return l;
}
else return new ArrayList<>();
}
}
it prints:
6
2
3
1
6
2
3
The max: 6
If the requirements are constrained (as in this case) then one can get by without the need for generating all combinations resulting in a linear solution. Also, I'm sorting at the end. Note: you could easily get the result with a single pass on the returned list to find the maximum product as specified in other answers.

How to print an integer with commas every 'd' digits, from right to left

I had to write a program that will receive an int 'n' and another one 'd' - and will print the number n with commas every d digits from right to left.
If 'n' or 'd' are negative - the program will print 'n' as is.
I although had to make sure that there is no commas before or after the number and I'm not allowed to use String or Arrays.
for example: n = 12345678
d=1: 1,2,3,4,5,6,7,8
d=3: 12,345,678
I've written the following code:
public static void printWithComma(int n, int d) {
if (n < 0 || d <= 0) {
System.out.println(n);
} else {
int reversedN = reverseNum(n), copyOfrereversedN = reversedN, counter = numberLength(n);
while (reversedN > 0) {
System.out.print(reversedN % 10);
reversedN /= 10;
counter--;
if (counter % d == 0 && reversedN != 0) {
System.out.print(",");
}
}
/*
* In a case which the received number will end with zeros, the reverse method
* will return the number without them. In that case the length of the reversed
* number and the length of the original number will be different - so this
* while loop will end the zero'z at the right place with the commas at the
* right place
*/
while (numberLength(copyOfrereversedN) != numberLength(n)) {
if (counter % d == 0) {
System.out.print(",");
}
System.out.print(0);
counter--;
copyOfrereversedN *= 10;
}
}
}
that uses a reversNum function:
// The method receives a number n and return his reversed number(if the number
// ends with zero's - the method will return the number without them)
public static int reverseNum(int n) {
if (n < 9) {
return n;
}
int reversedNum = 0;
while (n > 0) {
reversedNum += (n % 10);
reversedNum *= 10;
n /= 10;
}
return (reversedNum / 10);
}
and numberLength method:
// The method receives a number and return his length ( 0 is considered as "0"
// length)
public static int numberLength(int n) {
int counter = 0;
while (n > 0) {
n /= 10;
counter++;
}
return counter;
}
I've been told that the code doesn't work for every case, and i am unable to think about such case (the person who told me that won't tell me).
Thank you for reading!
You solved looping through the digits by reversing the number, so a simple division by ten can be done to receive all digits in order.
The comma position is calculated from the right.
public static void printWithComma(int n, int d) {
if (n < 0) {
System.out.print('-');
n = -n;
}
if (n == 0) {
System.out.print('0');
return;
}
int length = numberLength(n);
int reversed = reverseNum(n);
for (int i = 0; i < length; ++i) {
int nextDigit = reversed % 10;
System.out.print(nextDigit);
reversed /= 10;
int fromRight = length - 1 - i;
if (fromRight != 0 && fromRight % d == 0) {
System.out.print(',');
}
}
}
This is basically the same code as yours. However I store the results of the help functions into variables.
A zero is a special case, an exception of the rule that leading zeros are dropped.
Every dth digit (from right) needs to print comma, but not entirely at the right. And not in front. Realized by printing the digit first and then possibly the comma.
The problems I see with your code are the two while loops, twice printing the comma, maybe? And the println with a newline when <= 0.
Test your code, for instance as:
public static void main(String[] args) {
for (int n : new int[] {0, 1, 8, 9, 10, 234,
1_234, 12_345, 123_456, 123_456_789, 1_234_567_890}) {
System.out.printf("%d : ", n);
printWithComma(n, 3);
System.out.println();
}
}
Your code seems overly complicated.
If you've learned about recursion, you can do it like this:
public static void printWithComma(int n, int d) {
printInternal(n, d, 1);
System.out.println();
}
private static void printInternal(int n, int d, int i) {
if (n > 9) {
printInternal(n / 10, d, i + 1);
if (i % d == 0)
System.out.print(',');
}
System.out.print(n % 10);
}
Without recursion:
public static void printWithComma(int n, int d) {
int rev = 0, i = d - 1;
for (int num = n; num > 0 ; num /= 10, i++)
rev = rev * 10 + num % 10;
for (; i > d; rev /= 10, i--) {
System.out.print(rev % 10);
if (i % d == 0)
System.out.print(',');
}
System.out.println(rev);
}
Are you allowed to use the whole Java API?
What about something as simple as using DecimalFormat
double in = 12345678;
DecimalFormat df = new DecimalFormat( ",##" );
System.out.println(df.format(in));
12,34,56,78
Using...
,# = 1 per group
,## = 2 per group
,### = 3 per group
etc...
It took me a bunch of minutes. The following code snippet does the job well (explanation below):
public static void printWithComma(int n, int d) { // n=number, d=commaIndex
final int length = (int) (Math.log10(n) + 1); // number of digits;
for (int i = 1; i < Math.pow(10, length); i*=10) { // loop by digits
double current = Math.log10(i); // current loop
double remains = length - current - 1; // loops remaining
int digit = (int) ((n / Math.pow(10, remains)) % 10); // nth digit
System.out.print(digit); // print it
if (remains % d == 0 && remains > 0) { // add comma if qualified
System.out.print(",");
}
}
}
Using (Math.log10(n) + 1) I find a number of digits in the integer (8 for 12345678).
The for-loop assures the exponents of n series (1, 10, 100, 1000...) needed for further calculations. Using logarithm of base 10 I get the current index of the loop.
To get nth digit is a bit tricky and this formula is based on this answer. Then it is printed out.
Finally, it remains to find a qualified position for the comma (,). If modulo of the current loop index is equal zero, the dth index is reached and the comma can be printed out. Finally the condition remains > 0 assures there will be no comma left at the end of the printed result.
Output:
For 4: 1234,5678
For 3: 12,345,678
For 2: 12,34,56,78
For 1: 1,2,3,4,5,6,7,8

How can I change my code below to avoid the unexpected type error (required : variable; found: value)?

1.The fifth line of my code below returns an unexpected type error where a variable is required but a value found.
2.The code is a to check if a number n is Prime or not using a while loop.
public boolean primeNumberCheck(int n)
{
int divisionNumber = 2;
boolean primeCheck = true;
while (divisionNumber < n)
{
if (n%divisionNumber = 0)
{
primeCheck = true;
divisionNumber = n;
}
else
{
primeCheck = false;
}
divisionNumber++;
}
return primeCheck;
}
= is the assignment operator. n%divisionNumber returns a value, and you cannot assign a value to another value. So you get that error.
You need to compare the values using the == operator. So, you should do:
if (n%divisionNumber == 0)
Apart from the answers here , one more tip which I follow is doing :
if (0 == n%divisionNumber) rather than if (n%divisionNumber = 0).
this ensures that I haven't missed one "=" sign.
This is not an answer, just a more optimized algorithm based on Primality test on WikiPedia
public boolean primeNumberCheck(int n) {
// exclude numbers with divisor 2 or 3
if (n % 2 == 0) {
return false;
}
if (n % 3 == 0) {
return false;
}
// all integers can be expressed as (6k + i) for some integer k and
// for i = −1, 0, 1, 2, 3, or 4;
// 2 divides (6k + 0), (6k + 2), (6k + 4); and 3 divides (6k + 3).
// so we only have to test numbers of the form 6k +- 1
int k = 1;
int i = -1;
// we only need to check candidates up to SQRT(n)
double sqrtN = Math.Sqrt(n);
int candidateDivisor = getCandidateDivisor(k, i);
while (candidateDivisor < sqrtN) {
if (n % candidateDivisor == 0) {
return false;
}
// flip i between -1 and 1
i = -i;
// when i flips back to -1, increment k
if (i < 0) {
k++;
}
candidateDivisor = getCandidateDivisor(k, i);
}
return true;
}
private int getCandidateDivisor(k, i) {
return (6 * k) + i;
}
DISCLAIMER: I have not tested this code

Given a number N, find the smallest even number E such that E > N

Given a number N, find the smallest even number E such that E > N and digits in N and E are same.
Print NONE otherwise.
Sample:
Case1
Input
N = 34722641
Output
E = 34724126
Case2
Input
N = 8234961
Output
E = 8236194 (instead of 8236149)
My second case passed first case i am getting wrong output
public static int nextDigit(int number) {
String num = String.valueOf(number);
int stop = 0;
char[] orig_chars = null;
char[] part1 = null;
char[] part2 = null;
orig_chars = num.toCharArray();
for (int i = orig_chars.length - 1; i > 0; i--) {
String previous = orig_chars[i - 1] + "";
String next = orig_chars[i] + "";
if (Integer.parseInt(previous) < Integer.parseInt(next))
{
if (Integer.parseInt(previous) % 2 == 0) {
String partString1 = "";
String partString2 = "";
for (int j = 0; j <= i - 1; j++) {
partString1 = partString1.concat(orig_chars[j] + "");
}
part1 = partString1.toCharArray();
for (int k = i; k < orig_chars.length; k++) {
partString2 = partString2.concat(orig_chars[k] + "");
}
part2 = partString2.toCharArray();
Arrays.sort(part2);
for (int l = 0; l < part2.length; l++) {
char temp = '0';
if (part2[l] > part1[i - 1]) {
temp = part1[i - 1];
part1[i - 1] = part2[l];
part2[l] = temp;
break;
}
}
for (int m = 0; m < part2.length; m++) {
char replace = '0';
if (part2[m] % 2 == 0) {
replace = part2[m];
for (int n = m; n < part2.length - 1; n++) {
part2[n] = part2[n + 1];
}
part2[part2.length - 1] = replace;
break;
}
}
System.out.print(part1);
System.out.println(part2);
System.exit(0);
}
}
}
System.out.println("NONE");
return 0;
}
First idea was to generate the next permutation until an even one is found. This works well for small inputs or when an even permutation is nearby, but badly for large inputs such as 2135791357913579 where many permutations have to happen "to the right" before the sole even digit is moved into place.
גלעד ברקן suggested a patch which with minor adjustment provides a superior algorithm.
As with the next permutation algorithm, we find indices i and j with i < j where the ith digit is less than the jth digit. When we swap those digits, this makes a larger number then N.
Apply the additional constraint that there is an even number to the right of i after the swap.
Take the largest such even number to the right of the index (might not be the one you just swapped), move it to the end. This guarantees evenness.
Then sort the remaining digits in between in ascending order. This provides the smallest permutation available.
The algorithm in Clojure could be written as follows. I tried to stick to a fairly imperative style. See if you can translate.
(defn num->digits [n] (mapv #(Character/getNumericValue %) (str n)))
(defn digits->num [v] (when (seq v) (read-string (apply str v))))
(defn swap
"Swap elements at index i and j in vector v"
[v i j]
(assoc (assoc v i (v j)) j (v i)))
(defn find-max-where
"Find index i in vector v such that (v i) is the largest satisfying pred"
[pred v]
(first
(reduce-kv
(fn [[k m] i x]
(if (and m (> m x))
[k m]
(if (pred x) [i x] [k m])))
[nil nil]
v)))
(defn next-even-perm [v]
(->>
(for [j (range (count v))
i (range j)
:when (< (v i) (v j))
:let [v (swap v i j)
k (find-max-where even? (vec (subvec v (inc i))))]
:when k
:let [v (swap v (+ (inc i) k) (dec (count v)))]]
(concat (subvec v 0 (inc i))
(sort (subvec v (inc i) (dec (count v))))
[(peek v)]))
(map vec) sort first))
(defn next-even-num [n] (-> n num->digits next-even-perm digits->num))
Provided examples:
(next-even-num 34722641)
;=> 34724126
(next-even-num 8234961)
;=> 8236194
(next-even-num 4321)
;=> nil (no solution)
Hard cases for previous algorithm
(time (next-even-num 2135791357913579))
; "Elapsed time: 1.598446 msecs"
;=> 3111335557779992
(time (next-even-num 13244351359135913))
; "Elapsed time: 1.713501 msecs"
;=> 13245111333355994
(time (next-even-num 249999977777555553333311111N))
; "Elapsed time: 1.874579 msecs"
;=> 251111133333555577777999994N
Latest edit fixes problem where we were always wanting to swap with an even number moving right instead of just having any even number to the right whether or not it was involved in the swap. For example, the following failed in the previous edit, now fixed.
(next-even-num 1358)
;=> 1538
Many has suggested permutation, but for this problem, dynamic programming with bit-mask will be another solution.
For dynamic programming, the number of digit can be up to 20 digits, while with normal permutation, it can only be used if N has less than 12 digits. (With time constraint is 1 second , typically for competitive programming)
So the idea is, starting from the most significant digit to the least significant digit, at each step, we try to find the smallest value starting from this digit to the end, at each digit, we have two cases:
If the number being created is already larger than N for example N is 12345 and currently, we are 23xxx. (We need to find the smallest xxx in this case).
If the number is not yet larger than N , example N is 12345 and we have 12xxx.
Moreover, at the last digit, we need to determine if the created number is even or odd.
So, we have our simple recursive code:
public int cal(boolean[] selected, int[] num, int digit, boolean larger) {
//Arrays selected will tell which digit in N has already selected,
//int digit will tell currently, which digit we are checking
//boolean larger tells is the number already larger than N
if (digit + 1 == selected.length) {//Last digit
for (int i = 0; i < selected.length; i++) {
if (!selected[i]) {
if (num[i] % 2 != 0) {
return -1; // -1 means this is an invalid value
} else {
if (larger) {
return num[i];
} else {
return -1;
}
}
}
}
}
int result = -1;
for (int i = 0; i < selected.length; i++) {
if (!selected[i]) {
if (larger) {
selected[i] = true;
int val = (int) (num[i] * Math.pow(10, digit) + cal(selected, num, digit + 1, larger));
if (val != -1 && (result == -1 || result > val)) {
result = val;
}
} else if (num[i] >= num[digit]) {
int val = (int) (num[i] * Math.pow(10, digit) + cal(selected, num, digit + 1, num[i] > num[digit]));
if (val != -1 && (result == -1 || result > val)) {
result = val;
}
}
}
}
return result;
}
From this state, we notice that actually, the boolean [] selected can be replaced by a bit-mask value (Read about bitmask here Link) , So we can easily represent the state of this recursive by this array int [mask][larger] dp
Notice that the parameter digit is not necessary as we can easily determine the digit we are checking by counting the number of digit left to be selected.
Finally, we have our solution:
import java.util.Arrays;
/**
*
* #author Trung Pham
*/
public class Test {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.largest(2135791357913579L));
}
long[][] dp;
public long largest(long N) {
String val = "" + N;
int[] num = new int[val.length()];
for (int i = 0; i < num.length; i++) {
num[i] = val.charAt(i) - '0';
// System.out.println(num[i] + " " + i);
}
dp = new long[1 << num.length][2];
for (long[] a : dp) {
Arrays.fill(a, -2);
}
return cal(0, num, 0);
}
public long cal(int mask, int[] num, int larger) {
//Arrays selected will tell which digit in N has already selected,
//int digit will tell currently, which digit we are checking
//int larger tells is the number already larger than N, if it is 1, it is larger, 0 is not.
int digit = 0;
for (int i = 0; i < num.length; i++) {
if (((1 << i) & mask) != 0) {
digit++;
}
}
if (dp[mask][larger] != -2) {
return dp[mask][larger];
}
if (digit + 1 == num.length) {//Last digit
//System.out.println(mask + " " + digit);
for (int i = 0; i < num.length; i++) {
if (((1 << i) & mask) == 0) {
if (num[i] % 2 != 0) {
return -1; // -1 means this is an invalid value
} else {
if (larger == 1) {
// System.out.println(num[i] + " " + i);
return num[i];
} else {
return -1;
}
}
}
}
return -1;
}
long result = -1;
int l = num.length;
for (int i = 0; i < num.length; i++) {
if (((1 << i) & mask) == 0) {
if (larger == 1) {
//System.out.println(num[i]* Math.pow(10,l - digit) + " " + digit);
long val = (long) (cal(mask | (1 << i), num, larger));
if (val != -1) {
val += num[i] * Math.pow(10, l - digit - 1);
if (result == -1 || result > val) {
result = val;
}
}
} else if (num[i] >= num[digit]) {
long val = (long) (cal(mask | (1 << i), num, num[i] > num[digit] ? 1 : 0));
if (val != -1) {
val += num[i] * Math.pow(10, l - digit - 1);
if (result == -1 || result > val) {
result = val;
}
}
}
}
}
return dp[mask][larger] = result;
}
}
Notice that this solution can still be improved if you notice that at each digit, we only selected value from 0 to 9 once, and the start digit cannot start with 0.
An attempt at a Haskell version of the newer algorithm in A. Webb's answer:
import qualified Data.Map as M
import Data.Ord (comparing)
import Data.List (sort,maximumBy)
import Data.Maybe (fromJust)
digs :: Integral x => x -> [x]
digs 0 = []
digs x = digs (x `div` 10) ++ [x `mod` 10]
nextE n
| e == -1 = "NONE"
| otherwise = concatMap show $ take (ie' + 1) (M.elems s)
++ sort (drop (ie' + 1) (M.elems s')) ++ [r]
where ds = M.fromList (zip [0..] (digs n))
rightMost (-1) _ = [(-1,0),(-1,0)]
rightMost ix xs
| even (x) && not (null $ filter (>x) xs) = [(x,ix),(y,iy)]
| otherwise = rightMost (ix - 1) (x:xs)
where x = fromJust (M.lookup ix ds)
(y,iy) = minimum . filter ((>= x) . fst)
$ zip xs [ix + 1..M.size ds - 1]
[(e,ie),(l,il)] = rightMost (M.size ds - 1) []
s = M.insert il e . M.insert ie l $ ds
(ir,r) = maximumBy (comparing snd) . M.toList
. M.filter even . snd $ M.split ie s
s' = M.delete ir s
ie' = fromIntegral ie
main = print (map (\x -> (x,nextE x)) [34722641,8234961,13244351359135913,3579])
Output:
*Main> main
[(34722641,"34724126"),(8234961,"8236194")
,(13244351359135913,"13245111333355994"),(3579,"NONE")]
(0.02 secs, 563244 bytes)
Cases where result will be "NONE":
Find the minimum even digit(0, 2, 4, 6, 8) from the given number. Let the digit is x.
Take the other digits, sort them in decreasing order. Let's assume this substring is S.
E = S + x (Here + means concatenation)
If no even digit is found in step 1, There is no such number.
If E, after step 2 is <= N, then there is no such number.
As there are only 5 possible digits those can be placed as the last digit of E, first we consider each of them. Let the current even digit be e. We'll scan N to find if e occurs in N.
If e doesn't occur in N, skip. Otherwise, we remove 1 occurrence of e from N and add it to the end of E. Let's assume the rest digits concatenate to E1.
If e > N % 10, then we need to find the permutation of E1 such that E1 >= N/10 and E1 is minimum. If e <= N %10 then we need a permutation of E1 such that E1 > N/10 and E1 is minimum of every such permutation. So the problem reduces to find a permutation of a number E1, that is greater than or equal to E1 (based on the value of e) and minimum.
You can take it from here and solve this problem as it only needs some careful coding from here to solve the next part of the problem.

Categories