C ++:有哪些通用方法可以使代码更有效地用于大数字?

时间:2017-04-01 08:39:28

标签: c++

请在回答这个问题时尽量帮助更广泛的社区,而不仅仅是专门帮助我的问题(虽然帮助我的问题也会很好;)

我似乎一次又一次地遇到这个问题与Project Euler上的简单问题。最常见的是需要计算素数的问题 - 对于大于约60,000的数字,这些问题总是无法终止。

我最近的问题是问题12:

  

通过添加自然数来生成三角数的序列。所以第7个三角形数字是1 + 2 + 3 + 4 + 5 + 6 + 7 = 28.前十个术语是:

     

1,3,6,10,15,21,28,36,45,55,......

     

让我们列出前七个三角形数字的因子:

     

1:1   3:1,3   6:1,2,3,6   10:1,2,5,10   15:1,3,5,15   21:1,3,7,21   28:1,2,4,7,14,28   我们可以看到28是第一个超过五个除数的三角形数。

     

第一个三角形数的值超过500个除数是多少?

这是我的代码:

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

int main() {

int numberOfDivisors = 500;

//I begin by looping from 1, with 1 being the 1st triangular number, 2 being the second, and so on.
for (long long int i = 1;; i++) {
    long long int triangularNumber = (pow(i, 2) + i)/2
    //Once I have the i-th triangular, I loop from 1 to itself, and add 1 to count each time I encounter a divisor, giving the total number of divisors for each triangular.
    int count = 0;
    for (long long int j = 1; j <= triangularNumber; j++) {
        if (triangularNumber%j == 0) {
            count++;
        }
    }
    //If the number of divisors is 500, print out the triangular and break the code. 
    if (count == numberOfDivisors) {
        cout << triangularNumber << endl;
        break;
    }
}

}

此代码为较小的数字提供了正确的答案,然后要么无法终止,要么需要一个年龄才能这样做!

首先,我能解决这个具体问题,以提高我的代码效率吗?

其次,对于我自己和其他新的C ++用户来说,为了提高代码效率,有哪些一般性提示? (即,将来我们在这里学到的东西应用。)

谢谢!

2 个答案:

答案 0 :(得分:0)

关键问题是你的最终状况不好。你应该在计数时停止&gt; 500,但你寻找一个完全匹配的计数== 500,因此你可能会在没有检测到它的情况下击败正确的答案,并继续......可能永远。

如果您解决了这个问题,可以将其发布到代码审核中。他们可能会这样说:

将其分解为单独的函数,用于查找下一个三角形数字,并计算某个数字的因子。

当您找到下一个三角形数字时,执行pow。我执行了一次添加。

要计算某个数字中的因子数,谷歌搜索可能有所帮助。 (例如http://www.cut-the-knot.org/blue/NumberOfFactors.shtml)您可以随时构建素数列表,并使用它来快速找到素数因子分解,从中可以计算因子的数量,而无需实际计算它们。当数字变大时,那个循环会变大。

答案 1 :(得分:0)

Tldr:76576500。

关于你的欧拉问题,一些数学:

初步1:
我们称之为第n个三角形数字T(n) T(n) = 1 + 2 + 3 + ... + n = (n^2 + n)/2(有时归于高斯,有时归于其他人)。要弄清楚这一点并不难:

1+2+3+4+5+6+7+8+9+10 =  
(1+10) + (2+9) + (3+8) + (4+7) + (5+6) =  
11 + 11 + 11 + 11 + 11 =  
55 =  
110 / 2 =  
(10*10 + 10)/2

由于它的定义,T(n) + n + 1 = T(n+1)a<bT(a)<T(b)的定义是微不足道的。

初步2:
我们称除数为D. D(1)=1D(4)=3(因为1 2 4) 对于具有n非重复素因子的c(不仅是任何除数,而是素因子,例如。n = 42 = 2 * 3 * 7c = 3),D(n)是{ {1}}:对于每个因素,有两种可能性(使用与否)。这些例子的9个可能除数是:1,2,3,7,6(2 * 3),14(2 * 7),21(3 * 7),42(2 * 3 * 7)。
更常见的是重复,c^2的解决方案将(Power + 1)相乘。示例D(n):因为它有两个3,问题不是“使用3或不使用”,而是“使用它1次,2次或不使用”(如果一次,“第一次”或“第二次”3不会改变结果)。权力1 2 1,126 = 2^1 * 3^2 * 7^1D(126)

初步3:
数字2*3*2=12n不能包含除1之外的任何公共素数因子n+1(技术上,1不是素数,但无论如何)。因为如果xn/x都是自然数,(n+1)/x必须也是,但那是(n+1)/x - n/x

回到高斯:如果我们知道某个1/xn(需要计算n+1D(n))的素因子,那么计算D(n+1)简单。 D(T(n))。由于T(N) = (n^2 + n) / 2 = n * (n+1) / 2n没有共同的素数因素,因此将所有因素放在一起并删除一个2因为“/ 2”就足够了。示例:n+1为7,因子n7 = 7^1。一起n+1 = 8 = 2^3,删除一个2是2^3 * 7^1。权力为2^2 * 7^12 1。要检查D(T(7)) = 3*2 = 6,6个可能的除数是1 2 4 7 14 28.

程序现在可以做什么:将所有n从1循环到某个东西,总是分解n和n + 1,使用它来得到第n个三角形数的除数,并检查它是否> 500 。

存在一个微小的问题,即没有有效的素数分解算法。但是对于有些小的数字,今天的计算机仍然足够快,并且将所有发现的因子从1保持为n也有助于找到下一个(对于n + 1)。潜在的问题2对于longlong来说数字太大了,但是这里也没有问题(可以通过尝试找到)。

通过下面介绍的流程和程序,我得到了

  

12375th三角形数字是76576500,有576个除数

T(7) = 28 = 2^2 * 7^1

关于速度的一般问题:

1)算法。

