项目欧拉#29替代解决方案

时间:2016-01-23 14:23:11

标签: python c algorithm math combinatorics

这是关于项目欧拉问题的问题。 您可以在此处找到问题的描述:https://projecteuler.net/problem=29

好的,首先,让我澄清一下我已经解决了这个问题,我只是在寻找一种更多基于数学的替代解决方案。
第二,为了不破坏任何尚未解决的问题,如果你还没有解决,请不要继续。 :)

所以,我使用Python解决了这个问题,因为它支持大数字和列表推导,我能够想出一个单行:

print(len(set([a ** b for a in range(2, 101) for b in range(2, 101)])))

现在,我试图通过使用更多的数学知识在C中解决它(C本身不支持大数字或列表推导)。 我遇到了这个帖子:PROJECT EULER #29接受的答案给了我一些想法,我想出了这个代码:

int main(void) {

    int found[1000];    // an array where I hold the found values(0 or 1)
    int e, b, i, count, x;

    count = 0;     // number of duplicates
    x = 3;
    for(i = 0; i < 1000; i++)
         found[i] = 0;


    for(e = 1; (int)pow(x, e) <= 100; e++) {
        for(b = 2; b <= 10; b++) {
            if(found[e * b])    // if the value has been found, then we have duplicate
                count++;
            found[e * b] = 1;   // mark that we found the value
        }
    }

    printf("count: %d\n", count);

    return 0;
}

使用此代码,我正在做您在答案底部可以看到的内容 上面,他展示了一些关于如何找到重复项的图表 x = 3,基于他之前解释的内容。我想做同样的事情。现在,如果你运行我的代码, 它根据上述答案的图表正确输出13,这是重复的数量。

所以,我试图扩展它以解决实际的项目euler问题,因为如果我能够找到重复的数量,那么我将从数字99 * 99中减去它(这是可能的功率组合,因为2 &lt; = a&lt; = 100且2&lt; = b&lt; = 100)这将是答案。结果是:

int main(void) {

    int found[1000];
    int e, b, i, count, x;

    count = 0;
    for(x = 2; x <= 100; x++) {
        for(i = 0; i < 1000; i++)
             found[i] = 0;


        for(e = 1; (int)pow(x, e) <= 100; e++) {
            for(b = 2; b <= 100; b++) {
                if(found[e * b])
                    count++;
                found[e * b] = 1;
            }
        }
    }

    printf("count: %d\n", count);

    return 0;
}

如果你注意,那些变化是我循环所有xs从2到100而b不是从2到10但是从2到100。 但是,程序打印814,这是不正确的。它应该是618。 任何帮助都非常感谢!我可能两次重复一些重复,但在哪里?代码有什么问题?此外,如果您有任何有助于构建新算法的数学解释,我们也非常感激!

编辑
我忘了提到的一件事是,如果不是放下:
for(x = 2; x <= 100; x++) 我这样做:
for(x = 2; x <= 6; x++)
即停止到6,它打印正确的答案。而这更令人感到奇怪。

EDIT2
我还要注意,对于8和9(而不是100),它给出了正确的结果。分别为44和54。

1 个答案:

答案 0 :(得分:4)

