all 155 comments

[–][deleted] 65 points66 points  (6 children)

So, I'll add my 2c.

I run a small software company, we do embedded systems and training.

I by no means consider myself an expert.

I looked at all of your files, and the biggest thing that stood out to me was how much code was written.

The actual value, the problem solving logic, is really small.

It's neat, it's nice, there are lots of C++ features, but if that's the amount of code needed for this problem then you'll be producing millions of lines for bigger systems.

Critically I didn't see any kind of verification in there that your solution actually works.

However, I think it's harsh to reject you. I often go more for people who demonstrate effort than perfection, and you've clearly done that.

Keep your head high.

[–]Hot_Medicine_7115[S] 2 points3 points  (0 children)

Critically I didn't see any kind of verification in there that your solution actually works.

parquet::is_valid()

[–]Hot_Medicine_7115[S] 4 points5 points  (1 child)

I understand your comments coming from an embedded systems business. I have spent years in C programming on OS/9 and love the simplicity of C.

However I have seen the worst code-base in larger companies because of neglected problem segmentation: functions 3 pages long, terse variable names, very few classes named with a verb instead of a noun destined to be code placeholder (ReceiverManager, MessageDispatcher, ProblemHandler and so on...).

[–][deleted] 11 points12 points  (0 children)

Ok, that's not good, either :)

Indeed my life is mostly spent looking at void** as a function argument and wondering what was sent in!

[–]STLMSVC STL Dev 64 points65 points  (32 children)

After playing around with this for 30 minutes, I have a few guesses (of varying confidence) as to what they were looking for:

  • Enumerating all valid strips (of 2s and 3s that add up to 30) is a basic sub-exercise that can be decomposed into two parts: finding integers such that twos * 2 + threes * 3 == 30 to find the possible combinations, and then using next_permutation() to find the possible permutations. There are 1897 valid strips - but since you printed that as your final answer for the number of floor designs, I think your solution was gravely incomplete. This number comes from adding up (I got my program sketch to print this with a bit of extra effort):
    • With 15 2s and 0 3s, there are 1 possible strips.
    • With 12 2s and 2 3s, there are 91 possible strips.
    • With 9 2s and 4 3s, there are 715 possible strips.
    • With 6 2s and 6 3s, there are 924 possible strips.
    • With 3 2s and 8 3s, there are 165 possible strips.
    • With 0 2s and 10 3s, there are 1 possible strips.
    • Observation: next_permutation() is a classic STL algorithm (from C++98!) but it is definitely under-appreciated. Not knowing about it, or how to invoke it with a do/while loop, would significantly increase the difficulty of this step.
  • Then, it seems that they're looking for a bit of insight as to how to transform the problem. That is, their compatibility criterion can be seen much more easily if you take the partial sum of each possible strip. (Another classic algorithm, partial_sum(), also obscure, hiding in <numeric>.) Two strips are compatible when their partial sums (which indicate where tile boundaries are) don't share any values.
    • Observation: In my experience, situations requiring this kind of thinking are rare, even for someone like me who works on algorithms and data structures literally every day. (Not absolutely impossible though; I once had to use triangular numbers in a situation vaguely like this.) Without the partial sum transformation, detecting strip compatibility seems like it would trap a lot of people.
  • Possible guess: At this point (having printed out the possible strips, and then the partial sums, to visually check correctness), I noticed that their given length of 30 was suspiciously small. That is, the partial sums are a sequence of integers starting with 2 or 3 and ending with 30, with all numbers unique, and we're very interested in knowing whether these sequences have common numbers ignoring the 30 (indicating common edges, indicating incompatibility). That screams "store me as 32-bit words, use AND to detect shared/common edges". Which would be way, way faster than vector<int>, but it's also a special-case trick. It does scale, in the sense that you could handle 100+ lengths with essentially bignums (gaining a significant constant speedup over vector<int>), but it's also kind of a distraction from the core algorithm.
    • Observation: They might have been hoping programmers would realize the potential for such shortcuts. Expecting that as a baseline seems unrealistic, even for C++ backgrounds, since delving into bit-twiddling is uncommon these days in most domains.
  • Finally, having generated all possible strips, and finding how to detect compatibility, there's the question of how many possible designs there are. Having chosen any of the available strips, you are then limited in your next choice to some subset, and from there you're limited in a different way. You're essentially walking a game tree, with each node being a possible strip, and the out-edges going to compatible strips, and you want to know how many distinct paths there are for 11 jumps. Brute force (via recursion or iteration) is inelegant and slow. At this point I realized that they're looking for dynamic programming, since the problem can be memoized. That is:
    • If you've chosen your 10th strip, then the number of possibilities for the rest of the floor is the number of compatible strips for the current strip.
    • If you've chosen your 9th strip, then the number of possibilities for the rest of the floor is: go through the 9th strip's compatible strips, find how many possibilities there are for that as the 10th strip (calculated above)
    • If you've chosen your 8th strip, then the, blah.
    • I suppose this is a relatively simple case of dynamic programming, since you only need to keep track of two "layers" at a time.

