What's the problem with this implementation of selection sort? - javascript

I am learning selection sort.
I am getting the correct output for some values, but not for all the values, don't know why??
Please find below code snippet:
function selectionSortRecursion(arr,p){
if( arr.length === 1){
return p;
}
min=arr[0];
for(var i =0;i<arr.length;i++){
if (arr[i]<min){
min = arr[i];
var minIdx=i;
}
}
temp=arr[0];
arr[0]=arr[minIdx];
arr[minIdx]=temp;
p.push(arr.shift());
return selectionSortRecursion(arr,p);
}
console.log(selectionSortRecursion([2,3,5,-3,20,0,2,6,-23],[]));

The problem is that the variable minIdx is not declared unless the body of the if statement inside the loop is executed. If the minimum element is at index 0, then arr[i] < min is never true and minIdx is undefined.
To solve it, write var minIdx = 0; before the loop, since min is initialised as the value at index 0. A couple of your other variables should be declared with var, too:
function selectionSortRecursion(arr, p) {
if(arr.length === 0) {
return p;
}
var min = arr[0];
var minIdx = 0;
for(var i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
minIdx = i;
}
}
var temp = arr[0];
arr[0] = arr[minIdx];
arr[minIdx] = temp;
p.push(arr.shift());
return selectionSortRecursion(arr, p);
}
Note that I've also changed the loop variable i to start at 1, since there's no need to compare index 0 with itself; and the base case of the recursion should be when arr.length is 0, not 1, to avoid losing the last element.

Related

Why is this function creating an infinite looop?

