I am trying to build a calculator in javascript but I am stuck and don't know how to proceed. every time someone click on 1 + 1 / 2, for exemple I am creating an array that pushes everything that was typed so in the case above the array would be
[1, "+", 1, "/", 2];
However, I can't figure it out how to transform this array into an actual mathematical value.
I had an idea of looping through all elements
like this:
for(var i=0; i<arrayCharacters.length ;i++){
if(arrayCharacters[i] != "*" || arrayCharacters[i] != "/" || arrayCharacters[i] != "+" || arrayCharacters[i] != "*"){
arrayNumbers.push(arrayCharacters.slice(0, i));
console.log(arrayNumbers);
}
}
It's very incomplete because I got stuck. Can anyone help me?
var result=eval(arrayCharacters.join(""));
You could also parse the expression manually, however this requires building up a tree, as math isnt evaluated left to right.
If you really want to parse it on your own (which is far better then eval), you could use a math notation that really goes from left to right , so its much easier to parse ( As #Scott suggested). An implementation:
var stack=[];
var arrayCharacters=[1,2,"/",1,"+"];
for(var i=0;i<arrayCharacters.length;i++){
var char=arrayCharacters[i];
if(typeof char==="number"){
stack.push(char);
continue;
}
var op2=stack.pop()||0;
var op1=stack.pop()||0;
var res;
if(char=="+"){
res=op1+op2;
}
if(char=="-"){
res=op1-op2;
}
if(char=="*"){
res=op1*op2;
}
if(char=="/"){
res=op1/op2;
}
stack.push(res);
}
var result=stack.pop();
Math Syntax (RPN (1)):
1+2 => 1 2 +
1/2+3 => 1 2 / 3 +
1+2/3 => 1 2 3 / +
(1+2)/3 => 1 2 + 3 /
1/2/3+4/5/6 => 1 2 / 3 / 4 5 / 6 / +
http://jsbin.com/zofeqitiba/edit?console
"eval" function is a very good choice in your case. Also you can use the math.js library, which comes with a powerful expression parser.
http://mathjs.org
Related
Problem statement: I'm trying to get string > binary without using the inbuilt method in javascript.
This is a piece of program where a string input (like "ABC") is accepted, then it is translated to an array of equivalent code value ([65,66,67]).
Function binary() will change a number to binary. But I'm unable to join them together to loop through all the contents. Please help. (I'm a noob, please forgive my bad code and bad explanation)
var temp3 = [65,66,67];
var temp2 = [];
var r;
for(i=0;i<temp3.length;i++) {
var r = temp3[i];
temp2.push(binary(r));
}
function binary(r) {
if (r === 0) return;
temp2.unshift(r % 2);
binary(Math.floor(r / 2));
return temp2;
}
console.log(temp2);
I think this is a cleaner version of this function. It should work for any non-negative integers, and would be easy enough to extend to the negatives. If we have a single binary digit (0 or 1) and hence are less than 2, we just return the number converted to a string. Otherwise we call recursively on the floor of half the number (as yours does) and append the final digit.
const binary = (n) =>
n < 2
? String (n)
: binary (Math.floor (n / 2)) + (n % 2)
console.log (binary(22)) //=> '10110'
console.log ([65, 66, 67] .map (binary)) //=> ['1000001', '1000010', '1000011']
In your function you have this code
var r = temp3[i];
I don't see any temp3 variable anywhere in your code above so I'd imagine that could be causing some issues.
Ultimately I want to take this:
2x + 3 = 5
and solve for x, by first subtract 3 from both sides so 2x = 2, then divide both sides by 2 so x = 1. I was thinking a lot how one should go about making a function like this in JavaScript that can return an array of the steps done in order, including the result. Obviously "eval" wouldn't do anything for this, so seemingly one has to re-create equations.
I initially thought to first of all, ignore X, and just try to make a function that can solve simple equations, without eval or any built-in function.
I figured that the first step is to break up the terms using .split, but I was having some trouble with this, as I need to split for multiple symbols. For example, say I have the simple expression to evaluate: 3 - 6 * 3 / 9 + 5. So before we even get into order of operations, just splitting up each term (and categorizing them) is the hard part, which is the main concrete-question I have at this point.
I started simply splitting one after the other, but I was having some problems, and especially considering the order.
function solve(eq) {
var minuses = eq.split("-"),
pluses = minuses.map(x=> x.split("+")),
timeses = pluses.map(x=>x.map(y=>y.split("*"))),
dividers = timeses.map(x=>x.map(y=>y.map(z=>z.split("/"))));
console.log(minuses, pluses, timeses, dividers);
}
solve("3 - 6 * 3 / 9 + 5");
As you can see, for each successive operator I need to map through each of he elements of the previous one to split it, and then I am left with an array of arrays etc...
So 1) how can I split up these terms more efficiently, without making a new variable for each one, and manually recursively mapping through each one? Seemingly I should just have some kind of dictionary of array keeping track of orders of operations (not considering parenthesis or exponents now): ["*","/","+","-"] -- and given that array, generate something similar to the last array in the above example ("dividers") which contains only constants, and somehow keep track of the which elements each of the stored arrays follows...
and 2) How can I solve the expression given the arrays of values?
I was just a little confused with the logic, I guess I need to work up from the last array and solve the constants one at a time, keeping track of which operator is the current one, but I'm not sure how exactly.
While your problem doesn't require to construct, binary expression tree is a good way to brainstorm the logic to solve a math query.
So for the query 3 - 6 * 3 / 9 + 5, the representative binary expression tree is:
plus
|_minus
| |_3
| |_divide
| |_times
| | |_3
| | |_6
| |_9
|_5
to solve above tree, you recursively solve from the leaf level up to the root.
Again, you don't need to construct a tree. It just helps us to see the logic of parsing here:
Get the last minus or plus expression in query and solve left and right child of that expression.
If no plus/minus, get the last times/division expression and solve left and right child
If meet a number, return that number value.
Given above logic, here is an implementation:
function solve(str) {
var expressionIndex = Math.max(str.lastIndexOf("-"), str.lastIndexOf("+"));
if (expressionIndex === -1) {
expressionIndex = Math.max(str.lastIndexOf("*"), str.lastIndexOf("/"));
}
if (expressionIndex === -1) {
var num = Number.parseInt(str.trim());
if (isNaN(num)) {
throw Exception("not a valid number");
} else {
return num;
}
} else {
var leftVal = solve(str.substring(0, expressionIndex).trim());
var rightVal = solve(str.substring(expressionIndex + 1).trim());
switch (str[expressionIndex]) {
case "+":
return leftVal + rightVal;
case "-":
return leftVal - rightVal;
case "*":
return leftVal * rightVal;
case "/":
return leftVal / rightVal;
}
}
}
function parse(str) {
var expressionIndex = Math.max(str.lastIndexOf("-"), str.lastIndexOf("+"));
if (expressionIndex === -1) {
expressionIndex = Math.max(str.lastIndexOf("*"), str.lastIndexOf("/"));
}
if (expressionIndex === -1) {
var num = Number.parseInt(str.trim());
if (isNaN(num)) {
throw Exception("not a valid number");
} else {
return { type: "number", value: num };
}
} else {
var leftNode = parse(str.substring(0, expressionIndex).trim());
var rightNode = parse(str.substring(expressionIndex + 1).trim());
return {
type: "expression",
value: str[expressionIndex],
left: leftNode,
right: rightNode
};
}
}
console.log(solve("3 - 6 * 3 / 9 + 5"));
console.log(parse("3 - 6 * 3 / 9 + 5"));
Above is a solution for very simple query with only +, -, *, / (no parenthesis, e.g.). For solving a equation like your first example requires a lot more of work.
EDIT: add a parse function to return the tree.
You can do that in following steps:
First of all use split() and split by the + and - which will occur after multiplication and division.
Then use map() on array and split() it again by * and /.
Now we have a function which will which will evaluate an array of numbers with operators to single number.
Pass the nested array to complete multiplication and division.
Then pass that result again to sovleSingle and perform addition and subtraction.
The function works same as eval as long as there are no brackets ().
Note: This doesnot matters the which occurs first among + and - or which occurs first among * and /. But *,/ should occur before +,-
function solveSingle(arr){
arr = arr.slice();
while(arr.length-1){
if(arr[1] === '*') arr[0] = arr[0] * arr[2]
if(arr[1] === '-') arr[0] = arr[0] - arr[2]
if(arr[1] === '+') arr[0] = +arr[0] + (+arr[2])
if(arr[1] === '/') arr[0] = arr[0] / arr[2]
arr.splice(1,1);
arr.splice(1,1);
}
return arr[0];
}
function solve(eq) {
let res = eq.split(/(\+|-)/g).map(x => x.trim().split(/(\*|\/)/g).map(a => a.trim()));
res = res.map(x => solveSingle(x)); //evaluating nested * and / operations.
return solveSingle(res) //at last evaluating + and -
}
console.log(solve("3 - 6 * 3 / 9 + 5")); //6
console.log(eval("3 - 6 * 3 / 9 + 5")) //6
I am not sure my issue is related to programming or related to concept of LLL algorithm and what has been mentioned on Wikipedia.
I decided to implement LLL algorithm as it has been written on Wikipedia (step-by-step / line-by-line) to actually learn the algorithm and make sure it is truly working but I am getting unexpected or invalid results.
So, I used JavaScript (programming language) and node.js (JavaScript engine) to implement it and this is the git repository to get the complete code.
Long story short, value of K gets out of range, for example when we have only 3 vectors (array size is 3, thus maximum value of index would be 2), but k becomes 3 and it is nonsense.
My code is step-by-step (line-by-line) implementation of the algorithm mentioned on Wikipedia and what I did was only implementing it. So I don't what is the issue.
// ** important
// {b} set of vectors are denoted by this.matrix_before
// {b*} set of vectors are denoted by this.matrix_after
calculate_LLL() {
this.matrix_after = new gs(this.matrix_before, false).matrix; // initialize after vectors: perform Gram-Schmidt, but do not normalize
var flag = false; // invariant
var k = 1;
while (k <= this.dimensions && !flag) {
for (var j = k - 1; j >= 0; j--) {
if (Math.abs(this.mu(k, j)) > 0.5) {
var to_subtract = tools.multiply(Math.round(this.mu(k, j)), this.matrix_before[j], this.dimensions);
this.matrix_before[k] = tools.subtract(this.matrix_before[k], to_subtract, this.dimensions);
this.matrix_after = new gs(this.matrix_before, false).matrix; // update after vectors: perform Gram-Schmidt, but do not normalize
}
}
if (tools.dot_product(this.matrix_after[k], this.matrix_after[k], this.dimensions) >= (this.delta - Math.pow(this.mu(k, k - 1), 2)) * tools.dot_product(this.matrix_after[k - 1], this.matrix_after[k - 1], this.dimensions)) {
if (k + 1 >= this.dimensions) { // invariant: there is some issue, something is wrong
flag = true; // invariant is broken
console.log("something bad happened ! (1)");
}
k++;
// console.log("if; k, j");
// console.log(k + ", " + j);
} else {
var temp_matrix = this.matrix_before[k];
this.matrix_before[k] = this.matrix_before[k - 1];
this.matrix_before[k - 1] = temp_matrix;
this.matrix_after = new gs(this.matrix_before, false).matrix; // update after vectors: perform Gram-Schmidt, but do not normalize
if (k === Math.max(k - 1, 1) || k >= this.dimensions || Math.max(k - 1, 1) >= this.dimensions) { // invariant: there is some issue, something is wrong
flag = true; // invariant is broken
console.log("something bad happened ! (2)");
}
k = Math.max(k - 1, 1);
// console.log("else; k, j");
// console.log(k + ", " + j);
}
console.log(this.matrix_before);
console.log("\n");
} // I added this flag variable to prevent getting exceptions and terminate the loop gracefully
console.log("final: ");
console.log(this.matrix_before);
}
// calculated mu as been mentioned on Wikipedia
// mu(i, j) = <b_i, b*_j> / <b*_j, b*_j>
mu(i, j) {
var top = tools.dot_product(this.matrix_before[i], this.matrix_after[j], this.dimensions);
var bottom = tools.dot_product(this.matrix_after[j], this.matrix_after[j], this.dimensions);
return top / bottom;
}
Here is the screenshot of the algorithm that is on Wikipedia:
Update #1: I added more comments to the code to clarify the question hoping that someone would help.
Just in case you are wondering about the already available implementation of the code, you can type: LatticeReduce[{{0,1},{2,0}}] wolfram alpha to see how this code suppose to behave.
Update #2: I cleaned up the code more and added a validate function to make Gram Schmidt code is working correctly, but still code fails and value of k exceeds number of dimensions (or number of vectors) which doesn't make sense.
The algorithm description in Wikipedia uses rather odd notation -- the vectors are numbered 0..n (rather than, say, 0..n-1 or 1..n), so the total number of vectors is n+1.
The code you've posted here treats this.dimensions as if it corresponds to n in the Wikipedia description. Nothing wrong with that so far.
However, the constructor in the full source file on GitHub sets this.dimensions = matrix[0].length. Two things about this look wrong. The first is that surely matrix[0].length is more like m (the dimension of the space) than n (the number of vectors, minus 1 for unclear reasons). The second is that if it's meant to be n then you need to subtract 1 because the number of vectors is n+1, not n.
So if you want to use this.dimensions to mean n, I think you need to initialize it as matrix.length-1. With the square matrix in your test case, using matrix[0].length-1 would work, but I think the code will then break when you feed in a non-square matrix. The name dimensions is kinda misleading, too; maybe just n to match the Wikipedia description?
Or you could call it something like nVectors, let it equal matrix.length, and change the rest of the code appropriately, which just means an adjustment in the termination condition for the main loop.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I try to convert Tricky's script (that generates a name from elite) to javascript here:
https://github.com/rubo77/eliteNameGen/blob/master/elite.js
But I get stuck at this LPC-code by Tricky:
digrams=ABOUSEITILETSTONLONUTHNO..LEXEGEZACEBISOUSESARMAINDIREAERATENBERALAVETIEDORQUANTEISRION
...
pairs = digrams[24..<1];
...
names[0..<2]
I couldn't find a manual to LPC that would explain this syntax.
In the End I want to create a javascript, that creates a random planet name from the old C64 game Elite.
I also found a python version, (but that seems a bit more complicated to me)
Ok I managed to port the code over, but I had to tweak a bit of the algorithm. The one that is provide by Tricky, for some reason, produces non-unique names. I used the tweakseed function to tweak the seeds to generate a list of random names.
Answer
To answer the question above, #MattBurland is correct. You would replace the following code:
pairs = digrams[24..<1];
with
pairs = digrams.substring(24);
The following code, however, is actually printing out the list of names. So you're indexing an array - in which case:
names[0..<2]
becomes
for (var i = 0; i < (names.length - 2); i++) {
names[i]
}
Analysis
Just to give this some more depth. I've analyzed the code and realized that rotatel, twist, tweakseed, and next were just used to create random numbers. I don't know enough about LPC, but I think that at the time it probably didn't have a pseudo-random number generator.
A lot of this code can be removed and just replaced with Math.random. The key part of this entire program is the variable digram. This sequence of characters produces Alien-like names. I figure it probably has something to do with alternation of consonants and vowels. Grabbing them in pairs randomly will almost always produce some sort of consonant + vowel pairing. There is the odd time where you'll end up with a name like 'Rmrirqeg', but in most cases, the names appear Alien-like.
Port
Below is a direct port of the code. You can use this jsFiddle to see it in action, but it uses AngularJS to print out the names instead of printing out a list like the code provided. genNames will produce an array of names, which you can use for whatever reason you want.
Note this port only works on IE9+, since it uses map, reduce, and forEach. Replace these with loops if you plan on using this on IE8 or below.
You can tweak this to produce names longer or shorter. However, the length of the names is dependent on the pairs array. Either use Math.random or something to make it completely wild.
var digrams = "ABOUSEITILETSTONLONUTHNO" +
"..LEXEGEZACEBISOUSESARMAINDIREA.ERATENBERALAVETIEDORQUANTEISRION";
function rotatel(x) {
var tmp = (x & 255) * 2;
if (tmp > 255) tmp -= 255;
return tmp;
}
function twist(x) {
return (256 * rotatel(x / 256)) + rotatel(x & 255);
}
function next(seeds) {
return seeds.map(function(seed) {
return twist(seed);
});
}
function tweakseed(seeds) {
var tmp;
tmp = seeds.reduce(function(total, seed) {
return total += seed;
}, 0);
return seeds.map( function ( seed, index, arr ) {
return arr[index + 1] || (tmp & 65535)
});
};
function makename(pairs, seeds)
{
var name = [];
/* Modify pair if you want to have names shorter or longer than 8 chars */
/* I'll leave that as an exercise for you. */
var pair = [0, 0, 0, 0];
var longname = seeds[0] & 64;
pair = pair.map(function() {
seeds = tweakseed(seeds);
return 2 * ((seeds[2] / 256) & 31);
});
pair.forEach(function(value, index, arr) {
if (longname || ( index < (arr.length - 1))) {
name.push(pairs[value]);
name.push(pairs[value + 1]);
}
});
return name.join('').toLowerCase()
.replace(/^\w/, function(letter) {
return letter.toUpperCase();
});
}
function genNames()
{
var names = [];
var pairs;
var num = 256;
var seeds = [23114, 584, 46931];
pairs = digrams.substring(24);
while (--num) {
names.push( makename(pairs, seeds) );
seeds = tweakseed(next(seeds));
}
return names;
}
For the range operator in LPC, this link helps:
http://www.unitopia.de/doc/LPC/operators.html
expr1[expr2..expr3] Extracts a
piece from an array or string.
expr2 or expr3 may be omitted, default is the begin
or end of expr1.
Negative numbers for expr2 or expr3
mean ``count from before the beginning'', i.e.
foo[-2..-1] is an empty array or string.
foo[<2..<1] gives the 2nd and last element of
the array resp. chars of the string.
So I'm guessing that:
pairs = digrams[24..<1];
Means get the substring starting at index 24 to the end of the string?
I'm trying to randomize objects in an object.
do{
e = enemyList[Math.floor(enemyList.length * Math.random())];
} while (e.level > (player.level + 5) && e.level < (player.level - 5));
return e;
How would I make it so "e" has to be between 5 levels above and 5 levels below in order for the loop to stop?
Yeah this is really easy, but my head hurts for some reason :p
I believe you should be using an or not an and. If it is 5 below OR if it is 5 above. Otherwise the condition will never be met.
You have the comparisons the wrong way round (+ should be - and vice versa):
} while (e.level > (player.level - 5) && e.level < (player.level + 5));
(The wording of your question is somewhat ambiguous; it could be that you should be using >= and <= instead of > and <.)