Javascript Array: Algorithm sorting and picking - javascript

In this case I have an array of 4 integers between 0 and 256 that need to be sorted ascending. eg:
[0, 12, 211, 4] when I sort the I get (of course): [0, 4, 12, 211]
I simply get the integer value by requesting Array[0] (first indexed)
now, my problem is; many times, there are equal values in the array. like:
[0, 0, 0, 12] // already sorted
In these cases I need to pick a random index from the topmost equal values (0,0,0), other possiblities are (after sorting):
[211, 211, 211, 255] // results in 0 OR 1 OR 2
[13, 13, 125, 256] // results in 0 OR 1
[4, 211, 211, 255] // results in 0
[0, 1, 1, 4] // results in 0;
so I need to pick a random index from the topmost values in a ascending sorted array.
Is that to be done while sorting , or in a simpler way than a lot of if-elses?

Sorting
If speed is important (which you seem to suggest it is) then have you looked at sorting networks? I have found these to be incredibly fast when sorting small sets of numbers.
To sort with a sorting network:
Network for N=4, using Bose-Nelson
Algorithm.
CreationDate: Tue Feb 15 04:44:06 2011
Creator: perl module
Algorithm::Networksort version 1.05.
Network for N=4, using Bose-Nelson
Algorithm. Input line. Comparator size
1. Comparator size 2. There are 5 comparators in this network, grouped
into 3 parallel operations.
[[0,1],[2,3]] [[0,2],[1,3]] [[1,2]]
This is graphed in 4 columns.
Pseudo:
if [0] > [1] { swap(0, 1) }
if [2] > [3] { swap(2, 3) }
if [0] > [2] { swap(0, 2) }
if [1] > [3] { swap(1, 3) }
if [1] > [2] { swap(1, 2) }
Finding Set of Indexes
Anyway this problem can be solved with a sort of divide and conquer (pseudo):
// First index is unique
if [0] != [1]
return 0
// First 2 are equal
else if [1] != [2]
return 0 or 1
// First 3 are equal
else if [2] != [3]
return 0 or 1 or 2
// All are equal
else
return 0 or 1 or 2 or 3
end
Or you can do this with a loop:
for i = 0 to 2
if [i] != [i+1]
return random(0 to i)
break loop
end if
loop
You should go for the algorithm which makes most semantic sense and is easiest to maintain probably over anything else, unless speed is crucial.

This will return a random index of equal values:
var myNums = new Array(211, 211, 211,211,214, 255);
myNums = myNums.sort();
if(myNums.length == 0)
alert("Array is zero sized");
else
{
var smallest = myNums[0];
var last=0;
var start = 0;
while(smallest == myNums[last])
last++;
last = last-1;
var randIndex = Math.floor(Math.random() *(last - start + 1)+ start);
alert(randIndex);
}
See it work here:
http://jsfiddle.net/rAbh3/

Making a for loop from right to left to select the elements will do the trick and if is done after the sorting process it will only add N to the complexity
Changing from nlogn to nlogn + n is not that much cpu expensive.
Edit:
The top most equal values in your example, shouldn't it be:
[211, 211, 211, 255] // results in 0 OR 1 OR 2
[13, 13, 125, 256] // results in 0 OR 1
[4, 211, 211, 255] // results in 1 or 2
[0, 1, 1, 4] // results in 1 or 2;
??

Related

Please Help me solve this problem i have trying to solve Range extraction

