【问题标题】:C# Triangle numbers optimizationC# 三角数优化
【发布时间】:2013-03-11 18:41:50
【问题描述】:

任务是找到一个至少有 500 个除数的三角形数。

例如 28 有 6 个除数:1,2,4,7,14,28

我的代码最多适用于 200 个除数,但对于 500 个除数,它会永远运行...

有什么办法可以优化代码。比如我想到了动态优化和记忆,但找不到方法?

            int sum = 0;
            int counter = 0;
            int count = 1;

            bool isTrue = true;
            while (isTrue)
            {
                counter = 0;
                sum += count;

                for (int j = 1; j <= sum; j++)
                {
                    if (sum % j == 0)
                    {
                        counter++;
                        if (counter == 500)
                        {
                            isTrue = false;
                            Console.WriteLine("Triangle number: {0}", sum);
                            break;
                        }
                    }
                }
                count++;
            }            
            Console.WriteLine("Number of divisors: {0}", counter);

【问题讨论】:

  • 找到这个数字的最简单方法是将 1 到 500 的数字相乘。
  • 看来你也可以通过这个公式得到第n个三角形数:(n * (n + 1)) / 2。在维基百科上看到过
  • 实际上,问题变成了寻找连续两个数字的情况,它们之间有 500 个因子。
  • @Todo:首先,很明显这是一个欧拉问题。第二,你为什么要欺骗你寻求帮助的人?如果您想要提示是因为您想自己解决难题,那么请不要寻求解决方案。说“我正在研究 Euler #12,我可以手动优化这段代码”。
  • @Todo:请不要再猜我们了。如果您告诉我们这是一个项目 Euler 问题,而您只是想知道下一步该往哪里看,我们就不会浪费时间猜测您需要/希望得到何种程度的帮助。你没有提到需要一个最小的解决方案,所以我给出了一个(比最小的大得多的)封闭形式的解决方案来解决问题。有人投了反对票,所以我删除了它。

标签: c# optimization divider


【解决方案1】:

忽略这个数字是三角形数字的事实。如果你能快速解决这个问题:

  • 给定任意数 n,确定它的除数数

那么显然你可以快速求解 Euler #12。只需列出易于计算的三角形数,确定每个数的除数,并在得到 500 或更大的结果时停止。

那么如何快速确定除数的数量呢?正如您所发现的,当数字变大时,工作量很大。

这里有一个提示。假设您已经有了 素数分解。让我们选择一个数字,比如 196。将其分解为素数:

196 = 2 x 2 x 7 x 7

我可以通过查看 196 有九个除数的因式分解告诉你。怎么样?

因为 196 的任何除数都具有以下形式:

(1, 2 or 2x2) x (1, 7 or 7x7)

显然有九种可能的组合:

1 x 1
1 x 7
1 x 7 x 7
2 x 1
2 x 7
2 x 7 x 7
2 x 2 x 1
2 x 2 x 7
2 x 2 x 7 x 7

选择另一个号码。 200,可以说。那是 2 x 2 x 2 x 5 x 5。所以有 十二种 种可能性:

1 x 1
1 x 5
1 x 5 x 5
2 x 1
2 x 5
...
2 x 2 x 2 x 5 x 5

看到模式了吗?您进行素数分解,按素数对它们进行分组,然后计算每组中有多少。然后将这些数字中的每一个加一并将它们相乘。同样,在 200 中,在素因数分解中有 3 个二和 2 个五。分别添加一个:四个三个。将它们相乘:12。那就是有多少个除数。

因此,如果您知道素因数分解,您可以非常快速地找到除数的数量。我们已经将除数问题简化为一个更简单的问题:你能弄清楚如何快速生成素数分解吗?

【讨论】:

  • 嗯,我得到了最后一部分,即:“每个加一:四和三。将它们相乘:十二......”,但我无法得到那部分:“(1, 2 或 2x2) x (1、7 或 7x7)"。还有像 197 这样的数字呢?你会先在 2/3/5/7 上划分它以检查它是否是素数吗?
  • @Todo:弄清楚如何进行质数分解由您自己决定;您将需要知道如何解决其他欧拉问题。为了解决您的问题:您不明白为什么 196 的任何除数必须具有 (1, 2 或 2 x 2) x (1, 7 或 7 x 7) 形式吗?这样想吧。假设有一个 196 的除数属于那种形式。它的主要因素是什么?
  • @Todo:现在,你可能会说,好吧,假设有一些 second 素数分解,一个 other 的分解不是 2 x 2 x 7 x 7,这乘以196。也许主要因素之一是那个其他形式。 那不可能发生。 确保你理解这一点,因为我不骗你,算术基本定理:每个整数都有一个唯一的 素数分解。
  • 我在 Wikipedia 中读到了 Theorem Of Arithmetic,它阐明了我的理解,尤其是那部分:“每个大于 1 的整数要么是素数本身,要么是素数的乘积” 老实说,我不知道这个事实,它肯定会在未来帮助我。谢谢!
  • @Todo:太好了!欧拉计划真正向您展示了素数是如何构建其余数字的砖块。了解这些积木,您将能够构建许多有趣的东西。
【解决方案2】:

这里有一些优化,我会为您提供一些优化。

最简单的就是改变

for (int j = 1; j <= sum; j++)
{
    if (sum % j == 0)
    {
        counter++;
        if (counter == 500)
        {
            isTrue = false;
            Console.WriteLine("Triangle number: {0}", sum);
            break;
        }
    }
}

如果你找到了 1 个除数,你已经找到了 2 个除数,所以把它改成

