Aggregating JavaScript array into several arrays with different element lengths? - javascript

I have an array with 101 values (representing people aged 0-100).
What would be a preferably fast and simple way of building these aggregated arrays in one go:
var input = [55,33,12 .. 98 more]
var output = {
//same as input
i1 = [],
//0-5, 6-10, 11-15 ... 96-100
i5 = [],
//0-10, 11-20, 21-30 ... 91-100
i10 = [],
//0-20, 21-40, 41-60 ... 81-100
i20 = [],
}
On a side note: Would you name these aggregate arrays by the interval ("i1", "i5") or by the number of groups/elements ("g100", "g20") - what is more intuitive if another programmers comes across these definitions?

You can reuse results of the aggregation to calculate the next array.
// sums up each n numbers from the input array
//
function groupSum(inarray, n) {
var outarray = [];
var sum = 0;
for (var i = 0; i < inarray.length; i++) {
sum += inarray[i];
if (i % n == n - 1) {outarray.push(sum); sum = 0;}
}
// add the last element
if (i % n != 0) { outarray.push(sum); }
return outarray;
}
var input = [55, 33, 12, 98, /* more numbers here */ 3, 4, 1, 2, 0, 7];
var output = {};
output.i1 = input;
output.i5 = groupSum(output.i1, 5);
output.i10 = groupSum(output.i5, 2);
output.i20 = groupSum(output.i10, 2);
Note that, as xanatos said, performance is not really of big concern here.
PS1: was not sure if you were trying to make output an object (as in this code) or 2D array.
PS2: since your first group always has 1 more element, you might need to adjust the code a bit for this special case.

Related

How can I produce a distance matrix for large datasets using Google Script?

