How to analyse complexity of an Algorithm that contains hidden loops made by external function or method? - javascript

First of all, I would like to apologize in case my title is not concise as it should be, but my point is, if you take a look at the following code which is selection sort algorithm, it's obvious for someone to analyze its complexity.
module.exports = function (arr) {
var temp;
for (var i = 0; i < arr.length; i++) {
var iTh = i;
for (var j = i+1; j < arr.length; j++) {
if (arr[j] < arr[iTh]) {
iTh = j;
}
}
temp = arr[i];
arr[i] = arr[iTh];
arr[iTh] = temp;
}
return arr;
}
But what if an algorithm contains hidden loops which are provided by particular language's functions or methods. For instance these two functions are both reversing a string, and they have JavaScript methods which have complexity behind them too.
So! How can someone analyze the complexity of these two and pick the optimal one? Or they don't qualify to be algorithms?
First Reverse
exports.reverse1 = function (str) {
if (str == undefined || str.length) {
return 0;
}
let collector = [];
for (var i = str.length; i >= 0; i--) {
collector.push(str.charAt(i));
}
return collector.join("");
}
Second Reverse
exports.reverse2 = function (str) {
if (str == undefined || str === "") {
return 0;
}
return str.split("").reverse().join("");
}

Related

Sum of Digits works in console but not when returned

So I'm doing a codewars challenge and I have no clue why my code isn't working. I'm a beginner so please don't hate on me.
This is my code:
function digital_root(n) {
let str = n.toString()
let arr = []
let sum = 0
for (let i = 0; i < str.length; i++) {
arr.push(str.charAt(i))
}
for (let i = 0; i < str.length; i++) {
sum += Number(arr[i])
}
let sumStr = sum.toString()
if (sumStr.length > 1) {
digital_root(sum)
} else if (sumStr.length == 1) {
return sum
}
}
It works when I console.log it but not when I return the value. I'm trying to learn recursion. Thanks for the help!
You need to return digital_root(sum) too, if sumStr.length > 1 in order to access recursive returned value.
you have to write return digital_root(sum) instead of just digital_root(sum).
check below:
function digital_root(n) {
let str = n.toString()
let arr = []
let sum = 0
for (let i = 0; i < str.length; i++) {
arr.push(str.charAt(i))
}
for (let i = 0; i < str.length; i++) {
sum += Number(arr[i])
}
let sumStr = sum.toString()
if (sumStr.length > 1) {
return digital_root(sum)
} else if (sumStr.length == 1) {
return sum
}
}
console.log("Digital Root :", digital_root('123456789'));
Ok, it seems you are missing dealing with the return value of digital_root when you call it recursively. See added "return" statement below.
function digital_root(n) {
let str = n.toString()
let arr = []
let sum = 0
for (let i = 0; i < str.length; i++) {
arr.push(str.charAt(i))
}
for (let i = 0; i < str.length; i++) {
sum += Number(arr[i])
}
let sumStr = sum.toString()
if (sumStr.length > 1) {
// ***** You need to deal with the return value of digital_root when you call it.
return digital_root(sum)
} else if (sumStr.length == 1) {
return sum
}
}
However, while JavaScript's functional coding style does support recursive functions, we need to be aware that most JavaScript compilers are not currently optimized to support them safely. Recursion is best applied when you need to call the same function repeatedly with different parameters from within a loop.
Please read this,
https://www.sitepoint.com/recursion-functional-javascript/#:~:text=However%2C%20while%20JavaScript's%20functional%20coding,parameters%20from%20within%20a%20loop.

If condition vs loop one item

I was just curious, is it worth to have if condition before looping some array, that in 90% will be array of 1 item?
Code example:
const a = [3];
const aLength = a.length;
if(aLength > 1) {
for(let i = 0; i < aLength; i++) {
func(i);
}
} else {
func();
}
function func(position = 0) {
console.log('hi' + position);
}
I agree with Federico's comment, a single for loop is the most readable in this case.
Also, even though you reuse it, there is not much point to extracting a.length into aLength
const a = [3];
for(let i = 0; i < a.length; i++) {
func(i);
}
function func(position) {
console.log('hi' + position);
}
Warning: very personal perspective down there, you could achieve the same level of clarity with comments too.
Well, unless the single element case has a very specific meaning in your domain. In which case, I would separate them with two functions with very specific names as follows:
const a = [3];
if(a.length > 1) {
handleMultiple(a);
} else {
handleSingleAndWhyItIsASpecialCase(a)
}
handleMultiple(array) {
for(let i = 0; i < array.length; i++) {
func(i);
}
}
handleSingleAndWhyItIsASpecialCase(array) {
func();
}
function func(position = 0) {
console.log('hi' + position);
}
As Hamid said below, you can easily turn it into a oneliner:
[45,63,77].forEach((element, index) => console.log(index));
Consider using forEach instead of map to make your intent clear though.
Write clean code and make everyone happy.
you can eliminate if and loop:
const a=[5,6,3]
a.forEach((value,index)=>console.log('hi'+index));

What is the problem in this simple js code?

