【问题标题】:Check if an integer has an integer cube root检查整数是否具有整数立方根
【发布时间】:2014-11-26 04:13:43
【问题描述】:

我正在编写一个程序来检查给定数字是否具有整数立方根。 这是我的代码:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std; 

int main(int argc, char const *argv[])
{
  double m;
  int c=0;
  int i;
  for(i=2;i<=1000000;i++)
  { 
    m = pow(i,(1./3.));
    if(m-(int)m == 0)
    {
      c++;
    }
  }
  cout<<c<<endl;
}

这里c 存储具有整数立方根的数字的数量。我的代码的问题是它总是给出两个作为答案,而答案应该大于两个,因为有很多数字,比如 8,64,27,...

我想知道为什么我得到的结果是两个。我无法捕捉到错误!

【问题讨论】:

  • 为什么要包含&lt;cstdio&gt;
  • 在使用浮点运算时检查相等性很少是一个好主意。相反,将计算出的根四舍五入到最接近的整数,然后检查它是否确实是立方根。当然,这暗示了您当前设计的一种更有效的替代方案。
  • luu 我每次写代码的时候都会写。 cheers 但我的代码有什么问题?
  • @SurayansTiwari m - (int)m 由于浮点错误,可能类似于 0.00000000001,即使对于似乎是整数 m 的东西,你已经完成了。
  • 最好使用乘法而不是pow,而且它也更快,因为您只需检查少于 100 个值

标签: c++ math


【解决方案1】:

发生的是舍入错误。由于 1/3 在 IEEE754 中无法准确表示,因此您得到的近似值略小于三分之一。

然后,例如,当您计算 pow(216, 1./3.) 时,结果为 5.9999999999999991118 。因此,当您执行 m - (int)m 时,您得到的 0.9999999999999991118 不等于零,因此您的测试失败。

您的代码发现的仅有的两种情况是两个最小的立方体(2 和 3),它们的错误非常小,以至于它仍然等于零。

要查看计算结果的完整精度,您可以将&lt;&lt; setprecision(20) 发送至cout。 (为此您可能需要#include &lt;iomanip&gt;)。

要解决此问题,您需要做两件事:

  • m - (int)m 替换为m - round(m) 或类似名称
  • 检查与0 非常接近的数字,而不是精确到0

See this question 用于讨论第二部分。这适用于我的小案例:

abs(m - round(m)) < 0.000001

但是,对于较大的数字,您可能需要考虑此 epsilon 值的大小。这种方法实际上可能在一般情况下不起作用,因为也许从来没有一个 epsilon 小到足以清除误报,但又大到足以捕获所有真实案例。

另一个改进是使用标准的cbrt 函数而不是这个pow

【讨论】:

  • Re "IEEE754",没有信息表明 OP 的 C++ 实现使用 IEEE 754。我认为,但我不确定,C++ 将浮点数的基数限制为 2 或 10 ,即不允许 3 和倍数,但要将其用作参数,您必须通过神圣标准来验证它。
  • @Alf 我的意思是:OP 发生了什么(不是:所有系统都发生了什么),这是一个安全的假设,他正在使用 IEEE754
  • 他可能坐在IBM mainframe上。
  • @Cheersandhth.-Alf 除非他使用一些以 3 为底(或 3 的倍数)的奇怪浮点类型,否则他永远无法得到确切的值或 1/3
  • @Cheersandhth.-Alf 我没听懂你。 IBM 大型机也不使用基数 3n 作为浮点数
【解决方案2】:

你也可以这样做(虽然我只是从一开始就将数字立方)

#include <iostream>
#include <cmath>

int main(int argc, char const *argv[])
{
    int c = 0;
    for (int i = 2; i <= 1000000; i++)
    {
        int m = std::round(std::pow(i, 1. / 3));
        if (std::round(std::pow(m, 3)) == i) // use std::round to be super safe
            c++;
    }
    std::cout << c << std::endl;
}

PS:请注意,我使用了std::round(std::pow(m,3)),因为std::pow 可能(而且很糟糕)给你一个不正确的结果,即使你的基数和参数是整数,请参阅

std::pow with integer parameters, comparing to an integer type

【讨论】:

    【解决方案3】:

    如果您要检查的数字很小(例如

    为从 0 到最大数字的所有数字创建一个布尔数组,然后标记其中所有实际上是较小整数的立方体的数字。您完全避免使用昂贵的 pow、root 甚至 float 函数。

    #include <iostream>
    
    void int_cubes(int num)
    {
        bool cubes[num]={false};
        for(int m=2; ; ++m)
        {
            int i=m*m*m;
            if(i<num) cubes[i]=true;
            else break;
        }
        size_t c=0;
        for(size_t i=0; i<num; ++i)
        {
            if(cubes[i]) c++;
        }
        std::cout<<"Overall number: "<<c<<"\n";
    }
    
    
    int main(int argc, char **argv)
    {
        int_cubes(atoi(argv[1]));
        
        return 0;
    }
    

    此外,这种方法的优点是您可以保留数组以供以后参考。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-18
      • 1970-01-01
      • 2023-03-18
      • 2012-12-08
      • 2012-11-15
      • 1970-01-01
      • 1970-01-01
      • 2015-07-15
      相关资源
      最近更新 更多