如何了解它们?对于(相对)简单的问题,要么是读书/维基百科等。或者如果可以的话,搞清楚。对于更难的东西,学习更多基础知识和获得经验是必要的,甚至可以理解它们,例如。学习CS和/或数学......数论对你的欧拉问题有很大的帮助。 (这对于理解MP3文件的压缩方式有所帮助......有很多方面,不可能知道所有内容。)。

2a)自动编译器优化常用的代码部分/模式

2b)手动计时哪些程序部分最慢,并且(当不用其他算法替换它时)以某种方式改变它。需要较少的数据发送到慢速设备(HDD,hetwork ......),较少的RAM内存访问,较少的CPU周期,与OS调度程序和内存管理策略一起更好地工作,更好地使用CPU管道/缓存等等。 ......这既是教育也是经验(也是一个大话题)。

由于长变量的大小有限,有时需要使用自定义类型,例如。一个字节数组,用于存储每个字节中的单个数字。这样,如果你愿意,可以将整个RAM用于单个数字,但缺点是你/某人必须为这种数字存储重新实现诸如添加之类的东西。 (当然,libs已经存在,没有从头开始编写所有内容)。

顺便说一下,#include <iostream> #include <vector> #include <cstdint> using namespace std; const int limit = 500; vector<uint64_t> knownPrimes; //2 3 5 7... //eg. [14] is 1 0 0 1 ... because 14 = 2^1 * 3^0 * 5^0 * 7^1 vector<vector<uint32_t>> knownFactorizations; void init() { knownPrimes.push_back(2); knownFactorizations.push_back(vector<uint32_t>(1, 0)); //factors for 0 (dummy) knownFactorizations.push_back(vector<uint32_t>(1, 0)); //factors for 1 (dummy) knownFactorizations.push_back(vector<uint32_t>(1, 1)); //factors for 2 } void addAnotherFactorization() { uint64_t number = knownFactorizations.size(); size_t len = knownPrimes.size(); for(size_t i = 0; i < len; i++) { if(!(number % knownPrimes[i])) { //dividing with a prime gets a already factorized number knownFactorizations.push_back(knownFactorizations[number / knownPrimes[i]]); knownFactorizations[number][i]++; return; } } //if this failed, number is a newly found prime //because a) it has no known prime factors, so it must have others //and b) if it is not a prime itself, then it's factors should've been //found already (because they are smaller than the number itself) knownPrimes.push_back(number); len = knownFactorizations.size(); for(size_t s = 0; s < len; s++) { knownFactorizations[s].push_back(0); } knownFactorizations.push_back(knownFactorizations[0]); knownFactorizations[number][knownPrimes.size() - 1]++; } uint64_t calculateDivisorCountOfN(uint64_t number) { //factors for number must be known uint64_t res = 1; size_t len = knownFactorizations[number].size(); for(size_t s = 0; s < len; s++) { if(knownFactorizations[number][s]) { res *= (knownFactorizations[number][s] + 1); } } return res; } uint64_t calculateDivisorCountOfTN(uint64_t number) { //factors for number and number+1 must be known uint64_t res = 1; size_t len = knownFactorizations[number].size(); vector<uint32_t> tmp(len, 0); size_t s; for(s = 0; s < len; s++) { tmp[s] = knownFactorizations[number][s] + knownFactorizations[number+1][s]; } //remove /2 tmp[0]--; for(s = 0; s < len; s++) { if(tmp[s]) { res *= (tmp[s] + 1); } } return res; } int main() { init(); uint64_t number = knownFactorizations.size() - 2; uint64_t DTn = 0; while(DTn <= limit) { number++; addAnotherFactorization(); DTn = calculateDivisorCountOfTN(number); } uint64_t tn; if(number % 2) tn = ((number+1)/2)*number; else tn = (number/2)*(number+1); cout << "the " << number << "th triangle number is " << tn << " and has " << DTn << " divisors" << endl; return 0; } 是一个浮点函数,可能会给你带来不准确的结果。在这种情况下使用它是不合适的。