I am currently producing a script that will compare a list of around 90 addresses to each other. The result of the script should be a list that contains the time taken to travel to each address from each other.
I've run into a series of issues whilst trying to resolve this. The main issue is that resulting distance matrix will have 8100 elements. Google script's max execution time is 30 minutes and thus the script keeps timing out.
Any ways that I can improve the script to make it run faster?
The aim of this script is to produce a list with StartID, EndID and Time. I would then be able to filter the list to find addresses within an hour of each other.
Thanks!
function maps(origin, destination) {
var driving = Maps.DirectionFinder.Mode.DRIVING
var transit = Maps.DirectionFinder.Mode.TRANSIT
var modeSet = driving
var directions = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.setMode(modeSet)
.setOptimizeWaypoints(true)
.getDirections()
var result = directions
return result;
}
function GoogleMaps() {
//get distance
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ABC");
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("EFG");
var lastrow = sheet.getLastRow();
var lastcolumn = sheet.getLastColumn();
var range = sheet.getRange(2, 3, lastrow-1, 3);
//var range = sheet.getRange(2, 3, 3, 3);
//Origin is in row 2, column 3
var values = range.getValues();
var output = []
for (var i = 0; i < values.length; ++i)
{
var loop1 = values[i]
var start = values[i][1]
var startId = values[i][0]
for (var j = 0; j < values.length; j++) {
var loop2 = values[j]
var end = values[j][1]
var endId = values[j][0]
var result = maps(start, end)
var status = result.status
try{
var time = result.routes[0].legs[0].duration.value / 60;
var row = [startId, endId, time]
output.push(row)
} catch(err){
Logger.log(err);
}
}
}
var outputLength = output.length
var outputRange = outputSheet.getRange(1,1,outputLength,3);
outputRange.setValues(output);
}
EDIT: updated number of elements in list
The first thing you want to do is reduce the number of operations you execute in your for loops. So let's start with analyzing that first, but from an algorithmic perspective.
In your current implementation, you're basically calculating the Cartesian Product on a set of 90 values to produce a new set consisting of 8100 values.
However, there are a number of redundant values in that result set, such that:
The result set includes calculations where the same address is used as both the starting and ending location.
The distance between 2 addresses is calculated twice; such that address A is the start address and address B is the end address and in another iteration address A is the end address and address B is the start address.
CAVEAT: I'm making the assumption that you cover the same distance during transit between two addresses regardless of one's transit
direction (ie. A-to-B or B-to-A). That may not be the case in your
scenario.
You can eliminate those redundancies by using an area of discrete mathematics called combinatorics; more specifically using this lovely formula:
If we let n = 90 and r = 2 we get the following:
That means, at our most optimal, we need an algorithm that produces no more than 4005 address pairs.
With that as our goal, [cracks fingers] its time to write a more optimal algorithm! But for illustrative purposes and in the interest of brevity lets use a smaller sample size of 4 addresses made up of one letter. The following array should suffice:
var addresses = ['a', 'b', 'c', 'd'];
Using the aforementioned formula we deduce there are 6 unique address pairs, which we can represent as follows:
ab bc cd
ac bd
ad
So how does one generate those pairs?
If you look at the representation above you'll notice a few things:
The number of columns is one less than the number of addresses in the array
With each successive column (from left to right) the number of address pairs per column is reduced by 1; ie. there are 3 pairs that start with 'a', 2 that start with 'b', 1 that starts with 'c'.
Also note, that as you progress from one column to the next, successive columns do not have any pairs with the starting character of the previous columns; ie. the 2nd column does not have any pairs starting with 'a' and the 3rd column does not have any pairs starting with 'a' or 'b'
Let's generalize these observations. Given an array of n addresses we can generate n - 1 columns. The length of each column shrinks by 1 such that the first column has n - 1 pairs, the 2nd column has n - 2 pairs, the 3rd column n - 3 pairs etc., where each column consist of pair combinations that omit addresses from previous columns.
Based on those rules we can set up a for loop as follows (run the script and it will generate a collection of objects whose 'start' and 'end' properties represent unique address pairs):
var addresses = ['a', 'b', 'c', 'd'];
var pairs = [];
var numColumns = addresses.length - 1;
var columnHeight;
var columnIndex;
var rowIndex;
for (columnIndex = 0; columnIndex < numColumns; columnIndex++) {
columnHeight = numColumns - columnIndex;
for (rowIndex = 0; rowIndex < columnHeight; rowIndex++) {
pairs.push({
"start":addresses[columnIndex],
"end":addresses[columnIndex + rowIndex + 1]
});
}
}
console.log(pairs);
So the above handles algorithmic optimizations, you'll need to tweak it for use with your implementation but it should serve as a good jumping-off point. However, while generating 4005 address pairs is relatively quick, processing those address pairs to find distance traveled via the Map API will likely be time intensive.
In the event that you still manage to exhaust the 30 minute script execution quota, you may want to consider using batch processing techniques, where you setup up your application to do calculations on smaller batches of address pairs, one batch at a time over a given period. You might even be able to process multiple batches concurrently if you setup your application correctly. But that's a post for another time.
This is perhaps not any better than what you have for performance but try to break it down into a more modular solution here, then you can decide which part to optimize perhaps by doing it in some subset at a time;
function getValuesArray(values) {
let valueArray = [];
for (let i = 0; i < values.length; ++i) {
valueArray.push({
id: values[i][0],
value: values[i][1]
});
}
return valueArray;
}
function GoogleMaps() {
//get distance
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ABC");
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("EFG");
var lastrow = sheet.getLastRow();
var lastcolumn = sheet.getLastColumn();
var range = sheet.getRange(2, 3, lastrow - 1, 3);
//var range = sheet.getRange(2, 3, 3, 3);
//Origin is in row 2, column 3
var values = range.getValues();
var output = [];
let list1 = getValuesArray(values);
// deep clone
const clone = (items) => items.map(item => Array.isArray(item) ? clone(item) : { ...item
});
// might only need list1 but usin two for clarity here
const list2 = clone(list1);
const listWork = [];
for (var a = 0; a < list1.length; a++) {
for (var j = 0; j < list2.length; j++) {
listWork.push({
dest: list2[j].value,
destId: list2[j].id,
origin: list1[a].value,
originId: list1[a].id
}
}
}
}
let results = [];
for (let w = 0; w < listWork.length; w++) {
results.push(startId: listWork.originId, endId: listWork.destId, map: maps(listWork.origin, listWork.dest));
}
for (let r = 0; r < results.length; r++) {
let result = results[r];
// seems to not be used
//var status = result.map.status;
let route = !!result.map.routes && result.map.routes[0] ? result.map.routes[0] : null;
if (route !== null &&
route.legs &&
route.legs[0] &&
route.legs[0].duration &&
route.legs[0].duration.value) {
let time = route.legs[0].duration.value / 60;
let row = [result.startId, result.endId, time];
output.push(row);
}
}
let outputLength = output.length;
let outputRange = outputSheet.getRange(1, 1, outputLength, 3);
outputRange.setValues(output);
}

