0
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

4

3 回答 3

6

将此代码替换为使用合理算法的代码,例如Eratosthenes 筛。最重要的“优化”是首先选择正确的算法。

如果您的数字排序算法是随机交换它们直到它们有序,那么您优化随机条目的选择、交换它们或检查它们是否有序并不重要。无论如何,糟糕的算法将意味着糟糕的性能。

于 2012-09-04T10:44:24.147 回答
1

您正在检查 25 9 = 3,814,697,265,625 个数字是否为素数。这是很多主要测试,而且总是需要很长时间。即使在所有数组条目 (in 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因为 if (num >) k > num/2、 then2*k > numnum不是 的倍数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 < √numnum/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就像这里可能的情况一样,计算平方根可能是更好的选择。

现在我们已经将试验除法循环的迭代次数限制为复合√numnum素数,并将运行时间减少至少 5000 倍(假设所有m[index] > 0,所以总是num >= 10^8),无论测试数字中有多少素数. 如果大多数取值num是素数较小的组合,则缩减因子要大得多,以至于通常运行时间几乎完全用于测试素数。

通过减少候选除数的数量可以获得进一步的改进。如果num能被 4, 6, 8, ... 整除,那么它也能被 2 整除,因此num % y对于偶数永远不会产生 0 y > 2。这意味着所有这些划分都是多余的。通过特殊的大小写 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 % y只会在num不是 3 的倍数时计算,所以这些除法也是多余的。您也可以通过特殊情况 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位占用256 MB,如果你只有奇数的标志[需要特殊情况检查是否num是偶数],你只需要128 MB来检查< 2^31常数时间内数字的素数,进一步减少所需空间因为筛子是可能的)。

到目前为止,主要测试本身。

如果m数组包含可被 2 或 5 整除的数字,则可能值得重新排序循环,将循环用于i最外层,如果可被 2 或 5 整除,则跳过内部循环m[i]- 所有其他数字都乘以幂在添加之前为 10,因此num将分别是 2 的倍数。5 而不是素数。

但是,尽管如此,运行代码仍然需要很长时间。九个嵌套循环散发着错误设计的味道。

你想做什么?也许我们可以帮助找到正确的设计。

于 2012-09-04T15:42:27.327 回答
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,62525*24*23*...*17 = 741,354,768,000

由于您现在已经指出m[]数组中的所有条目都小于 10,因此肯定会有重复,搜索时需要跳过这些条目。正如丹尼尔指出的那样,从顶部搜索,第一个找到的素数将是最大的。m[]这是通过按降序对数组进行预排序来实现的。

于 2012-09-05T08:57:51.477 回答