<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pack builder</title>
<!--<link rel="stylesheet" href="***.css">-->
<style>
body {
padding-top: 60px;
margin-left: 10px;
}
button {
margin-top: 10px;
margin-right: 0px;
margin-bottom: 10px;
margin-left: 0px;
padding-top: 5px;
padding-right: 10px;
padding-bottom: 5px;
padding-left: 10px;
}
textarea {
margin-top: 10px;
margin-right: 0px;
margin-bottom: 10px;
margin-left: 0px;
padding-top: 5px;
padding-right: 10px;
padding-bottom: 5px;
padding-left: 10px;
}
</style>
</head>
<body>
Cells Per Pack (id = cellsPerPack) :
<input type="text" id="cellsPerPack" value="12">
<br/>
<br/>
Cells (separated by space) (id = cells) : <br/>
<textarea style="width:300px;height:200px;" type="text" id="cells">3602, 3990, 3615, 4227, 3940, 4195, 3700, 3569, 4012, 3948, 4236, 4200, 4249, 4239, 4300, 3595, 4008, 3719, 4007, 3612, 3711, 4010, 3740, 4189, 4199, 4008, 4161, 3685, 4233, 3633, 4265, 3659, 4151, 3584, 3581, 4295, 3635, 3615, 3720, 4168, 3646, 3581, 4175, 3555, 4258, 4248, 3962, 4280, 4274, 4282, 3738, 3631, 3617, 4265, 3688, 4208, 3595, 4179, 4298, 4230, 4156, 4288, 4275, 4239, 4253, 3943, 3667, 3639, 3625, 4157, 3642, 4298, 4228, 3940, 3760, 3552, 4228, 3703, 4297, 3719, 3703, 3619, 3643, 4013
</textarea>
<br/>
<button id="optimizeBtn">Optimize</button>
<br/>
Optimized Pack (id = optimizedCells) <br/>
<textarea style="width:500px;height:510px;" type="text" id="optimizedCells"></textarea>
<script >
/*
The MIT License (MIT)
Copyright (c) 2016 Dex Wood
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var optimizeBtn = document.getElementById("optimizeBtn");
var cellsPerPackInput = document.getElementById('cellsPerPack');
var cellsInput = document.getElementById("cells");
optimizeBtn.onclick = function () {
var cellsPerPack = parseInt(cellsPerPackInput.value); //console.log("*** cellsPerPack " + cellsPerPack);
var cells = _.map(cellsInput.value.split(","), function (c) {return parseInt(c);}); //console.log("*** cells " + cells); // split string by comma
var numPacks = cells.length / cellsPerPack; //console.log("*** numPacks " + numPacks);
var averageDiv = parseInt(sum(cells) / numPacks); //console.log("*** averageDiv " + averageDiv);
var solution = simulatedAnnealingBatteries(cells, cellsPerPack, averageDiv); //console.log("*** solution " + solution);
var optimizedCellsInput = document.getElementById("optimizedCells");
var solutionPacks = multiPartition(solution, cellsPerPack);
var solutionOutput = ""; //console.log("*** Cost: " + cost(solution, numPacks, averageDiv));
solutionOutput += "Optimized Packs\n"; // a += b -> a = a + b
solutionOutput += "---------------\n\n";
solutionOutput += "Pack average: " + parseInt(averageDiv) + "\n\n";
for (var idx in solutionPacks) {
var packNum = parseInt(idx) + 1;
solutionOutput += "Pack " + packNum + "\n";
solutionOutput += JSON.stringify(_.sortBy(solutionPacks[idx], function (d)
{return -d;
})) + "\n"; console.log("*** OutPut1 " + solutionOutput); //console.log("*** Sum " + sum(solutionPacks[idx]));
solutionOutput += "Verschil : " + parseInt(sum(solutionPacks[idx]) - averageDiv) + "\n\n";
}
optimizedCellsInput.value = solutionOutput; //console.log("*** OutPut " + solutionOutput);
}
// -----------------------------------------
// FUNCTIES
// -----------------------------------------
function multiPartition(cells, n) {
var partitions = [];
var packs = (cells.length / n);
for (var i = 0; i < packs; i++) {
partitions.push([]);
}
for (var idx in cells) {
partitions[idx % packs].push(cells[idx]);
}
return _.filter(partitions, function (p) {
return p.length > 0;
})
}
function cost(solution, numPacks, averageDiv) {
var packDivision = multiPartition(solution.slice(), numPacks);
var totalCost = _.map(packDivision, function (p) {
return Math.abs(sum(p) - averageDiv);
});
return sum(totalCost);
}
function acceptanceProb(curCost, cost, temperature) {
return Math.pow(Math.E, (curCost - cost) / temperature);
}
function sum(items) {
return _.reduce(items, function (a, b) {
return a + b;
});
}
/* Simulated annealing -> https://nl.wikipedia.org/wiki/Simulated_annealing
JavaScript Assignment Operators
Operator Example Same As
---------------------------------------
= x = y x = y
+= x += y x = x + y
-= x -= y x = x - y
*= x *= y x = x * y
/= x /= y x = x / y
%= x %= y x = x % y
Number.MAX_VALUE the largest possible number in JavaScript and VBA 1.7976931348623157e+308
Math.random() returns a random number between 0 and 1 0.7290565902133195
slice() The slice() method returns the selected elements in an array, as a new array object
split() The split() method is used to split a string into an array of substrings, and returns the new array. Does not change the original string
_.sortBy The _.sortBy() function is used to sort all the elements of the list in ascending order according to the function given to it as a parameter.
*/
function simulatedAnnealingBatteries(cells, numPacks, avgDiv) {
var currentSolution = cells.slice(); //console.log("*** currentSolution " + currentSolution); //
var curCost = Number.MAX_VALUE; //console.log("*** curCost " + curCost); //
var minTemperature = 1e-6; //console.log("*** minTemperature " + minTemperature); // 0.000001
var temperature = 1.0; //console.log("*** temperature " + temperature); // 1
var cooling = 0.93; //console.log("*** cooling " + cooling); // 0.93
while (temperature > minTemperature) {
for (var i = 0; i < 100; i++) { // loop through a block of code 100 times
var neighboringSolution = currentSolution.slice(); //console.log("*** ("+ i +") neighboringSolution " + neighboringSolution);
var first = parseInt(Math.random() * cells.length); //console.log("*** ("+ i +") first " + first); //
var second = parseInt(Math.random() * cells.length); //console.log("*** ("+ i +") second " + second);
var tmp = neighboringSolution[first]; //console.log("*** ("+ i +") tmp " + tmp);
neighboringSolution[first] = neighboringSolution[second]; //console.log("*** ("+ i +") neighboringSolution[first] " + neighboringSolution[first]);
neighboringSolution[second] = tmp; //console.log("*** ("+ i +") neighboringSolution[second] " + neighboringSolution[second]);
var newCost = cost(neighboringSolution, numPacks, avgDiv); //console.log("*** ("+ i +") newCost " + newCost);
var prob = acceptanceProb(curCost, newCost, temperature); //console.log("*** ("+ i +") prob " + prob);
if (prob > Math.random()) {
currentSolution = neighboringSolution;
curCost = newCost;
}
}
temperature *= cooling; //console.log("*** |||||| *** ("+ i +") temperature " + temperature); // x = x * y
}
//console.log("*** loop i = " + i);
return currentSolution;
}
//-------------------------------------------------------------------------------------
// Underscore.js 1.9.1
// http://underscorejs.org
// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
//-------------------------------------------------------------------------------------
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this ||
{};
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
// Naked function reference for surrogate-prototype-swapping.
var Ctor = function(){};
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for their old module API. If we're in
// the browser, add `_` as a global object.
// (`nodeType` is checked to ensure that `module`
// and `exports` are not HTML elements.)
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
// The 2-argument case is omitted because we’re not using it.
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
var builtinIteratee;
// An internal function to generate callbacks that can be applied to each
// element in a collection, returning the desired result — either `identity`,
// an arbitrary callback, a property matcher, or a property accessor.
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
return _.property(value);
};
var shallowProperty = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object.
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = shallowProperty('length');
var isArrayLike = function(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
// Return the results of applying the iteratee to each element.
_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Create a reducing function iterating left or right.
var createReduce = function(dir) {
// Wrap code that reassigns argument variables in a separate function than
// the one that accesses `arguments.length` to avoid a perf hit. (#1991)
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (!initial) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
return function(obj, iteratee, memo, context) {
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
_.reduce = _.foldl = _.inject = createReduce(1);
// Return all the elements that pass a ****h test.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// Sort the object's values by a criterion produced by an iteratee.
_.sortBy = function(obj, iteratee, context) {
var index = 0;
iteratee = cb(iteratee, context);
return _.pluck(_.map(obj, function(value, key, list) {
return {
value: value,
index: index++,
criteria: iteratee(value, key, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
// Utility Functions
// -----------------
// Creates a function that, when passed an object, will traverse that object’s
// properties down the given `path`, specified as an array of keys or indexes.
_.property = function(path) {
if (!_.isArray(path)) {
return shallowProperty(path);
}
return function(obj) {
return deepGet(obj, path);
};
};
}());
</script>
</body>
</html>