The problem i try to solve is this using js :
A format for expressing an ordered list of integers is to use a comma separated list of either:
-individual integers
-or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. The range includes all integers in the interval including both endpoints. It is not considered a range unless it spans at least 3 numbers. For example "12,13,15-17"
Complete the solution so that it takes a list of integers in increasing order and returns a correctly formatted string in the range format.
Example:
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
// returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20"
so my idea was to use 3 functions :
1- newRangeStart: creates a new Array in results to store the range numbers and puts in it the first element of the range (RangeStarter).
2-olSupp : deletes elements from the list that were used by the 3rd function RangeArr so that we get a new Arr with a new RangeStarter using 1st function.
3-RangeArr : uses the 1st function than adds elements from the list to the array created by it which are consecutive starting from the Range starter, and then uses the 2nd function to delete the elements used from the ol so the next time we use the RangeArr function it creates another range.
By repeating the RangeArr function with a while loop that runs until ol becomes empty we will have a resuts array with arrays inside of it that contains ranges.
now the poblem is when i run RangeArr function it doesn't delete the used elements from the ol as i want i tried to fix the olSupp function several times but it just doesn't work i think there is a problem in my entire code pls someone help me to fix it here is my code:
function solution(list){
// TODO: complete solution
let ol = [...list];
let results = [];
/*This adds a new array for a range by adding the first number of the range to
an array (2D array) and stores it in the resuts array */
function newRangeStart(orderedlist,result){
result.push([orderedlist[0]]);
return result;
}
/*This functions takes the ol and deletes elements that are found in the results
so that the next time we run the newRangeStart function it creates an other array
for another range with a different start number*/
function olSupp(orderedlist,result){
let toRemove = result.flat();
let newList = [];
for (let i = 0; i < orderedlist.length; i++) {
if(!toRemove.includes(orderedlist[i])){
newList.push(orderedlist[i]);
}
}
orderedlist = [...newList];
return orderedlist;
}
/*Finally RangeArr function creates a range from the ol (ordered list)
starting by the first element of the results array and then uses olSupp to delete
the used numbers from the ol */
function RangeArr (orderedlist,result){
newRangeStart(orderedlist,result);
let i = 0;
while(orderedlist[i+1]- orderedlist[i] == 1 && orderedlist[i+2]- orderedlist[i+1]== 1) {
result[i].push(orderedlist[i+1],orderedlist[i+2]);
i = i+1;
}
olSupp(orderedlist,result);
return result;
}
/*we execute the RangeArr function until ol becomes emepty
and this will give us multiple arrays in the result array containing
the elements of each range found in the ol */
//PS: i didnt put the code beacuse it causes an infinte loop using while
RangeArr(ol,results);
console.log(ol,results);
}
solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]);
const solution = arr => {
let s = null, d = 0;
const result = arr.sort((a, b) => a - b).reduce((p, c, i, arr) => {
d++;
if (!s) s = c;
if (arr[i + 1] - s > d) {
s === c ? p.push(s) : d < 3 ? p.push(s, c) : p.push(`${s}-${c}`);
s = null;
d = 0;
}
if (arr[i + 1] === undefined) s === c ? p.push(s) : d < 3 ? p.push(s, c) : p.push(`${s}-${c}`);
return p;
}, []);
return result;
}
console.log(solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]));
Variables:
s (start) for saving the number that starts a range.
d (distance) for counting the distance between the start and end of a range.
for a and b read the doc for javascript sort method to learn more.
same with p (pervious), c (current), i (index) and arr (original array) read the doc for javascript reduce method
Logic:
we can determine a range (for example: [1, 2, 3, 4, 5] is equal to "1-5") by calculate the distance between the starting number 1 and the ending number 5. so the distance between 1 and 5 is 4 because 1 needs to increment by 1 four times to reach 5 and the distance inside the array between the starting number 1 (index 0) and the ending number 5 (index 4) is also 4. so if we take the end minus the start 5 - 1 = 4 if it matches to the correct distance inside the array which is 4 then it is a range "1-5". let's have another example [1, 2, 3, 6, 7, 8], this should be "1-3" and "6-8". when we calculate the distance with 1 and 6 (6 - 1 = 5) we get 5 which is incorrect, because it doesn't match the correct distance inside the array (1 is at index 0 and 6 is at index 3, 3 - 0 = 3. the distance between 1 and 6 is only 3 index apart and not 5, that isn't a range). but if we do the calculate with 1 and 3 it matches our criteria and it's a range "1-3".
Code:
we have to do the calculation inside a loop (i'm using reduce method because it's convenient). first thing i do in the loop is d++ to track the distance of index(s) that the loop had travel inside the array. if (!s) s = c; is for check we've saved a starting number or not. if (arr[i + 1] - s > d) { ... } this is where we do the calculation to see if the current element inside the array minus s is greater than the distance index we've travel or not. if it's true then it means s and the last element must be a range and we push that range in the result array. and then we reset s and d to let them work on the next range.
Update
const solution = arr => {
let s = null;
return arr.sort((a, b) => a - b).reduce((p, c, i, arr) => {
if (!s) s = c;
if (c + 1 !== arr[i + 1]) {
s === c ? p.push(s) : c - 1 === s ? p.push(s, c) : p.push(`${s}-${c}`);
s = null;
}
return p
}, [])
}
console.log(solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]));

