Assigning every possible combination of a range of numbers to variables - javascript

Given a range of numbers (1-25) how can I create a loop so that at each iteration I get a unique set of variables.
To give an example, if I was doing it with 4 numbers my variables would be:
on loop 1:
a = 1, b = 2, c = 3, d = 4,
on loop 2:
a = 1, b = 2, c = 4, d = 3
etc.
What I am trying to do is iterate over every possible number for each position (think sudoku)
so in a 3x3 grid: a = top left position, b = top middle, etc...
so similar to sudoku I would have a condition (each row = 65: a + b + c + d + e= 65)
so what I want to do is have a loop where I can assign all the values to variables:
for (something) {
var topLeft = (determined from loop)
var nextPosition = etc.
My solution is currently like so:
var numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
var a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,s,t,u,v,w,x,y;
var vars = [a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,s,t,u,v,w,x,y];
var counter = 0;
var found = false;
while(found == false) {
for (var asdf = numbers, i = asdf.length; i--; ) {
var random = asdf.splice(Math.floor(Math.random() * (i + 1)), 1)[0];
vars[i] = random;
}
if (
{a+b+c+d+e = 65,
f+g+h+i+j = 65,
k+l+1+n+o = 65,
p+q+r+s+t = 65,
u+v+w+x+y = 65,
a+f+k+p+u = 65,
b+g+l+q+v = 65,
c+h+1+r+w = 65,
d+i+n+s+x = 65,
e+j+o+t+y = 65,
u+q+1+i+e = 65,
a+g+1+s+y = 65}
) {
console.log(a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,s,t,u,v,w,x,y);
found = true;
}
counter++;
}
However the obvious problem with this is that it's just randomly selecting values. So it will take an incredible amount of time. I can't work out how to iterate over every possible combination (without having 25 for loops) so I can check which values will pass the condition.

Given a range of numbers (1-25) how can I create a loop so that at each iteration I get a unique set of variables.
It sounds like you are talking about the permutations of a set. You can find a bunch of different algorithms to do this. Here is a nice one from this StackOverflow answer:
function getArrayMutations(arr, perms = [], len = arr.length) {
if (len === 1) perms.push(arr.slice(0))
for (let i = 0; i < len; i++) {
getArrayMutations(arr, perms, len - 1)
len % 2 // parity dependent adjacent elements swap
? [arr[0], arr[len - 1]] = [arr[len - 1], arr[0]]
: [arr[i], arr[len - 1]] = [arr[len - 1], arr[i]]
}
return perms
}
getArrayMutations([1, 2, 3])
> [ [ 1, 2, 3 ],
[ 2, 1, 3 ],
[ 3, 1, 2 ],
[ 1, 3, 2 ],
[ 2, 3, 1 ],
[ 3, 2, 1 ] ]
Be careful though! Permutations are factorial which means they grow really fast.
P(n, k) =
This means that if you want to permute 25 numbers, you are looking at 1.551121e+25 possible combinations which is getting into the not-computable-in-your-lifetime territory.
What I am trying to do is iterate over every possible number for each position (think sudoku) so in a 3x3 grid: a = top left position, b = top middle, etc...
Two dimensional arrays (really just lists of lists) are a great way to store matrix data like this. It doesn't fundamentally change the math to change the representation from a single array, but it might be easier to think about. I'm not 100% sure if you want a 3x3 grid or a 5x5 grid but I'll assume 5x5 since you have 25 numbers in your example. You can easily reshape them like this:
function reshapeArray(array, n=5) {
let result = []
let row = 0
let col = 0
for (let i = 0; i < array.length; i++) {
if (col == 0) {
result[row] = []
}
result[row][col] = array[i]
col++
if (col == n) {
col = 0
row++
}
}
return result
}
reshapeArray([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25])
> [ [ 1, 2, 3, 4, 5 ],
[ 6, 7, 8, 9, 10 ],
[ 11, 12, 13, 14, 15 ],
[ 16, 17, 18, 19, 20 ],
[ 21, 22, 23, 24, 25 ] ]
so similar to sudoku I would have a condition (each row = 65: a + b + c + d + e= 65)
Now that you have your data in an iteratable array, you can very easily check this or any other constraint. For example:
/**
* Checks if a matrix (a 2-d array like the output from reshapeArray())
* meets our criteria.
*/
function checkMatrix(matrix) {
for (let row = 0; row < matrix.length; row++) {
let rowSum = 0
for (let col = 0; col < matrix[row].length; col++) {
rowSum += matrix[row][col]
}
// The row sum does not equal 65, so this isn't the one!
if (rowSum != 65) {
return false
}
}
// All the row sums equal 65
return true
}
If you want add extra rules (like having the columns sum to 65 as well) just modify the code to check for that. You can get the value at any point in the matrix by indexing it matrix[row][col] so matrix[0][0] is the upper-left, etc.
However the obvious problem with this is that it's just randomly selecting values. So it will take an incredible amount of time. I can't work out how to iterate over every possible combination (without having 25 for loops) so I can check which values will pass the condition.
Yes, it will. Sudoku is an NP-Hard problem. If you haven't seen complexity classes before, that's just a very mathematically formal way of saying that there's no clever solution that's going to be significantly faster than just checking every possible solution. This hypothetical problem is not exactly the same, so it might be possible, but it has a very np-ish feel to it.
Currently, your pseudocode solution would look like this:
let permutations = getPermutations() // You're going to need to change this part
// because getting all the permutations
// ahead of time will take too long.
// Just picking random numbers each time is
// not actually a terrible idea. Or, look at
// generator functions (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
for (let permutation of permutations) {
let matrix = reshapeArray(permutation)
if (checkMatrix(matrix)) {
console.log("Found it")
console.log(matrix)
break
}
}
If there is only one possible solution that matches your criteria, you will never find it this way. If there is a relatively high density of solutions, you will probably find some. If you really want to solve this problem I would recommend first looking at it from a mathematical perspective -- can you prove that it is or isn't NP? can you make some prediction about the density of solutions?

Not sure what the question really is. I would store the range in an Array:
function range(start, stop = null, upperCase = false){
let b = start, e = stop, s = 'abcdefghijklmnopqrstuvwxyz', x;
const a = [], z = s.split(''), Z = s.toUpperCase().split('');
if(typeof b === 'string'){
s = z.indexOf(b.toLowerCase());
if(e === null){
x = z.length;
}
else{
x = z.indexOf(e.toLowerCase())+1;
}
if(upperCase){
return Z.slice(s, x);
}
return z.slice(s, x);
}
else if(e === null){
e = b; b = 1;
}
for(let i=b; i<=e; i++){
a.push(i);
}
return a;
}
function permuCount(array){
let c = 1;
for(let i=0,n=1,l=array.length; i<l; i++,n++){
c *= n;
}
return c;
}
function comboCount(array){
let l = array.length;
return Math.pow(l, l);
}
console.log(range(2, 23)); console.log(range(10)); console.log(range('d'));
console.log(range('g', 'p')); console.log(range('c', 'j', true));
// here is where you'll have an issue
const testArray = range(0, 9);
console.log(permuCount(testArray));
console.log(comboCount(testArray));
As you can see there are way too many combinations. Also, you should have already see the following post: Permutations in JavaScript?

Related

Sequence in an array

I have an algo exercise to do and I have no idea how to do it, as the requirement is to use only basic programming concepts. So - the point is to find the longest sequence of numbers in an array that are growing or not changing value.
So for the array [1,1,2,4,0,1,7,4], it would be [1,1,2,4].
It should have as small time and memory complexity as possible. Any solutions, tips? Much love and thanks in advance for any advice or feedback.
That's what I've managed to do in the last 10 minutes, but I feel like I'm doing it in the most complex way possible...
function idk(array) {
var current = 0;
var winner = 0;
var currentArray = [];
var winnerArray = [];
for (let i = 0; i <= array.length; i++) {
if (array[i + 1] >= array[i]) {
currentArray.push(array[i]);
current = currentArray.length;
} else {
currentArray.push(array[i]);
if (currentArray.length > best.length) {
// copy array and append it to the new array?
}
}
}
return winnerArray;
}
Try this javascript algorithm:
var array = [1, 8, 1, 1, 5, 7, 2, 2]
var output = []
array.forEach(function(value, index) {
if (array[index - 1] <= value && index != 0) {
output[output.length - 1].push(value)
} else {
output.push([value])
}
})
var longestArray = []
output.forEach(function(arrayCompare, index) {
if (arrayCompare.length > longestArray.length || index == 0) {
longestArray = arrayCompare
}
})
console.log(longestArray)
The first forEach loops through the elements of array. If the element is larger than or equal to the previous element, it adds it to the last array in output. If it is not, then it creates a new array, and pushes the array into output. This creates arrays with "growing" sequences.
After that, it loops through each sequence, and checks if the length of the sequence is greater than the current longest sequence, which is stored in longestArray. If it is, it changes longestArray to that. If it isn't, it does nothing.
Note that both of these loops have exceptions if the index is 0, since there is no element with index -1 (therefore such an exception had to be made).
Also, here's the same implementation in python:
array = [1, 8, 1, 1, 5, 7, 2, 2]
output = []
index = 0
while index < len(array):
value = array[index]
if (array[index-1] <= value and index !=0):
output[-1].append(value)
else:
output.append([value])
index += 1
longestArray = []
index = 0
while index < len(output):
arrayCompare = output[index]
if index==0 or len(arrayCompare) > len(longestArray):
longestArray = arrayCompare
index += 1
print(longestArray)
Just why loop over the end of the array?
Because you could take this advantage to gather the last longest sequence without having a check after the loop for having found a longest sequence.
But in parts:
Why not a temporary array? Because there is no need to use it, if you collect the values. the startignn index of a sequence is important and the actual indec to decide if the sequence is longer then the previously found one.
The loop feature two conditions, one for continuing the loop, if in sequence and another to check if the actual ended sequence is longer. And for storing the actual index.
The last loop with a check for an undefined value is false, it does not continue the loop and the nect check reveals either a new longest sequence or not.
Some other annotation:
winnerArray has to be an empty array, because of the check later for length
The sequence check take the previous element, because the loop starts with the first index and the previous element is given.
The Big O is O(n).
function idk(array) {
let winnerArray = [],
index = 0;
for (let i = 1; i < array.length + 1; i++) {
if (array[i - 1] <= array[i]) continue;
if (i - index > winnerArray.length) winnerArray = array.slice(index, i);
index = i;
}
return winnerArray;
}
console.log(...idk([1, 1, 2, 4, 0, 1, 7, 4])); // [1, 1, 2, 4]
console.log(...idk([1, 8, 1, 1, 5, 7, 2, 2])); // [1, 1, 5, 7]
console.log(...idk([1, 8, 1, 1, 5, 7, 2, 2, 2, 2, 2]));
Here is the dynamic programming algorithm in Python:
def longest_sequence(numbers):
length = len(numbers)
L = [0] * length #L stores max possible lengths at each index
L[-1] = 1 # base case
for i in range(length-2, -1, -1):
max_length = L[i]
for j in range(i, length):
if (L[j] > max_length) and (numbers[j] > numbers[i]):
max_length = L[j]
L[i] = max_length + 1
#trace back
max_length = max(L)
result = []
for k in range(max_length, 0, -1):
result.append(numbers[L.index(k)])
numbers = numbers[L.index(k):]
L = L[L.index(k):]
return result
my_numbers = [36,13,78,85,16,52,58,61,63,83,46,19,85,1,58,71,26,26,21,31]
print(longest_sequence(my_numbers))
#>>[13, 16, 52, 58, 61, 63, 83, 85]
To be optimal, you should not use intermediate lists during processing. You only need to hold indexes, sizes and the previous value:
def longSeq(A):
longStart,longSize = 0,0 # best range so far (index and size only)
start,size,prev = 0,0,None # current range (index and size)
for i,a in enumerate(A):
if i == 0 or prev <= a: # increase current range
size += 1
prev = a
if size > longSize: # track longest so far
longStart,longSize = start,size
else:
start,size,prev = i,1,a # sequence break, restart current
return A[longStart:longStart+longSize]
output:
print(longSeq([1,1,2,4,0,1,7,4])) # [1, 1, 2, 4]
print(longSeq( [1,8,1,1,5,7,2,2])) # [1, 1, 5, 7]
This will perform in O(n) time and use O(1) space.

Improve the speed of a JavaScript function

I have a task I found on CodeWars and I managed to solve it, however, after submitting is says:
Execution timed out: (12000 ms)
When I try to test the function is passed, but I guess it is too slow.
Before you condemn me for not finding the answer on my own. I don't really care about submitting that as a response, but I have no idea how to make it faster and that is why I am here.
Here is the function:
const ls = [0, 1, 3, 6, 10]
const partsSums = (ls) => {
const sum = []
for(let i = 0, len = ls.length; i < len + 1; i++) {
let result = ls.slice(i).reduce( (accumulator, currentValue) => accumulator + currentValue, 0)
sum.push(result)
}
return sum
}
Here are the instructions:
Let us consider this example (array written in general format):
ls = [0, 1, 3, 6, 10]
Its following parts:
ls = [0, 1, 3, 6, 10]
ls = [1, 3, 6, 10]
ls = [3, 6, 10]
ls = [6, 10]
ls = [10]
ls = []
The corresponding sums are (put together in a list): [20, 20, 19, 16,
10, 0]
The function parts_sums (or its variants in other languages) will take
as parameter a list ls and return a list of the sums of its parts as
defined above.
For this kind of array maipulations, you better not use build in methods, like slice or reduce, because they are slow in comparison to a for loop, or any other looping approaches.
This approach takes a sinlge loop and uses the index for getting a value of the given array and takes the last sum of the new array.
Some speed tests on Codewars: Sums of Parts:
5621 ms with sparse array sum = []; sum[i] = 0; (the first version of this answer),
3452 ms with Array(i + 1).fill(0) and without sum[i] = 0;,
1261 ms with Array(i + 1) and sum[i] = 0; (find below),
3733 ms with Icepickle's first attempt.
const
partsSums = (ls) => {
let i = ls.length;
const sum = Array(i + 1);
sum[i] = 0;
while (i--) sum[i] = sum[i + 1] + ls[i];
return sum;
},
ls = [0, 1, 3, 6, 10];
console.log(...partsSums(ls));
You can still take a more functional approach but optimise the way you're doing the calculations.
Here is the idea - since you're trying to sum all items, then sum all but the first, then sum all but the second, etc., mathematically equivalent to getting the sum then subtracting from it each number in order and keeping the total.
[sum([41, 42, 43]), sum([42, 43]), sum([43]), sum([])]
is the same as:
total = sum([41, 42, 43])
[total - 0, total - 0 - 41, total - 0 - 41 - 42, total - 0 - 41 - 42- 43]
is the same as:
total = sum([41, 42, 43])
[total -= 0, total -= 41, total -= 42, total -= 43]
Generalised, this looks like:
total = sum([a1, a2, ..., aN])
[total -= 0, total -= a1, total -= a2, ..., total -= aN]
Using the trusty Array#reduce we can derive the sum once. Then we can derive the new array using Array.map using ls.map(num => total -= num).
The only problem here is that we get one less item - we don't calculate the initial total -= 0 which has to exist for all items. One way to do it is to append it to the start [0].concat(ls) will create the correct array to map over. However, since we already know what the value there would be, we can skip this step and directly substitute with total (after all the result of total -= 0 is total and leaves total unchanged). So, we can directly use [total].concat(ls.map(num => total -= num)) to start with total and add the rest of the items. to the end.
const ls = [0, 1, 3, 6, 10]
const partsSums = (ls) => {
let total = ls.reduce((a, b) => a + b, 0);
return [total]
.concat(
ls.map(num => total -= num)
);
}
console.log(partsSums(ls));
Personally, I would just use the previous sum to calculate the next, I don't see any need to re-iterate all the previous sums, so, I would probably go for a basic loop and then reverse the results, like so
function partsSums(ls) {
const result = [0];
if (ls.length === 0) {
return result;
}
for (let i = ls.length, q = 0; i--; q++) {
result.push(result[q] + ls[i]);
}
return result.reverse();
}
or, without reversing, look more like Nina's solution (except for predefining the length of the array)
function partsSums(ls) {
const len = ls.length;
const result = new Array(len+1);
result[len] = 0;
for (let i = len; i--;) {
result[i] = result[i+1] + ls[i];
}
return result;
}
Both also seem to run faster than Nina's on codewars nodejs engine, in the first part probably because of push, in the second one, probably because the array's length is defined from the start, for more information see this question
A solution using normal for loop along the time of execution .
var arr = [0, 1, 3, 6, 10];
function giveList(array){
var sum=0;
for(let i=0;i<array.length;i++){
sum=sum+array[i];
}
var result = [];
result.push(sum);
var temp;
for(let i=0;i<array.length;i++){
temp=sum-array[i];
result.push(temp);
sum=sum-array[i];
}
return result;
}
console.time();
console.log(giveList(arr));
console.timeEnd();
const partsSums = (ls, sum = 0) =>
[...ls, 0].reverse().map(x => sum = x + sum).reverse();
Takes around 1100 ms when I run it on CodeWars, which is slightly faster than other answers.
The repeated operation is too more. e.g: when you compute sum of [3, 6, 10], the up step [1, 3, 6, 10] already compute。 So you can think in another direction, back to end compute the sum of array
const ls = [0, 1, 3, 6, 10];
function partSums(ls) {
const len = ls.length;
const dp = [];
if(len === 0) { return [0] }
dp[len] = 0;
dp[len - 1] = ls[len - 1];
for (let i = len - 2; i >= 0; i--) {
dp[i] = dp[i + 1] + ls[i];
}
return dp;
}

JavaScript sudoku solver is stuck in infinite loop for some boards/doesn't work for all boards

I am new to stackoverflow. I searched for related questions, but none seems to answer my questions. I am in my first semester of Computer Science and am currently taking the Programming 1 course. We only work with JavaScript for the whole course and so JavaScript is the only programming language I know, so far. Consequently, I have a fairly limited understanding, intuitive or otherwise, of programming at large. English is also not my first language, so forgive me for mistakes.
Nonetheless, here's my problem: I need to program a sudoku solver. I have actually succeeded in coding a solver for a sudoku which is considered "easy". It takes a fraction of a second to do it, so I am fairly content with the results. The problem is: there are some sudokus which do not work, namely the ones who are considered "hard". Needless to say, the solver needs to work for all sudokus. Below are snippets of code for examples of both "easy" and "hard" sudoku boards, as well the code for my solver. I tried to annotate as best I could to describe the functioning, but there is clearly a problem since it will not solve the hard one. It is actually stuck in an infinite loop.
var easyBoard = [
[1,0,0,3,0,0,9,5,2],
[0,4,0,6,0,0,1,0,0],
[3,0,0,1,0,0,0,0,0],
[0,6,4,7,2,0,0,1,0],
[8,7,0,9,0,6,0,2,4],
[0,2,0,0,8,5,7,6,0],
[0,0,0,0,0,1,0,0,7],
[0,0,7,0,0,9,0,4,0],
[2,3,9,0,0,4,0,0,1]
];
var hardBoard = [
[4,0,0,6,0,7,0,8,5],
[0,0,0,0,0,0,6,0,0],
[0,0,7,0,0,0,0,0,0],
[0,5,0,0,0,3,0,0,4],
[3,7,0,0,0,8,0,0,0],
[6,0,0,2,0,0,0,0,0],
[8,0,0,0,0,0,3,1,0],
[0,3,1,0,4,9,0,0,0],
[0,0,0,0,0,0,0,0,9]
];
var solve = function (board) {
var empty = []; // We create an array for the 1x1 squares with no value, so we can call upon them with ease later on.
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board[i][j] === 0) {
empty.push([i,j]);
}
}
}
for (var i = 0; i < empty.length;) { // We check every possible value for all empty 1x1 squares.
var row = empty[i][0]; // Used for row and 3x3 square checks
var column = empty[i][1]; // Used for column and 3x3 square checks
var value = board[row][column] + 1; // We start at 1, because obviously 0 is not a Sudoku value.
var found = false; // We assume the value is invalid, unless it passes all three tests.
while (!found && value <= 9) { // As long as the value is invalid, we increase by one until it reaches more than 9.
var equal = false; // We assume for now that the value is not equal to any other in its row, column or 3x3 square.
for (var y = 0; y < 9; y++) {
if (board[row][y] === value) {
equal = true;
}
}
for (var x = 0; x < 9; x++) {
if (board[x][column] === value) {
equal = true;
}
}
for (var x = 3*Math.floor(row/3); x < 3*Math.floor(row/3)+3; x++) {
for (var y = 3*Math.floor(column/3); y < 3*Math.floor(column/3)+3; y++) {
if (board[x][y] === value) {
equal = true;
}
}
}
if (!equal) { // If the value is not equal to any other in its row, column or 3x3 square, it is valid.
found = true; // We have found a valid value, for now.
board[row][column] = value; // We assign said value to the corresponding board 1x1 square, for now.
i++; // We then move on to the next empty 1x1 square.
}
else {
value++; // If the value is invalid, we simply try the next possible value.
}
}
if (!found) { // If, from 1 to 9, the value is invalid, it means the one before is invalid.
board[row][column] = 0; // We then re-assign an empty value to the 1x1 square, before backtracking.
i--; // We go back to the previous 1x1 square to try a different value.
}
}
};
// test routines
var clone2 = array => array.slice().map( row=>row.slice());
function easyTest() {
var board = clone2( easyBoard);
solve( board);
console.log( "easy board solution:");
console.log( board);
}
function hardTest() {
var board = clone2( hardBoard);
solve( board);
console.log( "hard board solution:");
console.log( board);
}
<button type="button" onclick="easyTest()">Easy Test</button>
<button type="button" onclick="hardTest()">Hard Test</button>
The code works for the first one, but not the second. Is it because a backtracking/brute-force algorithm is not fast enough for the "hard" sudoku? Is it that it will just take several hours or is there an issue within my code which causes a problem with the first board, but not the second?
I am sorry if something is not clear, and I swear I have tried understanding the answers to other similar questions, but all of them contained several notions or operators/objects or whatever which I have no knowledge of. If someone could point out the problems within my code, and tell me if it is possible to solve the second board with it or if I need another method altogether.
Thank you so much in advance!
P.S.: A lot of people talk about objects within JavaScript, or object-oriented programming. I don't know if it's relevant, but we haven't seen any of that yet.
Something is not right. The code you posted solved the "hard" board in 1800 milliseconds. After some optimization I got that down to around 300 milliseconds on the same Windows laptop used for testing.
I'm providing the optimized version here to test if the uni computer can run it if you wish to try. I am quite wary of suggesting doing so if you are still working on a brute force solution, but it is certainly a version of your code!
In the end you may be able to test if the uni computer is simply not allowing brute force algorithms sufficient time to complete (or implements an instruction "step" limit in a custom JS engine it runs).
function emptyCells( board) {
var empty = [];
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board[i][j] === 0) {
var boxRow = 3* Math.floor( i/3);
var boxCol = 3* Math.floor( j/3);
empty.push([i,j, boxRow, boxCol]);
}
}
}
return empty;
}
function isUnique( board, empty, value) {
var row, col;
// test row
row = board[empty[0]];
for( col = 0; col < 9; ++ col) {
if( value == row[col]) {
return false;
}
}
// test col
col = empty[1];
for( var row = 0; row < 9; ++row) {
if( value == board[ row][col]){
return false;
}
}
// test box
var boxRow = empty[2];
var boxCol = empty[3];
for( var i = 3; i--;) {
row = board[ boxRow++];
for( var j = 3; j--;) {
if( row[boxCol + j] == value) {
return false;
}
}
}
return true;
}
var solve = function (board) {
var empty = emptyCells( board);
nextEmpty:
for (var i = 0; i < empty.length;) { // We check every possible value for all empty 1x1 squares.
var row = empty[i][0]; // Used for row and 3x3 square checks
var column = empty[i][1]; // Used for column and 3x3 square checks
var value = board[row][column] + 1; // We start at 1, because obviously 0 is not a Sudoku value.
var cell = empty[i];
while (value <= 9) { // test values up to 9.
if( isUnique( board, cell, value)) {
board[row][column] = value; // We assign said value to the corresponding board 1x1 square, for now.
i++; // Move on to the check next empty cell.
continue nextEmpty;
}
value++; // If the value is invalid, we simply try the next possible value.
}
board[row][column] = 0;
if( i == 0) { // board is not solvable
return null;
}
i--; // We go back to the previous 1x1 square to try a different value.
}
return board;
};
var board = [
[4,0,0,6,0,7,0,8,5],
[0,0,0,0,0,0,6,0,0],
[0,0,7,0,0,0,0,0,0],
[0,5,0,0,0,3,0,0,4],
[3,7,0,0,0,8,0,0,0],
[6,0,0,2,0,0,0,0,0],
[8,0,0,0,0,0,3,1,0],
[0,3,1,0,4,9,0,0,0],
[0,0,0,0,0,0,0,0,9]
];
var t0 = Date.now();
solve(board);
var t1 = Date.now();
console.log( " in " + (t1-t0) + "ms");
console.log( board.map( row=> row.join(',')).join('\n'));
console.log( "\n solved in " + (t1-t0) + "ms");
The second one gets solved on my computer in ~ 10 seconds
[4, 9, 3, 6, 1, 7, 2, 8, 5]
[2, 8, 5, 9, 3, 4, 6, 7, 1]
[1, 6, 7, 8, 5, 2, 4, 9, 3]
[9, 5, 8, 1, 6, 3, 7, 2, 4]
[3, 7, 2, 4, 9, 8, 1, 5, 6]
[6, 1, 4, 2, 7, 5, 9, 3, 8]
[8, 4, 9, 5, 2, 6, 3, 1, 7]
[5, 3, 1, 7, 4, 9, 8, 6, 2]
[7, 2, 6, 3, 8, 1, 5, 4, 9]

