Recursive Function - Square Root Calculation - Too much recursion - javascript

I am attempting to write a function which provides results similar to the JavaScript built-in math.sqrt function. I am using the trial and error method where the function guesses, and then subsequently refines the guess for the square root of the number the user enters into the program, until the correct value is successfully reached.
Currently a recursive function with an if.. else statement is used to perform the calculations, and adjust the guess until it reaches the user defined target number when squared.
The function provides the correct answer for 11, 22, 33, 44, 55, any number where the square root is not a decimal, among others. However it returns an error - too much recursion with other numbers such as 13, although the program gets very close to solving the problem - usually just .00000000000000x above or below the target number.
This function has gone through multiple iterations where I finally settled on using two arrays to store previous guesses, sorted into one or the other depending on whether they were too large or too small, and storing the latest guess from each array in a variable which can be used in calculating the next guess.
Previous iterations were reaching the error of too much recursion much earlier, and were only returning correct results for numbers where the correct answer was arrived at within the first run through of the function, otherwise my browser would crash.
// HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Function library example</title>
<style>
input {
font-size: 2em;
margin: 10px 1px 0;
}
</style>
</head>
<body>
<input class="numberInput" type="text">
<p></p>
// JavaScript
<script>
var input = document.querySelector('.numberInput');
var para = document.querySelector('p');
function findSquareRoot(num){
function find(guess, square, lastSmallGuess, lastLargeGuess){
var smallerArray = [];
var greaterArray = [];
if(square == num){
return guess;
} else if(square > num){
greaterArray.push(guess);
console.log('Last Small Guess : ' + lastSmallGuess); // shows guess in developer tools
console.log(' Last Large Guess : ' + lastLargeGuess); // shows guess in developer tools
console.log((((guess * 1) + (lastSmallGuess * 1)) / 2), (((guess * 1) + (lastSmallGuess * 1)) / 2) * (((guess * 1) + (lastSmallGuess * 1)) / 2), lastSmallGuess, greaterArray[greaterArray.length-1]); // shows calculation process in dev tools
return find((((guess * 1) + (lastSmallGuess * 1)) / 2), (((guess * 1) + (lastSmallGuess * 1)) / 2) * (((guess * 1) + (lastSmallGuess * 1)) / 2), lastSmallGuess, greaterArray[greaterArray.length-1]);
} else if(square < num){
smallerArray.push(guess);
console.log('Last Small Guess : ' + lastSmallGuess); // shows guess in developer tools
console.log(' Last Large Guess : ' + lastLargeGuess); // shows guess in developer tools
console.log((((guess * 1) + (lastLargeGuess * 1)) / 2), (((guess * 1) + (lastLargeGuess * 1)) / 2) * (((guess * 1) + (lastLargeGuess * 1)) / 2), smallerArray[smallerArray.length-1], lastLargeGuess); // shows calculation process in dev tools
return find((((guess * 1) + (lastLargeGuess * 1)) / 2), (((guess * 1) + (lastLargeGuess * 1)) / 2) * (((guess * 1) + (lastLargeGuess * 1)) / 2), smallerArray[smallerArray.length-1], lastLargeGuess);
}
}
return find((num * 1) / 2, ((num * 1) / 2) * ((num * 1) / 2), 0, 0);
}
input.onchange = function() {
var num = input.value;
if(isNaN(num)) {
para.textContent = 'You need to enter a number!';
} else {
para.textContent = num + ' square root is ' + findSquareRoot(num) + '. ' +
num + ' square root is ' + Math.sqrt(num) + '.';
}
}
</script>
</body>
</html>
I expect my function to consistently deliver the same answer as the built in math.sqrt function. Although even when the function causes the browser to crash and therefore does not return the result, it has arrived at an answer very close to the correct one, as shown in the console section of the developer tools. With other numbers it returns precisely the same result as math.sqrt
Thank you for taking the time to read my question.

Related

Unexpected token 'this' (javascript)

