如何正确使用Math.Pow()?

时间:2016-06-15 19:15:43

标签: c# algorithm

我想找到方程式的所有正整数解 a^3 + b^3 = c^3 + d^3其中a,b,c,d是1到1000之间的整数;

for (int a = 1; a <= 1000; ++a)
{
    for (int b = 1; b <= 1000; ++b)
    {
        for (int c = 1; c <= 1000; ++c)
        {
            for (int d = 1; d <= 1000; ++d)
            {
                if (Math.Pow(a, 3) + Math.Pow(b, 3) == Math.Pow(c, 3) + Math.Pow(d, 3))
                {
                     Console.WriteLine("{0} {1} {2} {3}", a,b,c,d);
                }
            }
      }
}

我知道d = Math.Pow(a^3 + b^3 - c^3, 1/3),所以

for (int a = 1; a <= 1000; ++a)
{
    for (int b = 1; b <= 1000; ++b)
    {
        for (int c = 1; c <= 1000; ++c)
        {
            int d = (int)Math.Pow(Math.Pow(a, 3) + Math.Pow(b, 3) - Math.Pow(c, 3), 1/3);

            if (Math.Pow(a, 3) + Math.Pow(b, 3) == Math.Pow(c, 3) + Math.Pow(d, 3))
            {
                Console.WriteLine("{0} {1} {2} {3}", a,b,c,d);
            }
        }
   }
}

但是第二种算法产生的结果数量要少得多。我的代码中的错误在哪里?

我尝试(double)1/3,但第二个算法仍然给我较少数量的结果,然后是第一个

4 个答案:

答案 0 :(得分:10)

这些答案中没有一个是正确的。

你的问题是:

  

我的代码中的错误在哪里?

如果您使用双精度算法来解决整数问题,那么您做错了。根本不要使用Math.Pow,特别是不要使用它来提取立方根,并期望得到一个精确的整数答案。

那么你应该如何解决这个问题?

让我们更聪明地做不做不必要的工作。你的程序发现1 3 + 12 3 = 9 3 + 10 3 ,但也是12 3 + 1 3 = 10 3 + 9 3 ,依此类推。如果您知道第一个,那么您可以很容易地知道第二个。

那么我们该怎么做才能提高效率呢?

首先,b必须大于a。这样我们就不会浪费任何时间来确定1 3 + 12 3 = 12 3 + 1 3

同样,d必须大于c

现在,我们也可以说cd必须在 ab之间你知道为什么吗?

一旦我们实施了这些限制:

    for (int a = 1; a <= 1000; ++a)
        for (int b = a + 1; b <= 1000; ++b)
            for (int c = a + 1; c < b; ++c)
                for (int d = c + 1; d < b; ++d)
                    if (a * a * a + b * b * b == c * c * c + d * d * d)
                        Console.WriteLine($"{a} {b} {c} {d}");

你的程序变得更快。

现在,如果您愿意以更短的时间交换更多内存,还有办法让它更快。你能想到这个节目浪费时间的一些方法吗?一遍又一遍地完成相同计算的次数是多少次?我们怎样才能改善这种状况?

我们可能会注意到,每次通过三个内部循环计算a * a * a

    for (int a = 1; a <= 1000; ++a)
    {
        int a3 = a * a * a;
        for (int b = a + 1; b <= 1000; ++b)
        {
            int b3 = b * b * b;
            int sum = a3 + b3;
            for (int c = a + 1; c < b; ++c)
            {
                int c3 = c * c * c;
                int d3 = sum - c3;
                for (int d = c + 1; d < b; ++d)
                    if (d3 == d * d * d)
                        Console.WriteLine($"{a} {b} {c} {d}");
            }
        }
    }

但我们可能比那更聪明。例如:如果我们创建了一个将多维数据集映射到多维数据集根的Dictionary<int, int>怎么办?只有1000个!然后我们可以说:

    for (int a = 1; a <= 1000; ++a)
    {
        int a3 = a * a * a;
        for (int b = a + 1; b <= 1000; ++b)
        {
            int b3 = b * b * b;
            int sum = a3 + b3;
            for (int c = a + 1; c < b; ++c)
            {
                int c3 = c * c * c;
                int d3 = sum - c3;
                if (dict.HasKey(d3))
                {
                    d = dict[d3];
                    Console.WriteLine($"{a} {b} {c} {d}");
                }
            }
        }
    }

现在你不必计算d的立方体或立方根;你只是查看它是否是一个立方体,如果是,它的立方根是什么。

答案 1 :(得分:3)

即使是双精度,也不能用任何IEEE浮点类型精确表示1/3。

因此,您实际上并未计算某个值的多维数据集根,而是将该值提升至0.333333333333333333333333333334或稍微偏离版本的幂。这会引入舍入误差,导致误差因子增加。

考虑a = 995,b = 990,c = 990的情况:你的第一个循环会产生d = 995的匹配,但由于舍入错误,你的计算产生d = 994,这不符合你的条件。这很少会匹配,这可以解释您所看到的结果。

要解决这个问题,你可以完全使用浮点运算,但是这会变得很混乱,因为你必须检查范围,因为代表问题,即使这样,只有在你找到一个可能合适的范围之后,你才需要检查整数版本。另一种解决方案是从数学角度而不是蛮力来解决这个问题,尽管这可能需要内核方法并且变得非常混乱。

答案 2 :(得分:1)

替换

int d = (int)Math.Pow(Math.Pow(a, 3) + Math.Pow(b, 3) - Math.Pow(c, 3), 1/3);

int d = (int)Math.Round(Math.Pow(Math.Pow(a, 3) + Math.Pow(b, 3) - Math.Pow(c, 3), 1.0/3), 0);
if(d > 1000) continue; // Restrict solutions as in brute force algorithm

修复了两个错误,

  1. 1/3被计算为整数除法,得到0。使用1.0/3强制浮点除法。
  2. 由于浮点错误,计算可能会返回说3.999999994而不是4。当强制转换为整数时,会将其截断为3,从而有效地删除解决方案。使用Math.Round(3.999999994, 0)将其向上舍入为4.0,并将其作为整数转换为4

答案 3 :(得分:0)

您的问题是,此行的结果:int d = (int)Math.Pow(Math.Pow(a, 3) + Math.Pow(b, 3) - Math.Pow(c, 3), 1/3);可能是非整数值,无法满足您的要求。但是通过将其转换为int,您可以获得更多的解决方案。

您应该将d更改为double,然后检查d是否为1到1000之间的整数值:

double d = Math.Pow(Math.Pow(a, 3) + Math.Pow(b, 3) - Math.Pow(c, 3), 1.0/3.0);
double epsilon = 0.000000001;
double dint = Math.Round(d, 0);
if (dint<=d+epsilon && dint>=d-epsilon && dint>=1-epsilon && dint<=1000+epsilon)
{
   Console.WriteLine("{0} {1} {2} {3}", a,b,c,d);
}

编辑:我添加了一个epsilon以确保您的双重比较可以正常工作