javascript - find subset that gives maximum sum under a certain limit (subset sum )

I have an array with some integer values, and I need to get a subset of them that gives me the maximum sum that is inferior to a given value.
So let's say I have this array:
[40, 138, 29, 450]
I would like to get a subset of this array that maximize the sum but is inferior to a limit given by the user, let's say 250. In this case it should return [139, 40, 29].
I had a look at this question and related answer, and tried to use the code given, but I didn't understand it very well. Anyway I've tried it, setting the min to 0 and the max to the limit given, but it keeps returning me "5" that is not correct, since the limit is like 300 and the numbers in my array are all over 50.
I couldn't find anything that could help me, so I'm asking if anyone could give me some code or pseudocode to understand how to do this.
Basically you could either add the element at the index to a temporary array or not. Then check if the index reaches the lenght of the array or if the sum is greater than the wanted sum, then either check the sum and add the temp array to the result set, or not.
Proceed until all indices are visited.
function getCombinations(array, sum) {
function add(a, b) { return a + b; }
function fork(i, t) {
var r = (result[0] || []).reduce(add, 0),
s = t.reduce(add, 0);
if (i === array.length || s > sum) {
if (s <= sum && t.length && r <= s) {
if (r < s) {
result = [];
}
result.push(t);
}
return;
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
console.log(getCombinations([40, 138, 29, 450], 250));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A fast and compact solution:
function maxSum(input, limit) {
const sums = {};
let max = 0;
const collectSums = (n, i, values) => {
for (; i < input.length; i++) {
const sum = n + input[i];
if (sum <= limit) {
values.push(input[i]);
if (sum >= max && values.length > 1) {
max = sum;
sums[max] = values.slice(); // https://jsperf.com/copying-an-array
}
collectSums(sum, i + 1, values);
}
}
values.pop();
};
collectSums(0, 0, []);
return sums[max] || [];
}
Apart from the necessary iterations of the input this solution tries to keep complexity low by not using costly array operations. Only a found subset has to be copied to keep track of possible combinations. Still, there are probably more clever solutions possible to improve performance.
The method will return the last found combination, this means that two input lists with the same values in different order might yield different results:
maxSum([1, 4, 200, 5], 205) == [200, 5];
maxSum([5, 200, 1, 4], 205) == [200, 1, 4];
If you want all possible combinations replace this line:
sums[max] = values.slice(); // https://jsperf.com/copying-an-array
with this:
sums[max] = sums[max] || [];
sums[max].push(values.slice());
All combinations are then returned:
maxSum([1, 4, 200, 5], 205) == [[1, 4, 200], [200, 5]];
But note that this will always return an array of arrays, even when there is only one possibility:
maxSum([40, 138, 29, 450], 250) == [[40, 138, 29]];
Here's a brute force solution. First we get every possible combination of values from the original array, take their sum, and see which of those gets us the highest value without overflowing the given maximum.
var ary = [40, 138, 29, 450];
// Function to construct a power set. A power set is just the set of
// all possible subsets of some given set.
function makePowerSet(ary) {
powerSet = [];
for (let ps = 1; ps <= Math.pow(2, ary.length); ps++) {
subset = [];
for (let i = 0; i < ary.length; i++) {
if (ps & Math.pow(2, i)) subset.push(ary[i]);
}
powerSet.push(subset);
}
return powerSet;
}
// Function to calculate the sum of an array.
function getSum(ary) {
return ary.reduce((sum, cur) => {
return sum + cur;
}, 0);
}
function getSubsetBelow(val, ary) {
let bestSoFar;
let bestSoFarSum = 0;
for (let subset of makePowerSet(ary)) {
const sum = getSum(subset);
if (sum > val) continue;
if (sum > bestSoFarSum) {
bestSoFar = subset;
bestSoFarSum = sum;
}
}
console.log("Got a best sum value of", bestSoFarSum, "with the subset", bestSoFar);
}
getSubsetBelow(250, ary)
This seems very similar to the knapsack problem, which is NP-hard, so I don't know if you'll ever be able to find an efficient algorithm for this. However, there are definitely a few optimizations that can be made to what I've written here, for example, any element of the array already greater than the limit can't be part of the solution (easy way to eliminate 450).
#Find a maximum sum of a compact subsequence of array elements.
import sys
def solution(A):
max_ending = max_slice = -sys.maxsize
if(len(A)==1):
return A[0]
else:
for a in A:
max_ending =max(a,max_ending + a)
max_slice = max(max_slice, max_ending)
return max_slice

Optimize- get third largest num in array

So, I was working on this challenge to return the third largest number in an array. I had got it worked out until I realized that I must account for repeat numbers. I handled this by adding 3 layers of for loops with variables i, j, and k. You'll see what I mean in the code. This is not terribly efficient or scalable.
My question is, how can I optimize this code? What other methods should I be using?
function thirdGreatest (arr) {
arr.sort(function(a, b) {
if (a < b) {
return 1;
} else if (a > b) {
return -1;
} else {
return 0;
}
});
for ( var i = 0; i < arr.length; i++) {
for (var j = 1; j < arr.length; j++) {
for (var k = 2; k < arr.length; k++) {
if (arr[i] > arr[j]) {
if (arr[j] > arr[k]) {
return arr[k];
}
}
}
}
}
}
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3
Since you already sorted the array, it seems like you should be fine iterating over the list and keep track of the numbers you have already seen. When you have seen three different numbers, return the current one:
var seen = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== seen[0]) {
if (seen.length === 2) {
return arr[i];
}
seen.unshift(arr[i]);
}
}
function thirdGreatest (arr) {
arr.sort(function(a, b) {
return b - a;
});
var seen = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== seen[0]) {
if (seen.length === 2) {
return arr[i];
}
seen.unshift(arr[i]);
}
}
}
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3
Note: You can simplify the sort callback to
arr.sort(function(a, b) {
return b - a;
});
// With arrow functions:
// arr.sort((a, b) => b - a);
The callback has to return a number that is larger, smaller or equal to 0, it doesn't have to be exactly -1 or 1.
A one-"line"r using Set to remove duplicates
Array.from(new Set(arr)).sort(function(a, b) {
return b - a;
})[2];
Set now has reasonable browser support
The optimal solution is to do this in a single pass O(n) time. You do not need to sort the array - doing so makes your solution at-least (n log n).
To do this in as single pass, you simply need three temporary variables: largest, secondLargest, thirdLargest. Just go through the array and update these values as necessary (i.e. when you replace largest it becomes second largest, etc...). Lastly, when you see duplicates (i.e. currentValue == secondLargest), just ignore them. They don't affect the outcome.
Don't forget to check for edge cases. You cannot provide an answer for [2, 2, 2, 2, 2] or [3, 2].
Try to think about what data structure you can use here. I suggest a set. Every time you add a nested loop your function gets exponentially slower.
Edited:
function thirdGreatest(arr) {
var s = Array.from(new Set(arr)).sort(function(a, b) {
return a - b;
})
return s[2] || s[1] || s[0] || null;
}
Working Example
We need to be able to handle:
[1,2,1,2] // 2
[1,1,1,1] // 1
[] // null
This assumes that you get an array passed in.
If you do not have a third largest number, you get the second.
If you do not have a second largest you get the first largest.
If you have no numbers you get null
If you want the 3rd largest or nothing, return s[2] || null
Many of the other answers require looping through the initial array multiple times. The following sorts and deduplicates at the same time. It's a little less terse, but is more performant.
const inputArray = [5,3,23,24,5,7,3,2,5,10,24,2,31,31,31];
const getThirdGreatest = inputArray => {
const sorted = [inputArray[0]]; // Add the first value from the input to sorted, for our for loop can be normalized.
let migrated = false;
let j;
for(let i = 1; i<inputArray.length; i++) { // Start at 1 to skip the first value in the input array
for(j=0; j<sorted.length; j++) {
if(sorted[j] < inputArray[i]) {
// If the input value is greater than that in the sorted array, add that value to the start of the sorted array
sorted.splice(j,0,inputArray[i]);
migrated = true;
break;
} else if(sorted[j] === inputArray[i]) {
// If the input value is a duplicate, ignore it
migrated = true;
break;
}
}
if(migrated === false) {
// If the input value wasn't addressed in the loop, add it to the end of the sorted array.
sorted[sorted.length] = inputArray[i];
} else {
migrated = false;
}
}
// Return the third greatest
return sorted[2];;
};
const start = performance.now();
getThirdGreatest(inputArray); // 23
const end = performance.now();
console.log('speed: ', end - start); // 0.1 - 0.2ms
One single iteration O(n) and very fast method of doing this is making your own Set like object. The advantageous point is making no comparisons at all when constructing our "sorted" list of "unique" elements which brings enormous efficiency. The difference is very noticeable when dealt with huge lists like in the lengths exceeding 1,000,000.
var arr = [5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31],
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null))),
third = sorted[sorted.length-3];
document.write(third);
If you think Object.keys might not return a sorted array (which i haven't yet seen not) then you can just sort it like it's done in the Set method.
Here i tried it for 1,000,000 item array and returns always with the correct result in around 45msecs. A 10,000,000 item array would take like ~450msec which is 50% less than other O(n) solutions listed under this question.
var arr = [],
sorted = [],
t0 = 0,
t1 = 0,
third = 0;
for (var i = 0; i<1000000; i++) arr[i] = Math.floor(Math.random()*100);
t0 = performance.now();
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null)));
third = sorted[sorted.length-3];
t1 = performance.now();
document.write(arr.length + " size array done in: " + (t1-t0) + "msecs and the third biggest item is " + third);

Categories

Resources