I'm new to coding, and this is my first post. Any help you can offer is greatly appreciated. I'm trying to create a level calculation system that you might see in an RPG for a character with different stats. When I run the program at different values, I get the following message:
Unexpected token 'this'
I have put the section of code where the error message occurs in stars.
const Sean = {
fullName: `Sean`,
attack: 1,
strength: 1,
defense: 38,
hitpoints: 40,
prayer: 15,
range: 98,
magic: 99,
calcCombatLevel: function () {
this.Meleelvl = .25 * (this.defense + this.hitpoints + Math.floor((this.prayer / 2))) + ((13 / 40) * (this.attack + this.strength));
this.magiclvl = .25 * (this.defense + this.hitpoints + Math.floor((this.prayer / 2))) + ((13 / 40) * (this.magic * (3 / 2)));
this.rangelvl = 0.25 * (this.defense + this.hitpoints + Math.floor((this.prayer / 2))) + ((13 / 40) * (this.range * (3 / 2)));
if ((this.attack + this.strength) > ((2 / 3) * this.magic) && (this.attack + this.strength) > ((2 / 3) * this.range)) {
return this.meleelvl;
}
// **********************************
else if ((2 / 3) this.magic > (this.attack + this.strength) && (this.magic >** this.range)) {
return this.magiclvl;
}
else if((2 / 3) * this.range > (this.attack + this.strength) && (this.range > this.magic)) {
return this.rangelvl;
}
}
}
Sean.calcCombatLevel();
console.log(Math.floor(Sean.lvl));
The first elseif has:
(this.magic >** this.range)
// also missing * at start, has (2/3) magic, where the 'this' error is from
which I assume you meant to be:
else if ((2 / 3) * this.magic > (this.attack + this.strength) && (this.magic > this.range)) {
Didn't play with the code but that's the only first obvious thing that stands out to me.
Also as someone commented above Sean.lvl doesn't seem to be defined, so the output at end probably isn't working either. You could do something like the following instead (or define a field named lvl):
var mylvl = Math.floor(Sean.calcCombatLevel());
console.log(mylvl);
And at least in one place your variable names have inconsistent casing, this.Meleelvl and this.meleelvl, fix that too (first line in function should have this.meleelvl, keeping all variable names lowercase).

Inconsistent timing and performance

As a learning experiment I decided to test out some numerical method exercises. I wanted to test the difference in computation time in order to visualize the difference. However, upon trying to run the program I'm coming across some strange behavior. I'm running an old version of node if that matters (v8.9.4). The code is below.
let n = 1000000000;
polyEvalTimeTest(n, "power", f_pow);
polyEvalTimeTest(n, "mult", f_mult);
function polyEvalTimeTest(n, name, f){
const startTime = Date.now();
for(let i = 0; i < n; i++){
let x = i * 0.001;
let y = f(x);
}
const endTime = Date.now();
console.log(name + " : " + (endTime - startTime));
}
function f_pow(x){
return 2.0 * Math.pow(x, 4) + 3.0 * Math.pow(x, 3) - 3.0 * Math.pow(x, 2) + 5.0 * x - 1;
}
function f_mult(x){
return 2.0 * x*x*x*x + 3.0 * x*x*x - 3.0 * x*x + 5.0 * x - 1;
}
When I run as above, I get output similar to
power : 621
mult : 11962
Which is obviously not correct. When I comment out the "power", test I get output similar to
mult : 620
When I comment out the "mult" test, I get output similar to
power : 623
If I reverse the calls to polyEvalTimeTest(), I get output similar to
mult : 619
power : 40706
Obviously I am missing something. Can someone explain to me what is causing this behavior to me?
If I just create an copy-paste version without using polyEvalTimeTest(), it works fine.

How to generate a random number that is evenly divisible by another randomly generated number

I'm working on a simple math flashcards app using JavaScript and jQuery. The available operations are addition, subtraction, multiplication, and division, each of which have functions that use generateTop() and generateBottom() to assign values to HTML elements.
I'm having trouble figuring out how to use my division problem function to generate random numbers so that thetopNumber is evenly divisible by the bottomNumber. I'm using the following code to generate random numbers for each problem:
let topNumber = 0;
let bottomNumber = 0;
function generateTop(max, min){
topNumber = Math.floor(Math.random() * (max - min) + min);
return topNumber;
};
function generateBottom(max, min){
bottomNumber = Math.floor(Math.random() * (max - min) + min);
return bottomNumber;
};
Here is my current division problem generator:
function division(){
problemTop.html(generateTop(1, 144));
problemBottom.html(generateBottom(1, topNumber));
opSym.html("รท");
}
I can adjust the max and min to get a positive integer for addition, subtraction, and multiplication problems but am stuck on how to ensure that I get a positive integer for division problems. I've tried messing with some different loop ideas but haven't gotten anything to work. I want to keep min at 1 and max at 144.
I've checked SO for solutions but can only find how to generate random numbers divisible by one other explicit, hard-coded number. How can I adjust my division function so that the topNumber can be divided evenly by the bottomNumber?
Add a denomenator parameter to your generateTop function, and pass in the randomly generated "bottom" (denominator) number returned from generateBottom. Multiply the new random number created in generateTop by the denominator, and return that.
Just add a single if statement to make your function recursive:
function generateBottom(max, min) {
bottomNumber = Math.floor(Math.random() * (max - min) + min);
if (topNumber % bottomNumber) {
generateBottom(max, min);
}
return bottomNumber;
}
What this does is it divides topNumber and checks if a remainder exists. If there is a remainder, it means the two do not divide, so it is recalculated. Otherwise, it returns bottomNumber as per usual.
You can do like this :
var a = Math.floor(Math.random() * 10)
var b = a * Math.floor(Math.random() * 10)
var c = b/a;
document.write(b + " is divisible by " + a + " ( *" + c + ")" )
In a * b, b is always divisible by a.
let topNumber = 0;
let bottomNumber = 0;
function generateTop(max, min, a){
topNumber = a * Math.floor(Math.random() * (max - min) + min);
return topNumber + " is divisible by " + a + " ( *" + topNumber/a + ")";
};
function generateBottom(max, min){
bottomNumber = Math.floor(Math.random() * (max - min) + min);
return bottomNumber;
};
document.write(generateTop(10, 1, generateBottom(10, 1)))

how to get delay working with setInterval and clearInterval

I'm writing a snippet where JS displays all elements of an array as a counter, with random delay between beach of the steps in loop. However, clearInterval and setInterval don't seem to be working, as what I observe is that it keeps printing values forever in the browser console.
here's the code
var low = Math.floor(Math.random() * (80 - 65 + 1)) + 65;
var high = Math.floor(Math.random() * (95 - low + 1)) + low;
// generate array between low to high
heartbeats = Array.from( {length: 15}, () => Math.floor(Math.random() * (high - low + 1)) + low );
// sort it
heartbeats = heartbeats.sort((a,b) => a - b);
for (i in heartbeats){
var randomPause = Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000;
theLooper = setInterval( function () {
if (i == (heartbeats.length - 1) ) clearInterval(theLooper);
console.log("Random Pause Is :: " + randomPause);
console.log(heartbeats[i]);
name1.innerHTML = heartbeats[i];
},randomPause);
}
what I get instead of counter is a print of low then high instead of printing all the elements in the array. and console log keeps printing high forever
Problem:
In fact in your actual code you are not printing the values of the array after a respective delay one by one, you are just printing all the elements in a banch after the delay is passed.
And when you use setInterval the display won't stop, it will be displaying for ever.
Solution:
You need to use setTimeout, inside your loop, instead of setInterval, it will delay the instruction once, and make sure to increment the delay of the setTimeout function according to the iterated index use:
setTimeout(function() {
console.log("index: " + i + " & element: " + el);
}, i * 1000);
Demo:
This is a Demo snippet:
var low = Math.floor(Math.random() * (80 - 65 + 1)) + 65;
var high = Math.floor(Math.random() * (95 - low + 1)) + low;
// generate array between low to high
heartbeats = Array.from({
length: 15
}, () => Math.floor(Math.random() * (high - low + 1)) + low);
// sort it
heartbeats = heartbeats.sort((a, b) => a - b);
heartbeats.forEach(function(el, i) {
setTimeout(function() {
console.log("index: " + i + " & element: " + el);
}, i * 1000);
});
Problem
I think your variable scope is causing the problem
Solution
Change for(i in heartbeats) to for (let i in heartbeats), otherwise your i inside the setInterval callback will be always the last index of your array
Detailed doc can be found here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

JavaScript stops execution half way and evaluates wrong answer?, Console.log is correct

I didn't think this was possible until console.log(); shown me that whats happening is impossible.
I can't understand how this is possible it's like those variables are being modified before code execution finishes.
I got this JavaScript code with debugging in it.
It's wrapped in this.
$('#buyAmountInput').keyup(function () {
var buyAmount = parseFloat($(this).val());
var totalPrice = 0;
var max = $(this).attr("max");
var min = $(this).attr("min");
if (buyAmount != $(this).val()) {
if (isNaN(buyAmount)) {
buyAmount = 1;
$(this).val('');
} else {
$(this).val(buyAmount);
}
} else {
if (buyAmount > max) {
buyAmount = max;
$(this).val(buyAmount);
} else if (buyAmount < min) {
buyAmount = min;
//$(this).val(buyAmount);
}
}
totalPrice = buyAmount * unitPrice;
//lots of code trimmed off here.
largessAmount = Math.round(buyAmount * largessRule.rebate) / 100;
////
console.log("Buy amount " + buyAmount + " LargessRebate " + largessRule.rebate);
console.log("Total Price " + totalPrice);
console.log("Largess Amount " + largessAmount);
console.log("New rate " + Number(totalPrice / (buyAmount + largessAmount)).moneyFormat());
console.log("No .moneyFormat() New rate " + Number(totalPrice / (buyAmount + largessAmount)));
console.log("( " + totalPrice + " / ( " + buyAmount + " + " + largessAmount + " ))");
////
$('#unitPrice').html(Number(totalPrice / (buyAmount + largessAmount)).moneyFormat());
});
Debug looks like this
Buy amount 5000 LargessRebate 20
Total Price 4250
Largess Amount 1000
New rate 0.71
No .moneyFormat() New rate 0.7083333333333334
( 4250 / (5000 + 1000))
function fastKeyListener content_script.js:208
Buy amount 5000 LargessRebate 20
Total Price 4250
Largess Amount 1000
New rate 0.00 //<- What happened here
No .moneyFormat() New rate 0.00008499830003399932 //<- What happened here
( 4250 / (5000 + 1000)) //<- Third line executed this will give good rate..
Even if the variables are global and this code is in a keypress up jQuery callback function.
The variables are printed before they are executed by console.log() and they are correct but the answer is dead wrong.
Here is the moneyFormat() which I don't think could be the problem could it?
var effective_bit = -2;
Number.prototype.moneyFormat = function () {
var num = this;
sign = (num == (num = Math.abs(num)));
num = Math.floor(num * Math.pow(10, -effective_bit) + 0.50000000001);
cents = num % Math.pow(10, -effective_bit);
num = Math.floor(num / Math.pow(10, -effective_bit)).toString();
for (var i = 0; i < Math.floor((num.length - (1 + i)) / 3); i++)
num = num.substring(0, num.length - (4 * i + 3)) + ', ' + num.substring(num.length - (4 * i + 3));
if (effective_bit < 0) {
if (cents < 10 && effective_bit == '-2') cents = "0" + cents;
money = (((sign) ? '' : '-') + num + '.' + cents);
} else {
money = (((sign) ? '' : '-') + num);
}
return money;
};
I didn't post the whole code as it's very large, but you can see it live here
Just put into the Unit to buy of 4999, then scroll to 5000 it's all good.. try putting like 5001 or 50000 it will reset it to 5000 and give wrong answer in the process.
EDIT:
could of simplified the question to why does the console.log() functions evaluate incorrect answer if the same equation generated with the same variables just 1 line after in execution after gives correct answer, even on calculator.
Like some quantum going on here, bear with me there is nothing I could have done from 1 line to another line during that code-execution no breakpoints were set nothing plus those callbacks are functions generated in their own sandbox I believe so they are like ajax threads all working to complete sooner or later they all work separately from each other, so nothing working together here to mess it up. What you think could possibly happen here? some temporarily corruption or something?
This occurs sometimes when doing claulations using string variables.
Try converting the buyAmount and any variable that came from HTML to number before any calculation.
You can use the Number() function or parseFloat().
http://jsfiddle.net/rcdmk/63qas2kw/1/

Categories