How do you make an offset for putting things in an array while having it loop

I am trying to make a code that sets the second array of characters to the second half of the first array of characters, but where I can change a number and it changes where each character is on the second array.
For instance if array 1 is [1,2,3,4,5,6,7,8,9,0] and the variable changing it is 3, then array 2 should be [9,0,6,7,8].
I have tried
for(var q = Math.floor(Charecter1.length/2); q<Charecter1.length;q++){
var v = q + document.getElementById("seed").value
for (var w = 0; v >= Charecter1.length; w++){
v-=Math.ceil(Charecter1.length/2)
}
Charecter2[v - Math.floor(Charecter1.length/2)] = Charecter1[(-1*q)+(Charecter1.length-1)]
}
When it starts,
Charecter1 is ["q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l",";",":","'",'"',"z","x","c","v","b","n","m",",","<",">","`","~","1","!","2","#","3","#","4","5","%","6","7","&","8","9","0","_","="," ","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M"]
document.getElementById("seed").value is 0
Charecter2 should be ["3","#","4","5","%","6","7","&","8","9","0","_","="," ","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M"]
but, when i run the code it says Charecter2 is [empty, "e", empty × 9, "w", empty × 9, "q", empty × 9, "r"]. I do not understand why this is not working.
You could take a temporary array with the last half of the array and build from this array the last part first and the first part at last with the given offset.
This approach uses a bitwise shifting by one to the right. The result is an integer value, like
Math.floor(array.length / 2)
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
offset = 3,
temp = array.slice(array.length >> 1),
result = [...temp.slice(offset), ...temp.slice(0, offset)];
console.log(result);

Best method to sort a nested array based on multiple nested values

I need to determine the most economical combination of [8, 10, 12] to equal a given number (between 8 and 60) and store that combination in a variable for later use. Numbers can repeat (8+8=16, 10+10=20, etc). The given number will always be even. It is acceptable for combinations to exceed the given number by 2 (eg given number is 14, acceptable combination would be 8+8).
I can calculate the combinations and store them in a nested array. I can then sort the array on one of the indices which will solve the problem in many cases. I've tried some conditional sorting on the array but the results have been inconsistent.
My code...
var combo = (num) => {
var arr = [];
let units = [8, 10, 12];
for (let i = 0; i < units.length; i++) {
arr.push([units[i],Math.floor(num / units[i]),num%units[i]]);
}
console.log(arr);
};
//combo(24);
//returns
//[[8, 3, 0],[10, 2, 4],[12, 2, 0]]
While both the first and third nested arrays are correct, the third would be the most economical and desired result.
//combo(28)
//returns
//[[8, 3, 4],[10, 2, 8],[12, 2, 4]]
In this case, the second array is the desired result
//combo(42)
//returns
//[[8, 5, 2],[10, 4, 2],[12, 3, 6]]
In this case, the third array is the desired result as replacing the 6 with an 8 later will exceed the given num by 2 - an acceptable result.
My goal is to first isolate the desired result and store it in a separate variable OR in a consistent location within the array (through sorting) for use later. I've discovered 1000 ways not to do this but, a successful method escapes me. I would classify my javascript experience as 'high-beginner' or 'low-intermediate' as I'm learning in my spare time. I may be approaching this from the wrong direction so any guidance is greatly appreciated. Hopefully, my description of the problem makes sense.
Thanks in advance.
According to your comments, the optimal solution is the one with the smallest factor and the remainder being zero, that is trivial.
If none such solution is found, I think what you want is to first sort on the second field descending (least lengths) and then on the percentage of the remainder of the full length ascending in order to minimize cutoff. Consider:
var combo = (length) => {
var optimals = [];
var suboptimals = [];
[8, 10, 12].forEach( (unit) => {
let lengths = Math.floor(length / unit);
let remainder = length % unit;
let fraction = remainder / unit ;
if ( remainder == 0 )
optimals.push( [unit, lengths] );
else
suboptimals.push( [unit, lengths, remainder, fraction] );
} );
// This only works as long as the original input list is sorted ascending
// Because then we know the most optimal solution must be the one
// with the biggest factor so we can simply return the last solution
if ( optimals.length > 0 )
return optimals[ optimals.length - 1 ];
// Else we sort the suboptimals
suboptimals.sort( (a, b) => {
return a[1] < b[1] ? -1 : // First on # of lengths descending
a[1] > b[1] ? 1 :
a[3] > b[3] ? -1 : // Then on fraction ascending
a[3] < b[3] ? 1 :
0;
});
return suboptimals[0];
};
console.log( combo(24) );
console.log( combo(28) );
console.log( combo(42) );
Prints:
> Array [12, 2]
> Array [10, 2, 8, 0.8]
> Array [12, 3, 6, 0.5]

