- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
The answer to the lemon drop problem is exactly 9/110, computed in 4 milliseconds using a recursive algorithm. :)
Using a language with easy memoization makes a recursive solution very straightforward. Built-in rational arithmetic is just the icing on the cake.
I don't know why the problem proposer has an issue with recursive solutions. Especially if tail call elimination is guaranteed, they can be the most elegant and obviously correct implementations.
Admin
4 milliseconds ?? mine ran in 1.03 milliseconds... (same answer)
Admin
Admin
you shouldn't criticize an obviously acceptable time...
when your's isn't the best..
0.8 milliseconds
Admin
Since we know that George prefers lemon, the odds of that being the last one in the bag is zero. It's the same principal that guarantees candy corn will be at the bottom of your Halloween sack.
Admin
Why exactly do you think recursion is bad style? The algorithm is inherently recursive, the recursion here could be a simple goto with parameters, so why not? Probably the compiler will tail-optimize it anyway (maybe not during debugging or such).
Please stop bashing recursive algorithms in iterative ones for the sake of your very own "good style".
PS. I'm from Italy, so excuse my bad English.
PPS. Eric problem
let x = #l (number of lemons ...) let y = #c (number of cherries ...) let p(x,y) = pl(probability that last is lemon ...)
let x != or y != 0 (the game end when there are only 1 candy)
Of course p(0,y) = 0 and p(x,0) = 1;
Then we have p(x,y) where x != 0 and y != 0 let px = x/(x+y) and py y/(x+y) (probability to extract a lemon or a cherries this turn)
If a lemon is extracted then it will be eaten so the probability will change to p(x-1,y)
Else a cherry is extracted and you extract a new one with the same probabilities px and py
So p(x,y) = pxp(x-1,y) + py(pxp(x-1,y) + pyp(x,y-1)) Or p(x,y) = (py + 1)pxp(x-1,y) + py**2*p(x,y-1)
The Haskell code is produced with a find-replace
Cristal clear, a bright example of the elegance of recursion, but actually is VERY slow.
So recursion is slow, recursion suck. You can easily see than this function is not tail-recursive so it is slow, we need to uglify it in c, add accumulators etc.
WRONG!
It's very slow because it will unnecessarily recompute p(x,y) with the same (x,y) over and over, this is the optimization we have to do and it is totally transversal to the whole recursive-iterative thing.
this code (again Haskell) will do the trick
CAPTCHA aptent
Admin
I was afraid I'd trigger a competition. :P
After switching to floating point and eliminating a common subexpression, I have a less correct and less understandable solution that now runs in 1-2 milliseconds. ;)
But yeah, times will vary by hardware, optimization, etc. The important part was the "obviously acceptable time."
Admin
One day I will learn to reread.. (inverted x and y and a comma typo)
Admin
Admin
I knew better than to replace my motherboards cheep knock-off Chinese capacitors for state of the art flux-capacitors!!
Admin
In C++, you would have to cast the return of the malloc():
struct foo *foo = (struct foo *) malloc(sizeof(foo) * count);
or, shorter:
foo *foo = (foo *) malloc(sizeof(foo) * count);
In C, you do not need that cast (and malloc() already indicates that it might be C); however, sizeof(foo) does not work there; you need to use sizeof(struct foo), unless there was a typedef. </nitpicking>
Admin
Thank God for my keyboard layout where if i miss the shift I get "select + " which does not compile! MY KEYBOARD LAYOUT IS DEV PROOF!
Admin
100%, because my George is smart enough to look in the flupping bag first, and take out the hated Cherry flavored candies.
Admin
Or traded the cherries with Della who actually likes cherry and dislikes lemon.
Admin
I agree with everything. I used Common Lisp and did get 9/110 but in 28 ms.
Again, I agree about there being no problem with recursion. The issue is memoization. (Which wasn't free for me in Common Lisp and led to bugs.)
Admin
A bit of playing around leads to the empirical conjecture that under the rules of the game, if we have x lemons and y cherries,
P(last candy is lemon) = x / (x+y)(y+1)
I have no idea why but every test case I've tried fits it.
Of course my last conjecture (that the answer was 0.5) was garbage.
Admin
I must admit the memoization wasn't completely free for me either. I used PLT Scheme in conjunction with Dave Herman's memoize.plt library. After doing the memoization manually on too many Project Euler problems, I was very happy to run across these macros. A quick import, and the only change I needed to make the recursive solution fully memoized was change one "define" to "define/memo".
You might check to see if someone has provided similar macros for Common Lisp.
Admin
[quote user=drf]2. The potential security implications of a buffer overrun attack, depending on where the application is used.[/quote] Well, it should notice it wants to write off the end of the array, and fail with an error to the user, as opposed to fail dramatically. [quote user=drf]3. The developer implemented a workaround "feature" to accomodate a buffer overrun that would still require testing.[/quote] Sure. But the likelihood of having introduced a bug is quite small. The likelihood of dynamic allocation producing a bug is larger (albeit a one time cost). [quote user=drf]4. Depending again on the specifics on the application, the conversion to dynamically-allocated memory would likely be trivial[/quote] In that case, hard-coding the array would be a bad solution. But I'll give the coder the benefit of the doubt and say it's non-trivial. Depending on the specifics of the application, failure may be more desirable than dynamic reallocation.
Okay, the instances of my extreme example are probably outweighed by yours 1,000,000 to 1. [quote user=drf]5. Potential end-user support requests and user grievances should the application crash unexpectedly.[/quote] If an error message says "You must provide less than 40,000 records to this application." non-technical users can figure it out. [quote user=drf]6. Additional documentation necessary to properly document the circumstances and workaround in (3).[/quote] Yes, if someone did this in a production environment, they better document why.
I can see doing this if
the number of records isn't known when the array is first allocated it's only used internally the number of records grows at such a slow rate that the new number will last at least a year or two the failure is graceful and provides enough info in the "Critical Error" dialog to allow a developer to find the #define in less than 10 minutes (counting getting the app out of source control) and it's none-mission critical.
Other than those limitations, I think you were overly harsh.
Admin
I thought the 8 Report was due to some dialects of SQL letting you refer to columns by number instead of name, but I looked it up and it only applies to the ORDER BY clause. (The real WTF there is that no one ever seems to reply to "how do I query columns by number?" with "whyTF do you want to?")
Admin
Well, Mark, since you've decided to crowd-source spell checking: the word is "indelible".
Fixed! Thanks! - MB
Admin
Admin
I'm The Octopus! I got 8 of everything!
Admin
WHO 8 THE GREAT REPORT?
Admin
Sure. But, then, so does this:
... if you have enough stack.
I wonder if the original example was loop racism or recursion fanaticism?
Admin
Well, 1 is not a WTF on itself, it will generate compiler warnings, but those variables won't affect the program in any way.
It's an array of strings, what is the problem?
Yes, that is quite wrong. The program will work on most computer architectres, but is teoreticaly wrong. I don't know if it is valid C++, but that is not that important on real life. Anybody that try to maintain the code will be confused by this problem and it will generate a warning, that is probably ignored toghether with the ones of unused variables. That is a problem, I junt don't think that it is a WTF on itself.
It can easily be perfomed on a recursive way too. By the way, it was easily performed on a recursive way, that is a bit more clear than the iterative way (proof of that is that the developer probably couldn't make it work iteratively).
It was iterative, the comment was just left there.
Yeah, that is slow. Just the developing team can know if that is a problem of a feature.
Not a WTF at all.
Yes, may be a problem. But comparing floating point numbers are guaranteed to fail, while comparing strings will only fail if those strings were created by the wrong algorithms.
Ok, no idea if it should be that way, but I can think about reasons for that. One of them is to simplify the use of fixed point numbers (use them as float, compare them as strings). Anyway, passing huge arrays around has no performance hit, so if the array was needed for some other reason, there is no problem reusing it here.
Well, the developer was smart enough to read the algorithm for a binary search, then, when he couldn't make it work, was smart enough to redo it recursively and get something that (badly) works. I guess he could have read the text that always come with binary search explanations, that say it only works on ordered arrays.
Admin
Recursion isn't a bad approach to brute-force the computation. Any brute-force approach will end up with large state trees, and the recursive approach has the advantage of being clear, since it necessarily encodes conditional probabilities.
Doing it algebraically (on paper) and plugging in values is faster though.
Admin
Not to nitpick, but the OP asked for odds, which are approx. 11.22:1 against :). My spreadsheets are linked to my blog entry about this: "Lemon Candy and Dynamic Programming".
That was a fun problem!
Admin
Can we see the CL code for it? Might be instructive for those of use wishing to learn CL!
Admin
and that's with just a loop, splice and push..
captcha: vindico
Admin
Likely just an example of a beginner programmer who hasn't been exposed much to optimization concerns.
When generally stated or thought about the problem lends itself very well to a recursive solution. The general thought is for binary search you start in the middle, if you found the element return it, if not and it's less than the middle element search the bottom half, otherwise search the top half. If someone doesn't know well enough to understand or think about optimization, recursion is the most straight forward and simplest way to both program and view the solution. Granted it's not a very well written or commented piece of code but I hardly think it's worthy of that much of a WTF.
And to those of your bringing up pointless concerns like "the array better be sorted or you'll have issues." Obviously it should be sorted before being sent to the function, and a binsearch function should not be wasting time checking if it's sorted. If I'm stupid enough to call "binsearch" on an unsorted array then I deserve to only get my result if I'm very lucky.
Admin
Someone email 8@. and tell him the binary search is crashing when given more than 4000 comments!
Admin
Guess you never heard of the Y2k bug?
Admin
Guess you never heard of the Y2k bug?
//now with quotey goodness
Admin
The person who said that the probability of the last candy is lemon is zero is absolutely right. I don't care what maths the rest of you did - you're wrong. It's IMPOSSIBLE if every time he gets a cherry one he puts it back that this will happen any other way.
How am I so certain? My daughter and I spent all night picking out all the red sweets and putting the lemon ones back - result, pot of 100% lemon sweets.
Admin
Impressive. But next time read the question properly first...
"But if the candy he chose is cherry flavored, he puts it back in the bag and then randomly picks a candy out of the bag and eats it regardless of the flavor."
Cherry ones only go back if they are the first ones out. Second ones get eaten.
I only know because I thought the same thing at first...
Admin
nicely documented... now i know where the end of the while loop is .... now its just a matter of finding the beginning...
Admin
Admin
A recursive solution for the cherries and lemons puzzle is perfectly acceptable, because it's just a puzzle. In order to find the exact probability, a simple way is to construct the decision/probability tree. This is easiest to code using recursion.
Nobody is going to use the most efficient algorithm possible to solve a trivial problem unless they're being graded. The problem comes when people start using programmer friendly algorithms in applications where efficiency actually matters.
Admin
I think that it is often slower than doing it itteratively, but for many problems, the elegance and simplicity of the algorithm compared to a very small performance hit still makes it a very valid choice.
This example shows one in which the performance hit is very large. For example, you can code this problem as follows:
The problem for the unwary is that the execution time increases dramatically as you increase the number of candies.
For example, if you insert a counter into the function to see how many times it is called, you find that with 10 lemons and 10 cherries, it is called 369,511 times, with 20 lemons and 10 cherries, it is called 6,009,029 times, and with 30 lemons and 10 cherries, it is called 1,695,321,055 times.
If you modify the algorithm so that it keeps a table of those values it has already calculated and uses those values when needed instead of recalculating them over and over and over again, then the algorithm is very well behaved.
Admin
Learn math.
Admin
Heh...
captcha:nibh
Admin
Since binary search is O(log n), I wouldn't worry about a stack overflow. Example: If the data set was HUGE: 1 trillion records, it could recurse no more than 40 times.
Admin
//beginning of while loop The 8 report had me in hysterics!
Admin
Admin
Not to mention that the story says that a larger array was implemented - but would only be used if teh smaller array overran (I think)....Seems more than just a little strange - it would be simpler (assuming you really didn't want dynamic memory allocation) yo ALWAYS use the larger array...
Admin
To make it suitable for WTF, of course.....
Admin
Or not buy them in the first place.
Admin
Admin
Admin
No Good! Wide Right!