I am wondering what`s the problem with this simple code. I am making a function where I need to get the length of the shortest word in a string. I know that I can find this function anywhere but,
why mine isn't working?
function findShort(s){
var arr = s.split(" ");
var out = 1000;
for (var i = 0; i < arr.length-1; i++){
if (arr[i] <= out){
out = arr[i].length;
}
}
return out;
}
The above function returns 1000 instead.
You need to compare the length of each word (arr[i].length), not each word itself (arr[i]) to the shortest length so far.
function findShort(s){
var arr = s.split(" ");
var out = 1000;
for (var i = 0; i < arr.length-1; i++){
if (arr[i].length <= out){ // <-- here!
out = arr[i].length;
}
}
return out;
}
Your problem is that when you compare strings in javascript, it doesn't use it's length. You have to use the "length" attribute of a String, like in the fixed code below. Also you have to save the result to give an output
function findShort(s){
var arr = s.split(" ");
var comp = 1000;
var out = "";
for (var i = 0; i < arr.length-1; i++){
if (arr[i].length <= comp){
comp = arr[i].length;
out = arr[i];
}
}
return out;
}
There still is a problem, if you want to have an array returned with all the shortest words (same length). You could add another if statement and make it add the word to an array when it's the same length, and clear it when there was found a shorter one.

How to avoid my permutation algorithm of ERROR:heap out memory

Today I try to solve a problem on codewars,it requires me give the permutations of a given string.
Firstly,I try to use a recursion function looks like:
function permutate(str) {
var result = [];
if (str.length == 1) {
return [str]
} else {
var preResult = permutate(str.slice(1));
for (var j = 0; j < preResult.length; j++) {
for (var k = 0; k < preResult[j].length + 1; k++) {
var temp = preResult[j].slice(0, k) + str[0] + preResult[j].slice(k);
result.push(temp);
}
}
return result;
}
}
After I click the attemp button,the OJ tells me there is an error caused by heap out memory.Because my function called with a long string:"abcdefghijkl".
Secondly,I rewrite my function by using loop.just like:
function perm(str) {
let result = [],tempArr = [];
let subStr = str;
while (subStr.length !== 0) {
if (result.length === 0) {
result.push(str[0]);
} else {
for (let i = 0; i < result.length; i++) {
let item = result[i];
let itemLen = item.length;
for (let j = 0; j < itemLen+1; j++) {
let temp = item.slice(0, j) + subStr[0] + item.slice(j);
tempArr.push(temp);
}
}
result = tempArr;
tempArr = [];
}
subStr = subStr.slice(1);
}
return result;
}
It works when the given string is short.But still cause Error.
So,I want to know why cause this error and if there is a permutation algorithm can run in Node(v6.11.0) without memory error?
I searched a lot and tried many methods,but nothing works.So I ask my first question on stackoverflow,hoping you can give me some help.Thanks!
Try the module https://github.com/miguelmota/permutations, or even try to use the code from the module
In addition to previous answer as a possible try out, you can try to increase process max memory limit size, for example with node --max-old-space-size=8192 which is in bytes, the node process will run with extended 8GB memory limit.

Extending String.prototype performance shows that function calls are 10x faster

I wanted to extend String object prototype with some utility method. It worked, but the performance was surprisingly low. Passing a string to a function is 10x times faster than overriding the String.prototype method that is doing the same thing. To make sure this really happens I created a very simple count() function and the corresponding methods.
(I was experimenting, and created three different versions of the method.)
function count(str, char) {
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
return n;
}
String.prototype.count = function (char) {
var n = 0;
for (var i = 0; i < this.length; i++) if (this[i] == char) n++;
return n;
}
String.prototype.count_reuse = function (char) {
return count(this, char)
}
String.prototype.count_var = function (char) {
var str = this;
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
return n;
}
// Here is how I measued speed, using Node.js 6.1.0
var STR ='0110101110010110100111010011101010101111110001010110010101011101101010101010111111000';
var REP = 1e3//6;
console.time('func')
for (var i = 0; i < REP; i++) count(STR,'1')
console.timeEnd('func')
console.time('proto')
for (var i = 0; i < REP; i++) STR.count('1')
console.timeEnd('proto')
console.time('proto-reuse')
for (var i = 0; i < REP; i++) STR.count_reuse('1')
console.timeEnd('proto-reuse')
console.time('proto-var')
for (var i = 0; i < REP; i++) STR.count_var('1')
console.timeEnd('proto-var')
Results:
func: 705 ms
proto: 10011 ms
proto-reuse: 10366 ms
proto-var: 9703 ms
As you can see the difference is dramatic.
The below proves that performance of method calls is neglectably slower, and that the function code it self is slower for methods.
function count_dummy(str, char) {
return 1234;
}
String.prototype.count_dummy = function (char) {
return 1234; // Just to prove that accessing the method is not the bottle-neck.
}
console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')
console.time('proto-dummy')
for (var i = 0; i < REP; i++) STR.count_dummy('1')
console.timeEnd('proto-dummy')
console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')
Results:
func-dummy: 0.165ms
proto-dummy: 0.247ms
Although on huge repetitions (like 1e8) prototyped methods proves to be 10x times slower than functions, this can be ignored for this case.
All this may be related only to a String object, because simple generic objects perform about the same when you pass them to functions or call their methods:
var A = { count: 1234 };
function getCount(obj) { return obj.count }
A.getCount = function() { return this.count }
console.time('func')
for (var i = 0; i < 1e9; i++) getCount(A)
console.timeEnd('func')
console.time('method')
for (var i = 0; i < 1e9; i++) A.getCount()
console.timeEnd('method')
Results:
func: 1689.942ms
method: 1674.639ms
I've been searching on Stackoverflow and binging, but other that the recommendation "do not extend String or Array because you pollute the name space" (which is not a problem for my particular project), I cannot find anything related to performance of methods compared to functions. So should I simply forget about extending the String object due to performance drop of added methods or there is more about it?
This is most likely because you are not using strict mode, and the this value inside your method is getting coerced to a String instance instead of being a primitive string. This coercion, and further method calls or property accesses on the String object, are slower than using primitive values.
You can (Edit: could, at least, in 2016) confirm this by repeating your measurement on var STR = new String('01101011…') which should have less overhead.
Then fix your implementation:
String.prototype.count = function (char) {
"use strict";
// ^^^^^^^^^^^^
var n = 0;
for (var i = 0; i < this.length; i++)
if (this[i] == char)
n++;
return n;
};

Categories