Cocoa - 嵌套循环的最大深度?

时间:2011-07-03 11:17:55

标签: python objective-c cocoa

我正在尝试编写一种算法来定位从10x10网格中选择10个值的可能解决方案。没有两个值可以共享同一行或列。有10个!组合(刚刚超过3,600,000)。

我的初始算法使用10个嵌套for循环,并简单地检查10个方块的每个可能组合。当我尝试在我的MacBook上运行应用程序时,需要花费很多分钟才能解除无聊,我将每个测试记录到控制台,这样我就能看到测试架起来了。

问题是应用程序运行到测试号714271然后冻结。这个结果是可重复的。

我认为它是一个记忆的东西,某个地方的某个计数器超过了它的最大允许值,但是当我谷歌它时,这个数字没有意义。


代码如下所示:

-(IBAction)findSolutions:(id)sender{

    NSMutableArray* flags = [[NSMutableArray alloc]initWithObjects:[NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], nil];

    NSMutableString* solutionsString = [[NSMutableString alloc]init];

    int a,b,c,d,e,f,g,h,i,j,z,sum;

    for(a=0;a<=9;a++){
        for(b=0;b<=9;b++){
            for(c=0;c<=9;c++){
                for(d=0;d<=9;d++){
                    for(e=0;e<=9;e++){
                        for(f=0;f<=9;f++){
                            for(g=0;g<=9;g++){
                                for(h=0;h<=9;h++){
                                    for(i=0;i<=9;i++){
                                        for(j=0;j<=9;j++){
                                            for(z=0;z<=9;z++){
                                                [flags replaceObjectAtIndex:z withObject:[NSNumber numberWithInt:0]];
                                            } //Clear the flags matrix

                                            //Load the flags matrix
                                            [flags replaceObjectAtIndex:a withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:b withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:c withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:d withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:e withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:f withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:g withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:h withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:i withObject:[NSNumber numberWithInt:1]];
                                            [flags replaceObjectAtIndex:j withObject:[NSNumber numberWithInt:1]];

                                            sum = 0;
                                            for(z=0;z<=9;z++){
                                                sum = sum + [[flags objectAtIndex:z]intValue];
                                            } // sum the flags matrix. Will = 10 if all columns are filled

                                            if (sum == 10) {
                                                NSLog(@"Test no. %d, sum=%d, a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d, i=%d, j=%d",y,sum,a,b,c,d,e,f,g,h,i,j);
                                                [solutionsString appendString:[NSString stringWithFormat:@"Test no. %d, sum=%d, a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d, i=%d, j=%d",y,sum,a,b,c,d,e,f,g,h,i,j]];
                                                [txtSolutionsFound setStringValue:solutionsString];

                                            } // These are possible solutions

                                            NSLog(@"a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d, i=%d, j=%d",a,b,c,d,e,f,g,h,i,j);

                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
 }

最终更新 我承认了。我放弃了尝试让代码在obj-c中工作并在Python中重写它。它已经运行了几个小时,检查了10 10个组合中的12亿个,没有对系统内存进行分类,平均使用的CPU时间比obj-c代码少50%。我喜欢Cocoa应用程序的外观并且UI的创建非常棒但是对于纯粹的可操作性,Python很难被击败。

Python代码如下所示:

row0 = [164,116,106,76,122,46,109,86,7,134]
row1 = [70,170,108,25,182,77,134,192,60,26]
row2 = [14,108,140,130,90,46,6,154,168,90]
row3 = [138,48,130,127,54,2,112,78,76,98]
row4 = [86,65,110,22,64,82,46,62,146,94]
row5 = [70,194,20,152,76,162,54,98,122,50]
row6 = [91,0,116,230,32,138,203,175,104,88]
row7 = [68,222,87,124,99,209,28,147,108,72]
row8 = [51,85,74,40,132,98,118,159,70,44]
row9 = [30,122,92,190,8,77,152,7,106,70]

maxvalue = 0

flagmatrix = [0,0,0,0,0,0,0,0,0,0]
solutionmatrix = [0,0,0,0,0,0,0,0,0,0]

for a in range(0,10):
    for b in range(0,10):
        for c in range(0,10):
            for d in range(0,10):
                for e in range(0,10):
                    for f in range(0,10):
                        for g in range(0,10):
                            for h in range(0,10):
                                for i in range(0,10):
                                    for j in range(0,10):
                                        for z in range(0,10):
                                            flagmatrix[z]=0
                                            #This will clear the flag matrix

                                        #Now load the matrix
                                        flagmatrix[a]=1
                                        flagmatrix[b]=1
                                        flagmatrix[c]=1
                                        flagmatrix[d]=1
                                        flagmatrix[e]=1
                                        flagmatrix[f]=1
                                        flagmatrix[g]=1
                                        flagmatrix[h]=1
                                        flagmatrix[i]=1
                                        flagmatrix[j]=1

                                        sum=0
                                        for flag in flagmatrix:
                                            sum = sum+flag
                                        if sum == 10:
                                             print "solution found at ",a,b,c,d,e,f,g,h,i,j
                                            solution = row0[a]+row1[b]+row2[c]+row3[d]+row4[e]+row5[f]+row6[g]+row7[h]+row8[i]+row9[j]
                                            print "Solution = ", solution
                                            if solution > maxvalue:
                                                maxvalue = solution
                                                solutionmatrix[1]=b
                                                solutionmatrix[2]=c
                                                solutionmatrix[3]=d
                                                solutionmatrix[4]=e
                                                solutionmatrix[5]=f
                                                solutionmatrix[6]=g
                                                solutionmatrix[7]=h
                                                solutionmatrix[8]=i
                                                solutionmatrix[9]=j


                                            print "Current maxvalue = ", maxvalue
print "Final max value = ", maxvalue
print "Final solution matrix = ", solutionmatrix

4 个答案:

答案 0 :(得分:2)

您的直接问题与嵌套for循环的最大深度无关,只是基本的Cocoa内存管理。您创建,然后在当前AutoreleasePool中累积数百万个对象。

有关详细信息,请尝试http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html

在这种情况下,使用NSNumber而不是常规int的开销很大。在性能方面你会更好(如果坚持使用这个算法)在C中重写。

这并不是说你不能为此使用Objective-C,你只需要了解你创建的对象的生命周期,而不是用尽你想要的临时变量的所有可用内存。

答案 1 :(得分:2)

必需的免责声明:算法不是很好并且应该进行改进,您通常不应该依赖语言功能来使可怜的算法可用。那说:

Python版本更好地运行代码的原因是因为它使用垃圾收集。在内存使用达到一定限制后,Python系统会查看已分配的内存,并释放代码不再需要的重用内存。

您的Objective-C版本正在使用手动内存管理。在你的情况下,内存被标记为“自动释放”,这意味着它将被释放(没有任何其他显式代码),以便在当前事件处理完成时重新使用 - 并且在您的代码中整个循环是单个“块”(这是一个技术术语;-))所以没有标记为“autorelease”的内存被重新发布。

现在Mac OS X上的Objective-C(但不是iOS)支持垃圾收集。转到项目设置,查找“Objective-C Garbage Collection”并将其设置为“Supported”。现在运行你的代码,它应该像Python版本一样糟糕,或者可能更好......

具有讽刺意味的是,你的算法虽然不是最优的,但似乎并没有真正分配大量内存;正如其他人指出的那样NSNumber应该只为每个实际数值分配一个实例。糟糕的记忆性能似乎是“自动释放”工作方式的副作用。垃圾收集不应该受到这种副作用的影响,所以你的代码可能会运行在更少的内存中,并且几乎没有垃圾收集。

答案 2 :(得分:1)

我运行了你的代码,并在几分钟内开始进入寻呼地狱。

现在,这是您程序的命令行版本。我已经为每次测试删除了所有临时NSStrings和NSLog的创建,并且还没有遇到NSLog以进行成功的测试。这个版本创建的唯一对象是NSNumbers,正如我在关于mustISignUp的回答的评论中提到的那样,NSNumber对象没有堆积,因为你只创建了两个 - NSNumber对象被实现了,所以几乎每次你显然创建一个NSNumber对象,实际上只是重用以前创建的对象。

因此,看到你的程序耗费大量内存是奇怪的。

当你的程序消耗大量内存时,下一步是使用Leaks模板在Instruments下运行它。这就是我做的。我每隔几秒就拍一次快照,仪器每次都会报告5-12 MB的内存增长。

查看其中一个Heapshot的内容(显示自上一个以来已经分配的内容),我发现它是所有非对象内存。查看一个分配的堆栈跟踪,我发现它是由autorelease分配的,显然是直接从main调用的。

双击main堆栈框架,它将我带到了NSNumber对象的“创建”之一。由此推断,尽管它重用了现有的对象,但它也保留并自动释放它们。

As documented,对象通过将自己添加到自动释放池来响应autorelease。这很重要,因为自动释放池的核心基本上是一个可变数组(在排出时释放所有内容)。通常情况下,这并不重要,因为对象的总大小远远大于它们的位移,可以说,在池中,但在你的情况下,你有两个对象,你在池中添加了数百万次该计划的生命。

解决方案是将代码中的“创建”表达式提升为变量声明:

NSNumber *zero = [NSNumber numberWithInt:0];
NSNumber *one = [NSNumber numberWithInt:1];

并将它们替换为以前出现过的各种变量。该程序的速度仍然很慢,但内存使用量现已持平。

我不知道这是否与你的问题有关,但也许是,无论如何,这是一个必要的改进。

此外,NSLog写入系统日志,该日志将转到磁盘。请考虑登录到NSTextView,甚至在自定义视图中绘制网格及其标记的位置。你必须将代码分解为不是一个长时间运行的循环,但这仍然是另一个必要的改进 - 无论如何你需要学习它,这是一个非常好的练习案例。

答案 3 :(得分:0)

这与N-Queens问题(它也检查对角线,因此它更像是N-Rook问题)有很大关系。

无论如何,不​​应该在主线程上完成这项繁重的工作(这将使应用程序无响应)。你是一个背景工作者(或几个,或GCD)。

从算法上讲,可能需要改进很多东西。要想到的第一件事就是在放置每个新项目时立即检查它们,这样你就不必完成内部循环了。

从技术上讲,我对for的数量没有限制 - 尽管其他代码结构可能更容易。