The first function determines if a number is prime. The second function is supposed to create an array with all prime numbers up to and including the max value, but it gives me an infinite loop for some reason.
function isPrime(num) {
for (i = 2; i < num; i++) {
if (num % i === 0) {
return false
}
}
if (num <= 1) {
return false;
}
return true;
}
function primes(max) {
var all = [];
for (i = 2; i <= max; i++) {
if (isPrime(i)) {
all.push(i);
}
}
}
primes(17);
Your i variable is global, so both functions use the same i. This means the first function changes it while the second one is looping.
As the first function will have set i to num-1 when it finishes, and num was the value of i before executing it, it effectively decrements i with one. And so i will get the same value in the next iteration of the loop in the second function, never getting forward.
Solve this by putting the var keyword in both functions.
for(var i=2; // ...etc)
The variable i in your two loops are global variables and they overwrite each other so the first loop never ends.
In ur code problem is with the scope of variable i, In prime no calculation, u can check upto the sqrt of no, if no is not divisible by any no upto its sqrt, then it will a prime no, Try this:
function isPrime(num) {
let k = Math.sqrt(num);
for (let i = 2; i <= k; i++) {
if (num % i === 0) {
return false
}
}
return true;
}
function primes(max) {
let all = [];
for (let i = 2; i <= max; i++) {
if (isPrime(i)) {
all.push(i);
}
}
console.log(all)
}
primes(17);
primes(25);

returning only the first value of the largest element

I'm trying to return the largest element in the array. With strings this means the longest string. How do I return only the first instance of the largest element.
My code:
function getLongestElement(arr) {
var max = "";
var counter = 0;
for (var i = 0; i < arr.length; i++){
if (arr[i].length > counter) max = arr[i]
}
return max;
}
getLongestElement(['one', 'two', 'three', "thre1"]); // "thre1" not "three".
I'm not quite sure whats wrong with this code. No matter what the largest value is it only returns the last element in the array. Help?
counter is initialized to 0, but you never change its value so the if statement with arr[i].length > counter is always true (unless arr[i].length == 0). To fix it you need to keep track of the largest element of the array inside the loop:
// I renamed counter to maxLength for readability
function getLongestElement(arr) {
var max;
var maxLength = -1;
for (var i = 0; i < arr.length; i++){
if (arr[i].length > maxLength){
maxLength = arr[i].length;
max = i;
}
}
return arr[max];
}
First, good alghoritm should make no assumptions. That means your max shouldn't start from "", but using first array's element. You also don't edit your counter value, that's your main problem. But it is redundant and you can write this function without counter.
function getLongestElement(arr) {
if (arr.length < 1) return /* Some Exception */;
var max = arr[0];
for (var i = 1; i < arr.length; i++) {
if (arr[i].length > max.length) max = arr[i];
}
return max;
}
You forgot to update counter
function getLongestElement(arr) {
var max = "";
var counter = 0;
for (var i = 0; i < arr.length; i++){
if (arr[i].length > counter) {
max = arr[i];
counter = max.length;
}
}
return max;
}
console.log(getLongestElement(['one', 'two', 'three', "thre1"])); // "thre1" not "three".
If you're looking for a pragmatic solution, I'd suggest lodash's _.maxBy:
_.maxBy(['one', 'two', 'three', "thre1"], function(str) {
return str.length;
})
If you're looking for a theoretical solution for the sake of learning,
function getLongestElement(arr) {
var max;
var counter = 0;
for (var i = 0; i < arr.length; i++){
if (arr[i].length > counter) max = arr[i]
counter = Math.max(arr[i].length, counter)
}
return max;
}
The key point here is to make sure you're updating the counter variable to whatever the currently longest length is.

Recursive Return Statement

This is a program that I have wrote to solve a problem. Check if there exists a sum of elements that equal to the maximum number in the array, return true if so, false otherwise.
var found = "false";
var max;
function ArrayAdditionI(array) {
max = Math.max.apply(null,array);
var p = array.indexOf(max);
array.splice(p,1);
array.sort(function(a, b){return a-b;});
found = findSum(array, 0, 0);
return found;
}
function findSum(array, sum, startIndex){
for(var i = startIndex; i < array.length ; i++){
sum += array[i];
if(sum === max){
found = "true";
break;
}else if(sum > max){
break;
}
if(i+2 < array.length && sum < max){
findSum(array, sum, i+2);
}
}
if(startIndex < array.length && sum !== max){
return findSum(array, 0, startIndex+1);
}
return found;
}
ArrayAdditionI(readline());
I had to use global variable, found, to indicate where a sum has been found or not. The return statement always returned undefined.
Also, if I use a return statement in the following if statement, the code does not work properly.
if(i+2 < array.length && sum < max){
return findSum(array, sum, i+2);
}
This is not the optimal solution to the problem, but this is the version I got working.
My question is Why am I getting undefined if I use return statement within the if statement. Also, I tried not using global and use return true if sum === max and at the very end return false, it always returns false or undefined.
-- UPDATE 2: Code with error results --
function ArrayAdditionI(array) {
var max = Math.max.apply(null,array);
//remove max element from array
var p = array.indexOf(max);
array.splice(p,1);
//sort array
array.sort(function(a, b){return a-b;});
//call find sum function
return findSum(array, 0, 0, max);
}
function findSum(array, sum, startIndex){
for(var i = startIndex; i < array.length ; i++){
sum += array[i];
if(sum === max){
return true;
}else if(sum > max){
break;
}
if(i+2 < array.length && sum < max){
**return** findSum(array, sum, i+2, max);
}
}
if(startIndex < array.length && sum !== max){
return findSum(array, 0, startIndex+1, max);
}
return false;
}
// calling the first function
ArrayAdditionI([ 7, 2,90, 31, 50 ]);
The start of the program is this call: ArrayAdditionI([ 7, 2,90, 31, 50 ]);
The return should be true.
Also, ArrayAdditionI([ 1,2,3,4 ]); is true.
However, ArrayAdditionI([ 1,2,3,100 ]); is false.
The return statement between ** **, when removed the code works, otherwise I either get false or undefined. I do not understand this part! Why does removing the return solves the problem, I thought every recursive call must be proceeded with a return statement.
Is the problem maybe due to multiple calls ? Am I using recursion in the improper way?
There are a few mistakes on your code that could lead to the error.
T.J. Crowder already said, use actual booleans instead of a string.
The found variable isn't defined inside your findSum function. That makes JavaScript assume you're setting a global variable.
Add var found = false; as the very first line of your findSum function.
Inside the last if inside your for there are a call to the findSum function but it's not returning it's value nor assigning it to the found variable.
Fix those and update your question with the results.
The following function should give you a true or false answer as to whether or not any combination of values inside an array produces the max figure.
var a = [
1, 1, 1, 1, 1, 1,
1, 1, 1, 9
]
var b = [1,1,1,5]
function MembersHoldMaxSum(arr) {
var i, r = false, index, max = Math.max.apply(null, arr), index;
for (i = 0; i <= arr.length - 1; i++) {
for (index = 0; index <= arr.length - 1; index++) {
var new_arr = [], ct;
for (ct = 0; ct <= arr.length - 1; ct++) {
if (index != ct) { new_arr.push(arr[ct]) }
}
while (new_arr.length != 1) {
var sum = 0, ct2 = 0;
for (ct2 = 0; ct2 <= new_arr.length - 1; ct2++) {
sum += new_arr[ct2];
}
if (sum == max) { return true }
new_arr.pop()
}
}
}
return r
}
var returns_true = MembersHoldMaxSum(a);
var returns_false = MembersHoldMaxSum(b);

how do I find the id of the minimum value of an array?

I know that you can have the minimum value of an array by typing
var min = Math.min.apply(null, array)
but this will return the smallest value of an array and not the id of this one
for exemple if I have these values:
array[1] = 24;
array[2] = 45;
I want it to return 1 (the ID holding the minimum value) but idk how to do, could someone help me with this issue please?
var index = array.indexOf(Math.min.apply(null, array));
You can use Array#reduce() to get the smallest number, while avoiding holes in the Array if needed.
array.reduce(function(obj, n, i) {
if (n < obj.min)
obj.i = i;
return obj;
}, {min:Infinity,i:-1}).i;
Or if performance and compatibility is a concern, you could just loop.
var res = -1;
var min = Infinity;
for (var i = 0; i < array.length; i++) {
if ((i in array) && array[i] < min) {
min = array[i];
res = i;
}
}
You can do it like this:
var id = array.indexOf(Math.min.apply(null, array));
Once you've got the value, you can use indexOf to get the index, like this:
var index = array.indexOf(Math.min.apply(null, array));
You should be aware that indexOf was only recently included in JavaScript (ES5/JS 1.6 to be precise) so you may want to find some wrapper for it if the function does not exist.
See the MDN for more information (which contains an example implementation of a backwards compatible function).
Just like the algorithm for finding the min value, but you have to track the minimum index as well
function minIndex(arr) {
if (!arr || arr.length === 0) {
return -1;
}
var min = arr[0];
var minIndex = 0;
for (var len = arr.length; len > 0; len--) {
if (arr[len] < min) {
min = arr[len];
minIndex = len;
}
}
return minIndex;
}
check out this fiddle

Variable amount of nested for loops

Edit: I'm sorry, but I forgot to mention that I'll need the values of the counter variables. So making one loop isn't a solution I'm afraid.
I'm not sure if this is possible at all, but I would like to do the following.
To a function, an array of numbers is passed. Each number is the upper limit of a for loop, for example, if the array is [2, 3, 5], the following code should be executed:
for(var a = 0; a < 2; a++) {
for(var b = 0; b < 3; b++) {
for(var c = 0; c < 5; c++) {
doSomething([a, b, c]);
}
}
}
So the amount of nested for loops is equal to the length of the array. Would there be any way to make this work? I was thinking of creating a piece of code which adds each for loop to a string, and then evaluates it through eval. I've read however that eval should not be one's first choice as it can have dangerous results too.
What technique might be appropriate here?
Recursion can solve this problem neatly:
function callManyTimes(maxIndices, func) {
doCallManyTimes(maxIndices, func, [], 0);
}
function doCallManyTimes(maxIndices, func, args, index) {
if (maxIndices.length == 0) {
func(args);
} else {
var rest = maxIndices.slice(1);
for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) {
doCallManyTimes(rest, func, args, index + 1);
}
}
}
Call it like this:
callManyTimes([2,3,5], doSomething);
Recursion is overkill here. You can use generators:
function* allPossibleCombinations(lengths) {
const n = lengths.length;
let indices = [];
for (let i = n; --i >= 0;) {
if (lengths[i] === 0) { return; }
if (lengths[i] !== (lengths[i] & 0x7fffffff)) { throw new Error(); }
indices[i] = 0;
}
while (true) {
yield indices;
// Increment indices.
++indices[n - 1];
for (let j = n; --j >= 0 && indices[j] === lengths[j];) {
if (j === 0) { return; }
indices[j] = 0;
++indices[j - 1];
}
}
}
for ([a, b, c] of allPossibleCombinations([3, 2, 2])) {
console.log(`${a}, ${b}, ${c}`);
}
The intuition here is that we keep a list of indices that are always less than the corresponding length.
The second loop handles carry. As when incrementing a decimal number 199, we go to (1, 9, 10), and then carry to get (1, 10, 0) and finally (2, 0, 0). If we don't have enough digits to carry into, we're done.
Set up an array of counters with the same length as the limit array. Use a single loop, and increment the last item in each iteration. When it reaches it's limit you restart it and increment the next item.
function loop(limits) {
var cnt = new Array(limits.length);
for (var i = 0; i < cnt.length; i++) cnt[i] = 0;
var pos;
do {
doSomething(cnt);
pos = cnt.length - 1;
cnt[pos]++;
while (pos >= 0 && cnt[pos] >= limits[pos]) {
cnt[pos] = 0;
pos--;
if (pos >= 0) cnt[pos]++;
}
} while (pos >= 0);
}
One solution that works without getting complicated programatically would be to take the integers and multiply them all. Since you're only nesting the ifs, and only the innermost one has functionality, this should work:
var product = 0;
for(var i = 0; i < array.length; i++){
product *= array[i];
}
for(var i = 0; i < product; i++){
doSomething();
}
Alternatively:
for(var i = 0; i < array.length; i++){
for(var j = 0; j < array[i]; j++){
doSomething();
}
}
Instead of thinking in terms of nested for loops, think about recursive function invocations. To do your iteration, you'd make the following decision (pseudo code):
if the list of counters is empty
then "doSomething()"
else
for (counter = 0 to first counter limit in the list)
recurse with the tail of the list
That might look something like this:
function forEachCounter(counters, fn) {
function impl(counters, curCount) {
if (counters.length === 0)
fn(curCount);
else {
var limit = counters[0];
curCount.push(0);
for (var i = 0; i < limit; ++i) {
curCount[curCount.length - 1] = i;
impl(counters.slice(1), curCount);
}
curCount.length--;
}
}
impl(counters, []);
}
You'd call the function with an argument that's your list of count limits, and an argument that's your function to execute for each effective count array (the "doSomething" part). The main function above does all the real work in an inner function. In that inner function, the first argument is the counter limit list, which will be "whittled down" as the function is called recursively. The second argument is used to hold the current set of counter values, so that "doSomething" can know that it's on an iteration corresponding to a particular list of actual counts.
Calling the function would look like this:
forEachCounter([4, 2, 5], function(c) { /* something */ });
This is my attempt at simplifying the non-recursive solution by Mike Samuel. I also add the ability to set a range (not just maximum) for every integer argument.
function everyPermutation(args, fn) {
var indices = args.map(a => a.min);
for (var j = args.length; j >= 0;) {
fn.apply(null, indices);
// go through indices from right to left setting them to 0
for (j = args.length; j--;) {
// until we find the last index not at max which we increment
if (indices[j] < args[j].max) {
++indices[j];
break;
}
indices[j] = args[j].min;
}
}
}
everyPermutation([
{min:4, max:6},
{min:2, max:3},
{min:0, max:1}
], function(a, b, c) {
console.log(a + ',' + b + ',' + c);
});
There's no difference between doing three loops of 2, 3, 5, and one loop of 30 (2*3*5).
function doLots (howMany, what) {
var amount = 0;
// Aggregate amount
for (var i=0; i<howMany.length;i++) {
amount *= howMany[i];
};
// Execute that many times.
while(i--) {
what();
};
}
Use:
doLots([2,3,5], doSomething);
You can use the greedy algorithm to enumerate all elements of the cartesian product 0:2 x 0:3 x 0:5. This algorithm is performed by my function greedy_backward below. I am not an expert in Javascript and maybe this function could be improved.
function greedy_backward(sizes, n) {
for (var G = [1], i = 0; i<sizes.length; i++) G[i+1] = G[i] * sizes[i];
if (n>=_.last(G)) throw new Error("n must be <" + _.last(G));
for (i = 0; i<sizes.length; i++) if (sizes[i]!=parseInt(sizes[i]) || sizes[i]<1){ throw new Error("sizes must be a vector of integers be >1"); };
for (var epsilon=[], i=0; i < sizes.length; i++) epsilon[i]=0;
while(n > 0){
var k = _.findIndex(G, function(x){ return n < x; }) - 1;
var e = (n/G[k])>>0;
epsilon[k] = e;
n = n-e*G[k];
}
return epsilon;
}
It enumerates the elements of the Cartesian product in the anti-lexicographic order (you will see the full enumeration in the doSomething example):
~ var sizes = [2, 3, 5];
~ greedy_backward(sizes,0);
0,0,0
~ greedy_backward(sizes,1);
1,0,0
~ greedy_backward(sizes,2);
0,1,0
~ greedy_backward(sizes,3);
1,1,0
~ greedy_backward(sizes,4);
0,2,0
~ greedy_backward(sizes,5);
1,2,0
This is a generalization of the binary representation (the case when sizes=[2,2,2,...]).
Example:
~ function doSomething(v){
for (var message = v[0], i = 1; i<v.length; i++) message = message + '-' + v[i].toString();
console.log(message);
}
~ doSomething(["a","b","c"])
a-b-c
~ for (var max = [1], i = 0; i<sizes.length; i++) max = max * sizes[i];
30
~ for(i=0; i<max; i++){
doSomething(greedy_backward(sizes,i));
}
0-0-0
1-0-0
0-1-0
1-1-0
0-2-0
1-2-0
0-0-1
1-0-1
0-1-1
1-1-1
0-2-1
1-2-1
0-0-2
1-0-2
0-1-2
1-1-2
0-2-2
1-2-2
0-0-3
1-0-3
0-1-3
1-1-3
0-2-3
1-2-3
0-0-4
1-0-4
0-1-4
1-1-4
0-2-4
1-2-4
If needed, the reverse operation is simple:
function greedy_forward(sizes, epsilon) {
if (sizes.length!=epsilon.length) throw new Error("sizes and epsilon must have the same length");
for (i = 0; i<sizes.length; i++) if (epsilon[i] <0 || epsilon[i] >= sizes[i]){ throw new Error("condition `0 <= epsilon[i] < sizes[i]` not fulfilled for all i"); };
for (var G = [1], i = 0; i<sizes.length-1; i++) G[i+1] = G[i] * sizes[i];
for (var n = 0, i = 0; i<sizes.length; i++) n += G[i] * epsilon[i];
return n;
}
Example :
~ epsilon = greedy_backward(sizes, 29)
1,2,4
~ greedy_forward(sizes, epsilon)
29
One could also use a generator for that:
function loop(...times) {
function* looper(times, prev = []) {
if(!times.length) {
yield prev;
return;
}
const [max, ...rest] = times;
for(let current = 0; current < max; current++) {
yield* looper(rest, [...prev, current]);
}
}
return looper(times);
}
That can then be used as:
for(const [j, k, l, m] of loop(1, 2, 3, 4)) {
//...
}

Categories