Get list of all possible 32 byte arrays

I'm trying to get an array that contains all the possible 32 byte long arrays. I should end up with something like this, where each subarray is holds 32 bytes
arr[0] = [0, 0 ...., 0, 0]
arr[1] = [0, 0 ...., 0, 1]
arr[2] = [0, 0 ...., 0, 2]
arr[255] = [0, 0 .... 0, 255]
arr[?] = [255, 195, .... 6, 3]
arr[n] = [255, 255, .... 255, 255]
There should be a way to do something like this with 3 for-loops right? I assume two of them would count to 255 and 32 respectively, but I'm not sure about the 3rd? Would it be all the number of all the possible arrays? How would one go about making a program that did this?
Also in terms of time, how long would it take to loop through all those arrays? I imagine quite a while since the number is probably really large, but I'm not sure just how large.

Debugging number of possible sums - JavaScript

I'm writing an algorithm that finds the number of possible sums from an array that contains unique values, and a second array that contains the quantity of each corresponding value in the first array. For example, the pair [10, 20, 50] and [1, 2, 1] indicates that the total number of elements that I am combining is actually [10, 20, 20, 50], because there are two instances of the number 20.
My algorithm is currently passing every test except one, and I cannot for the life of me figure out why, since it is passing other, more complicated pairings. Here is my algorithm so far:
function possibleSums(values, quantity) {
const sums = new Set([]);
//my recursive function that finds all possible combinations moving
//down from a specific starting index in the valueArrray:
const combinations = (valueArray, countArray, position, currentSum) => {
if (currentSum > 0) sums.add(currentSum);
for(let i = position; i < valueArray.length; i++){
if (countArray[i] === 0){
continue;
}
currentSum += valueArray[i];
//reduce the count of that value that is still available
countArray[i]--;
//send off a recursive call to find the sum with the next
//available value
combinations(valueArray, countArray, i, currentSum);
//return the original count since `i` is increasing past
//the current value's location in the valueArray
countArray[i]++;
}
}
for (let i = 0; i < values.length; i++){
//start the recursive function calls at each index in the value array
combinations(values, quantity, i, 0)
}
return sums.size
}
This algorithm passes array pairs like :
[3, 1, 1] and [111, 84, 104] with the expected output of 521
[1, 1, 1, 1, 1] and [9, 19, 18, 12, 19] with the expected output of 77
[1, 2, 3] and [2, 3, 10000] with the expected output of 30008
but is failing
[10, 50, 100, 500] and [5, 3, 2, 2] , outputting 96 when the expected output is 122
Can anyone spot what I am missing in my logic?
122 expected output is not too big a test case for logging :)
Let's log the parameters:
...
const combinations = (valueArray, countArray, position, currentSum) => {
console.log(countArray, position, currentSum)
if (currentSum...
We see this, which makes sense:
[ 0, 0, 0, 0 ] 3 1400
But then we also see:
[ 0, 0, 1, 0 ] 3 1400
[ 0, 1, 0, 0 ] 3 1400
...
[ 1, 1, 1, 0 ] 3 1400
which don't.
Changing the current argument during the iteration seems to be affecting the variable during other calls.
Changing
currentSum += valueArray[i];
...
combinations(valueArray, countArray, i, currentSum);
to
//currentSum += valueArray[i];
...
combinations(valueArray, countArray, i, currentSum + valueArray[i]);
seems to do the trick.

Categories

Resources