考虑到我有这样的矩阵(mXn):
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 | 0 | 0 | 0 | 0
0 | 1 | 1 | 2 | 1 | 1 | 0 | 0 | 0
0 | 1 | 4 | 9 | 4 | 1 | 0 | 0 | 0
1 | 2 | 9 | # | 9 | 2 | 1 | 0 | 0
0 | 1 | 4 | 9 | 4 | 1 | 0 | 0 | 0
0 | 1 | 1 | 2 | 1 | 1 | 0 | 0 | 0
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0
随机设置点#
。靠近它的值以波的形式受到影响。离该点越近,值越接近10。
哪种算法能够很好地找到#
,假设它会在更大的范围内使用?
编辑:我更感兴趣的是如何找到第一个非零数字,而不是查找#
本身,这是目的,但不是真正的问题。
想象一个充满零的巨大矩阵和#
隐藏的某个地方。该算法的详尽部分是找到第一个非零值。
答案 0 :(得分:6)
查找第一个非零值仅在信号对称且不包含旋转时才有效。考虑从Internet借来的以下示例(零=蓝色,最大=红色),注意第一个非零值位于右上角:
http://www.mathworks.com/matlabcentral/fileexchange/screenshots/1134/original.jpg
您可能需要查看gradient descent。通用算法是为连续函数定义的(你的是离散函数),但你仍然可以使用它。
它基本上在矩阵的某处初始化,在该点寻找渐变并向该方向移动,然后重复直到它收敛。你可以通过随机抽样来初始化它(选择一个随机单元格直到你得到一个非零值,你可以期望这比遍历更快并且找到一个非零值的平均值,自然取决于您的矩阵和信号大小)
一些属性:
限制:
对你来说这可能是一种矫枉过正,这可能是适当的,我不知道,你不提供更多的细节,但看看它可能是值得的。请注意,这里有一整套算法,有许多变化和优化。首先看一下维基百科的文章;)
答案 1 :(得分:3)
在最坏的情况下,你可能无法避免扫描整个矩阵,但是你可以通过逐步增加分辨率进行扫描来削减一些运行时间。
例如,你可以从一些(任意选择的)大距离采样开始,给你两种可能性:
您要么找到了一个非零值的点 - >那么你可以根据需要使用其他一些技术在峰值上本地“回家”(比如其他一些答案中提到的“渐变上升”)
您的搜索结果为空 - >这意味着扫描分辨率太大,波浪“穿过裂缝”,就像它一样。然后你的算法会降低分辨率(比如减半)并运行另一次扫描(如果巧妙地完成,你甚至可以跳过你在前一次运行中已经采样的那些点),只是更细粒度
所以你要继续以逐渐变小的分辨率扫描,直到你找到你想要的东西 - 前几次“粗略”扫描会更快,但成功的机会较小,但(取决于某些因素,就像完整矩阵的大小与“小波”的大小相比,平均而言,你必须有很好的机会找到目标,然后你必须将分辨率降低到足以扫描整个矩阵元素 - - 元素。
举例说明:
首次扫描:
#-------#-------
----------------
----------------
----------------
----------------
----------------
----------------
----------------
#-------#-------
----------------
----------------
----------------
----------------
----------------
----------------
----------------
第二次扫描:
o---#---o---#---
----------------
----------------
----------------
#---#---#---#---
----------------
----------------
----------------
o---#---o---#---
----------------
----------------
----------------
#---#---#---#---
----------------
----------------
----------------
第三次扫描:
o-#-o-#-o-#-o-#-
----------------
#-#-#-#-#-#-#-#-
----------------
o-#-o-#-o-#-o-#-
----------------
#-#-#-#-#-#-#-#-
----------------
o-#-o-#-o-#-o-#-
----------------
#-#-#-#-#-#-#-#-
----------------
o-#-o-#-o-#-o-#-
----------------
#-#-#-#-#-#-#-#-
----------------
依此类推('#'是新采样的单元格,'o'是先前采样的单元格,可以跳过)...
答案 2 :(得分:1)
首先将整个矩阵分成7x7
个小矩阵,因为矩阵之间的重叠最小化。
将矩阵分割成小矩阵后,遍历或随机选取每个小矩阵的某些点,检查是否存在非零值。
如果您从小矩阵中找到任何非零值,请从该非零值中找到#
。
答案 3 :(得分:1)
从0,0遍历矩阵,直到您达到非零值。
一旦你达到一个非零值,看看顶部,右边,底部,左边的邻居找到最大的一个(如果有多个,只选择其中一个)。
然后在你选择的最大的那个上做同样的事情,看看它的4个邻居并找到最大的一个,直到你点击#
。
答案 4 :(得分:1)
如果周围的模式总是相同的,那么根据相邻元素的值和我们遇到的第一个非零数字,我们总能在O(1)时间内预测#
点的正确位置
First non zero element-->| 1 | 1 | 2 |
0 | 1 | 4 | 9 |
1 | 2 | 9 | # |
例如,如果第一个非零数字为1且右边元素为1,则down为1,右下角为4,因此#
为(i + 2,j + 2) with(i,j)是当前元素的位置。
答案 5 :(得分:1)
假设模式始终相同,您需要检查每个方向的每五个单元格,从[2][2]
开始,直到找到非零值的方式。因此,您需要检查[2][2], [2][7], [2][12], ..., [7][2], [7][7], [7][12], ..., [12][2], ...
,依此类推。
一旦您发现这些单元格中的一个具有非零值,您只需检查其邻居及其邻居的邻居即可找到#
字符。
这是O(n^2)
,这是您可以做的最好的,因为您无法检查O(n^2)
个单元格。
答案 6 :(得分:1)
另一种可能的方法是迭代单元格,直到找到第一个非零数字。因为保证非零数字与波的中心在波的属性中在同一列中,所以我们可以简单地遍历列,直到我们找到aba
模式的点(找到示例中的9#9
),其中b
值将是wave的中心。
假设:
代码:
// iterate through cells
for (int y = 0; y < matrix.length; y++) {
for (int x = 0; x < matrix[0].length; x++) {
if (matrix[y][x] > 0) { // first non-zero value, in this case at point 3,3
int cellCurr = 0;
int cellOnePrev = 0;
int cellTwoPrev = 0;
for (; y < matrix.length; y++) { // iterate down rest of column
cellCurr = matrix[y][x];
if (cellCurr == cellTwoPrev) { // aba pattern found, center is b
System.out.println("found " + cellOnePrev + " at " + x + "," + (y - 1));
return;
}
// update necessary values
cellTwoPrev = cellOnePrev;
cellOnePrev = cellCurr;
}
}
}
}
示例案例的输出,其中10代替#:
found 10 at 3,6
答案 7 :(得分:1)
根据编辑,主要目的是找到第一个非零项。这是有道理的。一旦找到了这个,你就可以通过渐变 - 上升方法来找到最大值:只需从当前条目走到每个步骤中具有最高值的邻居,直到到达顶部。
但是,缺少一些可能重要的细节。例如,了解wave的形状可能很重要。在最初的问题中,它似乎有一些高斯形式。它还可以更多&#34;扁平&#34;?也就是说,相同的最大值是否会导致矩阵的更大区域被非零条目填充?
这里的关键点是 - 对于第一个,微不足道的优化 - 知道包含非零条目的区域的直径。如果您知道具有非零条目的区域的直径是n
,那么您可以使用步长n-1
遍历矩阵,并确保您将不错过了这个浪潮。
如果确实存在关于波浪可能位置的没有信息,那么我怀疑是否会有很大的改进空间。如果它可以任何地方,则您必须搜索无处不在。
但是,对于平凡搜索(无论步长是1还是n-1
),可能会有影响整体性能的决策。最突出的是:缓存效果。这是一个放置&#34; wave&#34;进入各种尺寸的matices。 (注意,&#34; wave&#34;实际上是矩形的,基于具有最大值的条目的moore邻域,为简单起见)。
它使用三种方法搜索第一个非零条目:
findNonZeroSimpleA
:只需遍历矩阵,专栏findNonZeroSimpleB
:只需遍历矩阵,行主要findNonZeroSkipping
:只需遍历矩阵,专栏,步长为n-1
它仅提供关于性能差异的粗略指示。我的PC的一些结果(没有详细说明设置,因为它不是基准):对于8000x8000矩阵,最大值为10,位于(6000,6000),三种方法的运行时间为< / p>
findNonZeroSimpleA: 28.783 ms
findNonZeroSimpleB 831.323 ms
findNonZeroSkipping 2.203 ms
正如您所看到的,遍历顺序隐含了最大的差异(只需交换两行 - 确保在这里使用正确的行!)。 &#34;跳过&#34;方法将运行时间大致减少一个与波浪大小相对应的因子。 (结果也可能在这里失真,同样由于缓存效应,当波浪大小为&#34;大&#34; - 但幸运的是,这些正是跳过方法特别有益的情况)。
import java.awt.Point;
public class WaveMatrixTest
{
public static void main(String[] args)
{
//basicTest();
speedTest();
}
private static void basicTest()
{
int size = 30;
int maxValue = 10;
int array[][] = new int[size][size];
placeValue(array, maxValue, 15, 15);
System.out.println(toString2D(array));
}
private static void speedTest()
{
int maxValue = 10;
int runs = 10;
for (int size=2000; size<=8000; size*=2)
{
for (int run=0; run<runs; run++)
{
int x = size/2+size/4;
int y = size/2+size/4;
runTestSimpleA(size, maxValue, x, y);
runTestSimpleB(size, maxValue, x, y);
runTestSkipping(size, maxValue, x, y);
}
}
}
private static void runTestSimpleA(int size, int maxValue, int x, int y)
{
int array[][] = new int[size][size];
placeValue(array, maxValue, x, y);
long before = System.nanoTime();
Point p = findNonZeroSimpleA(array, maxValue);
long after = System.nanoTime();
System.out.printf("SimpleA, size %5d max at %5d,%5d took %.3f ms, result %s\n",
size, x, y, (after-before)/1e6, p);
}
private static void runTestSimpleB(int size, int maxValue, int x, int y)
{
int array[][] = new int[size][size];
placeValue(array, maxValue, x, y);
long before = System.nanoTime();
Point p = findNonZeroSimpleB(array, maxValue);
long after = System.nanoTime();
System.out.printf("SimpleB, size %5d max at %5d,%5d took %.3f ms, result %s\n",
size, x, y, (after-before)/1e6, p);
}
private static void runTestSkipping(int size, int maxValue, int x, int y)
{
int array[][] = new int[size][size];
placeValue(array, maxValue, x, y);
long before = System.nanoTime();
Point p = findNonZeroSkipping(array, maxValue);
long after = System.nanoTime();
System.out.printf("Skipping, size %5d max at %5d,%5d took %.3f ms, result %s\n",
size, x, y, (after-before)/1e6, p);
}
private static void placeValue(int array[][], int maxValue, int x, int y)
{
int sizeX = array.length;
int sizeY = array[0].length;
int n = maxValue;
for (int dx=-n; dx<=n; dx++)
{
for (int dy=-n; dy<=n; dy++)
{
int cx = x+dx;
int cy = y+dy;
if (cx >= 0 && cx < sizeX &&
cy >= 0 && cy < sizeY)
{
int v = maxValue - Math.max(Math.abs(dx), Math.abs(dy));
array[cx][cy] = v;
}
}
}
}
private static Point findNonZeroSimpleA(int array[][], int maxValue)
{
int sizeX = array.length;
int sizeY = array[0].length;
for (int x=0; x<sizeX; x++)
{
for (int y=0; y<sizeY; y++)
{
if (array[x][y] != 0)
{
return new Point(x,y);
}
}
}
return null;
}
private static Point findNonZeroSimpleB(int array[][], int maxValue)
{
int sizeX = array.length;
int sizeY = array[0].length;
for (int y=0; y<sizeY; y++)
{
for (int x=0; x<sizeX; x++)
{
if (array[x][y] != 0)
{
return new Point(x,y);
}
}
}
return null;
}
private static Point findNonZeroSkipping(int array[][], int maxValue)
{
int size = maxValue * 2 - 1;
int sizeX = array.length;
int sizeY = array[0].length;
for (int x=0; x<sizeX; x+=size)
{
for (int y=0; y<sizeY; y+=size)
{
if (array[x][y] != 0)
{
return new Point(x,y);
}
}
}
return null;
}
private static String toString2D(int array[][])
{
StringBuilder sb = new StringBuilder();
int sizeX = array.length;
int sizeY = array[0].length;
for (int x=0; x<sizeX; x++)
{
for (int y=0; y<sizeY; y++)
{
sb.append(String.format("%3d", array[x][y]));
}
sb.append("\n");
}
return sb.toString();
}
}
答案 8 :(得分:1)
在你的情况下,最重要的是达到第一个非零值,有几种方法可以做到这一点,不同的情况需要不同的步骤,所以我将发布两种类型的通用方式。 / p>
我的建议是:对角旅行。
您应该从角落开始并沿对角线移动,直到达到第一个非零值。之后旅行系统发生变化。
我的建议:下棋。
你应该从角落开始,像棋盘一样移动(参见示例)
执行此操作直到达到第一个非零值。这里的旅行系统发生了变化。
达到非零值后执行此操作:检查全部8(实际上5个就足够了,您不需要从角落检查,但这个小事情可能难以实施)周围区域并转移到最大值。重复此步骤,直至找到#
。
答案 9 :(得分:0)
因为没有写入任务,我会先使用多线程查找非零数字,然后用单个线程查找#。如果矩阵足够大以抵消线程处理的较低性能,那么这将比单线程更好。
答案 10 :(得分:0)
假设您知道该区域的极限,找到第一个非零点应该有点像玩战舰,除非您知道目标同时具有宽度和高度,这是一个优势。考虑到你不知道波的非零部分的大小,首先假设它很大并且朝着假设更小的尺寸前进。
如果长度为1x1,则先测试0.33x0.33,0.33x0.66,0.66x0.33和0.66x0.66。如果未找到,则将步长减半至0.33 / 2并再次测试,直到一组点击中船。当你找到第一个点时,棘手的部分开始,因为没有相邻的点必然具有更高的值,所以在这种情况下你将不得不重复第一步,寻找高于你当前水平的东西。上一组命中确定要查看的新区域。如果相邻点的水平较高,那么显然会移动到那里。
如果区域足够大,这应该比对列和行求和更快,对吗?
答案 11 :(得分:-1)
你可以这样:
首先找到最大的没有。在矩阵中,然后去找那个最大的位置。在矩阵中,最后检查邻居将解决查找#。
的问题a = max(matrix)
[r,c] = find(matrix == a)
然后用r +/- 1和c +/- 1检查邻居,你会找到#。