Home > database >  Elegant way to reduce list by factor of 2 by combining neighboring elements
Elegant way to reduce list by factor of 2 by combining neighboring elements

Time:01-01

I have a list of numbers (sorted ascending) and I want to halve the list several times, and combine neighbors, something like this:

#!/usr/bin/env ts-node
'use strict';

const goFrom_131072_to_1024 = (v: number[]) => {

  let ret = [...v].sort();

  const reduceByHalf = (v: any[]) => {
    return v.reduce((a,b,currentIndex) => {
      
      if(currentIndex % 2 !== 0){
         return [a[0],b]
      }
      
      a[0].push((a[1]   b)/2); // take average of 2 neighbors
      return [a[0], null];

    },[[], null]);
  };

  while(ret.length > 1024){
    console.log(ret.length);
    ret = reduceByHalf(v)[0];
  }

  return ret;
}

console.log(
  goFrom_131072_to_1024(
    new Array(131072).fill(null).map((v,i) => i)
  )
);

when I run it (with ts-node), gets stuck at 65536, not really sure why. There is also one potential bug where if the list had a length that's an odd number, the last element would get lost (but it's an even numbered length so fine for now). Is there a more elegant way to do this?

(Ultimately the reason I am doing this is that the numbers in the list represent a histogram (probability distribution) and I want to represent the population with a smaller sample. Combining (in this case, averaging) neighbors might be a relatively crude way to do this, will take advice on better ways.)

CodePudding user response:

I'm not sure what you would call "elegant". Why not use a bigger average group? If you want to reduce 131072 by a factor of 128, then you could just make average of 128 numbers.

For example, if you do:

[1, 2, 3, 4, 5, 6, 7, 8]

And you want to reduce the list of 8 elements to 2 elements, you would make average groups of 4.

[[1, 2, 3, 4], [5, 6, 7, 8]]

Which would be:

[ (1 2 3 4)/4 ], [ (5 6 7 8)/4 ]

Which would get you the same result:

[ 2.5, 6.5 ]

So now, all you need to do is groups of 2^n and get the average of those groups.

function groupBy(list, t){
    let grouped = [];
    let max = Math.ceil(list.length/t);
    for(let i = 0; i < max; i  ){
        grouped.push(list.slice(i*t, (i 1)*t));
    }
    return grouped;
}

function average(list){
   return list.reduce((a, b) => a   b)/list.length;
}

function reduceByFactorOfN(list, factor){
   return groupBy(list, 2**factor).map(average);
}
  • Related