Related
I need to do something like this: Let's say I have an array:
[3, 4, 1, 2]
I need to swap 3 and 4, and 1 and 2, so my array looks like [4, 3, 2, 1]. Now, I can just do the sort(). Here I need to count how many iterations I need, to change the initial array to the final output. Example:
// I can sort one pair per iteration
let array = [3, 4, 1, 2, 5]
let counter = 0;
//swap 3 and 4
counter++;
// swap 1 and 2
counter++;
// 5 goes to first place
counter++
// now counter = 3 <-- what I need
EDIT: Here is what I tried. doesn't work always tho... it is from this question: Bubble sort algorithm JavaScript
let counter = 0;
let swapped;
do {
swapped = false;
for (var i = 0; i < array.length - 1; i++) {
if (array[i] < array[i + 1]) {
const temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
swapped = true;
counter++;
}
}
} while (swapped);
EDIT: It is not correct all the time because I can swap places from last to first, for example. Look at the example code above, it is edited now.
This is most optimal code I have tried so far, also the code is accepted as optimal
answer by hackerrank :
function minimumSwaps(arr) {
var arrLength = arr.length;
// create two new Arrays
// one record value and key separately
// second to keep visited node count (default set false to all)
var newArr = [];
var newArrVisited = [];
for (let i = 0; i < arrLength; i++) {
newArr[i]= [];
newArr[i].value = arr[i];
newArr[i].key = i;
newArrVisited[i] = false;
}
// sort new array by value
newArr.sort(function (a, b) {
return a.value - b.value;
})
var swp = 0;
for (let i = 0; i < arrLength; i++) {
// check if already visited or swapped
if (newArr[i].key == i || newArrVisited[i]) {
continue;
}
var cycle = 0;
var j = i;
while (!newArrVisited[j]) {
// mark as visited
newArrVisited[j] = true;
j = newArr[j].key; //assign next key
cycle++;
}
if (cycle > 0) {
swp += (cycle > 1) ? cycle - 1 : cycle;
}
}
return swp;
}
reference
//You are given an unordered array consisting of consecutive integers [1, 2, 3, ..., n] without any duplicates.
//still not the best
function minimumSwaps(arr) {
let count = 0;
for(let i =0; i< arr.length; i++){
if(arr[i]!=i+1){
let temp = arr[i];
arr[arr.indexOf(i+1)] =temp;
arr[i] = i+1;
count =count+1;
}
}
return count;
}
I assume there are two reasons you're wanting to measure how many iterations a sort takes. So I will supply you with some theory (if the mathematics is too dense, don't worry about it), then some practical application.
There are many sort algorithms, some of them have a predicable number of iterations based on the number of items you are sorting, some of them are luck of the draw simply based on the order of the items to be sorted and which item how you select what is called a pivot. So if optimisation is very important to you, then you'll want to select the right algorithm for the purpose of the sort algorithm. Otherwise go for a general purpose algorithm.
Here are most popular sorting algorithms for the purpose of learning, and each of them have least, worst and average running-cases. Heapsort, Radix and binary-sort are worth looking at if this is more than just an theoretical/learning exercise.
Quicksort
Worst Case: Θ(n 2)
Best case: Θ(n lg n)
Average case: Θ(n lg n)
Here is a Quicksort implementation by Charles Stover
Merge sort
Worst case: Θ(n lg n)
Best case: Θ(n lg n)
Average Case: Θ(n lg n)
(note they're all the same)
Here is a merge sort implementation by Alex Kondov
Insertion sort
Worst case: Θ(n2)
Best case: Θ(n)
Average case:Θ(n2)
(Note that its worst and average case are the same, but its best case is the best of any algorithm)
Here is an insertion sort implementation by Kyle Jensen
Selection sort
Worst case: Θ(n2)
Best case: Θ(n2)
Average case: Θ(n2)
(note they're all the same, like a merge sort).
Here is a selection sort algorithm written by #dbdavid updated by myself for ES6
You can quite easily add an iterator variable to any of these examples to count the number of swaps they make, and play around with them to see which algorithms work best in which circumstance.
If there's a very good chance the items will already be well sorted, insertion sort is your best choice. If you have absolutely no idea, of the four basic sorting algorithms quicksort is your best choice.
function minimumSwaps(arr) {
var counter = 0;
for (var i = arr.length; i > 0; i--) {
var minval = Math.min(...arr); console.log("before", arr);
var minIndex = arr.indexOf(minval);
if (minval != = arr[0]) {
var temp = arr[0];
arr[0] = arr[minIndex];
arr[minIndex] = temp; console.log("after", arr);
arr.splice(0, 1);
counter++;
}
else {
arr.splice(0, 1); console.log("in else case")
}
} return counter;
}
This is how I call my swap function:
minimumSwaps([3, 7, 6, 9, 1, 8, 4, 10, 2, 5]);
It works with Selection Sort. Logic is as follows:
Loop through the array length
Find the minimum element in the array and then swap with the First element in the array, if the 0th Index doesn't have the minimum value founded out.
Now remove the first element.
If step 2 is not present, remove the first element(which is the minimum value present already)
increase counter when we swap the values.
Return the counter value after the for Loop.
It works for all values.
However, it fails due to a timeout for values around 50,000.
The solution to this problem is not very intuitive unless you are already somewhat familiar with computer science or real math wiz, but it all comes down to the number of inversions and the resulting cycles
If you are new to computer science I recommend the following resources to supplement this solution:
GeeksforGeeks Article
Informal Proof Explanation
Graph Theory Explanation
If we define an inversion as:
arr[i]>arr[j]
where "i" is the current index and "j" is the following index --
if there are no inversions the array is already in order and requires no sorting.
For Example:
[1,2,3,4,5]
So the number of swaps is related to the number of inversions, but not directly because each inversion can lead to a series of swaps (as opposed to a singular swap EX: [3,1,2]).
So if one consider's the following array:
[4,5,2,1,3,6,10,9,7,8]
This array is composed of three cycles.
Cycle One- 4,1,3 (Two Swaps)
Cycle Two- 5,2 (One Swap)
Cycle Three- 6 (0 Swaps)
Cycle Four- 10,9,7,8 (3 Swaps)
Now here's where the CS and Math magic really kicks in: each cycle will only require one pass through to properly sort it, and this is always going to be true.
So another way to say this would be-- the minimum number of swaps to sort any cycle is the number of element in that cycle minus one, or more explicitly:
minimum swaps = (cycle length - 1)
So if we sum the minimum swaps from each cycle, that sum will equal the minimum number of swaps for the original array.
Here is my attempt to explain WHY this algorithm works:
If we consider that any sequential set of numbers is just a section of a number line, then any set starting at zero should be equal to its own index should the set be expressed as a Javascript array. This idea becomes the criteria to programmatically determined if in element is already in the correct position based on its own value.
If the current value is not equal to its own index then the program should detect a cycle start and recording its length. Once the while loop reaches the the original value in the cycle it will add the minimum number of swaps in the cycle to a counter variable.
Anyway here is my code-- it is very verbose but should work:
export const minimumSwaps = (arr) => {
//This function returns the lowest value
//from the provided array.
//If one subtracts this value the from
//any value in the array it should equal
//that value's index.
const shift = (function findLowest(arr){
let lowest=arr[0];
arr.forEach((val,i)=>{
if(val<lowest){
lowest=val;
}
})
return lowest;
})(arr);
//Declare a counter variable
//to keep track of the swaps.
let swaps = 0;
//This function returns an array equal
//in size to the original array provided.
//However, this array is composed of
//boolean values with a value of false.
const visited = (function boolArray(n){
const arr=[];
for(let i = 0; i<n;i++){
arr.push(false);
}
return arr;
})(arr.length);
//Iterate through each element of the
//of the provided array.
arr.forEach((val, i) => {
//If the current value being assessed minus
//the lowest value in the original array
//is not equal to the current loop index,
//or, if the corresponding index in
//the visited array is equal to true,
//then the value is already sorted
if (val - shift === i || visited[i]) return;
//Declare a counter variable to record
//cycle length.
let cycleLength = 0;
//Declare a variable for to use for the
//while loop below, one should start with
//the current loop index
let x = i;
//While the corresponding value in the
//corresponding index in the visited array
//is equal to false, then we
while (!visited[x]) {
//Set the value of the current
//corresponding index to true
visited[x] = true;
//Reset the x iteration variable to
//the next potential value in the cycle
x = arr[x] - shift;
//Add one to the cycle length variable
cycleLength++;
};
//Add the minimum number of swaps to
//the swaps counter variable, which
//is equal to the cycle length minus one
swaps += cycleLength - 1;
});
return swaps
}
This solution is simple and fast.
function minimumSwaps(arr) {
let minSwaps = 0;
for (let i = 0; i < arr.length; i++) {
// at this position what is the right number to be here
// for example at position 0 should be 1
// add 1 to i if array starts with 1 (1->n)
const right = i+1;
// is current position does not have the right number
if (arr[i] !== right) {
// find the index of the right number in the array
// only look from the current position up passing i to indexOf
const rightIdx = arr.indexOf(right, i);
// replace the other position with this position value
arr[rightIdx] = arr[i];
// replace this position with the right number
arr[i] = right;
// increment the swap count since a swap was done
++minSwaps;
}
}
return minSwaps;
}
Here is my solution, but it timeouts 3 test cases with very large inputs. With smaller inputs, it works and does not terminate due to timeout.
function minimumSwaps(arr) {
let swaps = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === i + 1) continue;
arr.splice(i, 1, arr.splice(arr.indexOf(i + 1), 1, arr[i])[0]); //swap
swaps++;
}
return swaps;
}
I'm learning how to make it more performant, any help is welcome.
This is my solution to the Main Swaps 2 problem in JavaScript. It passed all the test cases. I hope someone finds it useful.
//this function calls the mainSwaps function..
function minimumSwaps(arr){
let swaps = 0;
for (var i = 0; i < arr.length; i++){
var current = arr[i];
var targetIndex = i + 1;
if (current != targetIndex){
swaps += mainSwaps(arr, i);
}
}
return swaps;
}
//this function is called by the minimumSwaps function
function mainSwaps(arr, index){
let swapCount = 0;
let currentElement = arr[index];
let targetIndex = currentElement - 1;
let targetElement = arr[currentElement - 1];
while (currentElement != targetElement){
//swap the elements
arr[index] = targetElement;
arr[currentElement - 1] = currentElement;
//increase the swapcount
swapCount++;
//store the currentElement, targetElement with their new values..
currentElement = arr[index];
targetElement = arr[currentElement - 1];
}
return swapCount;
}
var myarray = [2,3,4,1,5];
var result = console.log(minimumSwaps(myarray));
you can also do it with a map. But its O(nlogn)
const minSwaps = (arr) =>{
let arrSorted = [...arr].sort((a,b)=>a-b);
let indexMap = new Map();
// fill the indexes
for(let i=0; i<arr.length; i++){
indexMap.set(arr[i],i);
}
let count = 0;
for(let i=0; i<arrSorted.length;i++){
if(arr[i] != arrSorted[i]){
count++;
// swap the index
let newIdx = indexMap.get(arrSorted[i]);
indexMap.set(arr[i],newIdx);
indexMap.set(arrSorted[i],i);
// sawp the values
[arr[i],arr[newIdx]] =[arr[newIdx],arr[i]];
}
}
return count;
}
I am working on the Codility Peak problem:
Divide an array into the maximum number of same-sized blocks, each of which should contain an index P such that A[P - 1] < A[P] > A[P + 1].
My own solution is provided below, but it only scores 45%. So my question is:
How can I still improve my solution?
The code snippet seems to be long, since I added some extra comments to make myself clearer:
function solution(A) {
var storage = [], counter = 0;
// 1. So first I used a loop to find all the peaks
// and stored them all into an array called storage
for(var i = 1; i < A.length - 1; i++) {
if (A[i] > A[i-1] && A[i] > A[i+1]) {
storage.push(i);
}
}
// 2. Go and write the function canBeSeparatedInto
// 3. Use the for loop to check the counter
for(var j = 1; j < A.length; j++) {
if (canBeSeparatedInto(j, A, storage)) {
counter = j;
}
}
return counter;
}
/* this function tells if it is possible to divide the given array into given parts
* we will be passing our function with parameters:
* #param parts[number]: number of parts that we intend to divide the array into
* #param array[array]: the original array
* #param peaks[array]: an storage array that store all the index of the peaks
* #return [boolean]: true if the given array can be divided into given parts
*/
function canBeSeparatedInto(parts, array, peaks) {
var i = 1, result = false;
var blockSize = array.length / parts;
peaks.forEach(function(elem) {
// test to see if there is an element in the array belongs to the ith part
if ((elem+1)/blockSize <= i && (elem+1)/blockSize> i-1) {
i++;
}
});
// set the result to true if there are indeed peaks for every parts
if (i > parts) {
result = true;
}
return result;
}
The main problem with my code is that it does not pass the performance test. Could you give me some hint on that?
I would suggest this algorithm:
Sort the peeks by the distance they have with their predecessor. To do that, it might be more intuitive to identify "valleys", i.e. maximised ranges without peeks, and sort those by their size in descending order
Identify the divisors of the array length, as the solution must be one of those. For example, it is a waste of time to test for solutions when the array length is prime: in that case the answer can only be 1 (or zero if it has no peeks).
Try each of the divisors in ascending order (representing the size of array chunks), and see if for each valley such a split would bring one of the chunks completely inside that valley, i.e. it would not contain a peek: in that case reject that size as a solution, and try the next size.
Implementation with interactive input of the array:
"use strict";
// Helper function to collect the integer divisors of a given n
function divisors(n) {
var factors = [],
factors2 = [],
sq = Math.sqrt(n);
for (var i = 1; i <= sq; i++) {
if (n % i === 0) {
factors.push(n / i);
// Save time by storing complementary factor as well
factors2.push(i);
}
}
// Eliminate possible duplicate when n is a square
if (factors[factors.length-1] === factors2[factors2.length-1]) factors.pop();
// Return them sorted in descending order, so smallest is at end
return factors.concat(factors2.reverse());
}
function solution(A) {
var valleys = [],
start = 0,
size, sizes, i;
// Collect the maximum ranges that have no peeks
for (i = 1; i < A.length - 1; i++) {
if (A[i] > A[i-1] && A[i] > A[i+1]) {
valleys.push({
start,
end: i,
size: i - start,
});
start = i + 1;
}
}
// Add final valley
valleys.push({
start,
end: A.length,
size: A.length - start
});
if (valleys.length === 1) return 0; // no peeks = no solution
// Sort the valleys by descending size
// to improve the rest of the algorithm's performance
valleys.sort( (a, b) => b.size - a.size );
// Collect factors of n, as all chunks must have same, integer size
sizes = divisors(A.length)
// For each valley, require that a solution must not
// generate a chunk that falls completely inside it
do {
size = sizes.pop(); // attempted solution (starting with small size)
for (i = 0;
i < valleys.length &&
// chunk must not fit entirely inside this valley
Math.ceil(valleys[i].start / size) * size + size > valleys[i].end; i++) {
}
} while (i < valleys.length); // keep going until all valleys pass the test
// Return the number of chunks
return A.length / size;
}
// Helper function: chops up a given array into an
// array of sub arrays, which all have given size,
// except maybe last one, which could be smaller.
function chunk(arr, size) {
var chunks = [];
for (var i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
// I/O management
inp.oninput = function () {
// Get input as an array of positive integers (ignore non-digits)
if (!this.value) return;
var arr = this.value.match(/\d+/g).map(v => +v);
var parts = solution(arr);
// Output the array, chopped up into its parts:
outCount.textContent = parts;
outChunks.textContent = chunk(arr, arr.length / parts).join('\n');
}
Array (positive integers, any separator): <input id="inp" style="width:100%">
Chunks: <span id="outCount"></span>
<pre id="outChunks"></pre>
When checking whether array can be splitted into K parts, you will in worst case (array of [1,2,1,2,1,...]) do N/2 checks (since you are looking at every peak).
This can be done in K steps, by using clever datastructures:
Represent peaks as an binary array (0 - no peak, 1 - peak). Calculate prefix sums over that. If you want to check if block contains a peak, just compare prefix sums at the start and end of the block.
And also you have small other problem there. You should not check number of block which does not divide the size of the array.
Currently working on an application which requires to display a set of values in different currencies. This is part of an application but everything I provide here should be enough as this is the main section I am working with. I have a json file which is read in and it is stored into an array called valuesArray, this array has all the information, such as the amount, currency, etc. With the currencies being sorted with highest first to the lowest on display like this:
EUR 500.00
USD 200.00
This is the code that I have created but it seems like this wouldn't be effective the more currencies I have. I've just put an array declaration above the function but just above this function is where I do all the json stuff and adding it into the array. $scope.valuesArray has data at this point.
$scope.valuesArray =[];
$scope.total = function()
{
var eur_total = 0;
var usd_total = 0;
if (typeof $scope.valuesArray != 'undefined')
{
var length = $scope.valuesArray.length;
for (var i = 0; i<length ; i++)
{
switch($scope.valuesArray[i].currency)
{
case "USD":
usd_total += parseFloat($scope.valuesArray[i].value);
break;
default:
eur_total += parseFloat($scope.valuesArray[i].value);
break;
}
}
}
var cost_total= [usd_total,eur_total];
total.sort(function(a, b){return b-a});
return format_to_decimal(total[0]) + "\x0A" + format_to_decimal(total[1]);
}
In my for loop I go through every single data in the array and break each currency down within the switch statement and finding the total amount of each currencies.
The last bit is kind of temporary as I couldn't figure out a different way of how to do it. I sort the totals for the currencies I have from the highest at the top.
I return the function with a function call for format_numeric_with_commas which gives me the value in proper currency format and this displays the value. Will update this and add that code when I get to it. But I have used the indexes as a rough logic to show what I want to get out of it. So in this case, total[0] should be 500.00 and total[1] should be 200.00.
On top of this I want to be able to display the currency type for each. So like the example above.
You can try to save all the calculations in the array with currency index.
$scope.valuesArray = [];
$scope.total = function () {
var totalsArray = [];
if (typeof $scope.valuesArray != 'undefined') {
var length = $scope.valuesArray.length
for (var i = 0; i < length; i++) {
if (!totalsArray[$scope.valuesArray[i].currency]) {
totalsArray[$scope.valuesArray[i].currency] = 0;
}
totalsArray[$scope.valuesArray[i].currency] += $scope.valuesArray[i].value;
}
}
var cost_total = [];
for (var k in totalsArray) {
cost_total.push(currency:k,value:totalsArray[k]);
}
cost_total.sort(function (a, b) {
return b.value - a.value
});
return format_to_decimal(cost_total[0].value)+cost_total[0].currency + "\x0A" + format_to_decimal(cost_total[1].value);
I have tried to implement this knapsack problem solution algorithm in JavaScript, but the solutions s_opt I get has a total weight greater than the L_max.
What am I doing wrong?
I suspect it could be something related to Closures in recursion.
/*
GENERAL:
Assume we have a knapsack and we want to bring as much stuff as possible.
Of each thing we have several variants to choose from. Each of these variants have
different value and takes different amount of space.
DEFINITIONS:
L_max = integer, size of the knapsack for the entire problem having N items
l = matrix, having the elements l[i-1][j-1] representing the space taken
by variant j of item i (-1 since indexing the matrices has index starting on zero, i.e. item i is stored at position i-1)
p = matrix, having the elements p[i-1][j-1] representing the value given by
by variant j of item i
n = total number of items (used in a sub-problem)
N = total number of items (used in the full problem, N >= n)
s_opt = vector having the optimal combination of variant selections s_i, i.e. s_opt = arg max p_sum
*/
function knapsack(L_max,l,p) {
// constructing (initializing) - they are private members
var self = this; // in order for private functions to be able read variables
this.N = l.length;
var DCached = []; // this is only used by a private function so it doesnt need to made public using this.*
this.s_opt = [];
this.p_mean = null;
this.L_max = L_max;
// define public optimization function for the entire problem
// when this is completed the user can read
// s_opt to get the solution and
// p_mean to know the quality of the solution
this.optimize = function() {
self.p_mean = D(self.N,self.L_max) / Math.max(1,self.N);
}
// define private sub-problem optimization function
var D = function(n,r) {
if (r<0)
return -Infinity;
if (n==0)
return 0;
if(DCached[n-1] != null) {
if(DCached[n-1][r-1] != null) {
return DCached[n-1][r-1];
}
}
var p_max = -Infinity;
var p_sum;
var J = l[n-1].length;
for(var j = 0; j < J; j++) {
p_sum = p[n-1][j] + D( n-1 , r - l[n-1][j] );
if(p_sum>p_max) {
p_max = p_sum;
self.s_opt[n-1] = j;
}
}
DCached[n-1] = [];
DCached[n-1][r-1] = p_max;
return p_max;
}
}
The client using this knapsack solver does the following:
var knapsackSolution = new knapsack(5,l,p);
knapsackSolution.optimize();
// now the client can access knapsackSolution.s_opt containing the solution.
I found a solution. When solving a sub-problem D(n,r) the code in the question returned the optimized value, but it didn't really manage the array s_opt in a proper way. In the modified solution, pasted below, I fixed this. Instead of only returning the optimized value of the knapsack also an array of chosen variants (e.g. the arg of the max) are returned. The cache is also modified to manage these two parts of the solution (both max value and arg max value).
The code below also contains an additional feature addition. The user can now also pass a value maxComputingComplexity controlling the computational size of the problem in some kind of heuristic manner.
/*
GENERAL:
Assume we have a knapsack and we want to bring as much stuff as possible.
Of each thing we have several variants to choose from. Each of these variants have
different value and takes different amount of space.
The quantity of each variant is one.
DEFINITIONS:
L_max = integer, size of the knapsack, e.g. max number of letters, for the entire problem having N items
l = matrix, having the elements l[i-1][j-1] representing the space taken
by variant j of item i (-1 since indexing the matrices has index starting on zero, i.e. item i is stored at position i-1)
p = matrix, having the elements p[i-1][j-1] representing the value given by
by variant j of item i
maxComputingComplexity = value limiting the product L_max*self.N*M_max in order to make the optimization
complete in limited amount of time. It has a serious implication, since it may cut the list of alternatives
so that only the first alternatives are used in the computation, meaning that the input should be well
ordered
n = total number of items (used in a sub-problem)
N = total number of items (used in the full problem, N >= n)
M_i = number of variants of item i
s_i = which variant is chosen to pack of item i
s = vector of elements s_i representing a possible solution
r = maximum total space in the knapsack, i.e. sum(l[i][s_i]) <= r
p_sum = sum of the values of the selected variants, i.e. sum(p[i][s_i]
s_opt = vector having the optimal combination of variant selections s_i, i.e. s_opt = arg max p_sum
In order to solve this, let us see p_sum as a function
D(n,r) = p_sum (just seeing it as a function of the sub-problem n combined with the maximum total space r)
RESULT:
*/
function knapsack(L_max,l,p,maxComputingComplexity) {
// constructing (initializing) - they are private members
var self = this; // in order for private functions to be able read variables
this.N = l.length;
var DCached = []; // this is only used by a private function so it doesnt need to made public using this.*
//this.s_opt = [];
//this.p_mean = null;
this.L_max = L_max;
this.maxComputingComplexity = maxComputingComplexity;
//console.log("knapsack: Creating knapsack. N=" + N + ". L_max=" + L_max + ".");
// object to store the solution (both big problem and sub-problems)
function result(p_max,s_opt) {
this.p_max = p_max; //max value
this.s_opt = s_opt; //arg max value
}
// define public optimization function for the entire problem
// when this is completed the user can read
// s_opt to get the solution and
// p_mean to know the quality of the solution
// computing complexity O(L_max*self.N*M_max),
// think O=L_max*N*M_max => M_max=O/L_max/N => 3=x/140/20 => x=3*140*20 => x=8400
this.optimize = function() {
var M_max = Math.max(maxComputingComplexity / (L_max*self.N),2); //totally useless if not at least two
console.log("optimize: Setting M_max =" + M_max);
return D(self.N,self.L_max,M_max);
//self.p_mean = mainResult.D / Math.max(1,self.N);
// console.log...
}
// Define private sub-problem optimization function.
// The function reads to "global" variables, p and l
// and as arguments it takes
// n delimiting the which sub-set of items to be able to include (from p and l)
// r setting the max space that this sub-set of items may take
// Based on these arguments the function optimizes D
// and returns
// D the max value that can be obtained by combining the things
// s_opt the selection (array of length n) of things optimizing D
var D = function(n,r,M_max) {
// Start by checking whether the value is already cached...
if(DCached[n-1] != null) {
if(DCached[n-1][r-1] != null) {
//console.log("knapsack.D: n=" + n + " r=" + r + " returning from cache.");
return DCached[n-1][r-1];
}
}
var D_result = new result(-Infinity, []); // here we will manage the result
//D_result.s_opt[n-1] = 0; // just put something there to start with
if (r<0) {
//D_result.p_max = -Infinity;
return D_result;
}
if (n==0) {
D_result.p_max = 0;
return D_result;
}
var p_sum;
//self.s_opt[n] = 0; not needed
var J = Math.min(l[n-1].length,M_max);
var D_minusOneResult; //storing the result when optimizing all previous items given a max length
for(var j = 0; j < J; j++) {
D_minusOneResult = D( n-1 , r - l[n-1][j] , M_max)
p_sum = p[n-1][j] + D_minusOneResult.p_max;
if(p_sum > D_result.p_max) {
D_result.p_max = p_sum;
D_result.s_opt = D_minusOneResult.s_opt;
D_result.s_opt[n-1] = j;
}
}
DCached[n-1] = [];
DCached[n-1][r-1] = D_result;
//console.log("knapsack.D: n=" + n + " r=" + r + " p_max= "+ p_max);
return D_result;
}
}
Given an array of values:
var values = new Array();
array.push(2);
array.push(3);
array.push(4);
I'd like to create an iterative function which can store every possible combination of values, for any length of array.
For example, in this case the possible values would be (1,1,1)(1,1,2)(1,1,3)(1,1,4)(1,2,1)(1,2,2)(1,2,3)(1,2,4)(2,1,1)(2,1,2)(2,1,3)(2,1,4)(2,2,1)(2,2,2)(2,2,3)(2,2,4)
I know that to do this I need to use an recursive function, which will go a level deeper and call the function again if the maximum depth has not been reached...
I know where to start is (probably, I think)
function iterativeLoop(level, depth) {
for(var i = 0; i < values.length; i++) {
if(level < depth) {
iterativeloop(level+1, depth);
}
else if (level=depth) {
}
}
}
I'm not sure how I can access the 'upper' levels once the function is called deeper though... i.e. I'm not sure how to access (1,2,4) and not just (?,?,4)
I hope that makes sense?
(Sorry I know my title isn't very good, I couldn't think how to concisely explain it)
I'm not sure how I can access the 'upper' levels once the function is called deeper though... i.e. I'm not sure how to access (1,2,4) and not just (?,?,4)
You will need to pass them on, e.g. in an array.
for(var i = 0; i < values.length; i++)
This should not be the outer iteration to perform, unless you want to construct a two-dimensional array of results in a simple nested loop (see below). Instead, you want value.length to be the depth you are recursing to. On every recursion level, you will iterate from 1 to values[level] then. And instead of passing a level, we will pass an array of the current state (the question marks from above) whose length is the level.
var values = [2,3,4];
function recurse(state) {
var level = state.length;
var depth = values.length;
if (level == depth) {
console.log.apply(console, state); // or whatever you want to do
} else {
for (var i=1; i<=values[level]; i++) {
state.push(i); // save current question mark
// notice state.length = level + 1 now
recurse(state); // enter next level
state.pop(); // delete it after we're so state doesn't grow infinitely :-)
}
}
}
recurse([]);
If you want to use your iteration over the values, you can do so by adding more and more states to a result array (growing by one value each level), which in the end will contain all possible combinations:
var values = [2,3,4];
var result = [[]]; // one empty state at level 0
for (var i=0; i<values.length; i++) {
var reslen = result.length,
val = values[i];
var mult = []; // will become the new result with a length of (reslen * val)
for (var j=0; j<reslen; j++) {
for (var k=1; k<=val; k++) {
var state = result[j].slice(); // make a copy
state.push(k);
mult.push(state);
}
}
result = mult;
}
// logging the `result` on each level will show us
// 0 - [[]]
// 1 - [[1],[2]]
// 2 - [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]]
// 3 - [[1,1,1],[1,1,2],[1,1,3],[1,1,4],[1,2,1],[1,2,2],[1,2,3],[1,2,4],[1,3,1],[1,3,2],[1,3,3],[1,3,4],[2,1,1],[2,1,2],[2,1,3],[2,1,4],[2,2,1],[2,2,2],[2,2,3],[2,2,4],[2,3,1],[2,3,2],[2,3,3],[2,3,4]]
You can see how this is similar to #Jason's approach.
You don't need recursion since the length of the arbitrary data set is defined at the beginning at runtime:
var numbers = [2,3,4];
var result_array = [];
var num_product = 1;
var i=0, j=0, k=0; // iterators
for (i=0; i<numbers.length; i++) {
num_product *= numbers[i];
}
for (i=0; i<num_product; i++) {
result_array.push([]);
}
for (i=0; i<result_array.length; i++) {
product = 1;
for (j=0; j<numbers.length; j++) {
k = (Math.floor(i/product)%numbers[j]) + 1;
product *= numbers[j];
result_array[i][j] = k;
}
}
tested and functional for any number of array elements.
A side-by-side benchmark shows this code to be significantly faster than the recursive code - if you are able to avoid recursion (e.g. you know enough information up front to be able to define the whole problem) then it's better to do so, and the problem as currently defined allows you to do that. If you're just trying to learn about recursion, then this isn't very helpful to you :)