for (int j = 1; j <= sum; j++)
{
    if (sum % j == 0)
    {
        if(sum/j < j)
            break;
        else if(sum/j == j)
            counter++;
        else
            counter +=2;

        if (counter == 500)
        {
            isTrue = false;
            Console.WriteLine("Triangle number: {0}", sum);
            break;
        }
    }
}

这会大大减少运行时间,但仍然需要很长时间。


您可以做的另一个优化是不开始检查表单sum,而是计算具有 500 个除数的最小数字。

然后你可以找到最大的三角形数,然后从那里开始。


如果您能找出有关此问题性质的其他特别之处,那么您就有可能大大减少运行时间。

【讨论】:

  • 哇,您添加到我的代码中的 if/else if/else 行产生了重大影响,并且不到一秒钟就解决了任务!!!非常感谢! :)
  • 我不明白你的第二种技术是如何工作的。你取一个有几百个素因子的数字,所以它将有 2^a 几百个除数,而不是 500 个除数。为什么在那个数之后的下一个最大的三角形数保证有 500 个除数?
  • @EricLippert 你是对的,它将有 500 多个除数。我已经消除了实际计算该数字的蹩脚尝试
  • @EricLippert 的想法是,如果您能以某种方式找到具有 500 个除数的最小数字,那么小于该数字的所有数字都不是正确的数字,您将浪费时间测试它们
  • @EricLippert 利用 63 500 的事实,我们得到 2^6 x 3^2 x 5^2 x 7 x 11 x 13 = 14414400有 500 多个除数。我做了一个快速的蛮力检查,这是最小的。
【解决方案3】:

一个数的除数是质因数的幂加一的乘积。例如:28 = 2^2*7^1,所以除数的数量是(2+1)*(1+1) = 6

这意味着,如果与数字的大小相比,您想要多个除数,您不希望任何一个素数出现得太频繁。换句话说:有至少 500 个除数的最小三角数很可能是小素数的小幂的乘积。

因此,与其检查每个数字是否能整除三角形数,不如查看一个最小素数列表,并查看每个素数在素数分解中出现的频率。然后使用上面的公式计算除数。

【讨论】:

    【解决方案4】:

    采取以下步骤:

    1.) 计算第一个 log(2, 499) 素数(不是 500,因为如果我弄错了 1 被算作除数,尽管它不是素数,因为它只有一个除数)。有很多解决方案,但你明白我的意思。

    2.) 三角形数的形式为 n * (n + 1) / 2,因为 1 + 2 + ... + 100 = (1 + 100) + (2 + 99) + ... + (50 + 51) = 101 * 50 = 101 * 100 / 2 = 5050是一个八岁的男孩,老师用这个任务惩罚了他)。

    1 + ... + n = (1 + n) + (2 + n - 1) + ... = n * (n + 1) / 2。

    3.) S = prod(first log(2, 499) 素数)

    4.) 求解 n * (n + 1) / 2 = S 的方程并计算其上限。你将有一个整数,我们称它为 m。

    5.)

    while (not(found))
        found = isCorrect(m)
        if (not(found)) then
            m = m + 1
        end if
    end while
    return m
    

    然后就可以了。如果我能帮助你,请告诉我。

    【讨论】:

    • 那个(杜撰的)故事通常归因于年轻的高斯。
    • 前 499 个素数的乘积有 2^499 个除数,而不是 500 个除数。
    • 我的答案已得到纠正,我已对您的评论表示赞同,因为它帮助我改进了答案。
    • 你可以做得比前九个素数的乘积更好。 2x3x5x7x1​​1x13x19x23 有 512 个除数,但 8x3x5x7x1​​1x13x19 也有 512 个除数,而且要小得多。你能找到有 500 个或更多除数的最小数吗?
    • log(2, 499)
    【解决方案5】:

    正如@EricLippert nad @LajosArpad 提到的,关键思想是仅迭代triangle numbers。您可以使用以下公式计算它们:

    T(n) = n * (n + 1) / 2

    这里是JSFiddle,您可能会觉得有帮助。

    function generateTriangleNumber(n) {
        return (n * (n + 1)) / 2;
    }
    
    function findTriangleNumberWithOver500Divisors() {
        var nextTriangleNum;
        var sqrt;
        for (i = 2;; i++) {
            var factors = [];
            factors[0] = 1;
            nextTriangleNum = generateTriangleNumber(i);
            sqrt = Math.pow(nextTriangleNum, 0.5);
            sqrt = Math.floor(sqrt);
            var j;
            for (j = 2; j <= sqrt; j++) {
                if (nextTriangleNum % j == 0) {
                    var quotient = nextTriangleNum / j;
                    factors[factors.length] = j;
                    factors[factors.length] = quotient;
                }
            }
            factors[factors.length] = nextTriangleNum;
            if (factors.length > 500) {
                break;
            }
        }
        console.log(nextTriangleNum);
    }
    

    【讨论】:

      【解决方案6】:

      顺便说一句,divisors of triangular number 搜索查询的第一个 Google 结果给出了这个:)

      Project Euler 12: Triangle Number with 500 Divisors

      看看有没有帮助。

      编辑:那篇文章的文字:

      第一个500位以上的三角形数是:76576500 花了 1 毫秒

      【讨论】:

      • 我知道它是 Project Euler 12 :) 但我不想知道它是如何在那里解决的……我想以我的方式解决它。
      • 哎呀...我应该在您阅读之前删除我帖子的编辑部分吗? :)
      • 不用担心...在我自己做之前我不会认为它解决了:)
      • 我不是我,但我认为否决票是因为 Project Euler 要求成员不要在他们的网站之外提出解决问题的方法:(
      猜你喜欢
      • 2015-11-04
      • 1970-01-01
      • 2012-02-10
      • 2017-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-31
      相关资源
      最近更新 更多