Suppose you have an n-point polygon represented as an n-by-2 matrix of polygon vertices, P. Assume that the polygon is closed; that is, assume that P(end,:) is the same as P(1,:).
Remove as many vertices as possible from P to make a second polygon, P2, that has exactly the same shape as P. P2 must also be closed. Your vertices in P2 should be in the same direction as P. That is, if the vertices in P are in clockwise order, then the vertices in P2 should also be in clockwise order.
If the first vertex in P is retained in the solution, it should be the first vertex in P2. If the first vertex in P needs to remove, then the first vertex in P2 should be the next retained vertex in P.
You can test your solution graphically as follows:
plot(P(:,1), P(:,2), 'r', 'LineWidth', 5); hold on plot(P2(:,1), P2(:,2), 'b'); hold off
The two plotted shapes should overlap exactly.
EXAMPLE
P = [1 1 2 1 3 1 4 1 4 2 4 3 3 3 2 3 1 3 1 2 1 1];
P2 = [1 1 4 1 4 3 1 3 1 1];
This problem is related to my 09-Jul-2012 blog post on MATLAB Central.
Thanks for Cris for pointing out a hole in my original test suite. I added a test case that has a removable vertex as the first row of P and rescored all entries.
I think test case 7 is wrong - the starting vertex [1 2] can be removed can't it?
Thanks, Richard. I have updated test case 7, and the solutions are being rescored.
And now I think the vertices for test case 7 are coming out in the wrong order (cyclic shifted)] :-)
I agree with Richard. I believe my solution is correct now, but test case 7 is failing it.
This is the second time I've gotten up in the middle of the night to try to fix this. ;-) In my previous attempt to fix test case 7, I was fooled by the diagram I drew.
I've modified test case 7 again. When I run your latest solution, Cris, it passes. We'll see what happens when the solutions all get rescored again.
Sorry Steve! Hope you got some sleep in the end :)
Final discussion of the problem is up on the MATLAB blogs now:
http://blogs.mathworks.com/steve/2012/08/28/wrapping-up-the-analysis-of-cody-solutions/
Aargh ... sorry, used a function from the neural network toolbox without realising it. Didn't know it existed (normr)
If there's any way this one could be deleted I'd like it to be, because I think it's cheating (and Sven and I had a good competition going on!)
It's alright Richard... when I get home I'll just add normr=@(x)normaliseVector3d(x) to the top of my script and we'll be back in business... we might be near an optimised business but business nonetheless. As long as we don't resort to hacking the test suite via some huge regexp string then I'm still in the game.
No, gaming the test suite is out! Although Steve could yet make a mess of all this by using a polygon with floating point vertices ...
Hehe... yep I'm sure that would send us scrambling to add a tolerance to whatever-the-current-best-solution-was. But if this were intended for a bwboundaries(BW,'noholes','minimal') type option to bwboundaries, then all coordinates could be assumed integers and even the normalisation would be an unnecessary step since bwboundaries never skips a pixel.
Hey guys ... Don't worry, I'm not going to mess with you by adding new test cases that are subject floating-point round-off error. This will be in interesting point to discuss when I post a follow-up on my blog.
Nicely done! Do you think we've converged on the optimum yet?
Cheers, obviously I shamelessly adopted your favourite function.
I honestly thought we'd converged to an optimum (although a slightly distorted use of that word) back at around 64, then 55, then 48... so I only tentatively think we've reached the end. I'd certainly be relieved to call it a draw :)
I thought that maybe we could get rid of the extra arguments to sum(,2) and any(,2) by converting to a row matrix, but doing so would add arguments circshift(), so that way may not have any improvements left.
I do know that *every single line* of our function generates an M-Lint warning :) Oh, except the function name now.
Yeah, I tried all sorts of ways to do that too! I was trying to figure out a better way to normalise the matrix. I discovered normr, posted my solution, and then realised that it was actually a toolbox function. I'd be happy to have that entry removed :| If you replace the normalisation line with normr(diff(P)) you can get the 36
Hah, "Undefined function or variable 'normr'." A while back I had a version with normalizeVector3d(), part of the geom3d() package I use all the time...
I'd be interested to see under the hood of normr... I hope it's vectorized via bsxfun()
no it doesn't! it does something like:
n = sqrt(sum(x.^2,2));
x = x ./ n(:, ones(1, size(x,2)));
Well then, I bet that the following would run faster and work on any dimensioned data (such as a [10,2,50] sized matrix representing 50 sets of 10 XY vectors):
normr = @(v)bsxfun(@rdivide, v, sqrt(sum(v.^2, 2)));
This one uses the best tool in MATLAB :)
Ha! I just knew I'd come back from soccer and see you drop the score a bit. Nice one. Best tool in MATLAB? I like bsxfun, but that's already there... maybe it's an anonymous function... we'll see...
Never give up, never surrender!
any hints on how to reduce the size of this thing?
Hi Anton, here's a hint: You're chopping down P then taking the difference between itself and its circshift. This is very close to just a simple diff() command. Circshift is a useful function, but not quite where you've got it ;)
Think of it this way:
A diff() will get vectors from one point to the next. If any neighbouring vectors point in the exact same direction, they are fair game to remove. More specifically, whenever the direction of the line *changes*, you've found a vertex you want to keep. How can you detect that?
Richard, this one's a team effort indeed!
stealing another one of Sven's tricks
Ahaha... I was hoping that silly ans trick was the on you used... turns out there's another somewhere!
The ans trick was the one I used ... to go from 54 to 52 ...
Yeah, there's one more you'll find that should get you to 51... unless it's already there :)
I didn't know you could do that!
Haha - speak of horrible cody tweaks - I found another!
Hah, Richard, this one's just a sneaky tweak to the only bit I could see that was left... I think you deserve the better score with some improvements that you made that I hadn't thought of (using circshift instead of indexing was a nice touch). I think we left the realm of "nice robust solution" at around the 74/75 mark, and now we've entered the "cody-driven tweak" shadowy underworld that I hope nobody ventures into when making real products :)
aargh! what did you do this time!
OK, figured it (except I used length!). Ther emust be something else :)
Oh, and thanks! But my score is only going down largely thanks to your tweaks. I think it's very much a team effort at this point.
Cheating a bit - assuming exact cancellation. So this method is only suitable for integer coordinates, and I'm not even convinced that that will always work!