找到重叠数字的观察结果如流程
首先让范围从2到10 所以数字将类似于2 2 ,2 3 ,2 4 ,2 5 ,2 6 ,2 7 ,2 8 ,2 9 ,2 10
3 2 ,3 3 ,3 4 ,3 5 ,3 6 , 3 7 ,3 8 ,3 9 ,3 10
4 2 ,4 3 ,4 4 ,4 5 ,4 6 , 4 7 ,4 8 ,4 9 ,4 10
5 2 ,5 3 ,5 4 ,5 5 ,5 6 , 5 7 ,5 8 ,5 9 ,5 10
6 2 ,6 3 ,6 6 ,6 5 ,6 6 , 6 7 ,6 8 ,6 9 ,6 10
7 2 ,7 3 ,7 7 ,7 5 ,7 7 , 7 7 ,7 8 ,7 9 ,7 10
8 2 ,8 3 ,8 8 ,8 5 ,8 8 , 8 8 ,8 8 ,8 9 ,8 10
9 2 ,9 3 ,9 4 ,9 5 ,9 6 , 9 7 ,9 9 ,9 9 ,9 10
10 2 ,10 3 ,10 4 ,10 5 ,10 6 , 10 7 ,10 8 ,10 9 ,10 10
关键点是 4 2 =(2 2 2 = 2 4 所以 4 2 ,4 3 ,4 4 ,4 5 ,4 < sup> 6 ,4 7 ,4 8 ,4 9 ,4 10 会的 2 4 ,2 6 ,2 8 ,2 10 ,2 12 ,2 14 ,2 16 ,2 18 ,2 20
你注意到,直到 2 10 我们仍然有重复的数字,之后我们开始有一个新的数字
所以使用这个观察重写上面的数字它将是
2 2 ,2 3 ,2 4 ,2 5 ,2 6 , 2 7 ,2 8 ,2 9 ,2 10
3 2 ,3 3 ,3 4 ,3 5 ,3 6 , 3 7 ,3 8 ,3 9 ,3 10
2 4 ,2 5 ,2 8 ,2 10 ,2 12 ,2 14 ,2 16 ,2 18 ,2 20
5 2 ,5 3 ,5 4 ,5 5 ,5 6 , 5 7 ,5 8 ,5 9 ,5 10
6 2 ,6 3 ,6 4 ,6 5 ,6 6 , 6 7 ,6 8 ,6 9 ,6 10
7 2 ,7 3 ,7 4 ,7 5 ,7 6 , 7 7 ,7 8 ,7 9 ,7 10
2 6 ,2 9 ,2 12 2 15 2 18 ,2 21 ,2 24 ,2 27 ,2 30 < / sup>
3 4 ,3 6 ,3 8 ,3 10 ,3 12 ,3 14 ,3 16 ,3 18 ,3 20
10 2 ,10 3 ,10 4 ,10 5 ,10 6 , 10 7 ,10 8 ,10 9 ,10 10
所以我们需要跟踪我们获得它的数量的数量,因为我们从2​​ 2 开始,数字是2,功率是2,增加功率的间隙是1。 代码是:

vector<int>  calcCache(int rangeStart, int rangeEnd)
{
    int maxBase = rangeEnd*rangeEnd;
    int maxStartPow = 1;
    while (maxBase > 0)
    {
        maxBase /= 2;
        maxStartPow++;
    }
    maxStartPow /= 2;
    vector<bool> seen(maxStartPow*rangeEnd, false);
    int i = rangeStart;
    vector<int> cahce;


    int maxprev = 0;

    int gap = 1;
    int startpow = 2 * gap;
    int j = pow(i, startpow);

    int diff = rangeEnd - rangeStart;
    int maxCurrent = diff*gap + startpow;

    while (j <= rangeEnd*rangeEnd)
    {

        int currPow = startpow;
        int k = 0;
        int currRes = 0;
        while (k <= diff)
        {

            if (!seen[currPow])
            {
                currRes++;
            }
            seen[currPow] = true;
            currPow += gap;
            k++;
        }
        cahce.push_back(currRes);

        maxprev = currPow - gap;


        gap++;
        startpow = 2 * gap;
        j = pow(i, startpow);
    }

    return cahce;
}
int distinctpowers(int rangeStart, int rangeEnd)
{
    vector<bool> arr(rangeEnd*rangeEnd + 1, false);
    int res = 0;

    vector<int> cache = calcCache(rangeStart, rangeEnd);
    for (int i = rangeStart; i <= rangeEnd; i++)
    {

        if (!arr[i*i])
        {
            int maxprev = 0;

            int gap = 1;
            int startpow = 2 * gap;
            int j = pow(i, startpow);

            int diff = rangeEnd - rangeStart;
            int maxCurrent = diff*gap + startpow;

            while (j <= rangeEnd*rangeEnd)
            {

                int currPow = startpow;
                res += cache[gap - 1];

                maxprev = currPow - gap;
                arr[j] = true;

                gap++;
                startpow = 2 * gap;
                j = pow(i, startpow);


            }
        }
    }
    return res;
}

您可以为此代码添加许多增强功能,例如使用位向量而不是bool数组。

编辑:


这里是对上述代码的一些解释,首先考虑每个不同的基数,从2到10 2 2 ,2 3 ,2 4 ,2 5 ,2 6 , 2 7 ,2 8 ,2 9 ,2 10
2 4 ,2 5 ,2 8 ,2 10 ,2 12 ,2 14 ,2 16 ,2 18 ,2 20
2 6 ,2 9 ,2 12 2 15 2 18 ,2 21 ,2 24 ,2 27 ,2 30 < / sup>

3 2 ,3 3 ,3 4 ,3 5 ,3 6 ,3 7 ,3 8 ,3 9 ,3 10
3 4 ,3 6 ,3 8 ,3 10 ,3 12 ,3 14 ,3 16 ,3 18 ,3 20

5 2 ,5 3 ,5 4 ,5 5 ,5 6 ,5 7 ,5 8 ,5 9 ,5 10

6 2 ,6 3 ,6 4 ,6 5 ,6 6 ,6 7 ,6 8 ,6 9 ,6 10

7 2 ,7 3 ,7 4 ,7 5 ,7 6 ,7 7 ,7 8 ,7 9 ,7 10

10 2 ,10 3 ,10 4 ,10 5 ,10 6 ,10 7 ,10 8 ,10 9 ,10 10

您是否注意到权力序列为每个新基数重复自我,并且基数2的序列中的最大数字 所以我们需要保存基础2的结果并将其重新用于另一个基础,这就是缓存的想法。
缓存中还有一件事是你需要弄清楚你有多少行2。所以从最大基数开始,每次除以2得到10 * 10,直到它变为零,但是这将给你最大基数2的最大功率作为最后一行的开始,这是我们的最后2 6 你开始从2到6的力量,每一行增加2,所以我们需要将结果除以2

int maxBase = rangeEnd*rangeEnd;
int maxStartPow = 1;
while (maxBase > 0)
{
    maxBase /= 2;
    maxStartPow++;
}
maxStartPow /= 2;

之后我们需要跟踪我们看到的功率,你可以遇到的最大功率是maxStartPow * rangeEnd。

vector<bool> seen(maxStartPow*rangeEnd, false);

然后我们开始在我们的基础中逐行进行,在这种情况下2 ,每一行记住我们看到的力量,当我们看到新的力量时,我们增加了这条线的结果。
这段代码最重要的部分是,在计算每一行之后我们需要存储它,因为我们将在主要问题中重用它。

int maxprev = 0;

int gap = 1;
int startpow = 2 * gap;
int j = pow(i, startpow);

int diff = rangeEnd - rangeStart;
int maxCurrent = diff*gap + startpow;

while (j <= rangeEnd*rangeEnd)
{

    int currPow = startpow;
    int k = 0;
    int currRes = 0;
    while (k <= diff)
    {

        if (!seen[currPow])
        {
            currRes++;
        }
        seen[currPow] = true;
        currPow += gap;
        k++;
    }
    cahce.push_back(currRes);

    maxprev = currPow - gap;


    gap++;
    startpow = 2 * gap;
    j = pow(i, startpow);
}

之后我们回到我们的distinguishedictPowers函数并逐个基地,逐行逐行,并从缓存函数重用我们的计算