Trying to solve a zigzag pattern for an algorithm question - javascript

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
}

Related

Alternate numbers into two rows

I think I'm having a brain fart, because I can't figure out a simple formula to be able sort a sequence of number into specific order so it can be printed 2 numbers per sheet of paper (one number on one half and second number on second half), so when the printed stack of paper cut in half, separating the numbers, and then these halves put together, the numbers would be in sequence.
So, let's say I have 5 numbers: 3,4,5,6,7, the expected result would be 3,6,4,7,5 or
0,1,2,3,4,5,6,7 would become 0,4,1,5,2,6,3,7
My thought process is:
create a loop from 0 to total number of numbers
if current step is even, then add to it total numbers divided in 2
Obviously, I'm missing a step or two here, or there is a simple mathematical formula for this and hopefully someone could nudge me in a right direction.
This is what I have so far, it doesn't work properly if start number set to 1 or 3
function show()
{
console.clear();
for(let i = 0, count = end.value - start.value, half = Math.round(count/2); i <= count; i++)
{
let result = Math.round((+start.value + i) / 2);
if (i && i % 2)
result = result + half -1;
console.log(i, "result:", result);
}
}
//ignore below
for(let i = 0; i < 16; i++)
{
const o = document.createElement("option");
o.value = i;
o.label = i;
start.add(o);
end.add(o.cloneNode(true));
}
start.value = 0;
end.value = 7;
function change(el)
{
if (+start.value > +end.value)
{
if (el === start)
end.value = start.value;
else
start.value = end.value;
}
}
<select id="start" oninput="change(this)"></select> -
<select id="end" oninput="change(this)"></select>
<button onclick="show()">print</button>
P.S. Sorry, about the title, couldn't come up with anything better to summarize this.
You could get the value form the index
if odd by using the length and index shifted by one to rigth (like division by two and get the integer value), or
if even by the index divided by two.
function order(array) {
return array.map((_, i, a) => a[(i % 2 * a.length + i) >> 1]);
}
console.log(...order([3, 4, 5, 6, 7]));
console.log(...order([0, 1, 2, 3, 4, 5, 6, 7]));

Find the N traversal of the Matrix / 2D array

given matrix / 2D array
1 2 3
4 5 6
7 8 9
we have to print
7 4 1 5 9 6 3 in javascript without using inbuilt functions
Here's a hint.
Assuming your matrix is presented as a 1-d array of integers:
m = [1,2,3,4,5,6,7,8,9];
which is logically assumed to be a "square" size (1x1, 2x2, 3x3, 4x4, etc...)
So the above array is logically assumed to be:
1 2 3
4 5 6
7 8 9
Let's use a helper function to help us obtain any that value at any given (x,y) position. With (0,0), being the top left value of the matrix
let getVal = (m, col, row) => {
let rowlen = Math.round(Math.sqrt(m.length));
let index = row * rowlen + col;
return m[index];
}
Hence, getVal(m, 0, 0) returns 1 for the top left corner and getVal(m,2,2) returns 9 for the value in the bottom right corner.
Now with that little helper function provided, do you think you can implement the three for loops for "going up from the bottom left", "diagonal from top left to bottom right", and "going up from the bottom right"?
var output= "";
var length = matrix.length;
// console.log(length)
for(var i = length - 1; i >= 0; i--){
output= output+ matrix[i][0] + " ";
}
// console.log(bag);
for(var j = 1; j < length; j++){
output= output+ matrix[j][j] + " ";
}
// console.log(bag);
for(var k = length - 2; k >= 0; k--){
output= output+ matrix[k][length - 1] + " ";
}
console.log(output);
}

Calculating the area of an irregular polygon using JavaScript

