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
的新手。
答案 0 :(得分:6)
使用合理的算法(例如Sieve of Eratosthenes)将此代码替换为代码。最重要的“优化”是首先选择正确的算法。
如果您对数字进行排序的算法是随机交换它们直到它们按顺序排列,那么优化选择随机条目,交换它们或检查它们是否有序无关紧要。糟糕的算法无论如何都意味着糟糕的表现。
答案 1 :(得分:1)
您是否正在检查25 9 = 3,814,697,265,625这些数字是否为素数。这是很多主要测试,并且总是需要很长时间。即使在最佳情况下(性能方面),当所有数组条目(在m
中)为0时(更不用说测试认为0是素数),因此试验分割循环永远不会运行,运行将花费数小时。当m
的所有条目都是正数时,代码将会运行数百年或数千年,从那以后每个数字将被试验除以超过50,000,000个数字。
查看主要检查,
check_prime = 1;
for ( y = 2; y <= num/2; y++)
{
if ( num % y == 0 )
check_prime = 0;
}
第一个明显的低效率是即使在找到除数并且num
的复合度建立之后,循环也会继续。 一旦知道结果,就会突破循环。
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
的除数较小,即{ {1}}(可能更小)。
所以 k
的最小非平均除数小于或等于num
。因此,循环只需运行√num
或{{1 }}。如果在该范围内未找到除数,则y <= √num
为素数。
现在问题是否循环
y*y <= num
或
num
第一个需要在每次迭代中对循环条件进行一次乘法,第二次计算循环外的平方根。
哪个更快?
这取决于for(y = 2; y*y <= num; ++y)
的平均大小以及是否有许多是素数(更确切地说,是最小素数的平均大小)。计算平方根需要比乘法更长的时间,以补偿成本,循环必须运行多次迭代(平均) - 无论是多少&#34;很多&#34;意味着超过20,超过100或超过1000,取决于。如果root = floor(sqrt(num));
for(y = 2; y <= root; ++y)
大于num
,可能就是这里的情况,可能计算平方根是更好的选择。
现在我们已经将试验分割循环的迭代次数限制为num
10^8
是复合还是素数,并将运行时间减少了至少5000(假设所有{{} 1}},所以总是√num
),无论测试数字中有多少素数。如果大多数值num
采用的是具有小素因子的复合材料,则缩减因子要大得多,通常情况下,运行时间几乎完全用于测试质数。
通过减少除数候选者的数量可以获得进一步的改进。如果m[index] > 0
可以被4,6,8,...整除,那么它也可以被2整除,因此num >= 10^8
永远不会为num
产生0。这意味着所有这些划分都是多余的。通过特殊的套管2并以2的步长递增除数候选,
num
要执行的分割数量和运行时间大致减半(假设有足够的不良情况,偶数的工作可以忽略不计)。
现在,只要num % y
是3的倍数(3本身除外),y > 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;
}
}
}
不是3的倍数时计算出来,所以这些划分也是多余。您也可以通过特殊套管3消除它们,让y
只运行不能被3整除的奇数(以num % y
开头,交替增加2和4)。这可以减少剩余工作的大约三分之一(如果存在足够的坏情况)。
继续进行淘汰过程,我们只需将num
除以{em> primes 而不超过y
,以确定它是否为素数。
所以通常最好找到不超过你检查的最大y = 5
的平方根的素数,将它们存储在一个数组中并循环
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;
}
}
是否在常数时间是素数(如果你只有奇数的标志,那么2 ^ 31位的筛子需要256 MB [需要特殊的套管来检查是否{{1}如果是偶数],你只需要128 MB就可以在恒定时间内检查数字num
的素数,可以进一步减少筛子所需的空间。
到目前为止主要测试本身。
如果num < 2^31
数组包含可被2或5整除的数字,则可能值得重新排序循环,将num
的循环放在最外层,并跳过内部循环{{1可以被2或5整除 - 所有其他数字在加法之前乘以10的幂,所以num
将是2的倍数。 5而不是素数。
但是,尽管如此,运行代码仍需要很长时间。九个嵌套循环充满了错误的设计。
你尝试做什么?也许我们可以帮助找到正确的设计。
答案 2 :(得分:0)
我们可以通过计算数字的每个部分来消除大量的冗余计算。这也显示了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,因此肯定会有重复项,搜索时需要跳过这些重复项。丹尼尔指出,从顶部搜索,第一个找到的素数将是最大的。这是通过按降序对m[]
数组进行预排序来实现的。