2017-02-03 9 views
5

Я пытаюсь написать функцию, которая принимает положительное целое число и возвращает следующее меньшее положительное целое число, содержащее одни и те же цифры, и -1, когда не существует меньшего числа, содержащего одни и те же цифры.Как оптимизировать мою функцию, которая принимает положительное целое число и возвращает следующее меньшее положительное целое число?

For example: 

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

Я написал код, который решает это, но я действительно не знаю, как его оптимизировать. Не могли бы вы мне помочь? Он работает довольно быстро на repl.it, но когда я его отправляю, он говорит, что он занимает более 1200 мс и не позволяет мне отправлять его, даже если все тесты проходят.

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; 
    } 
} 
+4

Возможно, это больше подходит в [обзоре кода] (http://codereview.stackexchange.com/) –

+0

Посмотрите на модели, чтобы локализовать проблему пространства , Например, вы должны только проверять перестановки цифр на входе. – 4castle

+0

Похоже, это ката на копейках – JohanP

ответ

2

алгоритм может быть как следующее:

  1. Для ввода числа n найти все числа, которые формируются с некоторым permutations из same digits и sort этих чисел. Например, если n=213, мы получаем отсортированный список как [123, 132, 213, 231, 312, 321]. (например, Permutations in JavaScript? может вам помочь).

  2. Найти index i с номером n в отсортированном виде. Если i>0 верните номер на номер index i-1, верните -1 (если это наименьшее число, отображаемое в первой позиции отсортированного списка).

Другой альтернативный алгоритм может быть следующим:

Decrement число n до и если вы не найдете тот, который имеет точно такие же цифры (в другом порядке, вы можете отсортировать цифры и проверить равенство) ,

Наиболее эффективным будет похож на тот ссылается @Paulpro (https://www.nayuki.io/page/next-lexicographical-permutation-algorithm)

  1. Найти в longest non-decreasing suffix от десятичной строкового представления n.
  2. Если целая строка n не убывает, тогда возврат -1 (их не может быть меньше).
  3. В противном случае выберите цифру сразу влево до начала суффикса как pivot и поменять его с (the leftmost и) the largest цифр в suffix, который smaller чем pivot. Верните этот номер.
+0

Как этот ответ разрешает вопрос _ «Как оптимизировать мою функцию» _? Как можно воспроизвести описание алгоритма как более эффективное, чем 'javascript' в ответе? – guest271314

+0

ну, он был помечен как «алгоритм», поэтому подумал о том, чтобы опубликовать его, о котором я мог думать, а также опубликовал некоторые ссылки на javascript для реализации, на мой взгляд, он может быть эффективно реализован, так как количество цифр будет ограничено, будет не так много перестановок, даже мы можем оптимизировать и генерировать неполный список перестановок и останавливаться, как только мы увидим номер 'n'. –

+0

_ «На мой взгляд, он может быть реализован эффективно». Алгоритмы начинаются с теории, но доказываются воспроизводимыми узорами. Можете ли вы показать и доказать алгоритм, который вы предлагаете с помощью 'javascript'? И что этот же алгоритм требует меньше времени для завершения, чем 'javascript' в Question? Иначе, что такое точка? Эффективность - это измеримый и воспроизводимый факт, а не теория. – guest271314

3

Ваш код может быть оптимизирован, например, вы можете использовать оператор break в своем внутреннем цикле, чтобы перейти к следующему номеру, как только вы узнаете, что текущий не будет работать (что должно запустите его примерно в половине случаев, но он по-прежнему довольно медленный для n, например 91234567), а вместо n.toString().split("").length в цикле используйте переменную, поэтому вам нужно только преобразовать n в массив один раз.

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; 
} 

Существует очень эффективный алгоритм для вычисления следующей перестановки, которая может быть легко адаптирована, чтобы получить предыдущий вместо (и возвращает -1, если в результате перестановки начинается с 0).Я приспособил this algorithm сделать:

[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(''); 
 
}

+0

_ «Существует очень эффективный алгоритм». Как это обосновано? «очень эффективный» по сравнению с? – guest271314

+1

@ guest271314 Это очевидно, глядя на него. Все, что он делает, - это цикл через массив не более двух раз, и вызывать срез, concat и reverse пару раз (не в цикле). – Paulpro

+0

Ничто не "очевидно". Очевидное может также ввести в заблуждение. Удивительно, что, когда в вопросе упоминается «эффективность», в «Ответ» не содержится реального воспроизводимого измерения «эффективности» предлагаемого подхода. Особенно там, где доступны инструменты, чтобы показать и доказать факт «эффективности». Время измеримо. ОП не просил теории. Показывать и доказывать факты. [Более эффективно использовать find() вместо> для дочернего селектора в jQuery?] (Http://stackoverflow.com/questions/39993827/is-it-more-efficient-to-use- find-rather-than-for-child-selector-in-jquery/39994266 # 39994266) – guest271314