在Java中的数组上滑动窗口

时间:2019-07-12 06:46:20

标签: javascript

我需要一个在Java数组上滑动的窗口。

以下是我的尝试,因为我找不到现成的解决方案:

function window(a,sz) { return a.map((_,i,ary)=> ary.slice(i,i+sz)).slice(0,-sz+1); }

它返回一个窗口数组,可以将其映射到各个窗口上。

什么是更好的解决方案?

4 个答案:

答案 0 :(得分:2)

通过它们的原型添加到本机JavaScript对象不是一个好主意。这可能会以意想不到的方式破坏事物,并给您和其他使用您的代码的人带来很多挫败感。在这种情况下,最好创建自己的函数。

要获得所需的功能,只需将数组传递给函数,然后从那里访问它。通过函数在数组上进行所需的方法调用。遵循KISS的原则,这里不再需要花哨的东西。

此外,请记住,将为数组的每个元素调用Array.map。这不是您真正需要的。如果目标是获得大小为n的滑动窗口,并且希望将每个窗口添加到新数组中,则可以使用如下函数:

var myArray = [1, 2, 3, 4, 5, 6, 7, 8];

function getWindows(arr, n){
if (n > arr.length)
{
   return arr;
}
var result = [];
let lastWindow = i < arr.length/n - n;
for (let i = 0; i < lastWindow; i += n)
{
   result.push(arr.slice(i, i + n));
}

return result;

}

因此,在这里,我们将获得一个窗口数组,这些窗口也是数组。调用console.log(getWindows(myArray, 3)),将得到以下输出:

0:Array(3)[1,2,3]

1:Array(3)[4,5,6]

2:Array(3)[7,8,9]

答案 1 :(得分:1)

使用JS ES6,您可以执行以下操作:

class SlidingWindow{
  constructor(windowSize) {
    this.deque = []; // for storing the indexex of the 'k' elements in the input 
    this.windowSize = windowSize;
  }

  compute(input){   
    let output = [];

    if(!input || input.length === 0) {
        return [];
    }

    if(input.length < this.windowSize) {
        return input;
    }

    for(let i=0; i < input.length; i++) {
        
        //if the index in the first element of the this.deque is out of bound (i.e. idx <= i-this.windowSize) then remove it
        if(this.deque.length > 0 && this.deque[0] === i-this.windowSize) {
            this.deque.shift(); //remove the first element
        }
                
        this.deque.push(i)
        
        if(i+1 >= this.windowSize) {
            output.push(this.deque.map(idx => input[idx]));
        }
    }
    return output;
  }
}

//Here is how to use it:
let slidingWindow = new SlidingWindow(3);

console.log('computed sliding windows: '+JSON.stringify(slidingWindow.compute([1,2,3,4,5,6,7,8,9])));

要计算每个滑动窗口的最大值,可以按以下方式自定义上面的代码:

class SlidingWindow{
  constructor(windowSize) {
    this.deque = []; // for storing the indexex of the 'k' elements in the input 
    this.windowSize = windowSize;
  }

  customCompute(input, processWindow, addOutput) {
    let output = [];

    if(!input || input.length === 0) {
        return [];
    }

    if(input.length < this.windowSize) {
        return input;
    }

    for(let i=0; i < input.length; i++) {
        
        //if the index in the first element of the this.deque is out of bound (i.e. idx <= i-this.windowSize) then remove it
        if(this.deque.length > 0 && this.deque[0] === i-this.windowSize) {
            this.deque.shift(); //remove the first element
        }

        processWindow(this.deque, input[i], input)

        this.deque.push(i)
        
        if(i+1 >= this.windowSize) {
            output.push(addOutput(this.deque, input));
        }
    }
    this.deque = [];
    return output;
  }
}

let slidingWindow = new SlidingWindow(3);

console.log('computed sliding windows: '+JSON.stringify(slidingWindow.compute([1,2,3,4,5,6,7,8,9])));

function processWindow(deque, currentElement, input){
  while(deque.length > 0 && currentElement > input[deque[deque.length -1]]) {
            deque.pop(); //remove the last element
        }
};
console.log('computed sliding windows maximum: '+JSON.stringify(slidingWindow.customCompute([1,3,-1,-3,5,3,6,7], processWindow, (deque, input) => input[deque[0]])));

答案 2 :(得分:0)

Array#reduce

避免.map后跟.slice()的合理选择是使用.reduce()生成窗口:

function toWindows(inputArray, size) { 
  return inputArray
    .reduce((acc, _, index, arr) => {
      if (index+size > arr.length) {
        //we've reached the maximum number of windows, so don't add any more
        return acc;
      }
      
      //add a new window of [currentItem, maxWindowSizeItem)
      return acc.concat(
        //wrap in extra array, otherwise .concat flattens it
        [arr.slice(index, index+size)]
      );
      
    }, [])
}

const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];

//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2))); 
console.log(JSON.stringify(toWindows(input, 3))); 
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));



//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
  .map(window => window.reduce((a,b) => a+b)) //sum
  .filter(x => x%2 === 1) //get odd
  .reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
  
