I am trying to break a 1D array into a 2D array where the sub-arrays are of varying lengths. This variance should follow the gaussian curve [or a mound shape]. So, say the 2D array variable we make is named gaussianCurve. The array within gaussianCurve[0] & gaussianCurve[n] would be of length 1, and gaussianCurve[n/2] would be a maximum provided by a parameter "maxArrayLength". This forces the number of gaussianCurve indexes to become variable.
Say I have the following psuedo-code:
function (oneDimentionalArray, maxArrayLength) {
// oneDimentionalArray is ["A","B","C","D","E","F","G","H","I","J","K"]
// maxArrayLength is 5
// Currently working like this (i.e. "batches"):
// return [["A","B","C","D","E"],["F","G","H","I","J"],["K"]]
// would LIKE it to work like this
gaussianCurve = []
gaussianCurve.push(["A"])
gaussianCurve.push(["B", "C"])
gaussianCurve.push(["D", "E", "F", "G", "H"])
gaussianCurve.push(["I", "J"])
gaussianCurve.push(["K"])
return gaussianCurve
}
Why would I want such a thing? Progress bars.
Any suggestions are welcome. I am just not seeing the answer in my minds eye.
EDIT: I feel it was worded poorly, so I am rewording it.
...gaussianCurve[0].length & gaussianCurve[gaussianCurve.length - 1].length would be 1, and gaussianCurve[gaussianCurve.length/2].length would be up to "maxArrayLength".
INPUT:
function gaussianRefactor(["A","B","C","D","E","F","G","H","I","J","K"], 1)
function gaussianRefactor(["A","B","C","D","E","F","G","H","I","J","K"], 2)
function gaussianRefactor(["A","B","C","D","E","F","G","H","I","J","K"], 4)
function gaussianRefactor(["A","B","C","D","E","F","G","H","I","J","K"], 8)
function gaussianRefactor(["A","B","C","D","E","F","G","H","I","J","K"], 16)
OUTPUT:
[["A"],["B"],["C"],["D"],["E"],["F"],["G"],["H"],["I"],["J"],["K"]]
[["A"],["B","C"],["D","E"],["F","G"],["H","I"],["J"],["K"]]
[["A"],["B","C","D"],["E","F","G","H"],["I","J","K"]]
[["A"],["B","C","D","E","F","G","H","I"],["J","K"]]
[["A","B","C","D","E","F","G","H","I","J","K"]]
No inner array may exceed the length of maxArrayLength
This was more inline with what I was thinking. I greatly dislike the way I am finding sigma. I know I should just reorder the formula to calculate it, but I have yet to get that to work. Anyway, here is the, "answer" though it fails for the smaller arrays I provided as examples in the question, it successfully does what I needed to do. If anyone has improvements they'd like to add, just let me know.
var gaussianRefactor = function(srcOneDimentionalArray, srcMaxArrayLength) {
var finalArray = [];
if (srcOneDimentionalArray.length <= srcMaxArrayLength) {
finalArray.push(srcOneDimentionalArray);
return finalArray;
}
if (srcMaxArrayLength === 1) {
for(var lengthOne = 0; lengthOne < srcOneDimentionalArray.length; lengthOne++)
finalArray.push([srcOneDimentionalArray[lengthOne]]);
return finalArray;
}
var maxArrayLength = srcMaxArrayLength;
var oneDimentionalArray = srcOneDimentionalArray.slice(0);
for (var x = srcMaxArrayLength; x > 1 && maxArrayLength / oneDimentionalArray.length > 0.3333; x--) {
maxArrayLength--;
}
var standardChunkSize = srcOneDimentionalArray.length / maxArrayLength;
var predictedSize = (3 * Math.floor(standardChunkSize)) % 2 === 0 ? 3 * Math.floor(standardChunkSize) + 1 : 3 * Math.floor(standardChunkSize);
var predictedSizeCenter = Math.ceil(predictedSize / 2);
var sigma = 0.2034185 * Math.pow(standardChunkSize, 1.963449);
var multiplicand = 1 / (Math.sqrt(sigma) * Math.sqrt(2 * Math.PI));
var centerGauss = maxArrayLength / multiplicand;
var mu = 0;
var delta;
var fraction;
var exponent;
var full;
var subArrayLength;
var subArray;
var notWideEnough = true;
var maxElements;
var maxAttempts = Math.max(Math.ceil(sigma), 100);
var currentAttempts = 0;
while (notWideEnough && currentAttempts < maxAttempts) {
maxElements = 0;
for (var j = 0; j < predictedSize; j++) {
delta = (j - predictedSizeCenter) - mu;
fraction = delta / Math.sqrt(sigma);
exponent = -0.5 * Math.pow(fraction, 2);
full = multiplicand * Math.exp(exponent);
subArrayLength = Math.floor(full * centerGauss);
maxElements += subArrayLength;
}
if (maxElements >= srcOneDimentionalArray.length) {
notWideEnough = false;
} else {
sigma = sigma + sigma * 0.05;
}
currentAttempts++;
}
if (currentAttempts === maxAttempts) {
return false;
}
for (var i = 0; i < predictedSize; i++) {
delta = (i - predictedSizeCenter) - mu;
fraction = delta / Math.sqrt(sigma);
exponent = -0.5 * Math.pow(fraction, 2);
full = multiplicand * Math.exp(exponent);
subArrayLength = Math.floor(full * centerGauss);
if (subArrayLength < 1 || oneDimentionalArray.length < 1) {
continue;
}
subArray = oneDimentionalArray.slice(0, subArrayLength);
oneDimentionalArray = oneDimentionalArray.slice(subArrayLength, oneDimentionalArray.length);
finalArray.push(subArray);
}
return finalArray;
}
INPUT
gaussianRefactor(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"], 1)
gaussianRefactor(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"], 2)
gaussianRefactor(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"], 4)
gaussianRefactor(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"], 8)
gaussianRefactor(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"], 16)
OUTPUT
[["A"],["B"],["C"],["D"],["E"],["F"],["G"],["H"],["I"],["J"],["K"]]
[["A"],["B"],["C"],["D"],["E"],["F","G"],["H"],["I"],["J"],["K"]]
[["A"],["B"],["C","D"],["E","F","G"],["H","I"],["J"],["K"]]
[["A"],["B"],["C","D"],["E","F","G"],["H","I"],["J"],["K"]]
[["A","B","C","D","E","F","G","H","I","J","K"]]
-- EDIT 2021 -- https://jsfiddle.net/brightmatter_og/xzfwjeaq/ I found the answer I was seeking. I will put it here if anyone is interested.
function removeMeFromTheList(removeMe, theList) {
for (let i = 0; i < theList.length; i++) {
if (theList[i] === removeMe) {
theList.splice(i, 1);
return;
}
}
}
function makeListOfGaussianLists(chunkWeights, objects) {
const listOfLists = [];
const chunkWeightsSize = Object.keys(chunkWeights).length;
if (chunkWeightsSize < 1 || objects.length < chunkWeightsSize) {
console.info("chunkWeights.length:" + chunkWeightsSize + " - objects.length:" + objects.length);
return null;
}
const elementCount = objects.length / chunkWeightsSize;
let modifiedElementCount = null;
for (let i = 0; i < chunkWeightsSize; i++) {
modifiedElementCount = parseInt(chunkWeights[i] * elementCount);
let innerList = [];
for (let j = modifiedElementCount; j > 0; j--) {
const obj = objects[0];
if (obj == null) {
break;
}
innerList.push(obj);
removeMeFromTheList(obj, objects);
}
listOfLists.push(innerList);
}
if (objects.length > 0) {
do {
for (let i = 0; i < listOfLists.length; i++) {
const obj = objects[0];
if (obj == null) {
break;
}
listOfLists[i].push(obj);
removeMeFromTheList(obj, objects);
}
} while (objects.length > 0);
}
return listOfLists;
}
function subListWeightBuilder(partitionCount) {
const gauss = [];
const population = 250;
const chunkWeights = {};
if (partitionCount > population) {
return chunkWeights;
}
for (let i = 0; i < population * 2; i++) {
gauss.push(randomGaussian());
}
gauss.sort(function(a, b){return a - b});
const partitionWidth = gauss.length / partitionCount;
let currentCount = 0;
let chunkWeightsIndex = 0;
let pastTheFirst = false;
for (let j = 0; j < gauss.length; j++) {
if (currentCount < partitionWidth) {
chunkWeights[chunkWeightsIndex] = 1 / (Math.abs(gauss[j]) + 0.66);
} else {
chunkWeightsIndex++;
currentCount = 0;
}
currentCount++;
}
let offset = 0;
for (let key in chunkWeights) {
offset += chunkWeights[key];
}
offset /= partitionCount;
offset = (1 - offset) + Number.MIN_VALUE;
for (let key in chunkWeights) {
let value = chunkWeights[key];
chunkWeights[key] = value + offset;
}
return chunkWeights;
}
function randomGaussian() {
let r = 0;
const iteration = 6;
for (let i = iteration; i > 0; i--) {
r += Math.random();
}
return r / iteration - 0.5;
}
function pressButton() {
let partitionCount = 23;
console.info(makeListOfGaussianLists(subListWeightBuilder(partitionCount), makeRandomString(333).split("")));
partitionCount = 41;
console.info(makeListOfGaussianLists(subListWeightBuilder(partitionCount), makeRandomString(666).split("")));
partitionCount = 67;
console.info(makeListOfGaussianLists(subListWeightBuilder(partitionCount), makeRandomString(1000).split("")));
partitionCount = 41;
console.info(makeListOfGaussianLists(subListWeightBuilder(partitionCount), makeRandomString(1333).split("")));
partitionCount = 23;
console.info(makeListOfGaussianLists(subListWeightBuilder(partitionCount), makeRandomString(1666).split("")));
}
function makeRandomString(size) {
const loops = parseInt(size / 11);
let theResult = "";
for(let i = loops; i > 0; i--) {
theResult += Array(11 + 1).join((Math.random().toString(36) + '00000000000000000').slice(2, 18)).slice(0, 11);
}
return theResult += Array((size - 11 * loops) + 1).join((Math.random().toString(36) + '00000000000000000').slice(2, 18)).slice(0, (size - 11 * loops))
}