I tried to do the classical problem to implement an algorithm to print all valid combinations of n pairs of parentheses. And I found this program (which works perfectly) :
There are more possible approaches for using the available parentheses generation code.
Use it exactly as it is, and convert the resulting set of parentheses strings into mountain representations.
Update it in order to directly generate the mountain strings. This is the alternative detailed in this answer.
This prevents dealing with complications of inserting newlines while constructing the solution. A new string will be generated from this matrix once a solution is complete.
Concatenate the strings associated to each row of the matrix, adding newlines after each row. Additionally (not implemented in the solution below), the trailing spaces of each row could be removed.
We use two position parameters, denoted row
and col
, as we are moving in two dimensions now, and they are the correspondents of the count
parameter in the old code. The row
and col
indicate the corner where the mountain line has led us so far. The col
(column) parameter increases by 1 after each character we add. The row
parameter is changed according to whether the current character corresponds to climbing or descending.
This was implicit in the 1D case, because we always ended up with strings of a fixed length and every new solution overwrote the previous ones. However, in the 2D case, if we don't clean up the path generated for a solution, we might see parts of it in the following solutions.
The size of the matrix is count
rows (because this is the maximum height of a solution that will be generated) and 2 * count
columns (because this is the length when using count
pairs of strokes). The matrix is initially filled with white spaces.
Below is the Java code that was updated according to the ideas above. Despite the modifications enumerated, the core logic is the same (the recursive structure is the same - the decision of whether to attempt adding an up-stroke / down-stroke and the termination criteria are not altered).
public static void addParen(ArrayList<String> list, int leftRem, int rightRem, char[][] str, int row, int col) {
if (leftRem < 0 || rightRem < leftRem) return; // invalid state
if (leftRem == 0 && rightRem == 0) { /* all out of left and right parentheses */
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length; i++) {
sb.append(String.copyValueOf(str[i]));
sb.append(System.lineSeparator());
}
list.add(sb.toString());
} else {
if (leftRem > 0) { // try a left paren, if there are some available
str[row][col] = '/';
addParen(list, leftRem - 1, rightRem, str, row - 1, col + 1);
str[row][col] = ' ';
}
if (rightRem > leftRem) { // try a right paren, if there’s a matching left
str[row + 1][col] = '\\';
addParen(list, leftRem, rightRem - 1, str, row + 1, col + 1);
str[row + 1][col] = ' ';
}
}
}
public static ArrayList<String> generateParens(int count) {
char[][] str = new char[count][count * 2];
for (int i = 0; i < str.length; i++) {
Arrays.fill(str[i], ' ');
}
ArrayList<String> list = new ArrayList<>();
addParen(list, count, count, str, count - 1, 0);
return list;
}
Below are the resulting mountains when the input is 3 (i.e. the width of the string is 6, because we have 3 up-strokes and 3 down-strokes):
/\
/ \
/ \
/\/\
/ \
/\
/ \/\
/\
/\/ \
/\/\/\
There are a few interesting questions that now can be answered about these strings.
(Q1) how many valid strings are there for a specific width?
(Q2) what is the probability of a random sequence of '/' and '\' to be a valid mountain?
(Q3) what is the probability of a random sequence containing equal number of '/' and '\' to be a valid mountain?
The table below answers to these questions for various string lengths:
Length Valid Total Prob. Q2 Equal / and \ Prob. Q3
2 1 4 25.0000% 2 50.0000%
4 2 16 12.5000% 6 33.3333%
6 5 64 7.8125% 20 25.0000%
8 14 256 5.4688% 70 20.0000%
10 42 1,024 4.1016% 252 16.6667%
12 132 4,096 3.2227% 924 14.2857%
14 429 16,384 2.6184% 3,432 12.5000%
16 1,430 65,536 2.1820% 12,870 11.1111%
18 4,862 262,144 1.8547% 48,620 10.0000%
20 16,796 1,048,576 1.6018% 184,756 9.0909%
22 58,786 4,194,304 1.4016% 705,432 8.3333%
24 208,012 16,777,216 1.2398% 2,704,156 7.6923%
26 742,900 67,108,864 1.1070% 10,400,600 7.1429%
28 2,674,440 268,435,456 0.9963% 40,116,600 6.6667%
30 9,694,845 1,073,741,824 0.9029% 155,117,520 6.2500%
Interesting task. The calculation can be done in parallel. I'll show the code within "not answer" tags, as it doesn't match the question's language criterion (made in the parallel array processing language Dyalog APL which in fact it does the job with one row of code). Please ignore that part as you wish. However, i'll show the data and what happens. It's rather intuitive.
< not answer >
fn←{(∧/(0≤+\a-~a),(⍵÷2)=+/a)⌿a←⍉(⍵⍴2)⊤⍳2*⍵} // Dynamic function, generates boolean matrix
format←{⍉↑(-1+(0.5×⍴⍵)-+\⍵-0,¯1↓~⍵)↑¨'\/'[1+⍵]} // Dirty format function
< /not answer >
Say the argument (the width of the mountains) is n=6.
Step 1. Generate all numbers between 0 and (2^6 - 1)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
Step 2: Grab the 2-base of each (they are vertically below. 0 is leftmost, then 1, etc):
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1. In fact, one only needs to generate numbers from 32 to 63, since we only want 2-base's that begin with 1. See topmost row in data above. Columns (numbers) with zeroes at top shouldn't really even be generated.)
2. In fact one only needs to generate even numbers, since last bit must be 0. See downmost row in data above. Columns (numbers) with ones at bottom shouldn't really even be generated.)
Step 3: Calculate the number of ones in each column:
0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4 1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
and do a boolean marking = 1 where the sum is half of N, that is 3 (ie. in total we must have as many uphills as downhills). This is our 1st boolean result:
0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 0 0 1 1 0 1 0 0 0 0 0 0 1 0 1 1 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0
Step 4: Ensure that we do not go "below horizon":
This means we must calculate a cumulative sum of each column, first:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4
0 0 1 1 1 1 2 2 1 1 2 2 2 2 3 3 1 1 2 2 2 2 3 3 2 2 3 3 3 3 4 4 1 1 2 2 2 2 3 3 2 2 3 3 3 3 4 4 2 2 3 3 3 3 4 4 3 3 4 4 4 4 5 5
0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4 1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
and then for the shifted bits (0's turned into 1's, and vice versa):
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
4 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0
5 5 4 4 4 4 3 3 4 4 3 3 3 3 2 2 4 4 3 3 3 3 2 2 3 3 2 2 2 2 1 1 4 4 3 3 3 3 2 2 3 3 2 2 2 2 1 1 3 3 2 2 2 2 1 1 2 2 1 1 1 1 0 0
6 5 5 4 5 4 4 3 5 4 4 3 4 3 3 2 5 4 4 3 4 3 3 2 4 3 3 2 3 2 2 1 5 4 4 3 4 3 3 2 4 3 3 2 3 2 2 1 4 3 3 2 3 2 2 1 3 2 2 1 2 1 1 0
then subtract the 2nd from the 1st, and get
¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
¯3 ¯3 ¯3 ¯3 ¯3 ¯3 ¯3 ¯3 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 1 1 1 1 1 1 1 1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 ¯1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3
¯4 ¯4 ¯4 ¯4 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 ¯2 0 0 0 0 ¯2 ¯2 ¯2 ¯2 0 0 0 0 0 0 0 0 2 2 2 2 ¯2 ¯2 ¯2 ¯2 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 2 2 2 2 2 2 2 2 4 4 4 4
¯5 ¯5 ¯3 ¯3 ¯3 ¯3 ¯1 ¯1 ¯3 ¯3 ¯1 ¯1 ¯1 ¯1 1 1 ¯3 ¯3 ¯1 ¯1 ¯1 ¯1 1 1 ¯1 ¯1 1 1 1 1 3 3 ¯3 ¯3 ¯1 ¯1 ¯1 ¯1 1 1 ¯1 ¯1 1 1 1 1 3 3 ¯1 ¯1 1 1 1 1 3 3 1 1 3 3 3 3 5 5
¯6 ¯4 ¯4 ¯2 ¯4 ¯2 ¯2 0 ¯4 ¯2 ¯2 0 ¯2 0 0 2 ¯4 ¯2 ¯2 0 ¯2 0 0 2 ¯2 0 0 2 0 2 2 4 ¯4 ¯2 ¯2 0 ¯2 0 0 2 ¯2 0 0 2 0 2 2 4 ¯2 0 0 2 0 2 2 4 0 2 2 4 2 4 4 6
and see which columns have no negative values; this is the 2nd boolean result:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Step 5: Grab an AND from the two boolean results above:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0
These are the locations for columns of the binary data that create the good mountains. Below column-wise to left, then transposed (for readability) to right. 1 is uphills, 2 edit:0 is downhills:
1 1 1 1 1 1 0 1 0 1 0 // 1 0 1 0 1 0 means /\/\/\
0 0 1 1 1 1 0 1 1 0 0
1 1 0 0 1 1 1 0 0 1 0
0 1 0 1 0 1 1 0 1 0 0 // means //\/\\
1 0 1 0 0 1 1 1 0 0 0
0 0 0 0 0
That is the good answer. If needed, we can apply a formatting:
format [the boolean result]
┌──────┬──────┬──────┬──────┬──────┐
│ │ │ │ │ /\ │
│ │ /\ │ /\ │ /\/\ │ / \ │
│/\/\/\│/\/ \│/ \/\│/ \│/ \│
└──────┴──────┴──────┴──────┴──────┘
A little bigger, n=10:
DISP format¨↓fn 10
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ /\ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ /\ │ │ │ │ │ │ │ │ │ │ │ │ │ │ /\ │ │ │ │ │ │ │ │ │ /\ │ /\ │ /\ │ /\ │ /\/\ │ / \ │
│ │ │ │ │ /\ │ │ │ │ │ /\ │ /\ │ /\ │ /\/\ │ / \ │ │ │ │ │ /\ │ │ │ │ │ /\ │ /\ │ /\ │ /\/\ │ / \ │ /\ │ /\ │ /\ │ /\ │ /\ /\ │ /\/\ │ /\/\ │ /\/\/\ │ /\/ \ │ / \ │ / \ │ / \/\ │ / \ │ / \ │
│ │ /\ │ /\ │ /\/\ │ / \ │ /\ │ /\ /\ │ /\/\ │ /\/\/\ │ /\/ \ │ / \ │ / \/\ │ / \ │ / \ │ /\ │ /\ /\ │ /\ /\ │ /\ /\/\ │ /\ / \ │ /\/\ │ /\/\ /\ │ /\/\/\ │ /\/\/\/\ │ /\/\/ \ │ /\/ \ │ /\/ \/\ │ /\/ \ │ /\/ \ │ / \ │ / \ /\ │ / \/\ │ / \/\/\ │ / \/ \ │ / \ │ / \/\ │ / \ │ / \ │ / \ │ / \/\ │ / \ │ / \ │ / \ │
│/\/\/\/\/\│/\/\/\/ \│/\/\/ \/\│/\/\/ \│/\/\/ \│/\/ \/\/\│/\/ \/ \│/\/ \/\│/\/ \│/\/ \│/\/ \/\│/\/ \│/\/ \│/\/ \│/ \/\/\/\│/ \/\/ \│/ \/ \/\│/ \/ \│/ \/ \│/ \/\/\│/ \/ \│/ \/\│/ \│/ \│/ \/\│/ \│/ \│/ \│/ \/\/\│/ \/ \│/ \/\│/ \│/ \│/ \/\│/ \│/ \│/ \│/ \/\│/ \│/ \│/ \│/ \│
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
Edit: Naturally one can do all this in a loop as well. Just take one number at time, and do the checks above (number of ones == half of n, does not go below horizon). Jump out if either check fails.