以优化嵌套循环
to optimize the nested loops
for( a=1; a <= 25; a++){
num1 = m[a];
for( b=1; b <= 25; b++){
num2 = m[b];
for( c=1; c <= 25; c++){
num3 = m[c];
for( d=1; d <= 25; d++){
num4 = m[d];
for( e=1; e <= 25; e++){
num5 = m[e];
for( f=1; f <= 25; f++){
num6 = m[f];
for( g=1; g <= 25; g++){
num7 = m[g];
for( h=1; h <= 25; h++){
num8 = m[h];
for( i=1; i <= 25; i++){
num = num1*100000000 + num2*10000000 +
num3* 1000000 + num4* 100000 +
num5* 10000 + num6* 1000 +
num7* 100 + num8* 10 + m[i];
check_prime = 1;
for ( y=2; y <= num/2; y++)
{
if ( num % y == 0 )
check_prime = 0;
}
if ( check_prime != 0 )
{
array[x++] = num;
}
num = 0;
}}}}}}}}}
上面的代码花了很多时间来完成执行。。事实上,它甚至还没有完成执行,我能做些什么来优化循环并加快执行速度??我是cpp
的新手。
将此代码替换为使用合理算法的代码,例如Eratosthenes筛。最重要的"优化"首先是选择正确的算法。
如果你的数字排序算法是随机交换,直到它们有序,那么你对随机条目的选择、交换或检查它们是否有序进行了多少优化都无关紧要。不管怎样,糟糕的算法都意味着糟糕的性能。
您正在检查25个9=3814697265625数字是否为素数。这是很多主要的测试,而且总是需要很长时间。即使在最好的情况下(为了性能),当所有数组条目(在m
中)都为0时(不要介意测试将0视为素数),这样试除法循环就永远不会运行,运行也需要数小时。当m
的所有条目都是正数时,代码将按原样运行数百或数千年,从那时起,每个数字将被50000000多个数字试除。
查看基本检查,
check_prime = 1;
for ( y = 2; y <= num/2; y++)
{
if ( num % y == 0 )
check_prime = 0;
}
第一个明显的低效率是,即使在已经找到除数并且CCD_一旦知道结果,就立刻跳出循环
check_prime = 1;
for ( y = 2; y <= num/2; y++)
{
if ( num % y == 0 )
{
check_prime = 0;
break;
}
}
不幸的是,你测试的所有数字都是素数,这不会改变任何事情,但如果所有(或几乎所有,对于足够大的值几乎)的数字都是复合的,那么运行时间将至少减少5000倍。
下一件事是,你把它分成num/2
。这是没有必要的。为什么你在num/2
停,而不在num - 1
停?因为你知道num
的最大正约数不能大于num/2
,因为如果(num >) k > num/2
,那么2*k > num
和num
不是k
的倍数。
这很好,不是每个人都看到了。
但你可以更进一步。如果num/2
是num
的除数,则意味着num = 2*(num/2)
(使用整数除法,num = 3
除外)。但是num
是偶数,它的复合性已经由除以2决定,所以如果num/2
除法成功,就永远不会尝试。
那么,下一个需要考虑的最大除数的可能候选者是什么呢?当然是num/3
。但如果这是num
的除数,那么num = 3*(num/3)
(除非num < 9
)和除以3已经解决了这个问题。
继续,如果k < √num
和num/k
是num
的除数,那么num = k*(num/k)
和我们看到num
有一个较小的除数——即k
(可能更小)。
因此num
的最小非平凡除数小于或等于√num
因此,循环只需要为y <= √num
或y*y <= num
运行。如果在该范围内没有找到除数,则num
是素数。
现在问题来了,是否循环
for(y = 2; y*y <= num; ++y)
或
root = floor(sqrt(num));
for(y = 2; y <= root; ++y)
第一个需要在每次迭代中对循环条件进行一次乘法运算,第二个需要在循环外对平方根进行一次计算。
哪个更快?
这取决于num
的平均大小,以及许多是否为素数(更准确地说,取决于最小素数的平均大小)。计算平方根需要比乘法长得多的时间,为了补偿这一成本,循环必须运行多次迭代(平均而言)——比如说,"多次"是指超过20次、超过100次还是超过1000次,这取决于具体情况。由于num
大于10^8
,可能与这里的情况一样,计算平方根可能是更好的选择。
现在,我们已经将试除法循环的迭代次数限制为√num
,无论num
是复合的还是素数,并且将运行时间减少了至少5000倍(假设所有num
0,所以总是num >= 10^8
),而不管测试的数字中有多少素数。如果num
取的大多数值都是具有小素数因子的复合物,则折减因子要大得多,在正常情况下,运行时间几乎完全用于测试素数。
可以通过减少除数候选的数量来获得进一步的改进。如果num
可被4、6、8、…整除。。。,那么它也可以被2整除,所以即使对于y > 2
,num % y
也不会产生0。这意味着所有这些分歧都是多余的。通过特殊的外壳2并在2的步骤中递增除数候选,
if (num % 2 == 0)
{
check_prime = 0;
} else {
root = floor(sqrt(num));
for(y = 3; y <= root; y += 2)
{
if (num % y == 0)
{
check_prime = 0;
break;
}
}
}
要执行的除法数量和运行时间大致减半(假设有足够多的坏情况,偶数的工作量可以忽略不计)。
现在,每当y
是3的倍数(而不是3本身)时,只有当num
不是3的倍数时才会计算num % y
,因此这些除法也是多余的。您还可以通过特殊的大小写3来消除它们,并让y
只遍历不能被3整除的奇数(从y = 5
开始,交替递增2和4)。这减少了大约三分之一的剩余工作(如果有足够多的坏案例)。
继续这个消去过程,我们只需要将num
除以不超过√num
的素数,就可以确定它是否是素数。
因此,通常情况下,最好找到不超过最大num
平方根的素数,将其存储在数组中并循环
root = floor(sqrt(num));
for(k = 0, y = primes[0]; k < prime_count && (y = primes[k]) <= root; ++k)
{
if (num % y == 0)
{
check_prime = 0;
break;
}
}
除非num
可以取的最大值足够小,否则,例如,如果您总是有num < 2^31
,那么你应该在比特筛中找到这个极限的素数,这样你就可以在恒定时间内查询num
是否是素数(如果你只有奇数的标志,2^31比特的筛需要256MB)[需要特殊的大小写来检查num
是否是偶数],您只需要128 MB就可以在恒定时间内检查数字< 2^31
的素性,可以进一步减少筛子所需的空间)。
到目前为止,主要测试本身。
如果m
数组包含可被2或5整除的数字,则可能值得对循环进行重新排序,将i
的循环放在最外面,如果m[i]
可被2和5整除,则跳过内部循环-所有其他数字在相加之前都乘以10的幂,因此num
将分别是2的倍数。5而不是素数。
但是,尽管如此,运行代码仍然需要很长时间。九个嵌套循环散发着错误设计的臭味。
你想做什么?也许我们可以帮助找到正确的设计。
我们可以通过计算数字的每个部分来消除大量冗余计算。这也显示了在2-3个轮子上进行的初等性试验划分测试,直到一个数字的平方根:
// array m[] is assumed sorted in descending order NB!
// a macro to skip over the duplicate digits
#define I(x) while( x<25 && m[x+1]==m[x] ) ++x;
for( a=1; a <= 25; a++) {
num1 = m[a]*100000000;
for( b=1; b <= 25; b++) if (b != a) {
num2 = num1 + m[b]*10000000;
for( c=1; c <= 25; c++) if (c != b && c != a) {
num3 = num2 + m[c]*1000000;
for( d=1; d <= 25; d++) if (d!=c && d!=b && d!=a) {
num4 = num3 + m[d]*100000;
for( e=1; e <= 25; e++) if (e!=d && e!=c && e!=b && e!=a) {
num5 = num4 + m[e]*10000;
for( f=1; f <= 25; f++) if (f!=e&&f!=d&&f!=c&&f!=b&&f!=a) {
num6 = num5 + m[f]*1000;
limit = floor( sqrt( num6+1000 )); ///
for( g=1; g <= 25; g++) if (g!=f&&g!=e&&g!=d&&g!=c&&g!=b&&g!=a) {
num7 = num6 + m[g]*100;
for( h=1; h <= 25; h++) if (h!=g&&h!=f&&h!=e&&h!=d&&h!=c&&h!=b&&h!=a) {
num8 = num7 + m[h]*10;
for( i=1; i <= 25; i++) if (i!=h&&i!=g&&i!=f&&i!=e&&i!=d
&&i!=c&&i!=b&&i!=a) {
num = num8 + m[i];
if( num % 2 /= 0 && num % 3 /= 0 ) {
is_prime = 1;
for ( y=5; y <= limit; y+=6) {
if ( num % y == 0 ) { is_prime = 0; break; }
if ( num % (y+2) == 0 ) { is_prime = 0; break; }
}
if ( is_prime ) { return( num ); } // largest prime found
}I(i)}I(h)}I(g)}I(f)}I(e)}I(d)}I(c)}I(b)}I(a)}
此代码还消除了重复的索引。正如您在评论中所指出的,您可以从5x5
网格中选择您的数字。这意味着您必须使用所有不同的索引。这将使要测试的数字计数从25^9 = 3,814,697,265,625
减少到25*24*23*...*17 = 741,354,768,000
。
由于您现在已经指出m[]
数组中的所有条目都小于10,因此肯定存在重复项,在搜索时需要跳过这些项。正如Daniel所指出的,从顶部搜索,第一个找到的素数将是最大的。这是通过按降序对m[]
数组进行预排序来实现的。
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 了解嵌套循环打印星号图案
- 无法掌握嵌套循环的写作技巧
- 在 c++ 中实现嵌套循环的更短方法吗?
- 从嵌套循环中的 std::list 中删除将返回访问冲突
- 毕达哥拉斯三重嵌套循环误解
- T(n) 表示嵌套循环
- 2 个嵌套循环的时间复杂度
- 嵌套循环背后的逻辑
- 使用 %s C++嵌套循环
- 嵌套循环和重复迭代器
- 如何在 c++ 下使用嵌套循环和正则表达式降低时间复杂度?
- C++在乘法图中放置随机值(嵌套循环)
- 如何使用 OpenMP 减少嵌套循环?
- 嵌套循环 C++ 的优化
- 如何在卷积程序的 c++ 中优化嵌套循环
- 改进了嵌套循环的优化
- 以优化嵌套循环
- 用于顺序内存访问的编译器嵌套循环优化
- 优化向量化嵌套循环