Random Selection from Array into new Array

I have seen this question answered here, but I have an additional question that is related.
Im trying to achieve:
the same thing, but, with the output being a selection of more than 1 number, the above works fine if you only want a single value returned.
How can I return (x) amount of outputs #7 in this case into a new var or array ...? Some guidance on best practice will also be appreciated ;)
Thanks a bunch....
Just for fun,
Objective:
Create a teeny weenie web App that returns 7 variable numbers in a range [ 1 - 49 ] into an array.
`
Think return a list of Lotto Numbers
Create new array from selection using _underscore.js [Sample function]
**** I know this is easier, but im trying to get an understanding
of using Vanilla JS to accomplish this
_.sample([1, 2, 3, 4, 5, 6], 3); => [1, 6, 2]
var getLoot = Array.from(Array(50).keys()) // Create array of 49 numbers.
console.info(getLoot);
var pick = getLoot[Math.floor(Math.random() * getLoot.length)];
pick;
// pick returns a single which is fine if you want a single but, ..
// I want something like this :
var pick = function() {
// code that will return 7 numbers from the array into a new Array
// will randomize every time pick is called...
}
If you want to return more than just 1 value you can store your results into a data structure like an array. Here is a solution to the problem
assuming you can pass in your array of 50 numbers into the pick() funciton.:
var getRandomArbitrary = function(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
var pick = function(fiftyNumberArray, numberOfValuesWanted) {
var randomNums = [];
for(var i = 0; i < numberOfValuesWanted; i++) {
randomNums.push(
fiftyNumberArray[getRandomArbitrary(0, 50)]
);
}
return randomNums;
};
var fiftyNumbers = [] // <- set your array of fifty numbers
pick(fiftyNumbers, 7);
Javascript's Math.random() will return a value in between 0 and 1 (exclusive). So to get an index scaled up to the correct value to look into your array, you would want to multiply that by the formula (max - min) + min
You can use Array.prototype.splice(), Math.floor(), Math.random(), for loop to remove elements from input array, return an array containing pseudo randomly picked index from input array without duplicate indexes being selected.
function rand(n) {
var res = []; var a = Array.from(Array(n).keys());
for (;a.length;res.push(a.splice(Math.floor(Math.random()*a.length),1)[0]));
return res
}
console.log(rand(50));
One good way of doing this job is shuffling the array and picking the first n values. Such as;
function shuffle(a){
var i = a.length,
j,
tmp;
while (i > 1) {
j = Math.floor(Math.random()*i--);
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
return a;
};
var arr = Array(50).fill().map((_,i) => i+1); //[1,2,3...49,50]
randoms = shuffle(arr).slice(0,7) // to get 7 random numbers from arrary arr
console.log(randoms)
This is probably what you want.
$(function()
{
var lol = [1,4,5,6,7,8,9];
function getPart(array,number)
{
var part = [],
index;
while(true)
{
if(part.length == number)
{
break;
}
index = $.random(0,part.length);
part.push(lol.splice(index,1));
}
return part;
}
});
$.random = function(min,max,filter)
{
var i,
n = Math.floor(Math.random()*(max-min+1)+min);
if(filter != undefined && filter.constructor == Array)
{
for(i=filter.length-1;i>-1;i--)
{
if(n == filter[i])
{
n = Math.floor(Math.random()*(max-min+1)+min)
i = filter.length;
}
}
}
return n;
}

How to push into the second dimension of a two dimensional array in JavaScript?

I have a list of players in denoted as
activeRange[x]
where x will vary from day-to-day.
Each of the x values will have to have AT LEAST 4 more subsequent values (likely a bit more). Ideally I'd like the array to look like:
activeRange[x][y]
So here's what I've done so far:
var MATCH = AllData[TotalRows][TotalColumns+1];
activeRange[TotNumPlayers].push(MATCH);
This is all located within 3 nested for loops.
TotNumPlayers
will iterate through a given set declared at the beginning (somewhat like 23). Once done, the
TotalRows
will iterate, then finally
TotalColumns
I'm running into the following error:
TypeError: Cannot find function push in object mitch
mitch is the value of activeRange[0]. I've been staring at this way too long, so any help would be appreciated!
EDIT: Code inserted below:
PLEASE IGNORE ALL THE COMMENTS. I COPY/PASTED THIS FROM A BIT OF CODE I USED YESTERDAY TO PERFORM A DIFFERENT FUNCTION.
This is the second time I've ever posted on this website, so trying to format this monster to be pretty was scary sounding. Hopefully this is good enough.
This is how activeRange was declared and initialized.
var activeRange = new Array();
for (var b=0; b<=lastRow-2; b++){
activeRange[b] = sheetRANK.getRange(b+2,1).getValue();
}
This is the function.
function getTotalScore(activeRange, w) {
Logger.clear()
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetWAR = ss.getSheetByName('WAR');
var sheetRANK = ss.getSheetByName('RANK');
var AllData = sheetRANK.getDataRange().getValues();
Logger.log('First');
for (var TotNumPlayers = 0; TotNumPlayers <= activeRange.length; TotNumPlayers++) {
Logger.log('Second');
var f = 0;
for (var TotalColumns = 0; TotalColumns <= AllData[0].length; ++TotalColumns) { // Init n. If n <= the total columns (second dimension), inc n.
Logger.log('Third');
for (var TotalRows = 0; TotalRows <= AllData.length; ++TotalRows) { // Init i. If i <= the total rows (first dimension), inc i.
Logger.log('Fourth');
//try{ // to avoid errors.
if (activeRange[TotNumPlayers] != "") {
Logger.log('Here?');
if (AllData[TotalRows][TotalColumns].valueOf().toUpperCase() == activeRange[TotNumPlayers].toUpperCase()) {
Logger.log('How About Here?');
var MATCH = AllData[TotalRows][TotalColumns + 1];
activeRange.push(TotNumPlayers, MATCH);
for (var Calc = 0; Calc <= activeRange[TotNumPlayers].length - 1; Calc++) {
var OverallScore = ((activeRange[TotNumPlayers][0] * 1.0) + (activeRange[TotNumPlayers][1] * .75) + (activeRange[TotNumPlayers][2] * .50) + (activeRange[TotNumPlayers][3] * .25));
sheetRANK.getRange(activeRange[TotNumPlayers] + 1, 2).setValue(OverallScore);
f = f + 1;
}
if (TotalRows == AllData.length - 1 && TotalColumns == AllData[0].length - 1 && f == 0) {
Browser.msgBox('No names matching \'' + activeRange[TotNumPlayers] + '\' found. Check your spelling!');
return;
}
}
}
}
}
}
}
Try thinking about what kind of data structures you can use to make your life easier. For this particular case, you have a list of players that you want to associate some data with. You'd probably use a structure like:
activeRange = [
{
name: 'mitch',
data: []
}
]
When you want to update the data, you'd simply call activeRange[0].data.push(someData).
activeRange is an array of players and each player is represented by an object with some properties, (name, data, etc).
Calling activeRange[0] yields the first player in your array and activeRange[0].data will yield the data associated with that player, which you can then manipulate however you want (push, pop, etc)
Based on your comments, you need a structure more like this
var activeRange = [
{
name: 'mitch',
otherData: [
10,
11,
12,
13
]
},
{
name: 'viper',
otherData: [
//values
]
}
]
you can access that by activeRange[0].otherData[2]
to add to it, just push into the sub array activeRange[0].otherData.push(newValue)

Efficiently find every combination of assigning smaller bins to larger bins

Let's say I have 7 small bins, each bin has the following number of marbles in it:
var smallBins = [1, 5, 10, 20, 30, 4, 10];
I assign these small bins to 2 large bins, each with the following maximum capacity:
var largeBins = [40, 50];
I want to find EVERY combination of how the small bins can be distributed across the big bins without exceeding capacity (eg put small bins #4,#5 in large bin #2, the rest in #1).
Constraints:
Each small bin must be assigned to a large bin.
A large bin can be left empty
This problem is easy to solve in O(n^m) O(2^n) time (see below): just try every combination and if capacity is not exceeded, save the solution. I'd like something faster, that can handle a variable number of bins. What obscure graph theory algorithm can I use to reduce the search space?
//Brute force
var smallBins = [1, 5, 10, 20, 30, 4, 10];
var largeBins = [40, 50];
function getLegitCombos(smallBins, largeBins) {
var legitCombos = [];
var assignmentArr = new Uint32Array(smallBins.length);
var i = smallBins.length-1;
while (true) {
var isValid = validate(assignmentArr, smallBins, largeBins);
if (isValid) legitCombos.push(new Uint32Array(assignmentArr));
var allDone = increment(assignmentArr, largeBins.length,i);
if (allDone === true) break;
}
return legitCombos;
}
function increment(assignmentArr, max, i) {
while (i >= 0) {
if (++assignmentArr[i] >= max) {
assignmentArr[i] = 0;
i--;
} else {
return i;
}
}
return true;
}
function validate(assignmentArr, smallBins, largeBins) {
var totals = new Uint32Array(largeBins.length);
for (var i = 0; i < smallBins.length; i++) {
var assignedBin = assignmentArr[i];
totals[assignedBin] += smallBins[i];
if (totals[assignedBin] > largeBins[assignedBin]) {
return false;
}
}
return true;
}
getLegitCombos(smallBins, largeBins);
Here's my cumbersome recursive attempt to avoid duplicates and exit early from too large sums. The function assumes duplicate elements as well as bin sizes are presented grouped and counted in the input. Rather than place each element in each bin, each element is placed in only one of duplicate bins; and each element with duplicates is partitioned distinctly.
For example, in my results, the combination, [[[1,10,20]],[[4,5,10,30]]] appears once; while in the SAS example in Leo's answer, twice: once as IN[1]={1,3,4} IN[2]={2,5,6,7} and again as IN[1]={1,4,7} IN[2]={2,3,5,6}.
Can't vouch for efficiency or smooth-running, however, as it is hardly tested. Perhaps stacking the calls rather than recursing could weigh lighter on the browser.
JavaScript code:
function f (as,bs){
// i is the current element index, c its count;
// l is the lower-bound index of partitioned element
function _f(i,c,l,sums,res){
for (var j=l; j<sums.length; j++){
// find next available duplicate bin to place the element in
var k=0;
while (sums[j][k] + as[i][0] > bs[j][0]){
k++;
}
// a place for the element was found
if (sums[j][k] !== undefined){
var temp = JSON.stringify(sums),
_sums = JSON.parse(temp);
_sums[j][k] += as[i][0];
temp = JSON.stringify(res);
var _res = JSON.parse(temp);
_res[j][k].push(as[i][0]);
// all elements were placed
if (i == as.length - 1 && c == 1){
result.push(_res);
return;
// duplicate elements were partitioned, continue to next element
} else if (c == 1){
_f(i + 1,as[i + 1][1],0,_sums,_res);
// otherwise, continue partitioning the same element with duplicates
} else {
_f(i,c - 1,j,_sums,_res);
}
}
}
}
// initiate variables for the recursion
var sums = [],
res = []
result = [];
for (var i=0; i<bs.length; i++){
sums[i] = [];
res[i] = [];
for (var j=0; j<bs[i][1]; j++){
sums[i][j] = 0;
res[i][j] = [];
}
}
_f(0,as[0][1],0,sums,res);
return result;
}
Output:
console.log(JSON.stringify(f([[1,1],[4,1],[5,1],[10,2],[20,1],[30,1]], [[40,1],[50,1]])));
/*
[[[[1,4,5,10,10]],[[20,30]]],[[[1,4,5,10,20]],[[10,30]]],[[[1,4,5,20]],[[10,10,30]]]
,[[[1,4,5,30]],[[10,10,20]]],[[[1,4,10,20]],[[5,10,30]]],[[[1,4,30]],[[5,10,10,20]]]
,[[[1,5,10,20]],[[4,10,30]]],[[[1,5,30]],[[4,10,10,20]]],[[[1,10,20]],[[4,5,10,30]]]
,[[[1,30]],[[4,5,10,10,20]]],[[[4,5,10,20]],[[1,10,30]]],[[[4,5,30]],[[1,10,10,20]]]
,[[[4,10,20]],[[1,5,10,30]]],[[[4,30]],[[1,5,10,10,20]]],[[[5,10,20]],[[1,4,10,30]]]
,[[[5,30]],[[1,4,10,10,20]]],[[[10,10,20]],[[1,4,5,30]]],[[[10,20]],[[1,4,5,10,30]]]
,[[[10,30]],[[1,4,5,10,20]]],[[[30]],[[1,4,5,10,10,20]]]]
*/
console.log(JSON.stringify(f([[1,1],[4,1],[5,1],[10,2],[20,1],[30,1]], [[20,2],[50,1]])));
/*
[[[[1,4,5,10],[10]],[[20,30]]],[[[1,4,5,10],[20]],[[10,30]]],[[[1,4,5],[20]],[[10,10,30]]]
,[[[1,4,10],[20]],[[5,10,30]]],[[[1,5,10],[20]],[[4,10,30]]],[[[1,10],[20]],[[4,5,10,30]]]
,[[[4,5,10],[20]],[[1,10,30]]],[[[4,10],[20]],[[1,5,10,30]]],[[[5,10],[20]],[[1,4,10,30]]]
,[[[10,10],[20]],[[1,4,5,30]]],[[[10],[20]],[[1,4,5,10,30]]]]
*/
Here's a second, simpler version that only attempts to terminate the thread when an element cannot be placed:
function f (as,bs){
var stack = [],
sums = [],
res = []
result = [];
for (var i=0; i<bs.length; i++){
res[i] = [];
sums[i] = 0;
}
stack.push([0,sums,res]);
while (stack[0] !== undefined){
var params = stack.pop(),
i = params[0],
sums = params[1],
res = params[2];
for (var j=0; j<sums.length; j++){
if (sums[j] + as[i] <= bs[j]){
var _sums = sums.slice();
_sums[j] += as[i];
var temp = JSON.stringify(res);
var _res = JSON.parse(temp);
_res[j].push(i);
if (i == as.length - 1){
result.push(_res);
} else {
stack.push([i + 1,_sums,_res]);
}
}
}
}
return result;
}
Output:
var r = f([1,5,10,20,30,4,10,3,4,5,1,1,2],[40,50,30]);
console.log(r.length)
console.log(JSON.stringify(f([1,4,5,10,10,20,30], [40,50])));
162137
[[[30],[1,4,5,10,10,20]],[[10,30],[1,4,5,10,20]],[[10,20],[1,4,5,10,30]]
,[[10,30],[1,4,5,10,20]],[[10,20],[1,4,5,10,30]],[[10,10,20],[1,4,5,30]]
,[[5,30],[1,4,10,10,20]],[[5,10,20],[1,4,10,30]],[[5,10,20],[1,4,10,30]]
,[[4,30],[1,5,10,10,20]],[[4,10,20],[1,5,10,30]],[[4,10,20],[1,5,10,30]]
,[[4,5,30],[1,10,10,20]],[[4,5,10,20],[1,10,30]],[[4,5,10,20],[1,10,30]]
,[[1,30],[4,5,10,10,20]],[[1,10,20],[4,5,10,30]],[[1,10,20],[4,5,10,30]]
,[[1,5,30],[4,10,10,20]],[[1,5,10,20],[4,10,30]],[[1,5,10,20],[4,10,30]]
,[[1,4,30],[5,10,10,20]],[[1,4,10,20],[5,10,30]],[[1,4,10,20],[5,10,30]]
,[[1,4,5,30],[10,10,20]],[[1,4,5,20],[10,10,30]],[[1,4,5,10,20],[10,30]]
,[[1,4,5,10,20],[10,30]],[[1,4,5,10,10],[20,30]]]
This problem is seen often enough that most Constraint Logic Programming systems include a predicate to model it explicitly. In OPTMODEL and CLP, we call it pack:
proc optmodel;
set SMALL init 1 .. 7, LARGE init 1 .. 2;
num size {SMALL} init [1 5 10 20 30 4 10];
num capacity{LARGE} init [40 50];
var WhichBin {i in SMALL} integer >= 1 <= card(LARGE);
var SpaceUsed{i in LARGE} integer >= 0 <= capacity[i];
con pack( WhichBin, size, SpaceUsed );
solve with clp / findall;
num soli;
set IN{li in LARGE} = {si in SMALL: WhichBin[si].sol[soli] = li};
do soli = 1 .. _nsol_;
put IN[*]=;
end;
quit;
This code produces all the solutions in 0.06 seconds on my laptop:
IN[1]={1,2,3,4,6} IN[2]={5,7}
IN[1]={1,2,3,4} IN[2]={5,6,7}
IN[1]={1,2,3,6,7} IN[2]={4,5}
IN[1]={1,2,5,6} IN[2]={3,4,7}
IN[1]={1,2,5} IN[2]={3,4,6,7}
IN[1]={1,2,4,6,7} IN[2]={3,5}
IN[1]={1,2,4,7} IN[2]={3,5,6}
IN[1]={1,2,4,6} IN[2]={3,5,7}
IN[1]={1,3,4,6} IN[2]={2,5,7}
IN[1]={1,3,4} IN[2]={2,5,6,7}
IN[1]={1,5,6} IN[2]={2,3,4,7}
IN[1]={1,5} IN[2]={2,3,4,6,7}
IN[1]={1,4,6,7} IN[2]={2,3,5}
IN[1]={1,4,7} IN[2]={2,3,5,6}
IN[1]={2,3,4,6} IN[2]={1,5,7}
IN[1]={2,3,4} IN[2]={1,5,6,7}
IN[1]={2,5,6} IN[2]={1,3,4,7}
IN[1]={2,5} IN[2]={1,3,4,6,7}
IN[1]={2,4,6,7} IN[2]={1,3,5}
IN[1]={2,4,7} IN[2]={1,3,5,6}
IN[1]={3,5} IN[2]={1,2,4,6,7}
IN[1]={3,4,7} IN[2]={1,2,5,6}
IN[1]={3,4,6} IN[2]={1,2,5,7}
IN[1]={3,4} IN[2]={1,2,5,6,7}
IN[1]={5,7} IN[2]={1,2,3,4,6}
IN[1]={5,6} IN[2]={1,2,3,4,7}
IN[1]={5} IN[2]={1,2,3,4,6,7}
IN[1]={4,6,7} IN[2]={1,2,3,5}
IN[1]={4,7} IN[2]={1,2,3,5,6}
Just change the first 3 lines to solve for other instances. However, as others have pointed out, this problem is NP-Hard. So it can switch from very fast to very slow suddenly. You could also solve the version where not every small item needs to be assigned to a large bin by creating a dummy large bin with enough capacity to fit the entire collection of small items.
As you can see from the "Details" section in the manual, the algorithms that solve practical problems quickly are not simple, and their implementation details make a big difference. I am unaware of any CLP libraries written in Javascript. Your best bet may be to wrap CLP in a web service and invoke that service from your Javascript code.

How to output every number from 1 to 10. Using random numbers in JavaScript [duplicate]

This question already has answers here:
How to randomize (shuffle) a JavaScript array?
(69 answers)
Closed 9 years ago.
I am trying to make a script which is outputting every number from 1-10.
Using a random number generator, in JavaScript.
I want every number to be unique.
Here is an example of what i would like the script to output:
5 9 7 6 1 3 4 8 2 10
This is my attempt:
var test = [];
var amountOfNumbers = 10;
var inArray = false;
var useNumbers = [];
for(var i=0; useNumbers.length<=amountOfNumbers; i++){
var rng = Math.floor((Math.random()*amountOfNumbers)+1);
for(var a=0; a<=test.length; a++){
if(rng == test[a]){
inArray == true;
}
}
if(!inArray){
document.write(rng);
test.push(rng);
useNumbers.push(rng);
}
}
Hope you can help.
for the record I am not interested in jQuery og any other library :)
1) How to fix your code
You have a few errors, among them the fact you don't reset inArray to false and that you don't iterate over the whole test array (use <, not <=). But using a loop to see if you already have the number isn't efficient, it's better to use an object as a map :
var test = [];
var amountOfNumbers = 10;
var useNumbers = {};
for(var i=0; test.length<amountOfNumbers; i++){
var rng = Math.floor((Math.random()*amountOfNumbers)+1);
if(!useNumbers[rng]){
document.write(rng);
test.push(rng);
useNumbers[rng] = true;
}
}
2) How to do it properly
Your algorithm will loop until it is lucky enough to find the remaining numbers. This isn't efficient and isn't predictable. The normal reliable practice is
to generate the array [1..10]
to shuffle it
Generating an array of the integers from 1 to N can be done with a simple loop or in a fancier way :
var arr = Array.apply(0,new Array(N)).map(function(_,i){ return i+1 });
Shuffling an array is usually done with the Fisher-Yates algorithm, for which you'll easily find JS implementations (it's easy to write anyway). A fast (theoretically not guaranteed to work with all future sort implementations) alternative is this one :
arr = arr.sort(function(a,b){ return Math.random()>0.5 });
The whole program
Your approach means to check over all the array in each step, looking if your random number is already inside the array, which means a lot lost time.
Best approach is disordering an ordered array. In each loop, we generate a random number (in the example, a number between 0 and 1) and with a 50% probability we change the item in the current position for other item in a random position (between 0 and the length of the array).
Hope it helps.
function disorder(arg) {
for (var i = 0; i < arg.length; i++) {
if (Math.random() < 0.5) {
var aux = arg[i];
var rndPos = Math.floor(Math.random()) * arg.length;
arg[i] = arg[rndPos];
arg[rndPos] = aux;
}
}
return arg;
}
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var myNewArray = disorder(myArray);
myNewArray.forEach(function(item) {
console.log(item);
});

Categories