I am trying to understand this recursion. I know how recursion works in factorial function but when it gets to this complex recursion like this I am confused. The most confusing part to me is this code
str.split('').map( (char, i) =>
permutations( str.substr(0, i) + str.substr(i + 1) )map( p => char + p))
First, with "abc", say, it will split into ["a","b","c"] and go through the map function, then go through the second map function to wrap each return with a, b, c, respectively. However, I am very confused at the recursion part.
I thought the first recursion in "a" with value of str as "abc" will return "bc", and second recursion with str value of "bc" will return "c", and so on.
But when I just ran this code to see a clear recursion, it returns
[ [ [ 'c' ], [ 'b' ] ], [ [ 'c' ], [ 'a' ] ], [ [ 'b' ], [ 'a' ] ] ]
This is most confusing to me. I just can't see how this recursion returns these values. Can anyone go more in detail through how this work, like illustrating your thought process step by step?
I am a visual learner. Thank you for your help.
function permutations(str) {
return (str.length <= 1) ? [str] :
// Array.from(new Set(
str.split('')
.map( (char, i) =>
permutations( str.substr(0, i) + str.substr(i + 1))
.map( p => char + p))
// .reduce( (r, x) => r.concat(x), [])
// ));
}
permutations('abc')
One way I prefer to analyze and create recursive solutions is to work as though it's mathematical induction1.
The trick is to show that the function returns the right value for our base case(s), and then show that if it returns the right value for our simpler cases, it will also return the right value for our current case. Then we know that it will work for all values, as long as each recursive call is to some simpler case that eventually leads to a base case.
So look at your function. I've reformatted it to make discussion easier, and I've restored the reduce call you've commented out. That turns out to be necessary to do this right (although we'll discuss a more modern alternative below.) You also commented out the Array .from (new Set( ... )) wrapper, which is used to remove duplicates in the case your string has repeated characters. Without this, "aba" returns ["aba", "aab", "baa", "baa", "aab", "aba"]. With it, we get ["aba", "aab", "baa"], which makes more sense. But that is separate from our recursion question.
The cleaned-up function looks like this:
function permutations (str) {
return (str .length <= 1)
? [str]
: str
.split ('')
.map ((char, i) =>
permutations (str .substr (0, i) + str.substr (i + 1))
.map (p => char + p)
)
.reduce ((r, x) => r .concat (x), [])
}
permutations('abc')
Our base cases are pretty simple, str.length <= 1. In that case we yield [str]. This only has two possibilities: the string is empty, and we return [''], or the string has a single character, say 'x', and we return ['x']. These are pretty clearly correct, so we move on to the recursive call.
Let's say we pass 'abc'. The split and map calls turn that into the equivalent of this:
[
permutations ('bc') .map (p => 'a' + p),
permutations ('ac') .map (p => 'b' + p),
permutations ('ab') .map (p => 'c' + p),
]
But we have made the assumption that our recursion works on the smaller strings of 'bc', 'ac', and 'ab'. That means that permutations('bc') will yield ['bc', 'cb'], and similarly for the others, so this is equivalent to
[
['bc', 'cb'] .map (p => 'a' + p),
['ac', 'ca'] .map (p => 'b' + p),
['ab', 'ba'] .map (p => 'c' + p),
]
which is
[
['abc', 'acb']
['bac', 'bca']
['cab', 'cba']
]
Now we do the reduce call, which successively concatenates each array onto the previous result, starting with [], to get
['abc', 'acb', 'bac', 'bca', 'cab', 'cba']
There is a cleaner way of doing this. We can replace the map call followed by this reduce call with a single flatMap call, like this:
function permutations (str) {
return (str .length <= 1)
? [str]
: str
.split ('')
.flatMap ((char, i) =>
permutations (str .substr (0, i) + str.substr (i + 1))
.map (p => char + p)
)
}
In any case, we've demonstrated our inductive trick. By assuming this works for the simpler cases, we show that it works for out current case. (And no, we haven't done this rigorously, only by example, but it wouldn't be terribly difficult to prove this with some sort of mathematical rigor.) When we combine that with the demonstration that it works for the base case, we show that it works for all cases. This depends on our recursive calls being simpler in some way that eventually leads to a base case. Here, the strings being passed to the recursive call are one character shorter than those we were supplied, so we know that eventually we will hit our str .length <= 1 condition. And thus we know that it works.
If you add the Array .from (new Set ( ... )) wrapper back on, this will also work for those cases with repeating characters.
1
You may or may not have run across induction, and you may or may not remember it if you did, but in essence, it's very simple. Here's a very simple mathematical induction argument:
We will prove that 1 + 2 + 3 + ... + n == n * (n + 1) / 2, for all positive integers, n.
First, we can easily see that it's true when n is 1: 1 = 1 * (1 + 1) / 2
Next we assume that the statement is true for all integers below n.
We show that it's true for n like this:
1 + 2 + 3 + ... + n is the same as 1 + 2 + 3 + ... + (n - 1) + n, which is (1 + 2 + 3 + ... (n - 1)) + n. But we know that the statement is true for n - 1 (since we assumed it's true for all integers below n), so 1 + 2 + 3 + ... + (n - 1) is, by substituting in n - 1 for n in the expression above, equal to (n - 1) * ((n - 1) + 1) / 2, which simplifies to (n - 1) * n / 2. So now our larger expression ((1 + 2 + 3 + ... (n - 1)) + n is the same as ((n - 1) * n / 2) + n, which we can simplify to (n^2 - n) / 2 + n and then to (n^2 - n + (2 * n)) / 2 and to (n^2 + n) / 2. which factors into n * (n + 1) / 2.
So, by assuming it's true for everything less than n we show that it's true for n as well. Together with the fact that it's true when n is 1, the principle of induction says that it's true for all positive integers n.
You may have seen induction stated slightly differently: If (a) it's true for 1 and (b) being true for n - 1 implies that it's true for n, then (c) it's true for all positive integers n. (The difference here is that we don't need the assumption that it's true for all integers below n, only for n - 1.) It's easy to prove the equivalence of these two models. And the everything below formulation usually makes for a more convenient analogy in recursive problems.
Let's examine permutations('abc').
'abc' is converted to ['a','b','c'] for mapping
Map
a
First, char='a',i=0.
Note that permutations(str.substr(0, i) + str.substr(i + 1)) means "get the permutations of all the characters EXCEPT the one I'm looking at. In this case, this means permutations('bc'). Let's assume this gives the correct outputs ['bc','cb'], as the inductive hypothesis.
.map(p => char + p) then tells us to prepend the character we are looking at ('a') to each of the smaller permutations. This yields ['abc',acb'].
b
Following the same logic, char='b',i=1'. permutations('ac') == ['ac','ca']. Final outputs are ['bac','bca']
c
Following the same logic, char='c',i=2'. permutations('ab') == ['ab','ba']. Final outputs are ['cab','cba'].
Thus the overall output of the function would be [['abc','acb'],['bac','bca'],['cab','cba']]...
This is actually a pretty unusual definition of permutations, one that I happen to never have seen before.
(well, as it turns out, I actually wrote an answer once with its near exact equivalent, and only a few years prior... oh my).
In pseudocode, the simply-recursive definition one usually sees is
perms [x, ...xs] = [ [...as, x, ...bs] | p <- perms xs, (as, bs) <- splits p]
but this one is
perms2 xs = [ [x, ...p] | (as, [x, ...bs]) <- splits xs, p <- perms2 [...as, ...bs]]
(with list comprehensions and patterns; sans the empty list cases; with the "natural" definition of splits which builds a list of all possibilities of breaking a list up into two parts).
There's a certain duality here... Interesting. And not "simply"-recursive. :)
Or, with some more named functions to be implemented in obvious ways,
perms [x, ...rest] = [ i | p <- perms rest, i <- inserts x p]
= flatMap (inserts x) (perms rest)
--- and this version,
perms2 xs = [ [x, ...p] | (x, rest) <- picks xs, p <- perms2 rest]
See also:
Understanding Peter Norvig's permutation solution in PAIP
Related
I have the following function fn(n).
function fn(n) {
if (n < 0) return 0;
if (n < 2) return n;
return fn(n - 1) + fn(n - 2);
}
I understand how this code works, but don't how to calculate time complexity for it.
Let’s do some examples:
For n = 3, you have 5 function calls. First fn(3), which in turn calls fn(2) and fn(1) and so on.
For n = 4, you have 9 function calls. First fn(4), which in turn calls fn(3) and fn(2) and so on.
Graphical representation of the 2 examples:
The leftmost nodes go down in descending order: fn(4), fn(3), fn(2), fn(1), which means that the height of the tree (or the number of levels) on the tree will be n.
The time complexity of this code is 2^n - 1. Although, if we count all calls will be just 9 calls for n = 4.
And the question is how we get 2^n - 1? I don't understand
One of the ways of calculating recursive algorithm's time complexity is using the Recursion Tree Method. For this particular case, we can write T(n)=T(n-1)+T(n-2)+2*O(1), where O(1) means constant time, since we have two comparison checks of n values using if. The recursion tree would look something like this:
1 n
2 (n-1) (n-2)
4 (n-2) (n-3) (n-3) (n-4)
8 (n-3)(n-4) (n-4)(n-5) (n-4)(n-5) (n-5)(n-6)
...
2^i for i-th level
Total work done will be the sum of all of the work done in each level, so we can write T(n)=1 + 2 + 4 + 8 + 16 + 32 + ... = 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + ... + 2^i. This is a geometric series which means that the T(n) is equal to (1-2^n)/(1-2) = (1-2^n)/(-1) = 2^n - 1. Because of that the time complexity of the function fn(n) is O(2^n).
You could also approach this using Back Substitution method. We know the following:
T(0)=1
T(1)=1
T(2)=1
T(n)=T(n-1)+T(n-2)+2*O(n)≈2*T(n-1)
Substituting T(n-2) with T(n-1) is allowed. In doing so you will get a higher bound which is still true for T(n-1)+T(n-2).
After the substitution you can also write T(n-1)=2*T(n-2), T(n-2)=2*T(n-3), ...
Using these values and substituting them recursively in to the T(n) you will get:
T(n)=2*2*T(n-2)
=2*2*2*T(n-3)
=2*2*2*2*T(n-4)
=...
=2^i * T(n-i)
From there you can write n-i=1 => i=n-1 and substitute the i value in T(n). You can do this because T(1)=1 is one of the base conditions.
=> T(i)=2^(n-1) * T(n-(n-1))=2^(n-1) * T(n-n+1)=2^(n-1) * T(1)=2^(n-1)
This means that the time complexity is O(2^n).
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 months ago.
Improve this question
I have a task to create a JS script that is able to find a string using binary search on an array containing all permutations of the alphabetic chars (only lower case) with length 6 - meaning all strings of this form:
['aaaaaa','aaaaab','aaaaac'.... 'zzzzzx','zzzzzy','zzzzzz']
(For a total of 26^6 items in the array)
Due to its size - I cannot generate the array locally and run a regular binary search on it, I need to be able to find the string in the n/2 position (n = 26^6) without creating the array.
On the other hand - I need to create some sort of 1-to-1 mapping between any string ('aaaaaa', 'zzzzzz') to a number and the other way around (from number to a string) which I can then make division calculations on and find the middle string and so on.
Preferably this should be in JS/TS as I want to make a node app out of it in the end.
Any ideas?
You can do something that works like binary numbers, I mean write the number in base26 and just use the exponant to find the corresponding letter at the corresponding spot.
let number = (26**6)/2
let exponants = number.toString(26)
let correspondingString = exponants
.split('')
.map(elem => parseInt(elem, 26))
.map(elem => (elem + 10).toString(36))
.join('')
console.log(correspondingString);
And reverse :
let string = 'naaaaa'
let correspondingNumber = string
.split('')
.map(elem => parseInt(elem, 36) - 10)
.map((elem, index) => elem*(26**(5-index)))
.reduce((sum, value)=> sum + value, 0)
console.log(correspondingNumber);
Note
This solution somewhat generalizes the question to larger numbers. The numbers relevant to the question can still be accomodated by the standard JS number type.
Solution
You can find the a-z representation for a given number by using JS's BigInt object (arbitrary size integers).
In case you are looking for the n/2-th number in a sorter permutation list, you'd go as follows:
let bn_x = ((26n ** 6n) / 2n) // BigInt notation
, s_x26 = bn_x.toString(26) // Convert in base 26. Digits are represented by 0-9,a-q
, s_atoz // Will hold s_x26 with digits represented by a-z
;
s_atoz =
Array
.from(s_x26) // string -> array of chars (ie. array of single-letter strings)
.map ( c => { // map a-q -> k-z, 0-9 -> a-j
return String.fromCharCode((( c.charCodeAt(0) < 'a'.charCodeAt(0) ) ? (c.charCodeAt(0) + ( 'a'.charCodeAt(0) - '0'.charCodeAt(0) )) : ( c.charCodeAt(0) + 10 )));
})
.join('') // array of chars -> string
;
console.log(s_atoz);
Of course, this specific result can also be deduced without computation.
The other way round works similar wrt the basic idea, but with a caveat: There is no radix-aware BigInt constructor at the time of writing, so the number needs to be assembled using the elementary steps from radix construction.
let s_atoz = 'naaaaa'
, abn_x26 =
Array
.from(s_atoz)
.map ( c => {
return BigInt(c.charCodeAt(0) - 'a'.charCodeAt(0));
})
, bn_x = abn_x26.reduce ( (previousValue, currentValue) => {
return BigInt(previousValue) * 26n + BigInt(currentValue);
}
, 0n
)
;
console.log(bn_x.toString());
If you just want to find the string that would occur at the given position in our imaginary array, we can calculate it with this numberToString function:
const n2s = (chars, len = chars .length) => (n) =>
(n < len ? '' : n2s (chars, len) (~~ (n / len)))
+ chars [n % len]
const fixedN2s = (digits, chars) => (n) =>
n2s (chars) (n) .padStart (digits, chars [0])
const numberToString = fixedN2s (6, 'abcdefghijklmnopqrstuvwxyz')
; [28, 268041553, 202214284, 26 ** 6 / 2] .forEach (
s => console .log (`numberToString (${s}) //=> "${numberToString (s)}"`)
)
We start with a helper function that does the bulk of the work, accepting first the alphabet we want to use. Here it's all the lower-case letters, but we could easily imagine doing the same against "abcde", for instance. It returns a function which takes a number, and then we peel off the last "digit: of that number, using it as an index into chars for the last character, and then for the rest of the string either returning an empty string (in our base case when n is less than our character count) or the value of a recursive call with that digit stripped and the remaining number shifted over by dividing our character count into the remainder.
We layer on a function, fixedN2s, which calls the above with an additional digits argument that tells the number of fixed positions to prefill with the first character. That is, n2s ('abc...z') (28) would yield 'bc', but we want to prefill with a, to get 'aaaabc'.
We use pass 6 and our alphabet to to this function to create numberToString, our main function.
Note that we could do the reverse simply enough as well, with somthing like this snippet:
const s2n = (chars,
encoding = [...chars] .reduce ((a, d, i) => ((a [d] = i), a), {})
) => ([...ss]) => ss .length == 0
? 0
: chars .length * s2n (chars, encoding) (ss .slice (0, -1)) + encoding [ss .at (-1)]
const stringToNumber = s2n ('abcdefghijklmnopqrstuvwxyz')
; ['abc', 'woolen', 'random', 'naaaaa'] .forEach (
s => console .log (`stringToNumber ("${s}") //=> ${stringToNumber (s)}`)
)
Request:
Using JavaScript, write a function that takes an integer. The integer represents the number of times a coin is flipped. Using recursive strategies only, return an array containing all possible combinations of coin flips. Use "H" to represent heads and "T" to represent tails. The order of combinations does not matter.
For example, passing in "2" would return:
["HH", "HT", "TH", "TT"]
Context:
I am relatively new to JavaScript as well as the concept of recursion. This is purely for practice and understanding, so the solution does not necessarily need to match the direction of my code below; any useful methods or other ways of thinking through this are helpful, as long as it's purely recursive (no loops).
Attempt:
My attempt at this started out simple however the "action" progressively got more convoluted as I increased the input. I believe this works for inputs of 2, 3, and 4. However, inputs of 5 or higher are missing combinations in the output. Many thanks in advance!
function coinFlips(num){
const arr = [];
let str = "";
// adds base str ("H" * num)
function loadStr(n) {
if (n === 0) {
arr.push(str);
return traverseArr();
}
str += "H";
loadStr(n - 1);
}
// declares start point, end point, and index to update within each str
let start = 0;
let end = 1;
let i = 0;
function traverseArr() {
// base case
if(i === str.length) {
console.log(arr);
return arr;
}
// updates i in base str to "T"
// increments i
// resets start and end
if(end === str.length) {
str = str.split('');
str[i] = "T";
str = str.join('');
i++;
start = i;
end = i + 1;
return traverseArr();
}
// action
let tempStr = str.split('');
tempStr[start] = "T";
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = tempStr.split('');
tempStr.reverse();
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = str.split('');
tempStr[end] = "T";
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = tempStr.split('');
tempStr.reverse();
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = str.split('');
tempStr[start] = "T";
tempStr[end] = "T";
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = tempStr.split('');
tempStr.reverse();
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
// recursive case
start++;
end++;
return traverseArr();
}
loadStr(num);
}
coinFlips(5);
Below is a long description about how to create such recursive functions. I think the steps described help solve a great number of problems. They are not a panacea, but they can be quite useful. But first, here's what we'll work toward:
const getFlips = (n) =>
n <= 0
? ['']
: getFlips (n - 1) .flatMap (r => [r + 'H', r + 'T'])
Determining our algorithm
To solve a problem like this recursively, we need to answer several questions:
What value are we recurring on?
For simple recursions, it's often a single numeric parameter. In all cases there must be a way to demonstrate that we are making progress toward some final state.
This is a simple case, and it should be pretty obvious that we want to recur on the number of flips; let's call it n.
When does our recursion end?
We need to stop recurring eventually. Here we might consider stopping when n is 0 or possibly when n is 1. Either choice could work. Let's hold off on this decision for a moment to see which might be simpler.
How do we convert the answer from one step into the answer for the next?
For recursion to do anything useful, the important part is calculating the result of our next step based on the current one.
(Again, there are possible complexities here for more involved recursions. We might for instance have to use all the lower results to calculate the next value. For an example look up the Catalan Numbers. Here we can ignore that; our recursion is simple.)
So how do we convert, say ['HH', 'HT', 'TH', 'TT'] into the next step, ['HHH', 'HHT', 'HTH', 'HTT', 'THH', 'THT', 'TTH', 'TTT']? Well if we look at the next result closely, we can see that the in first half all elements begin with 'H' and in the second one they begin with 'T'. If we ignore the first letters, each half is a copy of our input, ['HH', 'HT', 'TH', 'TT']. That looks very promising! So our recursive step can be to make two copies of the previous result, the first one with each value preceded by 'H', the second one by 'T'.
What is the value for our base case?
This is tied to the question we skipped. We can't say what it ends on without also knowing when it ends. But a good way to make the determination for both is to work backward.
To go backward from ['HHH', 'HHT', 'HTH', 'HTT', 'THH', 'THT', 'TTH', 'TTT'] to ['HH', 'HT', 'TH', 'TT'], we can take the first half and remove the initial 'H' from each result. Let's do it again. From ['HH', 'HT', 'TH', 'TT'], we take the first half and remove the initial 'H' from each to get ['H', 'T']. While that might be our stopping point, what happens if we take it one step further? Taking the first half and removing the initial H from the one remaining element leaves us just ['']. Does this answer make sense? I'd argue that it does: How many ways are there to flip the coin zero times? Just one. How would we record it as a string of Hs and Ts? As the empty string. So an array containing just the empty string is a great answer for the case of 0. That also answers our second question, about when the recursion ends. It ends when n is zero.
Writing code for that algorithm
Of course now we have to turn that algorithm into code. We can do this in a few steps as well.
Declaring our function
We write this by starting with a function definition. Our parameter is called n. I'm going to call the function getFlips. So we start with
const getFlips = (n) =>
<something here>
Adding our base case.
We've already said that we're going to end when n is zero. I usually prefer to make that a little more resilient by checking for any n that is less than or equal to zero. This will stop an infinite recursion if someone passes a negative number. We could instead choose to throw an exception in this case, but our explanation of [''] for the case of zero seems to hold as well for the negative values. (Besides, I absolutely hate throwing exceptions!)
That gives us the following:
const getFlips = (n) =>
n <= 0
? ['']
: <something here>
I choose here to use the conditional (ternary) expression instead of if-else statements because I prefer working with expressions over statements as much as possible. This same technique can easily be written with if-else instead if that feels more natural to you.
Handling the recursive case
Our description was to "make two copies of the previous result, the first one with each value preceded by 'H', the second one by 'T'." Our previous result is of course getFlips (n - 1). If we want to precede each value in that array with 'H', we're best using .map. We can to id like this: getFlips (n - 1) .map (r => 'H' + r). And of course the second half is just getFlips (n - 1) .map (r => 'T' + r). If we want to combine two arrays into one, there are many techniques, including .push and .concat. But the modern solution would probably be to use spread parameters and just return [...first, ...second].
Putting that all together, we get this snippet:
const getFlips = (n) =>
n <= 0
? ['']
: [...getFlips (n - 1) .map (r => 'H' + r), ...getFlips (n - 1) .map (r => 'T' + r)]
console .log (getFlips (3))
Examining the results
We can test this on a few cases. But we should be fairly convinced by the code. It seems to work, it's relatively simple, there are no obvious edge cases missing. But I still see a problem. We're calculating getFlips (n - 1) twice, for no good reason. In a recursive situation that it usually quite problematic.
There are several obvious fixes for this. First would be to give up my fascination with expression-based programming and simply use if-else logic with a local variable:
Replace conditional operator with if-else statements
const getFlips = (n) => {
if (n <= 0) {
return ['']
} else {
const prev = getFlips (n - 1)
return [...prev .map (r => 'H' + r), ...prev .map (r => 'T' + r)]
}
}
(Technically, the else isn't necessary, and some linters would complain about it. I think the code reads better with it included.)
Calculate a default parameter to use as a local variable
Another would be to use a parameter default value in the earlier definition.
const getFlips = (n, prev = n > 0 && getFlips (n - 1)) =>
n <= 0
? ['']
: [...prev .map (r => 'H' + r), ...prev .map (r => 'T' + r)]
This might rightly be viewed as over-tricky, and it can cause problems when your function is used in unexpected circumstances. Don't pass this to an array's map call, for instance.
Rethink the recursive step
Either of the above would work. But there is a better solution.
We can also write much the same code with a different approach to the recursive step if we can see another way of turning ['HH', 'HT', 'TH', 'TT'] into ['HHH', 'HHT', 'HTH', 'HTT', 'THH', 'THT', 'TTH', 'TTT']. Our technique was to split the array down the middle and remove the first letters. But there are other copies of that base version in the version of the array without one of their letters. If we remove the last letters from each, we get ['HH', 'HH', 'HT', 'HT', 'TH', 'TH', 'TT', 'TT'], which is just our original version with each string appearing twice.
The first code that comes to mind to implement this is simply getFlips (n - 1) .map (r => [r + 'H', r + 'T']). But this would be subtly off, as it would convert ['HH', 'HT', 'TH', ' TT'] into [["HHH", "HHT"], ["HTH", "HTT"], ["THH", "THT"], [" TTH", " TTT"]], with an extra level of nesting, and applied recursively would just yield nonsense. But there is an alternative to .map that removes that extra level of nesting, .flatMap.
And that leads us to a solution I'm very happy with:
const getFlips = (n) =>
n <= 0
? ['']
: getFlips (n - 1) .flatMap (r => [r + 'H', r + 'T'])
console .log (getFlips (3))
function getFlips(n) {
// Helper recursive function
function addFlips(n, result, current) {
if (n === 1) {
// This is the last flip, so add the result to the array
result.push(current + 'H');
result.push(current + 'T');
} else {
// Let's say current is TTH (next combos are TTHH and TTHT)
// Then for each of the 2 combos call add Flips again to get the next flips.
addFlips(n - 1, result, current + 'H');
addFlips(n - 1, result, current + 'T');
}
}
// Begin with empty results
let result = [];
// Current starts with empty string
addFlips(n, result, '');
return result;
}
In case this is of any interest, here's a solution that doesn't use recursion as such but make use of the Applicative type.
Except when n is 1, the list of all possible combinations is obtained by combining all possible outcomes of each coin flip:
22 → [H, T] × [H, T] → [HH, HT, TH, TT]
23 → [H, T] × [H, T] × [H, T] → [HHH, HHT, HTH, HTT, THH, THT, TTH, TTT]
...
A function that can take n characters and concat them can be written as such:
const concat = (...n) => n.join('');
concat('H', 'H'); //=> 'HH'
concat('H', 'H', 'T'); //=> 'HHT'
concat('H', 'H', 'T', 'H'); //=> 'HHTH'
//...
A function that produces a list of outcomes for n coin flips can be written as such:
const outcomes = n => Array(n).fill(['H', 'T']);
outcomes(2); //=> [['H', 'T'], ['H', 'T']]
outcomes(3); //=> [['H', 'T'], ['H', 'T'], ['H', 'T']]
// ...
We can now sort of see a solution here: to get the list of all possible combinations, we need to apply concat across all lists.
However we don't want to do that. Instead we want to make concat work with containers of values instead of individual values.
So that:
concat(['H', 'T'], ['H', 'T'], ['H', 'T']);
Produces the same result as:
[ concat('H', 'H', 'H')
, concat('H', 'H', 'T')
, concat('H', 'T', 'H')
, concat('H', 'T', 'T')
, concat('T', 'H', 'H')
, concat('T', 'H', 'T')
, concat('T', 'T', 'H')
, concat('T', 'T', 'T')
]
In functional programming we say that we want to lift concat. In this example I'll be using Ramda's liftN function.
const flip = n => {
const concat = liftN(n, (...x) => x.join(''));
return concat(...Array(n).fill(['H', 'T']));
};
console.log(flip(1));
console.log(flip(2));
console.log(flip(3));
console.log(flip(4));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script>const {liftN} = R;</script>
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am trying to solve a challenge on jshero.net. The challenge is :
Write a function list that takes an array of words and returns a string by concatenating the words in the array, separated by commas and - the last word - by an 'and'. An empty array should return an empty string.
Example: list(['Huey', 'Dewey', 'Louie']) should return 'Huey, Dewey and Louie'
Best solution I could come up with was this :
function list (a){
let myList = a.join(' and ' )
return myList
}
The problem is that I cannot figure out how to make the function to return just the "and" word just after the second name and not before it. Does anyone know how to solve this?
............................................................
Update : I found an alternative :
function list(arr){
let myL= arr.slice(0,2);
let myLi= myL.join(' , ')
let myL2 = arr.slice(2,3);
let myLi2= myL2.join(' and ');
let myList = myLi + myLi2
if (arr.length <=2){
return arr.join(' and ')} else {
return myList}
}
I am close, the outcome is "Huey , DeweyLouie" but somehow is still not adding the "add" word into it. Anyone got an ideea?
From my comment:
list.reduce((acc, cur, i) => acc + cur + (i < list.length - 2 ? ', ' : i < list.length -1 ? ', and ' : ''), '');
This is a more advanced example, since it uses some JavaScript specific functions that are less "general programming."
Here's an explanation:
list
.reduce(
(acc, cur, i) => // start of an arrow function (implicit 'return')
acc + cur + // + as in string concatenation
(
i < list.length - 2 ? // what to concatenate? comma or 'and'?
', ' : // if it's before the 2nd last index, use a comma.
i < list.length - 1 ? // else, double check if it's the very last one.
', and ' : // if it is, use ', and '.
'' // else-else (the space AFTER the last index), put nothing.
),
''); // starter argument for 'reduce'
The Array.reduce() function takes another function as its argument. That function will be run on each element in the list in order. On each subsequent element, the first argument (acc) will be whatever the function returned on the last item (in the case of the first item, it'll be the supplied starting arugment). This let's you gradually tack the each element (cur) onto the value that follows through the array.
Using ternaries lets you make it a one liner. If you wanted it to be more verbose:
const list = ['Huey', 'Dewey', 'Louie']
const output = list.reduce(function(acc, cur, i) {
let str = acc;
str += cur;
if (i < list.length - 2) {
str += ', ';
} else if (i < list.length - 1) {
str += ', and ';
}
return str;
}, '');
console.log(output);
If you're new to programming in general, this may seem a bit overwhelming (I know reduce did for me, too). But, if you're familiar with programming practices, this is a good example of a JS-specific way to do things.
Cheers, and good luck :-)
Interesting use case. In fact I thought it would be a nice addition to my tagged templates library!
So we have two separators:
delim: ', '
delimlast: ' and '
And a few cases:
[] -> ''
['Huey'] -> 'Huey'
['Huey', 'Dewey'] -> 'Huey and Dewey'
['Huey', 'Dewey', 'Louie'] -> 'Huey, Dewey and Louie'
Here's one way to approach this:
const list =
(xs, delim = ', ', delimlast = ' and ') =>
!Array.isArray(xs) ? xs
: xs.length === 0 ? ''
: xs.length === 1 ? xs[0]
: xs.length === 2 ? xs[0] + delimlast + xs[1]
: xs.slice(0, -1).join(delim) + delimlast + xs[xs.length-1];
list('Huey'); // 'Huey'
list([]); // ''
list(['Huey']); // 'Huey'
list(['Huey', 'Dewey']); // 'Huey and Dewey'
list(['Huey', 'Dewey', 'Louie']); // 'Huey, Dewey and Louie'
I am the author of a small library of tagged templates (yet another one!) and your question gave me the idea for another one. Thanks!
import {list} from '<DETAILS CAN BE FOUND IN MY PROFILE IF YOU ARE INTERESTED>';
const guys = ['Huey', 'Dewey', 'Louie'];
list`Hey ${guys}!`;
//=> "Hey Huey, Dewey and Louie!"
One approach will be to iterate over till the last -1 array values and add "," to them.
When reached the last-1 of the list add the "and" outside the loop
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