I got this as an assignment, but I'm stuck on implementing the area portion. The assignment is already turned in (finished about 80%). I still want to know how to implement this, though. I am stuck on line 84-86. Here is the prompt.
Prompt: Calculate the area of irregularly shaped polygons using JS
INPUT: a nested array of 3-6 coordinates represented as [x,y] (clockwise order)
OUTPUT: area calculated to 2 significant figures
Pseudocode:
loop the input and check for error cases:
a. array length < 3 or array length > 6
b. array is not empty
c. numbers within -10 and 10 range
loop to each inner array, and multiply x and y in the formula below:
sum_x_to_y = (X0 *Y1) + (X1* Y2)...X(n-1)* Yn
sum_y_to_x = (Y0 * X1) + (Y1-X2)...Y(n-1)* Xn
ex:
(0, -10) (7,-10) (0,-8) (0,-10)
| x | y |
| 0 |-10 |
| 7 |-10 |
| 0 |-8 |
| 0 |-10 |
sum_x_to_y = 0*-10 + 7*-8 + 0*-10 = -56
sum_y_to_x = -10*7 + -10*0 + -8*0 = -70
area = (sum_y_to_x - sum_x_to_y) / (2.00)
ex: area = -70 -(-56) = 57/2 = 7
return area.toPrecision(2) to have one sig fig
function PaddockArea(array_coords) {
var sum_x_to_y = 0;
var sum_y_to_x = 0;
var arr_size = array_coords.length;
if (arr_size === 0) {
//check for empty array
console.log("Invalid input. Coordinates cannot be empty.");
}
if (arr_size < 3 || arr_size > 7) {
//check input outside of 3-6 range
console.log("Input out of range.");
}
for (var i = 0; i < arr_size; i++) {
for (var j = 0; j < array_coords[i].length; j++) {
//test for inner coordinates -10 to 10 range
if (array_coords[i][j] < -10 || array_coords[i][j] > 10) {
console.log("Coordinates outside of -10 to 10 range.");
}
// I NEED TO IMPLEMENT TO calc for AREA here
sum_x_to_y += array_coords[i][j] * array_coords[j][i];
sum_y_to_x += array_coords[j][i] * array_coords[i][j];
var area = (sum_y_to_x - sum_x_to_y) / 2;
console.log(area.toPrecision(2) + "acres");
}
}
}
If you're just using Simpson's rule to calculate area, the following function will do the job. Just make sure the polygon is closed. If not, just repeat the first coordinate pair at the end.
This function uses a single array of values, assuming they are in pairs (even indexes are x, odd are y). It can be converted to using an array of arrays containing coordinate pairs.
The function doesn't do any out of bounds or other tests on the input values.
function areaFromCoords(coordArray) {
var x = coordArray,
a = 0;
// Must have even number of elements
if (x.length % 2) return;
// Process pairs, increment by 2 and stop at length - 2
for (var i=0, iLen=x.length-2; i<iLen; i+=2) {
a += x[i]*x[i+3] - x[i+2]*x[i+1];
}
return Math.abs(a/2);
}
console.log('Area: ' + areaFromCoords([1,1,3,1,3,3,1,3,1,1])); // 4
console.log('Area: ' + areaFromCoords([0,-10, 7,-10, 0,-8, 0,-10,])); // 7
Because you haven't posted actual code, I haven't input any of your examples. The sequence:
[[1,0],[1,1],[0,0],[0,1]]
is not a polygon, it's a Z shaped line, and even if converted to a unit polygon can't resolve to "7 acres" unless the units are not standard (e.g. 1 = 184 feet approximately).

rotate a matrix 45 degrees in javascript

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

Why isn't my attempted solution working?

When I run the code in the console, the browser just stops working (am assuming stack overflow).
I've come up with several different algorithms for solving this problem, but I thought this one would not cause any SOs.
The problem:
The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
Let us list the factors of the first seven triangle numbers:
1: 1
3: 1 3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
We can see that 28 is the first triangle number to have over five divisors.
What is the value of the first triangle number to have over five hundred divisors?
Failing solution:
function divisors(n){
var counter = 0;
var triangle = 3;
var triangle_add = 2;
while (counter < n){
for (var i = 1; i = triangle; i++){
if (triangle % i === 0){
counter++;
}
};
if (counter < n){
triangle_add++;
triangle = triangle + triangle_add;
counter = 0;
};
};
return triangle;
};
console.log(divisors(501));
Your solution is not working because, most probably, it is very slow. This problem can be solved much faster by the following method:
Find all the prime numbers smaller than some N (put, for example, N=100'000) using Sieve of Eratosthenes. It is quite fast.
As we know from elementary math each number can be written in the form X=p1^i1*p2^i2*...*pn^in where pj is prime number and ij is the power of corresponding prime number. The number of divisors of X is equal to (i1+1)*(i2+1)*...*(in+1) since that many different ways we can form a number which will be divisor of X. Having an array of prime numbers the number of divisors for X can be calculated quite fast (the code still has place to be optimized):
int divisorCount(long long X)
{
int c = 1;
for (int i = 0; PRIMES[i] <= X; ++i)
{
int pr = PRIMES[i];
if (X % pr == 0)
{
int p = 1;
long long r = X;
while (r % pr == 0)
{
r = r / pr;
++p;
}
c *= p;
}
}
return c;
}
Iterate through all triangle numbers and count divisor numbers for them using the above function. The i-th triangle number is i * (i + 1) / 2, so no need to keep a variable, increment it and add it each time.

Categories