A lot of your code seems to be Hackerrank's boilerplate code, so I'll ignore that (though it could use a review), and focus on the "meat" which is:
var value = 1;
for (var j = 0; j < data[i]; j++) {
if (j % 2 == 0)
value *= 2;
else value += 1;
}
process.stdout.write(value + '\n');
(It's run T times of course, but that's irrelevant for now.)
There's not a lot of code there, so there's little to review. Still, the if..else
is not great, if you ask me. For one, I'd advice that you always use braces - even for one-liners. But if you don't, at least use linebreaks:
if (j % 2 == 0)
value *= 2;
else
value += 1;
However, you can do it all in a ternary, which I would find more appropriate here:
value += (j % 2 === 0 ? value : 1);
The parentheses aren't required, but I find they make it more readable.
Of course, you could also rely on zero being false'y in JavaScript, and just do
value += (j % 2 ? 1 : value);
A slightly faster solution to the even/odd branching would be
value += (j & 1 ? 1 : value);
In other words: If the least-significant bit is 1, the number is odd.
In all, you get:
var value = 1;
for (var j = 0; j < data[i]; j++) {
value += (j & 1 ? 1 : value);
}
process.stdout.write(value + '\n');
Of course, there may be a purely mathematical, loop-less solution to this. But that's unfortunately not my strong suit.
Update: As mjolka points out in the comments, there is a very simple pattern to this. (And I feel pretty dumb for not realizing it.) I can't really explain it more succinctly than mjolka already has, so I'll just quote the comment here:
Look at the first few terms: 1, 2, 3, 6, 7, 14, 15, 30, 31, ... and compare that to powers of two: 2, 4, 8, 16, 32, ...
As is obvious, the output values are all equivalent to a power of 2, minus 1 or minus 2. In code, that can be expressed as a function like so:
// calculate tree height after n cycles
function utopiaTreeHeight(n) {
var exp = Math.ceil(n / 2) + 1, // calculate the exponent
value = Math.pow(2, exp) - 1; // power of 2, minus 1
return value - (n & 1); // subtract another 1 if n is odd, and return
}
Which means that the rest of the code is just:
for(var i = 0 ; i < data.length ; i++) {
var value = utopiaTreeHeight(data[i]);
process.stdout.write(value + '\n');
}
Now, that's pretty clean, I'd say. Big tip of the hat to mjolka!
Below are my previous (iterative and naïve) solutions. I'll leave them in my answer, only because there's probably something of value in there, even if this particular problem has a much more elegant solution.
(End of update.)
Now, about repeating it T times: If you're processing many test cases, it might be worth it to do some pre-processing. All you really need is to iterate to the highest cycle-count. So if you're asked to solve for data = [3, 6, 5]
what you really want to just solve for N = 6
but store the intermediate values for N = 3
and N = 5
along the way. You only need to loop once; from zero to Nmax.
Of course, doing so will require extra setup, which may be less efficient than simply doing what you're doing now, if there's only one or two test cases.
Just for fun, though, one solution might be:
var cycles = data.length, // cache this for later
sorted = data.slice(0).sort(), // copy and sort the input values (slightly faster if you use an explicit comparison function)
limit = sorted[cycles - 1], // get the max
target = sorted.shift(), // grab the lowest value (our first target cycle)
value = 1, // initial tree height
values = {}; // a place to store values
// loop to the highest cycle-count (note the range is 0..limit)
for(var n = 0 ; n <= limit ; n++) {
while( n === target ) {
// we reached our target cycle, so store the current value
values[n] = value;
// and grab the next target
target = sorted.shift();
}
value += (n & 1 ? 1 : value);
}
// print results in the correct order
for(var i = 0 ; i < cycles ; i++) {
process.stdout.write(values[data[i]] + '\n');
}
The while
loop is there to handle duplicates in the input.
A simpler solution would be to simply store every value for N in 0..Nmax in an array. That tradeoff would be memory consumption. If data = [1, 923123]
you'd end up storing 923121 values (~7MB at worst) you're not going to use. Crazy example, but if you don't know your input, well...
Still, such an approach could look like:
var limit = Math.max.apply(null, data),
values = [],
value = 1;
for(var n = 0 ; n <= limit ; n++) {
values.push(value);
value += (n & 1 ? 1 : value);
}
for(var i = 0 ; i < data.length ; i++) {
process.stdout.write(values[data[i]] + '\n');
}