I have a few large 2D arrays like:
1 2 3 4 5
--------------
1 | 0 1 1 1 0
2 | 0 1 1 1 0
3 | 0 1 0 1 1
4 | 0 1 0 1 1
So, the largest rectangular block (by area) satisfying ==1
starts at (1,2) and its dimensions are (2,3).
How to find it with Mathematica without iterating explicitly?
NB:
Just to ease your testing here is one of my samples:
matrix = ImageData@Binarize@Import@"http://i.stack.imgur.com/ux7tA.png"
A viable option is to ignore the dictum to avoid iteration.
First a routine to find the largest length given a fixed width. Use it on the transposed matrix to reverse those dimensions. It works by divide and conquer, so is reasonably fast.
Here is the slower sweep code. We find the maximal dimensions and for one of them we sweep downward, maximizing the other dimension, until we know we cannot improve on the maximal area. The only efficiency I came up with was to increase the lower bounds based on prior lower bounds, so as to make the maxLength calls slightly faster.
This is better than Sjoerd's by an order of magnitude, being only terrible and not terrible^2.
Daniel Lichtblau
This is my attempt using
BitAnd
Result for belisarius' picture:
On the plus side this code seems faster than David's and Sjoerd's code, but for some reason it returns a rectangle which is one smaller in both dimensions than their result. Since the difference is exactly one I suspect a counting error somewhere but I can't find it at the moment.
Do you consider convolution as iterating explicitly? If not then it can be used to do what you want. With a simple kernel, say 3x3 1s, you can quickly zero out those non-contiguous 1s.
Edit:
Mathematica has built-in convolution function, you can use that, or brew your own:
Here's the pseudo code (untested of course :)
After that what's left are the contiguous square area of 1s. You can do connected area analysis and determine the biggest area from there.
I cannot compete with Heike's logic, but I can refactor her code a little.
I believe this is cleaner, and it runs about 20% faster.
Well, just to prove it's possible using functional programming here's my terribly, terribly inefficient brute force approach:
First, I generate a list of all possible squares, sorted in order of descending area:
For a given rectangle I do a
ListCorrelate
. If a free rectangle of this size can be found in the matrix there should be at least one number in the result that corresponds to the area of that rectangle (assuming the matrix contains only 1's and 0's). We check that usingMax
. As long as we don't find a match we look for smaller rectangles (LengthWhile
takes care of that). We end up with the largest rectangle number that fits in the matrix:On my laptop, using belisarius' image, it took 156 seconds to find that the 11774+1th rectangle (+1 because the
LengthWhile
returns the number of the last rectangle that doesn't fit) is the largest one that will fit