Related
The question is as follows:
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: "PAHNAPLSIIGYIR"
Write the code that will take a string and make this conversion given a number of rows:
string convert(string s, int numRows);
Example 1:
Input: s = "PAYPALISHIRING", numRows = 3
Output: "PAHNAPLSIIGYIR"
Example 2:
Input: s = "PAYPALISHIRING", numRows = 4
Output: "PINALSIGYAHRPI"
Explanation:
P I N
A L S I G
Y A H R
P I
I have written the following code, but I am stuck in terms of how to flag the row as one time to be downward moving, where I increment the start row, but when it's zigzagging back to the top, it should be decremented. I am unable to figure out the logic to make this work without affecting the downward movement. Any help would be appreciated.
const convert = (s, numRows) => {
let startRow = 0
let endRow = numRows - 1
let startColumn = 0
let endColumn = Math.floor((s.length / 2) - 1)
s = s.split('')
let results = []
// to setup the columns
for (let i = 0; i < numRows; i++) {
results.push([])
}
while (startRow <= endRow && startColumn <= endColumn && s.length) {
for (let i = startRow; i <= endRow; i++) {
results[i][startColumn] = s.shift()
}
for (let i = endRow - 1; i >= startRow; i--) {
results[i][startColumn + 1] = s.shift()
startColumn++
}
//this line seems to be the issue
startRow++
}
return results
}
console.log(convert('PAYPALISHIRING', 4))
I rewrote your while loop as follows where I simply walk a "zigzag" pattern! Hopefully, it is simple enough to understand.
let c=0, row=0,col=0, down=0;
while(c<s.length) {
results[row][col]=s[c];
if(down==0) { // moving down
row++;
if(row==numRows) {
down = 1;
col++;
row-=2;
}
} else { // moving up
row--;
col++;
if(row==0) {
down=0;
}
}
c++;
}
Ps. Above code does not handle numRows < 3 so you have to manage them before this loop.
My precalculus is a little rusty, but the logic behind this problem seems like a sine wave. I made a math error somewhere in creating the sin equation that prevents this from working (r never equals c with the current paramaters), but hopefully this will help if this is the direction you choose to go in.
/*If x-axis is position in string, and y-axis is row number...
n=number of rows
Equation for a sin curve: y = A sin(B(x + C)) + D
D=vertical shift (y value of mid point)
D=median of 1 and n
n: median:
1 1
2 1.5
3 2
4 2.5
5 3
6 3.5
7 4
median=(n+1)/2
D=(n+1)/2
A=amplitude (from the mid-point, how high does the curve go)
median + amplitude = number of rows
amplitude = number of rows - median
A=n-D
C=phase shift
Phase shift for a sin curve starting at its lowest point: 3π/2
(so at time 1, row number is 1, and curve goes up from there)
C=3π/2
Period is 2π/B
n p
3 4
4 6
5 8
6 10
period=2(n-1)
2(n-1)=2π/B
B(2(n-1)=2π
B=2π/2(n-1)
B=π/(n-1)
Variables:
s = string
n = number of rows
c = current row number being evaluated
p = position in string
r = row number
*/
var output='';
function convert(s,n) {
D=(n+1)/2
A=n-D
C=(3*Math.PI)/2
B=Math.PI/(n-1)
for (c=1;c<=n;c++) { //loop from 1st row to number of rows
for (p=1;p<=s.length;p++) { //loop from 1st to last character in string
r=A*Math.sin(B*(p+C))+D //calculate the row this character belongs in
if (r==c) { output+= s.charAt(r) } //if the character belongs in this row, add it to the output variable. (minus one because character number 1 is at position 0)
}}
//do something with output here
}
While I was solving a question saying "add odd numbers from 1 to 20", I coded this:
var i, sum=0;
for (i=2; i<=20; i*2){
sum=sum+i;
}
document.write(sum);
When I launched it through a browser, it did not work. However, when I fixed i*2 into i+=2, it worked.
What am I missing? Am I not able to use *(multiplier) in For Loops?
If you need to add odd numbers from 1 to 20, then you need i+=2 as the third parameter of the for and need to initialize the variable to 1 to get the correct result:
var sum = 0;
for (var i = 1; i <= 20; i += 2) {
sum += i;
}
When you have
i += 2
2 is added to i and the result is stored into i. When you tried
var i, sum=0;
for (i=2; i<=20; i*2){
sum=sum+i;
}
i*2 calculates the value which is twice as big as i, but it will not change the value of i, so this would "work" instead:
var i, sum=0;
for (i=2; i<=20; i*=2){
sum=sum+i;
}
where
i *= 2
not only calculates the value twice as big as i, but stores the result into i as well. However, even though this will run, the result will not be correct, since you are using the wrong formula.
Also, you can calculate the result without using a for:
1 + 2 + ... + n = n * (n + 1) / 2
Assuming that n is pair: and since we know that we are "missing" half the numbers and all the pair numbers are bigger exactly with 1 than the previous impair numbers, we can subtract half of the sequence
n * (n + 1) / 2 - n / 2 = (n * (n + 1) - n) / 2 = (n * (n + 1 - 1)) /
2 = n * n / 2
and now we have exactly the double value of what we need, so the final formula is:
sum = n * n / 4;
Let's make this a function
function getOddSumUpTo(limit) {
if (limit % 2) limit ++;
return limit * limit / 4;
}
and then:
var sum = getOddSumUpTo(20);
Note that we increment limit if it is odd.
The issue is that you're not updating the value of the i in the for loop.
I want add odd numbers from 1 to 20
Then you need to change the initial value of i to 1.
var i, sum = 0;
for (i = 1; i <= 20; i += 2){
sum += i;
}
document.write(sum);
Also, you can find the sum of odd numbers from 1 to 20 by using a formula.
n = 20;
console.log(n % 2 == 0 ? (n * n)/ 4 : ((n + 1) * (n + 1))/4);
You can you just have to do it simillary to what you've written about sum.
You used there i += 2 and not i + 2.
The same way just change i * 2 to i *= 2.
Here is an working example
var i, sum = 0;
for (i = 2; i <= 20; i *= 2) {
console.log(i);
sum += i;
}
document.write(sum);
But a couple of things here.
First of all you wrote
add odd numbers from 1 to 20
and in all your examples you use sum on even numbers.
Secondly, by multiplying you will not achieve your desired goal (as you can see in a snippet above in a console)
So to actually
add odd numbers from 1 to 20
you should do it like this:
var i, sum = 0;
for (i = 1; i <= 20; i += 2) {
console.log(i);
sum += i;
}
document.write(sum);
EDIT
If you want to add even numbers you still can't use multiplying.
Why? Simply because you said yourself that you want a sum of numbers.
So let's say that we start with 2.
If we multiply it by 2 it has the value 4 which is fine.
But now look what happens in the next iteration. Our variable i which has the value 4 is multiplied by 2 and now its new value is 8. So what about 6?
Next iteration multiply 8 by 2 and its new value is 16.
Do you see where this is going?
And when you use i += 2 instead of i *= 2?
So if we start with 2 and than we add 2 its new value is 4.
In next iteration we add 2 to 4 and we have 6.
And so on.
If you want to test it, here is an example with multiplying and adding.
Pay attention to console logs
var i;
console.log("Multiplying");
for (i = 2; i <= 20; i *= 2) {
console.log("i value is: " + i);
}
console.log("Adding");
for (i = 2; i <= 20; i += 2) {
console.log("i value is: " + i);
}
What you are looking is this :
let sum = 0;
for(var i = 2; i <= 20; i += 2){
sum += i;
}
document.write(sum)
Another take on this :
// set to n (what you want). Set to n + 1
var N = 21;
// The main driver code create an array from 0-(N-1) and removes all even nums
let a = Array.apply(null, {length: N}).map(Number.call, _ => +_).filter(_=>_%2)
// console.log the array
console.log(a)
You can use whatever expression in loop header, even this is a valid for loop statement for (;;) which simply runs forever (equivalent to while(true)).
Problem is that you are not updating the i counter in for (i=2; i<=20; i*2) so the i will stays the same throughout the execution of the loop.
If you change it to for (i=2; i<=20; i = i*2) or for (i=2; i<=20; i *=2) then it will work.
It is the same as if you did
let i = 1;
i * 2;
console.log(i);
i = i * 2;
console.log(i);
The first i * 2 doesn't update the i while the second one does.
You can also translate the for loop into while loop to see the error more clearly.
// wrong
let i = 1;
while(i <= 20) {
i * 2;
// do something
}
// right
let i = 1;
while(i <= 20) {
i = i * 2 // or i *= 2
// do something
}
Just a side note, if you wanted to perform sum on more types of sequences efficiently than you could use a generator based approach and write your sum function and describe each type of a sequence with a generator function.
function *generateOdd(start, end) {
for (let i = start; i <= end; i++) {
if (i % 2 === 1) { yield i; }
}
}
function *generateEven(start, end) {
for (let i = start; i <= end; i++) {
if (i % 2 === 0) { yield i; }
}
}
function sumNums(gen, start, end) {
const generator = gen(start, end);
let res = 0;
let item = generator.next();
while (!item.done) {
res += item.value;
item = generator.next();
}
return res;
}
console.log(sumNums(generateOdd, 0, 20));
console.log(sumNums(generateEven, 0, 20));
/* sum of the Odd number using loop */
function sumOfOddNumbers(n){
let sum= 0;
for(let i = 1; i <= n; i++) {
if(i % 2 !== 0){
sum = sum + i;
}
}
return sum;
}
// 567 = 1+3+5+7+9+11+13+15+17+19+21+23+25+27+29+31+33+35+37+39+41+43+45+47
let n = 47;
let sum = sumOfOddNumbers(47);
alert('sumOfOddNumbers(' + n + ') = ' + sum);
I'm trying to fill 9 boxes with numbers, these numbers could be number 1 or number 2, being that number 2 can only be 4 times and number 1 should fill the other 5 times left... i know it's a problem of simple logic but someway I can't reach my goal... look at the piece of code that I have...
<script type="text/javascript">
for (cicloTipo = 1; cicloTipo < 10; cicloTipo++) {
var tipo = Math.floor((Math.random() * 2) + 1);
document.write(tipo);
}
</script>
You can start with an array of the required values, then either shuffle the array or randomly select values from it. Some say Math.random isn't truely random, but it should be good enough.
The following uses splice to select values, so the loop iterates backwards since splicing shortens the source array each time.
function getRandoms(){
for (var seed=[1,1,1,1,1,2,2,2,2], result=[], i=seed.length; i; i--) {
result.push(seed.splice(Math.random() * i | 0, 1)[0]);
}
return result;
}
// Show randomness of result
(function() {
var s = new Array(30).fill('+');
var r;
for (var i=9; i; ){
document.getElementById(--i).textContent = s.join('');
}
var j = 300; // Number of runs
var delay = 20; // Default delay in ms
function display(lag) {
delay = lag || delay;
getRandoms().forEach(function(v, i, rand) {
var el = document.getElementById(i);
if (v == 1) {
el.textContent = el.textContent.slice(0,-1);
// If run out of "+", add some to every line
if (!el.textContent.length) {
for (var k=0; k < 9; k++) {
document.getElementById(k).textContent += '++++++++++';
}
}
} else {
el.textContent += '+';
}
if (i == 0) {
document.getElementById('msg').innerHTML = 'Remaining: ' + j +
'<br>' + 'Values: ' + rand.join('');
}
});
--j;
if (j > 0) {
setTimeout(display, delay);
}
}
display(50);
}());
// Single call
// console.log(getRandoms().join());
<span id="0"></span><br>
<span id="1"></span><br>
<span id="2"></span><br>
<span id="3"></span><br>
<span id="4"></span><br>
<span id="5"></span><br>
<span id="6"></span><br>
<span id="7"></span><br>
<span id="8"></span><br>
<span id="msg"></span>
For fun I've added a display of the distribution. Each line represents a value in the result array from 0 to 8 and starts with a set number of "+" symbols. Each time a 1 is in the related position, a "+" is removed. Each time a 2 is in the position, a "+" is added. Since there are more 1s than 2s, the lines slowly get shorter. When a line gets to zero length, 10 more "+" are added to every line.
The important part is that the lines stay about equivalent lengths and that the same lines aren't longest or shortest after each run. If you think you see a pattern emerging, it must be sustained for at least 100 runs to show a bias.
Here's a solution which ensures that no more than 4 2's are in the chain.
it means that the digits chain can contain from 0 2's to 4 2's and the rest is 1's
// 2s Counter
var c1 = c2 = 0;
for (var cicloTipo = 1; cicloTipo < 10; cicloTipo++) {
var tipo = Math.floor((Math.random() * 2) + 1);
// check if it's a 2 and 4 2s have been encountred
if (tipo == 2) {
if (c2 < 4) {
// increment counter
c2++;
} else {
tipo = 1;
c1++;
}
}
// check if it's a 1 and 5 1s have been encountred
else if (tipo == 1) {
if (c1 < 5) {
// increment counter
c1++;
} else {
tipo = 2;
c2++;
}
}
document.write(tipo);
}
it looks like your criteria forces 4 2's and 5 1's;
i fixed this code to fit this criteria but #Paul Rooney 's suggestion is the best.
I am going through the Odin Project and part of that is doing questions 1-3 in the Euler project. I am stumped on question 2:
"By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms."
I am so frustrated! What am I doing wrong? Here's what I have so far. Thanks!
function f() {
var fib = [];
fib.push(1,2,3);
var i = fib.length;
var total = 0;
while(fib[i] < 4000000) {
var x = fib[i-2] + fib [i-1];
if(x % 2 == 0) {
total += x;
}
} return total;
}
console.log(f());
The fibonacci sequences starts 1, 1, 2, not 1, 2, 3.
Also, your solution looks like it will work, but you are storing every number in the sequence instead of just the last two, so this will gobble memory comparatively.
As #DLeh notes, the fibonacci sequence starts with 1,1,2 - not 1,2,3. However, that doesn't change the result of summing the even valued items. The problem you're having is that at this point:
while(fib[i] < 4000000) {
fib[i] is undefined, so the function immediately exits with the total staying at 0. Also within the while loop, you're not pushing the next item in the sequence into your array. The below code fixes both of these problems:
function f() {
var fib = [];
fib.push(1,1);
var i = fib.length;
var total = 0;
while(fib[i-1] < 4000000) {
var x = fib[i-2] + fib [i-1];
fib.push(x);
i = fib.length;
if(x % 2 == 0) {
total += x;
}
} return total;
}
console.log(f()); //4613732
#DLeh also pointed out that you're storing more numbers than needed, this solution works without using the array:
function f() {
var f1 = 1;
var f2 = 1;
var total = 0;
while (f2 < 4000000) {
var t = f1 + f2;
if (t % 2 == 0)
total += t;
f1 = f2;
f2 = t;
}
return total;
}
console.log(f()); //4613732
Just for grins, note that you can do this problem without any use of %, and just + operations. Every third value in the sequence is even. That is, 2 is followed by 3 (odd), and then 3 + 2 is 5 (odd), but that sum of two odd numbers gets us back to even (8) and the cycle repeats.
Thus:
function evenFibTotal(limit) {
var a = 1, b = 1, c = 2, total = 0;
while (c < limit) {
total += c;
a = b + c;
b = a + c;
c = a + b;
}
return total;
}
On each iteration, the second trailing value is set to the next value in the sequence (b + c), and that plus the current one is the first trailing value, and finally the next even Fibonacci number is the sum of those two.
(There's also the closed solution but it's no fun :)
given a matrix like this one:
1 2 3
4 5 6
7 8 9
which can be represented as a 2 dimensional array:
arr = [[1,2,3], [4,5,6], [7,8,9]];
rotate the array so that it is read diagonally at a 45 degree angle and prints out this:
1
4 2
7 5 3
8 6
9
I spent a while coming up with a solution that I don't even fully intuitively understand, but it works, at least for 3x3 and 4x4 matrices. I was hoping to see more logical and clean implementations.
Here's my solution:
arr = [[1,2,3,0],[4,5,6,0],[7,8,9,0], [0,0,0,0]];
// arr[i][j];
transform(arr);
function transform(ar) {
// the number of lines in our diagonal matrix will always be rows + columns - 1
var lines = ar.length + ar[0].length - 1;
// the length of the longest line...
var maxLen = ~~(ar.length + ar[0].length)/2;
var start = 1;
var lengths = [];
// this for loop creates an array of the lengths of each line, [1,2,3,2,1] in our case
for (i=0;i<lines; i++) {
lengths.push(start);
if (i+1 < maxLen) {
start++;
} else {
start--;
}
}
// after we make each line, we're going to append it to str
var str = "";
// for every line
for(j=0; j<lengths.length; j++) {
// make a new line
var line = "";
// i tried to do it all in one for loop but wasn't able to (idk if it's possible) so here we use a particular for loop while lengths of the lines are increasing
if (j < maxLen) {
// lengths[j] is equal to the elements in this line, so the for loop will run that many times and create that many elements
for(c=0; c<lengths[j]; c++) {
// if ar[r][c], the pattern here is that r increases along rows (as we add new lines), and decreases along columns. c stays the same as we add rows, and increases across columns
line += ar[lengths[j]-1-c][c] + " ";
// when we've added all the elements we need for this line, add it to str with a line break
if (c == lengths[j]-1) {
line += "\n"; str += line;
}
}
} else {
// when we're headed down or decreasing the length of each line
for (r=0; r<lengths[j]; r++) {
// the pattern here tripped me up, and I had to introduce another changing variable j-maxLen (or the distance from the center). r stays the same as rows increase and decreases across columns. c increases along rows and decreases across columns
line += ar[lengths[j]-r+j-maxLen][j-maxLen+r +1] + " ";
// that's all our elements, add the line to str;
if (r == lengths[j] -1) {
line += "\n"; str += line;
}
}
}
}
console.log(str);
}
The main idea is to partition the original matrix indexed by (i,j) according to i+j.
This is expressed in the code snippet rotated[i+j].push(arr[i][j]) below:
arr = [[1,2,3], [4,5,6], [7,8,9]];
var summax = arr.length + arr[0].length - 1; // max index of diagonal matrix
var rotated = []; // initialize to an empty matrix of the right size
for( var i=0 ; i<summax ; ++i ) rotated.push([]);
// Fill it up by partitioning the original matrix.
for( var j=0 ; j<arr[0].length ; ++j )
for( var i=0 ; i<arr.length ; ++i ) rotated[i+j].push(arr[i][j]);
// Print it out.
for( var i=0 ; i<summax ; ++i ) console.log(rotated[i].join(' '))
Output:
1
4 2
7 5 3
8 6
9
In Ruby
Produces same output:
puts arr.transpose.flatten.group_by.with_index { |_,k|
k.divmod(arr.size).inject(:+) }.values.map { |a| a.join ' ' }
function transform(ar) {
var result = [],
i, x, y, row;
for (i = 0; i < ar.length; i++) {
row = [];
for (x = 0, y = i; y >= 0; x++, y--) {
row.push(ar[y][x]);
}
result.push(row);
}
for (i = 1; i < ar[0].length; i++) {
row = [];
for (x = i, y = ar[0].length - 1; x < ar[0].length; x++, y--) {
row.push(ar[y][x]);
}
result.push(row);
}
return result;
}
This returns the rotated array, to print it out as you go just replace each result.push(row); line with console.log(row.join(" "));.
Here's my approach:
var origMatrix = [[1,2,3,4,5], [4,5,6,7,8], [9,10,11,12,13], [14,15,16,17,18], [19,20,21,22,23]];
var maxSize = origMatrix.length;//Presuming all internal are equal!
var rotatedMatrix = [];
var internalArray;
var keyX,keyY,keyArray;
for(var y=0;y<((maxSize * 2)-1);y++){
internalArray = [];
for(var x=0;x<maxSize;x++){
keyX = x;
keyY = y - x;
if(keyY > -1){
keyArray = origMatrix[keyY];
if(typeof(keyArray) != 'undefined' && typeof(keyArray[keyX]) != 'undefined'){
internalArray.push(keyArray[keyX]);
}
}
}
rotatedMatrix.push(internalArray);
}
//log results
for(var i=0;i<rotatedMatrix.length;i++){
console.log(rotatedMatrix[i]);
}
Here's a JSFiddle of it in action (open the Console to see the results)
The Idea: Walk the Diagonals and Clip
You could use the diagonal enumeration from Cantor, see Cantor pairing function,
which is used to show that the set N x N has the same cardinality as the set N (i.e. natural numbers can be mapped one to one to pairs of natural numbers) and combine it with a condition that one skips those values which lie outside the rectangular matrix.
The Cantor pairing function pi takes two natural numbers i and j, i.e. the pair (i, j) and maps it to a natural number k
pi : |N x |N -> |N : pi(i, j) = k
Use the reverse mapping to get this
pi^-1 : |N -> |N x |N : pi^-1(k) = (i, j)
i.e. one enumerates the cells of the "infinite Matrix" N x N diagonally.
So counting up k and applying the inverse function will give the proper pair of indices (i, j) for printing the rotated matrix.
Example:
0->(0, 0) 2->(0, 1) | 5->(0, 2) 9->(0, 3) . .
1->(1, 0) 4->(1, 1) | 8->(1, 2)
3->(2, 0) 7->(2, 2) |
---------------------+ <- clipping for 3 x 2 matrix
6->(3, 0)
.
.
Calculation of the Inverse Cantor Pair Function
For input k these formulas give the pair (i, j):
w = floor((sqrt(8*k + 1) - 1) / 2)
t = (w*w + w) / 2
j = k - t
i = w - j
See the link given above for a derivation.
Resulting Algorithm
Given a m x n matrix A: i from [0, .., m - 1] enumerates the rows, and j from [0, .., n - 1] enumerates the columns
Start at k = 0
calculate the corresponding index pair (i, j)
print the matrix value A[i, j] if the indices i and j lie within your matrix dimensions m and n
print a new line, once your i hit the top of the matrix, i.e. if i == 0
increment k
continue with step 2 until you arrived at the index pair (i, j) = (n - 1, n - 1)
Sample Implementation in JavaScript
Note: I tried this out in the MongoDB shell, using its print() function.
Helper functions
function sprint(k) {
var s = '' + k;
while (s.length < 3) {
s = ' ' + s;
}
return s;
}
function print_matrix(a) {
var m = a.row_size;
var n = a.column_size;
for (var i = 0; i < m; i++) {
var s = '';
for (var j = 0; j < n; j++) {
s += sprint(a.value[i][j]);
}
print(s);
}
}
The Inverse of the Cantor pairing function
// inverse of the Cantor pair function
function pi_inv(k) {
var w = Math.floor((Math.sqrt(8*k + 1) - 1) / 2);
var t = (w*w + w) /2;
var j = k - t;
var i = w -j;
return [i, j];
}
The algorithm
// "rotate" matrix a
function rot(a) {
var m = a.row_size;
var n = a.column_size;
var i_max = m - 1;
var j_max = n - 1;
var k = 0;
var s = '';
do {
var ij = pi_inv(k);
var i = ij[0];
var j = ij[1];
if ((i <= i_max) && (j <= j_max)) {
s += sprint(a.value[i][j]);
}
if (i == 0) {
print(s);
s = '';
}
k += 1
} while ((i != i_max) || (j != j_max));
print(s);
}
Example usage
// example
var a = {
row_size: 4,
column_size: 4,
value: [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ]
};
print('in:');
print_matrix(a);
print('out:');
rot(a);
Output for 4x4 Matrix
in:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
out:
1
5 2
9 6 3
13 10 7 4
14 11 8
15 12
16
This method works for any m x n Matrix, e.g. 4 x 5:
in:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
out:
1
6 2
11 7 3
16 12 8 4
17 13 9 5
18 14 10
19 15
20
or 4 x 3:
in:
1 2 3
4 5 6
7 8 9
10 11 12
out:
1
4 2
7 5 3
10 8 6
11 9
12