【问题标题】:Project Euler #29 alternative solutionProject Euler #29 替代解决方案
【发布时间】:2016-04-30 02:40:00
【问题描述】:

这是一个关于 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)])))

现在,我正在尝试通过使用更多数学知识在 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,即重复数。

所以,我尝试扩展它来解决实际的项目欧拉问题,因为如果我能够找到重复的数量,那么我只需从数字 99 * 99 中减去它(这是可能的功率组合,因为 2

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;
}

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

编辑
我忘了提到的是,如果不是把:
for(x = 2; x &lt;= 100; x++) 我愿意:
for(x = 2; x &lt;= 6; x++)
即停到6,它会打印正确的答案。这更离奇。

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

【问题讨论】:

  • 如果你想改进工作代码,你应该传递给codereview.stackexchange.com :)
  • 谢谢,我会考虑的!只是这并没有真正起作用,因为它会产生错误的结果。
  • 我是用和你一模一样的单线做的。但是,如果您想要一种更数学的方法,您可以在完全不计算幂的情况下做到这一点。例如,64 可以写成 43 和 82,因为 26 = (23)**2 = (2**2)**3。也就是说,我们写出 6 = 3*2 = 2*3 并使用指数定律。当然,这适用于任何基数,而不仅仅是 2,而且对于任何指数,您都可以考虑不同的因素。现在您必须担心重复计算。
  • 是的,如果您看到我上面提到的线程,这就是他提到的内容,根据他的回答,我试图解决问题。正如您所说,基本问题是重复计算。 :)
  • 每次count 递增时,尝试打印出 x、e、b。然后你可以看到你在数什么。

标签: python c algorithm math combinatorics


【解决方案1】:

寻找重叠数字的观察如流程
首先让范围 从 2 到 10,这样数字就会像
22, 23, 24 、25、26、27、28、29 , 210
32、33、34、35、36、 37、38、39、310
42、43、44、45、46、 47、48、49、410
52、53、54、55、56、 57、58、59、510
62、63、66、65、66、 67、68、69、610
72、73、77、75、77、 77、78、79、710
82、83、88、85、88、 88、88、89、810
92、93、94、95、96、 97、99、99、910
102、103、104、105、106、 107、108、109、1010
关键是42 = (22)2 = 24 所以
42、43、44、45、46、47、48、49、410将会 24、26、28、210、212 sup>、214、216、218、220
你有没有注意到直到 210 我们仍然有重复的数字,之后我们开始有一个新的数字
所以用这个观察重写上面的数字它将是
22、23、24、25、26、 27、28、29、210
32、33、34、35、36、 37、38、39、310
24 、25、28、210、212、2 14、216、218、220
52、53、54、55、56、 57、58、59、510
62、63、64、65、66、 67、68、69、610
72、73、74、75、76、 77、78、79、710
26 、29、212 215218、221、224、227、230
34、36、38、310、312 、314、316、318、320
102、103、104、105、106、 107、108、109、1010
所以我们需要跟踪我们获得它的权力的数字,例如我们从 22 开始,数字是 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;
}

您可以在此代码中添加很多增强功能,例如使用位向量而不是布尔数组。

编辑:


这是对上述代码的一些解释,首先考虑每个不同的基数,从 2 到 10。
22、23、24、25、26、 27、28、29、210
24 、25、28、210、212、2 14、216、218、220
26、29、212 215218、221、224、227、230

32、33、34、35、36、 37、38、39、310
34 、36、38、310、312、3 14、316、318、320

52、53、54、55、56 sup>、57、58、59、510

62、63、64、65、66 sup>、67、68、69、610

72、73、74、75、76 sup>、77、78、79、710

102、103、104、105、106 sup>、107、108、109、1010

您是否注意到幂序列在每个新碱基上都在自我重复,碱基 2 的序列中的数字最大。
所以我们需要将我们的结果保存为基数 2 并将其与另一个基数重用这就是缓存的想法。
缓存中的另一件事是您需要弄清楚您有多少以 2 为底的行。所以从 10*10 的最大底数开始,每次除以 2 直到它变为零,但这会给你以 2 为底的最大功率作为最后一行的开始,即 6 作为我们的最后 2 6 sup> 并且您从 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);

然后我们开始在我们的基础中逐行遍历,每一行都会记住我们已经看到的幂,当我们看到新的幂时,我们会增加这一行的结果。
这段代码最重要的部分是,在计算完每一行之后,我们需要存储它,因为我们将在我们的主要问题中重用它。

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);
}

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

【讨论】:

  • 你在这个答案上帮了我很多忙!但是,我认为有一些问题。首先,当您从 2 到 10 进行示例时,在代码之前,您将第 7 行中的 2^15 视为重复,如果我没记错的话,那不是。这似乎是代码也存在的问题。对于 rangeStart = 2 和 rangeEnd = 10,它返回 68,而它应该返回 69(代码也将 2^15 计为重复)。最后,对于rangeStart = 2 and rangeEnd=100,也就是PE问题,它返回9068,当它应该返回9183时,可能是因为上面的原因。尽管如此,感谢您的时间! :)
  • 是的,你是对的 :)。我会更正它并添加另一个增强功能!这是在计算所有以 2 为底的幂之后,我们将保存结果并以 3、4、5 为底重用它,依此类推。
  • 天哪,你花了很多时间在这上面,非常感谢!! :) 我也在尝试通过结合我们的代码想法来找到问题的解决方案!
  • 它的工作! :) 如果您能解释一下它是如何工作的,或者放一些 cmets,我将不胜感激,因为它是非常大的代码(尽管它非常快)。无论如何,非常感谢!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2011-03-03
  • 1970-01-01
  • 2022-11-29
  • 2013-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多