请在回答这个问题时尽量帮助更广泛的社区,而不仅仅是专门帮助我的问题(虽然帮助我的问题也会很好;)
我似乎一次又一次地遇到这个问题与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 ++用户来说,为了提高代码效率,有哪些一般性提示? (即,将来我们在这里学到的东西应用。)
谢谢!
答案 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<b
,T(a)<T(b)
的定义是微不足道的。
初步2:
我们称除数为D. D(1)=1
,D(4)=3
(因为1 2 4)
对于具有n
非重复素因子的c
(不仅是任何除数,而是素因子,例如。n = 42 = 2 * 3 * 7
有c = 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^1
为D(126)
。
初步3:
数字2*3*2=12
和n
不能包含除1之外的任何公共素数因子n+1
(技术上,1不是素数,但无论如何)。因为如果x
和n/x
都是自然数,(n+1)/x
必须也是,但那是(n+1)/x - n/x
。
回到高斯:如果我们知道某个1/x
和n
(需要计算n+1
和D(n)
)的素因子,那么计算D(n+1)
简单。 D(T(n))
。由于T(N) = (n^2 + n) / 2 = n * (n+1) / 2
和n
没有共同的素数因素,因此将所有因素放在一起并删除一个2因为“/ 2”就足够了。示例:n+1
为7,因子n
和7 = 7^1
。一起n+1 = 8 = 2^3
,删除一个2是2^3 * 7^1
。权力为2^2 * 7^1
,2 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;
}
是一个浮点函数,可能会给你带来不准确的结果。在这种情况下使用它是不合适的。