console.log(output)

然后可以根据需要将其缩短:

function toWindows(inputArray, size) { 
  return inputArray
    .reduce(
      (acc, _, index, arr) => (index+size > arr.length) ? acc : acc.concat([arr.slice(index, index+size)]),
      []
    )
}

const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];

//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2))); 
console.log(JSON.stringify(toWindows(input, 3))); 
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));



//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
  .map(window => window.reduce((a,b) => a+b)) //sum
  .filter(x => x%2 === 1) //get odd
  .reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
  
console.log(output);

Array.from

可以使用Array.from来简化方法,首先生成具有适当长度的数组,然后使用生成的窗口填充该数组:

function toWindows(inputArray, size) {
  return Array.from(
    {length: inputArray.length - (size - 1)}, //get the appropriate length
    (_, index) => inputArray.slice(index, index+size) //create the windows
  )
}

const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];

//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2))); 
console.log(JSON.stringify(toWindows(input, 3))); 
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));



//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
  .map(window => window.reduce((a,b) => a+b)) //sum
  .filter(x => x%2 === 1) //get odd
  .reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
  
console.log(output)

Generator

另一种替代方法是使用生成器函数,而不是预先计算所有窗口。这对于使用滑动窗口方法进行更懒惰的评估可能很有用。如果需要,您仍然可以使用Array.from计算所有窗口:

function* windowGenerator(inputArray, size) { 
  let index = 0;
  
  while(index+size <= inputArray.length) {
    yield inputArray.slice(index, index+size);
    
    index++;
  }
}

function toWindows(inputArray, size) {
  //compute the entire sequence of windows into an array
  return Array.from(windowGenerator(inputArray, size))
}

const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];

//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2))); 
console.log(JSON.stringify(toWindows(input, 3))); 
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));



//somewhat more realistic usage:
//find the sum closest to a target number when adding three numbers at a time
const veryLargeInput = [17, 95, 27, 30, 32, 38, 37, 67, 53, 46, 33, 36, 79, 14, 19, 25, 3, 54, 98, 11, 68, 96, 89, 71, 34, 31, 28, 13, 99, 10, 15, 84, 48, 29, 74, 78, 8, 90, 50, 49, 59, 18, 12, 40, 22, 80, 42, 21, 73, 43, 70, 100, 1, 44, 56, 5, 6, 75, 51, 64, 58, 85, 91, 83, 24, 20, 72, 26, 88, 66, 77, 60, 81, 35, 69, 93, 86, 4, 92, 9, 39, 76, 41, 37, 63, 45, 61, 97, 2, 16, 57, 65, 87, 94, 52, 82, 62, 55, 7, 23];
const targetNumber = 100;

console.log(`-- finding the closest number to ${targetNumber}`)

const iterator = windowGenerator(veryLargeInput, 3);

let closest = -1;

for (const win of iterator) {
  const sum = win.reduce((a, b) => a+b);
  
  const difference = Math.abs(targetNumber - sum);
  const oldDifference = Math.abs(targetNumber - closest);
  
  console.log(
    `--- evaluating: ${JSON.stringify(win)}
    sum: ${sum},
    difference with ${targetNumber}: ${difference}`
  );
  
  if (difference < oldDifference) {
    console.log(`---- ${sum} is currently the closest`);
    closest = sum;
    
    if (difference === 0) {
      console.log("----- prematurely stopping - we've found the closest number")
      break;
    }
  }
  
}

console.log(`-- closest sum is: ${closest}`)

答案 3 :(得分:0)

您考虑过递归吗?

  • l是每个窗口的大小
  • xs是您的列表
  • i是我们需要进行的迭代次数,xs.length - l
  • out包含结果

可以使用xs.slice(i, i + l)获得切片。在每次递归时,i都会递增,直到i到达下一个切片将包含少于l个元素的地步。

const windows = (l, xs, i = 0, out = []) =>
  i > xs.length - l
    ? out
    : windows(l, xs, i + 1, [...out, xs.slice(i, i + l)]);


console.log(windows(3, [1,2,3,4,5,6,7,8,9]));

还有一个flatMap的非递归解决方案。

使用flatMap,您可以在每次迭代时返回一个数组,它将在最终结果中展平:

const duplicate = xs => xs.flatMap(x => [x, x]);
duplicate([1, 2]);
//=> [1, 1, 2, 2]

因此,您可以返回切片(用[]包装),直到i超过限制xs.length - l

const windows = (l, xs) =>
  xs.flatMap((_, i) =>
    i <= xs.length - l
      ? [xs.slice(i, i + l)]
      : []);

console.log(windows(3, [1,2,3,4,5,6,7,8,9]))


请注意,在诸如之类的某些库中,这称为aperture

  

返回一个新列表,该列表由n个连续元素的元组组成。如果n大于列表的长度,则返回一个空列表。

aperture(3, [1,2,3,4,5,6,7,8,9]);
//=> [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9]]

您可以看到以前有几个人有相同的问题: