Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to optimize my function that takes a positive integer and returns the next smaller positive integer?

I am trying to write a function that takes a positive integer and returns the next smaller positive integer containing the same digits, and -1 when there is no smaller number that contains the same digits.

For example:

nextSmaller(21) == 12
nextSmaller(531) == 513
nextSmaller(2071) == 2017

I wrote a code that solves this, but I don't really know how to optimize it further. Could you please help me? It runs fairly fast on repl.it, but when I submit it, it says it takes more than 1200ms and doesn't allow me to submit it even though all the tests pass.

function nextSmaller(n) {
var nArray= n.toString().split("")
var minimumNum = 1 + Array(nArray.length).join('0')
for(var i=n-1; i >= minimumNum; i--) {
  var newNumArray = i.toString().split('');
  var counter = 0;
  for (var j=0; j<newNumArray.length; j++) {
    if (nArray.indexOf(newNumArray[j]) > -1) {
        counter++
        nArray.splice(nArray.indexOf(newNumArray[j]), 1)
        if (counter === n.toString().split("").length) {
        return i;
        }
     } 
  }
       nArray = n.toString().split("");
       if (i === Number(minimumNum)) return -1;
  }
}
like image 553
Abdel Shokair Avatar asked Feb 03 '17 05:02

Abdel Shokair


People also ask

Which function takes a positive integer and returns the sum?

Python: Function that takes a positive integer and returns the sum of the cube of all the positive integers smaller than the specified number - w3resource. w3resource.

How to optimize a function in Python?

my_function <- function ( x) { # Create function x ^3 + 2 * x ^2 - 10 * x } Now, we can apply the optimize () command to optimize our user-defined function. In this example, we are using a lower interval limit of -1 and an upper interval limit of 1.

When to return -1 when the next smaller number is smaller?

Also return -1 when the next smaller number with the same digits would require the leading digit to be zero. nextSmaller ( 9) == -1 nextSmaller ( 111) == -1 nextSmaller ( 135) == -1 nextSmaller ( 1027) == -1 // 0721 is out since we don't write numbers with leading zeros

What is the optimize R function?

Let’s dig in… Definition: The optimize R function performs one dimensional optimization. Basic R Syntax: You can find the basic R programming syntax of the optimize function below. In the following, I’ll show an example for the application of the optimize function in R programming.


2 Answers

Your code could be optimized a bit, for instance you could use a break statement in your inner loop to move on to the next number as soon as you know the current one isn't going to work (that should make it run in about half the time, but it is still quite slow for an n like 91234567) and instead of n.toString().split("").length in the loop, use a variable so you only need to convert n to an array once.

function nextSmaller(n) {
  var nArray = n.toString().split("")
  var length = nArray.length;
  var minimumNum = 1 + Array(length).join('0')
  for(var i=n-1; i >= minimumNum; i--) {
    var newNumArray = i.toString().split('');
    var counter = 0;
    for (var j=0; j<newNumArray.length; j++) {
      if (nArray.indexOf(newNumArray[j]) < 0)
          break;
      counter++
      nArray.splice(nArray.indexOf(newNumArray[j]), 1)
      if (counter === length) {
          return i;
      }
    }
    nArray = n.toString().split("");
  }
  return -1;
}

There is a very efficient algorithm for computing the next permutation, which can easily be adapted to get the previous one instead (and return -1 if the resulting permutation starts with 0). I adapted this algorithm to do that:

[21,531,2071,912345678,9123545678,915345678].forEach( x => console.log( nextSmaller( x ) ) );

function nextSmaller(n) {
  
    const arr = ( n + '' ).split( '' ).map( Number );
  
    // Find longest non-decreasing suffix
    let i, prev = 9;
    for ( i = arr.length; i--; ) {
        if ( arr[ i ] > prev )
            break;
        prev = arr[ i ];
    }
  
    // If whole sequence is non-decreasing,
    // it is already the smallest permutation
    if ( i < 0 )
        return -1;
  
    const pivot_i = i;
    const pivot = arr[ pivot_i ];
  
    for ( i = arr.length; i--; ) {
        if ( arr[ i ] < pivot )
            break;
    }
  
    arr[ pivot_i ] = arr[ i ];
    arr[ i ] = pivot;

    if ( arr[ 0 ] === 0 )
        return -1;
  
    return +arr.slice( 0, pivot_i + 1 ).concat( arr.slice( pivot_i + 1 ).reverse( ) ).join('');
}
like image 97
Paul Avatar answered Sep 23 '22 07:09

Paul


The algorithm could be like the following:

  1. For the input number n find all numbers that are formed with some permutations of the same digits and sort these numbers. For example, if n=213, we get the sorted list as [123, 132, 213, 231, 312, 321]. (e.g., Permutations in JavaScript? can help you).

  2. Find the index i of the number n in the sorted list. If i>0 return the number at index i-1 else return -1 (if it's the smallest number appearing at the first position of the sorted list).

Another alternative algorithm could be the following:

Decrement the number n until and unless you find one that has exactly same digits (in a different order, you can sort the digits and check for equality).

The most efficient will be similar to the one referred to by @Paulpro(https://www.nayuki.io/page/next-lexicographical-permutation-algorithm)

  1. Find the longest non-decreasing suffix from the decimal string representation of n.
  2. If the entire string n is non-decreasing then return -1 (there can't be any smaller).
  3. Otherwise choose the digit immediately left to the start of the suffix as pivot and swap it with (the leftmost and) the largest digit in the suffix that is smaller than the pivot. Return this number.
like image 30
Sandipan Dey Avatar answered Sep 23 '22 07:09

Sandipan Dey