Генерация целочисленных последовательностей с увеличением / уменьшением разницы между последовательными значениями - PullRequest
0 голосов
/ 15 сентября 2018

Я пытаюсь сгенерировать 2 последовательности целых чисел: одну, где разница в соседних значениях неуклонно увеличивается, и другую, где разница неуклонно уменьшается.

Пример желаемого вывода:

По возрастанию с возрастающей разницей: [1, 3, 6, 10, 15]

По убыванию с возрастающей разницей: [15, 13, 10, 6, 1]

По возрастанию с уменьшением разности: [1, 6, 10, 13, 15]

По убыванию с уменьшением разности: [15, 10, 6, 3, 1]

Приведенный ниже фрагмент кода создает массив, в котором разница в соседних значениях постоянно увеличивается (но пока не учитывает ввод значений останова). Я застрял на правильной математике для создания аналогичного массива, где различия в соседних значениях неуклонно уменьшаются.

const ranger = (start = 0, stop = 0, len = 1) => {
  let d = (start <= stop) ? 1 : -1;
  let step = Math.round(Math.abs(stop - start) / len);
  let arr = Array.from(Array(len), (x, i) => i + 1).map(
    x => (x !== 1) ? Math.round(start + d * x * step * (x / len)) : start
  );
  return arr;
}

console.log('asc', ranger(5, 100, 10));
console.log('desc', ranger(100, 5, 10));

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

Мое оригинальное решение было более сложным, чем нужно, и не могло обрабатывать нулевые и отрицательные значения (подробности см. В истории изменений). Ниже приведен несколько лучший подход к созданию массивов целочисленных последовательностей с увеличением или уменьшением различий между последовательными числами (хотя он все еще неэффективен).

const georange = (start, stop, len, rev = false) => {
  // requires non-negative value for len
  let adj = 0;
  if (start < 1 || stop < 1) {
    adj = start < stop ? 1 - start : 1 - stop;
    start += adj;
    stop += adj;
  }
  
  let step = Math.pow(stop / start, 1 / (len - 1));
  let range = Array(len).fill(start).map((x, i) => Math.round(x * Math.pow(step, i)));
  if (rev) {
    let steps = range.map((x, i, arr) => (i === 0) ? 0 : x - arr[i - 1]).reverse();
    range = steps.map((_, i, arr) => (i === 0) ? start : arr.slice(0, i).reduce((a, b) => a + b) + start);
  }
  
  if (adj) {
    range = range.map(x => x - adj);
  }
  
  return range;
}

let ascincr = georange(1, 15, 5);
console.log(ascincr);
// [1, 2, 4, 8, 15]

let ascdecr = georange(1, 15, 5, true);
console.log(ascdecr);
// [1, 8, 12, 14, 15]

let descincr = georange(15, 1, 5, true);
console.log(descincr);
// [15, 14, 12, 8, 1]

let descdecr = georange(15, 1, 5);
console.log(descdecr);
// [15, 8, 4, 2, 1]

В этом подходе используется базовая математика, лежащая в основе геометрических последовательностей , и немного поворачивает ее, чтобы генерировать либо восходящие последовательности с возрастающей разницей, либо нисходящие последовательности с уменьшающейся разницей (независимо от того, являются ли входные значения положительными или отрицательными - в отличие от нормальное поведение геометрической последовательности). Установка флага rev на true позволяет получить различия в последовательных терминах, создаваемых обычной геометрической последовательностью, и обратить их вспять, чтобы создать либо восходящие последовательности с уменьшающимися различиями, либо нисходящие последовательности с увеличивающимися различиями.

Этот пересмотр стал результатом рассмотрения простого подхода генерации диапазона, подобного приведенному ниже, и других аналогичных подходов из этого вопроса , а затем тестирования способов получения динамического значения шага из определенной длины (в отличие от наличия определенный шаг определяет длину).

const range = (start, stop, step) => {
  // requires positive step for ascending ranges and negative step for descending ranges
  return Array(Math.ceil((stop - start) / step)).fill(start).map((x, i) => x + i * step);
}

let result = range(1, 15, 3);
console.log(result);
// [1, 4, 7, 10, 13]
0 голосов
/ 15 сентября 2018

похоже, ваш метод определения вашего шага немного не в порядке.

Давайте сначала посмотрим, как определить правильный шаг.

Вот наш тестовый диапазон: [1,3,6,10,15]. Разница между числами составляет [2,3,4,5], мы можем представить различия как: x + 1, x + 2, x + 3, x + 4. поэтому мы можем объединить их в 4x + 10. Это наше общее число увеличилось, и оно равняется нашему стоп-старту (15 - 1), который равен 14.

так что наше пересмотренное уравнение 14 = 4x + 10, которое при решении дает нам x = 1;

4 здесь представляет количество шагов, а 10 является суммой количества шагов. мы можем использовать формулу Карла Гаусса, чтобы определить сумму. формула (n / 2) (первое число + последнее число) = сумма

здесь n - количество шагов. и в нашем уравнении число шагов всегда будет равно 1. 1. Последнее число всегда будет равно 1.

так что наша переведенная формула Гаусса равна (len - 1) / 2 * (len - 1 + 1)

Затем мы включаем это в нашу формулу, чтобы определить шаг: шаг = (Math.abs (стоп - старт) - (len - 1) / 2 * (len - 1 + 1)) / (len - 1)

ПРОПУСТИТЕ ЗДЕСЬ, ЕСЛИ ВЫ НЕ ЗАИНТЕРЕСОВАНЫ В МАТЕМАТИКЕ, ЧТОБЫ НАЙТИ ШАГ

Хорошо. Теперь, когда мы правильно нашли шаг. давайте посмотрим код.

   

 function ranger(start = 0, stop = 0, len = 1) {
      let incArray = [];
      let step = (Math.abs(stop - start) - (len - 1)/2*(len - 1 + 1))/(len - 1);
      console.log('step is', step);

      if(start < stop){
        for(var i = 0, currentValue = start; currentValue < stop; i++){
          //use default value on our first iteration of the loop
           if(i> 0){
             //we are grabbing the previous item in the array and adding 
             //the step plus i to it
             //for ranger(1, 15, 5)) when i = 1, step = 1, incArray[1], so   
             // current value = 1 + 1 + 1 = 3;
             currentValue = (i + step + incArray[incArray.length -1])
           }
           incArray.push(currentValue)    
        }
      }
      else{
        for(var i = len, currentValue = start; currentValue > stop; i--){
          if(i< len ){
            currentValue = (-(i + step) + incArray[incArray.length -1]) 
          }
          incArray.push(currentValue)
          prevValue = currentValue;     
        } 
      }
      return incArray;
    }

console.log('asc', ranger(1, 15, 5));
console.log('asc', ranger(1, 21, 6));
console.log('desc', ranger(15, 1, 5));
//now try a range with a step of 2:
console.log('asc', ranger(1, 19, 5));

// asc (5) [1, 3, 6, 10, 15]
// desc (5) [15, 10, 6, 3, 1]

Это всего лишь черновик, поэтому вы можете аккуратно выполнить рефакторинг с картой и заставить его работать более динамично для подъема и спуска.

...