Project Euler #29 替代解决方案

zuk*_*o32 3 c python algorithm math combinatorics

这是一个关于 Project Euler 问题的问题。您可以在这里找到问题的描述:https : //projecteuler.net/problem=29

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

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

print(len(set([a ** b for a in range(2, 101) for b in range(2, 101)])))
Run Code Online (Sandbox Code Playgroud)

现在,我试图通过使用更多的数学知识(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;
}
Run Code Online (Sandbox Code Playgroud)

使用此代码,我正在执行您在上面答案底部看到的操作,根据他之前的解释,他展示了一些关于如何找到 x = 3 的重复项的图表。我正在尝试做同样的事情。现在,如果您运行我的代码,它会根据上述答案的图表正确输出 13,即重复的数量。

所以,我试图扩展它来解决实际的项目欧拉问题,因为如果我能够找到重复的数量,那么我将从数字 99 * 99 中减去它(这是可能的幂组合,因为 2 <= a <= 100 and 2 <= b <= 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;
}
Run Code Online (Sandbox Code Playgroud)

如果你注意的话,变化是我循环了所有从 2 到 100 的 xs,而 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。

Mah*_*mam 5

寻找重叠数字的观察是作为流程
首先让范围从 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 46,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 6 , 9 5 , 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 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
你有没有注意到直到210我们仍然有重复的数字,之后我们开始有一个新数字,
所以使用这个观察重写上面的数字,它将是
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 , 212,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
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;
}
Run Code Online (Sandbox Code Playgroud)

您可以为此代码添加很多增强功能,例如使用位向量而不是 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 , 221 2 24 2 27 2 30

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 为底。所以从最大底数开始,每次 10*10 除以 2 直到它变为零,但这会给你底数 2 的最大幂作为最后一行的开始,即 6 作为我们最后的 2 6并且你从2 到 6 每行增加 2,所以我们需要将结果除以 2

int maxBase = rangeEnd*rangeEnd;
int maxStartPow = 1;
while (maxBase > 0)
{
    maxBase /= 2;
    maxStartPow++;
}
maxStartPow /= 2;
Run Code Online (Sandbox Code Playgroud)

之后,我们需要跟踪我们看到的权力,您可以遇到的最大权力是 maxStartPow*rangeEnd。

vector<bool> seen(maxStartPow*rangeEnd, false);
Run Code Online (Sandbox Code Playgroud)

然后我们开始在我们的基础中逐行进行,在这种情况下为 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);
}
Run Code Online (Sandbox Code Playgroud)

之后我们回到我们的 distinictPowers 函数并逐个基础地进行,每个基础逐行并重用我们从缓存函数中的计算