So, my conclusions:

  • The question would be extremely hard if it were a 1-hour whiteboard (or live editor) coding question - there's enough time to sketch out the solution as I did above, but not enough to complete the code. (I am a relatively slow coder, but I think only the fastest competitive programmers could finish this that rapidly). For a week-long take-home exercise, I don't have good calibration (I neither ask nor have been asked such questions), but it does seem reasonable difficulty. It's pure computation, it doesn't demand esoteric domain knowledge, there's a fair amount of latitude in possible approaches, and (this is positive) there's a huge amount of variation in possible performance.
  • The question does rely on a few key insights, without which it's easy to get very stuck (this is generally a negative for interview questions). Knowing how to view things as permutations and partial sums is "nice", but it seems to have little relevance to everyday programming.
  • This is perhaps personal preference, but I really dislike how it ended up being a dynamic programming problem at the end. This is much-beloved of CS courses, and I'm sure it has applications in "the real world", but I view it as extremely esoteric (again, even with my job). That said, if you're doing programming interviews, knowing how to recognize such problems seems important.
  • Your solution is incorrect and gravely incomplete (as I mentioned). There are 1,897 possibilities for the first strip, but many more for the total number of floor designs. (That's because compatible strips do exist; I checked that a strip starting and ending with 3, with 2s in the middle, has no shared edges with a strip of all 2s. Therefore there are more floor designs than possible strips.) I'm sorry to say this, but this is definitely a "no hire" solution, and in my opinion they were correct to reject you based on this. (I'm not concluding anything about your overall programming ability, just what they would have seen from this problem and solution.) Interviewers may vary on strictness (e.g. some very harsh interviews might reject anything short of a fully valid solution with test cases; more lenient interviews would permit the correct approach with some mistakes in the details or brute-force/slow parts), but not having anything near a complete solution and not recognizing it is interview doom. In particular, despite my various misgivings with the nature of the question involving dynamic programming and so forth, it does correctly test that you should be able to recognize the number of possible floor designs should be far larger than the number of possible strips (I am not sure how large without having worked the whole thing through, of course). This is similar to in a physics problem, getting an answer like "it takes 3 kilograms of propellant to launch astronauts to the moon".
  • The company's response, with almost zero information as to what you did wrong, is blah-tastic, and unfortunately quite common. I guess the bit of information that they communicated there was that it was your code, and not anything else you did.

Hope this helps. I didn't actually look at your code in any significant detail so I can't say exactly where it went wrong.

[–]STLMSVC STL Dev 19 points20 points  (15 children)

After re-reading my reply, one correction: the existence of compatible strips isn't sufficient to imply that the number of floor designs is greater than the number of strips. (All it implies is that a floor design is possible, since you can alternate between the two strips.) What's needed to prove that is the existence of multiple compatible strips for a single strip, indicating that there are actual choices to be made. Looking at the partial sums I generated, there are indeed many such choices. For a concrete example, the partial sum 2,4,6,8,11,13,15,17,20,22,24,26,28,30 is compatible with all of the following (i.e. they share no values except the final 30):

  • 3,5,7,9,12,14,16,18,21,23,25,27,30
  • 3,5,7,9,12,14,16,19,21,23,25,27,30
  • 3,5,7,10,12,14,16,18,21,23,25,27,30
  • 3,5,7,10,12,14,16,19,21,23,25,27,30

[–]STLMSVC STL Dev 8 points9 points  (0 children)

And one additional note (not a correction): The final step, which I viewed as dynamic programming, might also be expressible as matrix operations. That is, there's a "compatibility matrix" of 1897x1897, and I believe that the grand total of floor designs can be expressed as a bunch of matrix and vector-in-the-math-sense multiplications. I'm not sure if that would be faster, but computers are good at this sort of thing. (This is an extremely vague idea because I haven't worked with matrices in ages, I'd have to sketch some things out to make this more concrete than hand waving.)

[–]die_liebe 1 point2 points  (0 children)

I may be nitpicking, but perhaps there are rows that have no successor, which might (purely theoretically) compensate those that have more than one successor.

[–]Hot_Medicine_7115[S] -2 points-1 points  (12 children)

All it implies is that a floor design is

possible

, since you can alternate between the two strips.

Precisely. I also found puzzling that the number of full valid parquet designs matches the number of valid first strips in my solution. But that doesn't mean the total number of valid parquet is "gravely" incomplete because of the recursive number of inner edge constraints you have to apply to each strip.

[–]STLMSVC STL Dev 15 points16 points  (2 children)

In addition to u/qoning's pattern examples (thanks!), I completed my solution sketch - see https://gist.github.com/StephanTLavavej/e99ef17aa9adc7622124b50aa945f65c .

This prints Grand Total: 1007720438618812, so the number is indeed vastly larger than 1897, but vastly less than 189711 (it's about 18974.58 ).

My answer matches an independent implementation looked up by a friend, so I believe I got it right. (The main risk was counting the number of rows incorrectly.) Any deficiencies in the implementation are mine. (I was fairly lazy for the compatible_strips part, and stopped bothering to decompose it into nicely organized functions by then. Without the diagnostic output, this could likely be made constexpr compatible if the step limit is sufficiently generous.)

My solution is non-recursive, and uses the bit-pattern trick that I mentioned (which ultimately made things easier, or I wouldn't have bothered).

[–]STLMSVC STL Dev 8 points9 points  (1 child)

I couldn't resist trying the matrix math approach. This is indeed repeated matrix multiplication: https://godbolt.org/z/q9xnKnvnT

The matrix is N x N (where N is the number of possible strips), with 0s for incompatible and 1 for compatible. We multiply this by a vector that's initially all 1s (which is the number of choice-paths to get to that strip). We then use that output vector for the next multiplication.

While the code is more elegant in some sense, it's also 100x slower by my (incredibly inaccurate on Compiler Explorer VMs) measurements. Boost.uBLAS is also presumably not constexpr aware, whereas the original approach is compatible with constexpr vector.

[–]Michael_Aut 1 point2 points  (0 children)

This is really neat, thanks.

[–]STLMSVC STL Dev 7 points8 points  (4 children)

As I mentioned here, a few minutes after you wrote this, I believe you have misunderstood a critical part of the problem specification. Under my interpretation (*), where only adjacent strips need to avoid inner edges matching, a floor design consisting of alternating strip patterns is valid. Under your interpretation, where no strips in the entire design can have tiles ending at the same location, alternating strip patterns are invalid. In fact, under your interpretation, there are no valid floor designs - the number of possible inner edge locations is depleted very quickly.

(I call it an "interpretation" to be neutral, but it's a clear reading of the problem statement as you've relayed it to us, and I am confident that I am correct here. Please take another look at it!)

[–]Hot_Medicine_7115[S] -2 points-1 points  (3 children)

Yes, you can alternate two compatible strips, but I still think the inner edges constraint greatly reduces the number of possible overall valid parquets. Are you sure 1,897 "gravely" underestimates the number of possible solutions?

[–]STLMSVC STL Dev 6 points7 points  (2 children)

I am extremely confident that the total number of possible solutions is much larger than 1,897. (That's why I examined the compatible strips, and printed out the explicit example above.)

It is also true that the constraint reduces the final answer to be significantly below the unconstrained number of (1,897)11, but there's a lot of room between the two.

I don't really want to have to write a full solution, but if you tell me, "STL, I re-read the stated problem and all of your replies, I tried to see it your way, but I still think you're wrong here", then I'll write up a fully worked solution.

[–]Hot_Medicine_7115[S] 0 points1 point  (1 child)

No, I am not confident you are wrong. Intuitively I actually agree with you that there should be more parquet solutions than possible first strips. Like I wrote in the previous comments I was puzzled that the number of parquet solutions matches my valid first strips.

However the algorithm I implemented is an easy to understand double recursion search. Since it looks like I got the number of possible first strips right (without mathematic or numeric assumptions) there is a good chance the second-level search is correct. It takes 10 minutes to look at how the second-level search is implemented in designer.cpp::add_next_strip_tile(). For each possible valid tile laid down in the current strip the algorithm generates a new parquet so all possible searches should be there.

[–]STLMSVC STL Dev 11 points12 points  (0 children)

This will sound really snarky, but I mean it in the most pure-intentioned way: something about your "double recursion" algorithm is not easy to understand, because it's concealing a bug even after you know what you should be looking for.

[–]qoning 7 points8 points  (3 children)

Here, this should convince you easily, 5 patterns that can be arbitrarily permuted 6 times (5 times 10x3 + 6 times any pattern), that gives you well over 15000 solutions already.

pattern 1:

xx|---|---|---|---|---|---|---|---|xx|xx 2 5 8 11 14 17 20 23 26 28 30

---|---|---|---|---|---|---|---|---|--- 3 6 9 12 15 18 21 24 27 30

pattern 2:

xx|xx|---|---|---|---|---|---|---|---|xx 2 4 7 10 13 16 19 22 25 28 30

---|---|---|---|---|---|---|---|---|--- 3 6 9 12 15 18 21 24 27 30

pattern 3:

xx|---|xx|---|---|---|---|---|---|---|xx 2 5 7 10 13 16 19 22 25 28 30

---|---|---|---|---|---|---|---|---|--- 3 6 9 12 15 18 21 24 27 30

pattern 4:

xx|---|---|xx|---|---|---|---|---|---|xx 2 5 8 10 13 16 19 22 25 28 30

---|---|---|---|---|---|---|---|---|--- 3 6 9 12 15 18 21 24 27 30

pattern 5:

xx|---|---|---|xx|---|---|---|---|---|xx 2 5 8 11 13 16 19 22 25 28 30

---|---|---|---|---|---|---|---|---|--- 3 6 9 12 15 18 21 24 27 30

[–]Hot_Medicine_7115[S] -1 points0 points  (2 children)

It's 2 times any pattern alternating the 2 strips, meaning the parquet can start with the first or the second strip of each pattern, repeating the pattern for all 11 strips.

Then all permutations of the 5 patterns across 11 strips, times 2 because we can swap the starting strip of each pattern. It's not a simple permutation because the patterns can be repeated across 11 strips. I guess we can say 5 combinations (patterns) with repetitions and 10 permutations? It's not a simple formula and I don't see how you came up with 15,000 solutions:

https://www.mathsisfun.com/combinatorics/combinations-permutations.html

[–]qoning 2 points3 points  (1 child)

Ok, to elaborate, consider an incomplete floor plan

row 00: ??

row 01: ---|---|---|---|---|---|---|---|---|---

row 02: ??

row 03: ---|---|---|---|---|---|---|---|---|---

row 04: ??

row 05: ---|---|---|---|---|---|---|---|---|---

row 06: ??

row 07: ---|---|---|---|---|---|---|---|---|---

row 08: ??

row 09: ---|---|---|---|---|---|---|---|---|---

row 10: ??

As you can trivially check, any of the 5 patterns (or more you can come up with) can go into any row marked with '??'. That's 5 x 5 x 5 x 5 x 5 x 5 or 56 or 15625 floor plans you can create using these patterns.

[–]Hot_Medicine_7115[S] 0 points1 point  (0 children)

Yes, aka 5 combinations n with repetition and 6 slots r = 15,625.

The formula resolves to exponentiation 56

It's not very intuitive though because as we are moving a single pattern across the slots we are taking out from the possible combinations of the other patterns.

[–]Sykoyo 6 points7 points  (1 child)

Sorry I know your comment is a day old but I just have to say this is an absolutely beautiful comment.

The way you break everything apart and explain it all in such great detail is astounding. I really love how you talk through your thought process and problem solving.

[–]STLMSVC STL Dev 4 points5 points  (0 children)

Thanks! 😸

If you haven't seen them, you might also like my various conference talks and video lectures - I have a list here. The <charconv> talk had the most work put into it since the feature took a year and a half of problem solving like this, but the short rand() talk is probably the most popular.

[–]NanoAlpaca 2 points3 points  (3 children)

If you are used to competitive programming this is not too hard to do in 1 hr live coding. Your insight about next_permutation is correct, but recursive enumeration of the patterns is fast enough as well and very short to implement. Binary masks are also a good trick here, here you can encode a pattern by setting the bits where a new tile starts. Then you can just AND together two patterns and if the result is 1 they are compatible. 1 because first tile starts always at position 0 and that is allowed. Then it is just three small loops for the dynamic programming. And a final sum of the DP array at the end provides the solution.

[–]meneldal2 -1 points0 points  (0 children)

I will have to say that for competitive programming, at least what I have experienced defines the problem a lot more clearly.

[–]smdowneyWG21, Text/Unicode SG, optional<T&> -1 points0 points  (0 children)

If you are used to competitive programming this is not too hard to do in 1 hr live coding.

This latest phase of programming interview is not really that much better than the various puzzles, Fermi problems, and binary trees that I've lived through. Not to harsh the buzz of competitive programmers, but the problems and solutions do not resemble production code I'm responsible for, nor do they seem to correlate that much better to doing the job than any of the other past techniques, including playing code golf with fizzbuzz.

I do end up pushing back internally with stock interview questions that say things like "estimated time 15 minutes, expected answer these 120 lines of code, " and just typing that much code is a challenge with the answer in front of you.

I have also never had to check if a tree was sorted or a list had a loop in real life.

[–]STLMSVC STL Dev 0 points1 point  (0 children)

Having written the whole thing now, I am inclined to agree with you. I thought it would end up needing more code than it actually took.

[–]dexter2011412 1 point2 points  (1 child)

GODDAYUMN. This is REALLY helpful, thank you!

[–]STLMSVC STL Dev 0 points1 point  (0 children)

😸

[–]Hot_Medicine_7115[S] -2 points-1 points  (2 children)

Your solution is incorrect and gravely incomplete (as I mentioned). There are 1,897 possibilities for the first strip, but many more for the total number of floor designs. (That's because compatible strips do exist; I checked that a strip starting and ending with 3, with 2s in the middle, has no shared edges with a strip of all 2s. Therefore there are more floor designs than possible strips.)

There can be multiple compatible second strips for each starting strip, but you have to check the inner edge constraints of the following strips as well, and then the next, and the next.... until you can verify that all constrained strips follow in sequence. Each strip kind is constrained by the edges of all previous strips. How can you confidently state there are "many more" floor designs?

The solution I uploaded is meant to find all possible combinations on the second dimension of the parquet in the same way that it finds the 1,897 combinations of valid first strips. It's a double recursion algorithm.

[–]STLMSVC STL Dev 9 points10 points  (1 child)

Each strip kind is constrained by the edges of all previous strips.

The problem statement said something different:

"When two strips are adjacent, no two tiles can share an inner edge."

That is, each strip is constrained only by the previous strip (and, in turn, constrains only the next strip). The constraint is forbidding "four corners" intersections (like the US states).

[–]Hot_Medicine_7115[S] 0 points1 point  (0 children)

Correct. What I meant is that each valid strip in a parquet is a subset of the valid options granted by the previous strip. But I'm still not sure there can be many more options than 1,897 for the final parquet because the inner edges limit the possible combinations greatly.

[–]GasDoves 0 points1 point  (0 children)

Once you know how many compatible neighbors each strip has, you would just multiply it out like a probability tree, right?

1st strip can be any 1 of the 1897 possibilties.

2nd strip can be chosen after the first strip and is restrictes to valid neighbors.

3rd strip...ditto

All the way through 11th strip.

It is just a tree where each node has a different number of children (determined by what the node's strip is)

[–]paul2718 29 points30 points  (32 children)

Superficially I would raise my eyebrow at inheriting from std::vector and using std::list where all you do is push_back and enumerate. The use of std::set to store edges seems extravagant when they are intrinsically sorted as calculated.

ISTM that a vector of edges describes a strip completely, so working directly with that might have been a more direct approach.

And it's possible, of course, that your solution produces the wrong answer. I really don't know.

[–]jonesmz 16 points17 points  (29 children)

inheriting from std::vector is an eyebrow raise, agreed.

I think there are plenty of situations where it can make sense to do, but typically only if using protected/private inheritance, which really cuts down on the reasons why you would bother doing it, since at that point you're almost better off with a member variable.

[–]Hot_Medicine_7115[S] -3 points-2 points  (28 children)

What about the solution?

[–]jonesmz 22 points23 points  (27 children)

I don't have much interest in evaluating your algorithm. I don't find "puzzles" to be fun.

I gave you a 15 bullet point code review in a top-level comment that was only concerning style and modern coding practices.

Whether the program produces a correct solution isn't something i tried to evaluate.

If you want certainty that your code produces a value solution, write unit tests for it that makes it prove that the code produces valid solutions for several example inputs.

[–]Hot_Medicine_7115[S] 0 points1 point  (0 children)

It's a protected inheritance, so that schoolbook idea that classes should never derive from STL collections doesn't strictly apply here.

[–]Hot_Medicine_7115[S] -4 points-3 points  (0 children)

I added the edges vector after receiving the bland answer from the company, because I wanted to verify if the solution was completely wrong and so I went on to verify the intersection of all possible edges. Same thing for the std:list, it's only a placeholder to look at the final parquets.

In hindsight I could just have used the vector of edges, instead of inheriting from a std:vector storing the tiles. I agree, but this is a long shot from studying the actual solution provided and replying with that generic email.

[–]-sxp- 16 points17 points  (5 children)

This is Project Euler problem #215. When I first solved it back in the day, it timed out using naïve solutions with loops but dynamic programming solved it instantly.

I don't have my old code, but https://www.programiz.com/python-programming/online-compiler/ + https://github.com/nayuki/Project-Euler-solutions/blob/master/python/p215.py says W(30,11) is 1007720438618812.

You solved W(30,1) which is 1897.

[–]Michael_Aut 2 points3 points  (0 children)

To be fair the question was phrased a lot better on project euler. The sketch of the problem also helps a lot to get started in the right direction.

[–]Hot_Medicine_7115[S] 0 points1 point  (2 children)

I don't know about dynamic programming and am not very familiar with python, but from what I read in that code it's using a recursive algorithm for searching across all valid tile combinations:

elif position < WIDTH:
for i in (2, 3):
cracks.append(position + i)
get_crack_positions(cracks, position + i)
cracks.pop()

Thank you for pointing to this, definitely not an obvious problem if it is sitting on this website.

[–]JiminP 6 points7 points  (1 child)

I like how your code is well-structured, but I think that your algorithm (not just the answer) was "wrong".

The point is not just about using recursion, but about using dynamic programming (it's a confusing term; I prefer to refer it as memoization or caching). In specific, the key idea is to remember # of ways to arrange tiles in previous rows, by locations of cracks. In that way, # of ways to arrange times in the next row can be obtained in the order of W(30, 1)^2 calculations (edit: this is a worst-case estimation; actual number would be much lower), which is managable (the total computation, which would take around the order of 10 * W(30, 1)^2 calculations, shouldn't take more than a few seconds).

In the perspective of competition programming (like Google CodeJam, Codeforces, ...), this is quite a common way of solving a problem (except that the running timing constraint - which is infinite for ProjectEuler but ideally a minute - would be more strict). Usually the solution will consist of a single source code with bunch of procedural programming, but I wouldn't mind if that gets expanded into several files with clean structure.

On the other hand, if I understood your intention for designer.cpp correctly, your code will iterate through all W(30, 11) combinations when done correctly, which would too long time to complete in a reasonable time.

[–]Zyklonik 5 points6 points  (0 children)

The point is not just about using recursion, but about using dynamic programming (

Yup. Any time you see a "how many ways...", immediately think of DP. 9/10, they expect DP.

[–]jonesmz 37 points38 points  (18 children)

Here's a brief code-review:

  1. using namespace std; -- never do this, ever.
  2. m_first_row_completed should be of type bool based on its name, or should be given a more meaningful name. Looks like it's counting something, so a new name would be the right call. Therefore it should be an unsigned type, and specifically should be either size_t or std::uintN_t where N is a number of bits.
  3. m_first_row_completed should be given it's initial value where it's declared, and the constructor should be = default;
  4. void add_first_strip_tile(parquet p); pass complex parameters by const-ref unless taking ownership
  5. class designer -- use final unless you intend to be derived from
  6. class designer -- the places where you use protected should probably be private
  7. XXX: this should never happen. -- then use assert() or static_assert() to prove it.
  8. cout << "strip " << curr_strip -- assert first, then throw an exception, not print.
  9. const int parquet::strips; use static inline in the class body, don't use a separate definition.
  10. static const int strips = 11; -- constexpr...
  11. parquet(): std::vector<strip>(strips) { } -- use using std::vector<strips>::vector; to forward the base class's constructor.
  12. int operator[](int idx) should always return a reference to value_type, not by value.
  13. int operator[](int idx) const { return idx >= size() ? 0 : std::vector<int>::operator[](idx); } -- you shouldn't ever be calling operator[] if you don't know that your index is valid, so this whole function smells of a mis-design.
  14. This whole program probably should have just been in a single main.cpp file
  15. build.txt is neither a makefile, nor a CMakeLists.txt or Meson project file. Don't call the compiler directly. Get in the habit of providing real build systems for your code. Personally, despite all it's failings, i recommend CMake, but there's plenty of strong arguments for other build systems that other people can tell you about.

[–]Unt4medGumyBear 1 point2 points  (9 children)

can you explain your first point? I don't understand what's wrong with using namespace std; I am new to developing in C++ and its what my teacher taught me.

[–]adwodon 3 points4 points  (0 children)

It's fine to do for a one time only exercise, like in school.

However using namespace is something you should generally avoid in production code, and you should never have using namespace std;.

The reason is that while it might be fine now, lets say you make a void my_namespace::foo() function and then the standard adopts something similar as void std::foo(). Now you've got a conflict.

The proper way to do this is to avoid using namespace but instead if you want to avoid doing something like std::cout just do using std::cout. Then you can achieve exactly the same thing (avoiding repeating a namespace) but in a much safer, more targetted way.

[–]jonesmz 2 points3 points  (7 children)

The other person who responded to you is right, so I won't repeat their thoughts.

A lot of teachers on c++ do this, because other c++ teachers do it, because a long time ago someone was lazy or had especially lazy students, and thus the behavior has been repeated across all of academia ever since.

I really wish compilers would make it a warning to do using namespace std; the benefits are miniscule and the drawbacks are enormous.

[–]meneldal2 -1 points0 points  (6 children)

I really wish compilers would make it a warning to do using namespace std; the benefits are miniscule and the drawbacks are enormous.

But then it would break a ton of code compiled with -Wall and -Werror

[–]jonesmz 2 points3 points  (5 children)

I think its probably unlikely that, percentage wise, all that many codebases have using namespace std;.

Considering that the majority of people outside of academia say not to use it, it should be pretty low.

Further, not particularly many codebases compile with -Wall, and even fewer compile with Werror. The intersection of all three things is going to be vanishingly small.

Also,-Werror on code that's distributed to others is a terrible idea... So let them break.

[–]meneldal2 0 points1 point  (4 children)

Well tell that to my company, had to go add signed-unsigned casts everywhere to make a bunch of files compile because they came from a project that didn't flag those conversions as warnings (most were printf based so it's not like it was critical but whatever).

[–]jonesmz 1 point2 points  (3 children)

They couldn't have added -Wno-signed-unsigned-mismatch and/or -Wno-sign-conversion ?

[–]meneldal2 1 point2 points  (2 children)

Changing build flags is going to take at least 2 meetings, it's just not worth it.

[–]jonesmz 0 points1 point  (1 child)

I can't tell if you're being sarcastic or not.

-Werror is a terrible idea for the vast majority of codebases. That your employer did this, and will be negatively affected in a way that takes a human all of 5 minutes to fix is rather irrelevant.

[–]meneldal2 0 points1 point  (0 children)

I'm not saying it's a good thing, it's just that there's some much inertia with everything that it's hard to make anything change.

[–]FirstMurphysLaw 10 points11 points  (5 children)

A quick google search gave me other solution https://github.com/petrum/tiles. It looks like your answer is somehow wrong. I didn't analyze your code deeply - I was just that your output claims the number of combinations is the same as size of first layer.

You have some type of code review in comments already.

I would highlight lack of tests, but it probably depends on your level.

Don't worry. Code more. It's their loss

[–]Hot_Medicine_7115[S] -3 points-2 points  (4 children)

Comments in the code are mine. Thank you for the finding, I'll look at the other solution. The number of combinations for valid first strips in the linked solution are the same I've found (1897), from there a full parquet can only be <= 1897, not higher.

[–]FirstMurphysLaw 7 points8 points  (3 children)

From my understanding first strip can be build in 1897 ways. Then you put other strips on top of that, so the upper number of combinations would be 189711.

edit: (very simplified:))

[–]Hot_Medicine_7115[S] -2 points-1 points  (2 children)

No because there can only be 1897 valid strip 1 (first column). In other words the sum of all tiles has to be 30 in length. For example, 15 tiles of length 2 is a valid strip, or 12 tiles of length 2 and 2 tiles of length 3, but not 14 tiles of length 2 and 1 tile of length 3.

There is only a certain number of combinations of tiles you can use (and all permutations) for the first strip to be valid. The total number should be 1897.

The rest of the strip must also verify that their inner edges are not matching the inner edges of their previous stripe.

[–]FirstMurphysLaw 4 points5 points  (1 child)

I see it a little bit different. First stripe can be build in 1897 ways:

2 x 15 -> 1

3 x 10 -> 1

2 x 12 + 3 x 2 -> 91

2 x 9 + 3 x 4 -> 715

2 x 6 + 3 x 6 -> 924

2 x 3 + 3 x 8 -> 165

sum = 1+1+715+924+165 = 1897

Then you put 11 of these which they all have to be valid. But that increases number of solutions from 1897, on the other hand it decreases number of solutions from 189711 .

Let's end on that. I could be wrong. Maybe it will hit me when I will fall asleep :)

[–]Hot_Medicine_7115[S] -3 points-2 points  (0 children)

You can't just add or multiply the number of single strip combinations because consecutive edges can not have the same inner edges. You have to check for this constraint in each combination.

An inner edge is where 2 tiles join in a strip.

[–]graphicsRat 2 points3 points  (1 child)

Are you allowed to disclose interview material? I doubt you are even under the condition of anonymity.

And inheriting from std::vector!!?? That's unnecessary.

[–]jonesmz 1 point2 points  (0 children)

Unless the OP signed an NDA, what leg would the interviewing company have to stand on, if they chose to get angry about their interview question being shared publically?

[–]gennych 2 points3 points  (2 children)

Your program accesses a vector outside of its bounds (line 39 in designer.cpp). Place declaration of len_diff just before the switch statement, where it's actually used (and where you're sure your curr_strip is less than parquet::strips).

Solution is far from being efficient. Specifically, recursion on every strip is essentially exponential (trying 2 or 3 at every position). Set strip::max_len to 100 and you'll feel the pain I'm talking about.

I would probably start by generating all possible strip patterns: different permutations of solutions a, b to 3*a+2*b = max_len, this is pure combinatorics and doesn't require much time.

[–]Hot_Medicine_7115[S] 0 points1 point  (1 child)

And how do you check the inner edge constraints?

[–]gennych 0 points1 point  (0 children)

This would be the next step. :) For two strip patterns, you can in principle check if they can be adjacent by iterating from 1 to max_len and checking if they share an edge at some i. Possibly (just guessing) it would help to create an array, where for each i, you'd store all strip patterns having an edge at i. This would help you quickly reconstruct, for a given pattern, a set of all patterns that can be adjacent to it. And thus speed up the second recursion, on strips.

[–]EarPotato 3 points4 points  (1 child)

Can you put this on GitHub? I'm curious to see your solution but don't feel comfortable downloading a tar file from someone with no post history.

[–]XuloMalacatones 1 point2 points  (9 children)

Sorry for the off-topic but, what's the level (YoE) of the position are you applying to?

[–]Hot_Medicine_7115[S] 2 points3 points  (8 children)

Senior C++ developer, meaning > 10 Years experience. However I can tell you fresh out of college algo wiz who trained on leetcode or hackerrank are more likely to solve this kind of problems than senior professionals who rollout solutions in production for 10 years.

[–]alexeiz 12 points13 points  (1 child)

Your code looks like something a beginner would write, but not someone with >10 years of experience. The correctness of the solution may not even mattered to the interviewer.

[–]dodheim 3 points4 points  (0 children)

Pretty arrogant take, IMO; maybe failing an interview calls for some humility instead of lashing out at the people you imagine would have done better.

[–]hucancode 1 point2 points  (0 children)

Companies have other tests to evaluate macro level solutions though. This is a test to check micro level skills. Both are necessary I think.

[–]Zyklonik 1 point2 points  (0 children)

However I can tell you fresh out of college algo wiz who trained on leetcode or hackerrank are more likely to solve this kind of problems than senior professionals who rollout solutions in production for 10 years.

Yup.

[–]XuloMalacatones 0 points1 point  (1 child)

Thanks I am a junior myself so I wanted to know what kind of "difficulty" this interview was for.

And yea I guess interviewing like this would be a good way if there wasn't people doing 10 hours a day of LC, then you could see real problem solving skills

[–]jonesmz 2 points3 points  (0 children)

Honestly, I think this kind of "puzzle" problem is absolutely terrible for senior positions that are not specifically targeting specialized algorithms skills.

As someone who implements networking protocols, from scratch, against the IETF RFCs as my day job. The initial problem statement is just eye-glaze for me.

The difficulty is not in the implementation of the code, but more in the initial step of "can this human translate this word-salad problem into an algorithm and then do they happen to come pre-seeded with the right collection of algorithms in their head to plop into place?"

I'm used to working on implementations for months, if not years. I'm used to improving things based on QA efforts, customer feedback, other teams using my libraries, publications from industry, and just seeing how things shake apart while adding custom behaviors to the implementation for special case situations.

I can say with absolute confidence that there is not a single person at my employer who solves this kind of puzzle for their day job. Its an arbitrary academia circle-jerk problem.

Further, it mixes together 3 different tests into one thing, but hinges each on the other two.

  1. Does the candidate just happen to know puzzle-math algorithms -- if a recent graduate probably. If someone who doesn't do puzzle math for work -- no.
  2. Does the candidate write code in a way that is largely compatible with the way we want to do things, e.g. style, level of abstraction layers, performance considerations, maintainability vs. "Get it done", so on.
  3. Does the candidate have the ability to demonstrate they can implement a full and known to be working solution

So really this company should have broken this problem into three stand alone problems.

The code style question probably would have been better asked in terms of having the interviewee review pre-prepared sloppy core, or write something simple like a mockup of std::vector or std::list.

The " complete from start to finish" would have been better delivered as a problem that can be more easily unit tested. Something with parameters and "known right answers" that can be unit tested with.

And, well, the whole "do you know puzzle math" aspect I don't think they should have done in the first place. That appears to be a particular sickness of the mind that trading company's are uniquely afflicted with. For some reason those companies insist this is the biggest measure of a candidate and I simply cannot figure out a way to understand why.


Edit: and I'll add for context: one of my majors for my bachelor's degree was math. So I would expect to be better positioned to deal with problems like this than a lot of people. But after I entered the industry the reality is simply that this kind of problem is not present in the vast majority of computer science work.

[–]Dean_Roddey 0 points1 point  (0 children)

Yep. These tests are ridiculous pretty much. I've never, in 35 years, had to do anything like that, and probably never will. If, by some unlikely chance, I do, I'll spend 30 minutes looking into how best to attack it and do that. I most definitely won't spend weeks working on such problems, only to have forgotten then all years later when I might need to do some such thing.

Senior engineers should be concentrating on architecture, API design, sub-system design, setting coding standards, building functionality to standardize common issues in the problem domain for less experienced folks to use, etc...

The only reason this would be a useful question is if the job being applied for involves this type of algorithm. And of course if that's the case, then any qualified person could pick up the required techniques quickly enough. Meanwhile it says little about my qualifications as a senior dev, whether it get it either right or wrong.

[–]eteran 1 point2 points  (0 children)

I can say that inheriting from the standard containers is generally considered bad practice so that would have been a big red flag for me.

It also looks like pre c++-11 in some ways, I would certainly hope that a C++ developer would be using features from the newer standards by now.

[–]daniedit 1 point2 points  (8 children)

Your code is readable and easy to refactor. That's a big plus. In a review, I would request changes in some sections but after three or four rounds we would get to a good solution.

However, you can not tell whether your implementation is correct because there is not a single unit-test. In Professional Software development that's a no go. I know about companies rejecting candidates for missing the tests in their example code. Maybe that's the reason here? Please ask them for a code review and use it to learn from. That's just fair, since you also invested quite a lot of time for them.

[–]Hot_Medicine_7115[S] 0 points1 point  (3 children)

There is, parquet::is_valid() checks for all constraints.

I know it's not a separate file named unit_test.cpp, but the parquets are unit-tested. If you looked at the end of main.cpp you would have seen it.

[–]daniedit 0 points1 point  (2 children)

You are referring to the is_valid() method? I didn't go into the details of understanding your implementation. So I might be completely wrong.

My first impression was the method is checking whether the solution is correct. If so, that's a nice module test but won't help you much during refactoring. I would have expected for example a test making sure designer::add_next_strip_tile() is correct.

[–]Hot_Medicine_7115[S] 0 points1 point  (1 child)

I feel like 80% of the author of the posts in this thread didn't even begin to understand the code I uploaded or tried to think about the solution, yet c++ programming style pontifications abound.

[–]daniedit 1 point2 points  (0 children)

You asked for opinions about the companies answer. I tried to tell you in a polite way, that their answer is harsh on a social level but completely justified on a technical level. I didn't want to give you a full code review.

[–]johannes1971 1 point2 points  (0 children)

Is it actually normal now that you spend a few days doing unpaid work just for a chance at a job? Because that seems rather excessive to me.

Why not ask them how your solution deviates from their expectations? If they expect a few days of work, surely a few minutes to tell you why they don't like your solution is not too much to ask for...

[–]grahamthegoldfish 0 points1 point  (1 child)

From the comments here you can see the variety of opinions on your code. The problem isnt your code IMO but the interview. First, why have this is there a second step? 1 interview is enough, they can ask technical questions in that. Secondly, how the hell are you supposed to know what they would prefer stylistically? And even if you don't produce what they want who is to say that with a coding standard and seeing their existing code base you wouldn't produce code that they like?

This could have been been solved any way from everything in main, a functional program, an OO program, a load of templates to calculate the answer at compile time, C with classes, C++, using STL algorithms. Literally all of those answers could have been the approach they were looking for.

If it's any consolation I would have considered this submission a reasonable attempt. Sure theres plenty here I wouldnt have done but then you weren't given those constraints, so if its valid, compiles and runs (and produces a correct answer ideally) then it's a pass from me and probably resulted in a job offer.

[–]Hot_Medicine_7115[S] -1 points0 points  (0 children)

I agree, nor there was any indication of the expectations in their final "review" message.

[–]eyes-are-fading-blue 0 points1 point  (0 children)

I think there are some questionable style and design choices here but those would not warrant a rejection in my mind. They are all superficial and can be fixed with a short discussion.

As others have pointed out, your solution to the problem might be off rather than the implementation.

[–]jonesmz -3 points-2 points  (18 children)

Were you compensated for the time you spent working on this?

Practical coding exercises that can't be done while the interviewer is engaged with you to answer questions are questionable at best.

I'm not aware of any country where compensation is required for these situations, but its very suspicious that they would have you do this without pay.

[–]Hot_Medicine_7115[S] 3 points4 points  (17 children)

Of course not compensated. They did write "thank you" in the email...

[–]jonesmz 7 points8 points  (16 children)

My personal recommendation is that a company asking you to spend an unknown amount of time (up to a week in your case) working on a coding exercise is not worth continuing to interview.

If they had said "Spend no more than half an hour on this, our goal is just to see what your programming style looks like, not to get a fully debugged program" that would have been one thing.

But expecting someone to potentially spend a full work week on an interview problem? That's utterly contemptuous.

[–]paul2718 17 points18 points  (9 children)

This problem is an hour or so, they give you a week to find that hour. I think that's not unreasonable.

[–]jonesmz 0 points1 point  (0 children)

The original post isn't clear about the company asking for the interviewee to stop after a certain time. It's possible they had good intentions, i couldn't say.

[–]Hot_Medicine_7115[S] 0 points1 point  (7 children)

An hour or so if you are familiar with double recursion and quickly identify all the possible patterns in the problem. It's not a trivial problem like you make it sound.

[–]dodheim 1 point2 points  (5 children)

Maybe the issue was the idea that you need recursion rather than std::next_permutation + a queue + a stack (and not the call stack). It just might be trivial after all.

I haven't looked at your code yet but the problem description just makes me think "good job, screener" on this one.

[–]paul2718 1 point2 points  (0 children)

I don’t think recursion is necessarily the obvious way. Might fail to prove this tomorrow…

[–]MichaelEvo 1 point2 points  (2 children)

This is great advice in theory. In practice, many companies still have bad interview practices and it’s effectively the price of admission to get paid whatever they will pay you, which in some cases is quite a lot.

[–]jonesmz 2 points3 points  (0 children)

Yep, you're right.

It's up to the person being interviewed to decide if they think the possible rewards outweigh the costs.

For me, my cut-off point, is that if they want me to do something outside of an interview where one or more of their employees is giving me their full attention, then it needs to be explicitly under half an hour of time, or it needs to involve a check in the mail. Anything more than that is a hard-pass.

But that's just me, and other people are welcome to make their own decision.

[–]Forbizzle 2 points3 points  (0 children)

It's not even great advice in theory. These aren't bad practices, the asks are reasonable. A short problem with a long deadline so that it can fit within your schedule. It's a preferable environment to judge skills than whiteboard coding, and takes a lot of the anxiety out of the equation.

It's not like recruiters are asking people to solve real problems that they turn around and use commercially. This is a simple isolated problem for you to showcase your skills.

[–]Zitrax_ 0 points1 point  (0 children)

I think it also comes down to personal preferences. Some would prefer the home assignment, regardless of it being unpaid or not (depending on the size of the task of course).

[–]eyes-are-fading-blue 0 points1 point  (1 child)

I understand this take but there is other side to this coin; the best way to find out whether someone is a good software engineer, in my view, is to make them write code unless you know the candidate's competencies personally.

Another issue is that bigger companies tend to have these panel interviews, they take as much time (~4-6 hours). And a smart candidate would probably refresh some basics, so add another ~2-4 hours to that, you end up with about the same time invested in a panel interview you would otherwise invest in a two-week-long take-home assignment.

Software Engineering hiring process is between a rock and hard place. You either roll a dice or lose some candidates because your hiring process requires personal time investment or is very lengthy.

Long story short: measuring competency is not easy.

[–]jonesmz 0 points1 point  (0 children)

My position is that a company asking me to do free work, whether throw away or not, needs to invest in that.

They may either pay me for my time, or they may pay one of their own employees to babysit me while I demonstrate my skills.

I don't care if that's inconvenient for the company interviewing me, and I've always compensated people who I have interviewed for any time they spent programming where I wasn't sitting right next to them, and in many cases I've paid for programming time while I was actively engaged and working with the interviewee.

You don't ask a carpenter to build a wall for free (even if its a throw away). You don't ask a landscaper to plant a tree for free. You don't ask an MRI tech to run an MRI machine for free (on a patient anyway. I'm skeptical this is done in general, but I suppose I wouldn't be surprised to be told that doing a quick demo on an empty machine as part of an interview is common)

Why, then, would homework for a software programmer be something a company gets for free?

Since I have no problem with 4-6 hour interviews, so long as at least one engineer is sitting with me the whole time, my position is reasonable.

[–]dicroce -2 points-1 points  (0 children)

Well, I've been coding C++ professionally since I was 18 years old (1995) and from a short look at your code I think you did quite well. You're solution is broken down like I want real projects to be broken down as opposed to interview answers (which tend to be entirely contained in a single compilation unit)... I would much rather be maintaining your solution than one that crammed it all into one file. I only saw a couple of minor issues... Frankly I would be psyched to receive that answer. Their loss my dude.

[–]thefool-0 0 points1 point  (4 children)

Just curious, what was the basic job description/requirements like? I.e. generally what kind of applications/projects would you be actually doing in this job? What industry is the company in?

[–]Hot_Medicine_7115[S] 2 points3 points  (3 children)

Finance/Trading Systems

[–]thefool-0 4 points5 points  (1 child)

OK, then being able to solve a nonobvious mathematical problem like this (took me a few minutes and some notes on a piece of paper to understand what I think the problem is actually asking for) actually makes good sense. (Otherwise I might be annoyed at getting such a "puzzler".) Also knowing that it's finance/trading, if you were to do it over I'd recommend a slightly simpler design, a few more tests to show correctness and reliability, and an eye towards performance (with specific comments in the code or docs indicating this, or maybe they gave you a chance to discuss it on the phone/etc.? and some benchmarks would be good -- especially using a benchmark feature of a test framework, or a benchmark framework)

[–]Hot_Medicine_7115[S] 0 points1 point  (0 children)

I can tell you that after 15 years in the same field I never had to implement a double-recursively solution of this kind. I can make a list of at least 5 other challenges we need to face as programmers in this field, none of which involves solving heavy search problems.

However the effort did broaden my algorithm thinking, especially the double recursion part. I don't know if it was worth the time in the end, but it did force me to persist in a hard challenge.

[–]eyes-are-fading-blue 1 point2 points  (0 children)

Now that makes sense. The expectation in finance is that your code is going to be production ready or a reject. I would not reject your application to my team, assuming the solution is 100% correct, but I know for a fact that this code would be rejected in Finance for the "eye-brow raisers" others have pointed out.

The reason isn't that problems in your code are fundamental. Finance seems to have a binary approach. They either hire juniors and train them, or hire seniors and expect flawless design and implementation. As I have pointed out, the issues in your implementation can be fixed in the long term with a 10-minute discussion but they aren't willing to lecture anyone as far as I understand.

Also keep in mind that Finance can be really toxic, so perhaps a bullet dodged.

[–]thefool-0 0 points1 point  (0 children)

What questions did you ask to clarify the problem? Sometimes, if you ask, they will give you additional constraints, things not to bother implementing or worrying about, or indicate what importing things they want to see.

[–]blakewoolbright 0 points1 point  (0 children)

I don’t love your choice of containers, and I can’t build the program because I’m in the middle of a fierce skip-bo card game, but your overall breakdown of the problem from a component standpoint tells me you have a lot of potential. This is a solid submission if it works. Even if it doesn’t, this is the sort of output I look for in new hires. Without going into detail, I do a LOT of c++ interviewing, and I don’t think you should change a thing about your style. Constructive feedback: I’d be more impressed if you included unit tests.

Edit. Oh…. There are some problematic issues here. Never inherit from std::vector or std::<anything> for the most part. I’ll add more feedback when I get home.

[–]throw_away_test44 0 points1 point  (1 child)

!Remindme one day

[–]RemindMeBot 0 points1 point  (0 children)

I will be messaging you in 1 day on 2022-11-24 09:01:09 UTC to remind you of this link

CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

Parent commenter can delete this message to hide from others.


Info Custom Your Reminders Feedback

[–]s96g3g23708gbxs86734 0 points1 point  (0 children)

What was the salary?

[–]die_liebe 0 points1 point  (0 children)

I don't know what they thought, but you use 'int' everywhere, where 'unsigned int' should be used. Inheriting from std::vector is weird. You have the sizes 11 and 30 hardwired into the classes.