【问题标题】:Finding all pairs of amicable numbers from 2 to an upper range - incorrect output查找从 2 到上限范围的所有友好数字对 - 输出不正确
【发布时间】:2019-01-13 15:57:27
【问题描述】:

我正在尝试编写一个函数来查找从 2 到给定上限的所有友好数字对。

我写了一个函数来求给定数的除数之和。

我还编写了一个函数,它使用以下事实:如果 num1 的除数之和为 S(num1),并且 num2=s(num1)- num1 的除数和相同,则 num1 和 num2 是友好数。

这是我的代码:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <math.h>

void amicableNumbers(int n);
int findSumOfDevisors(int num);

void main() {
    int upperRange;

    printf("enter the value for upper range: ");
    scanf("%d", &upperRange);
    printf("the amicable numbers between 2 and %d:\n", upperRange);
    amicableNumbers(upperRange);
}

void amicableNumbers(int n) {
    int sum, sum2, i;

    for (i = 2; i <= n; i++) {
        sum = findSumOfDevisors(i);
        if (sum > i && sum <= n) {
            sum2 = findSumOfDevisors(sum);
            if (sum2 == i)
                printf("%d and %d \n", i, sum);
        }
    }
}

int findSumOfDevisors(int num) {
    int sum = 1, i, n;
    
    n = (int)sqrt((double)num);
    for (i = 2; i <= n; i++) {
        if ((n % i) == 0) {
            sum += i;
            sum += n / i;
        }
    }
    return sum;
}

但是,当我运行它时,我得到了错误的输出。例如,对于upperRange 301,我知道2-301 范围内没有任何友好数字。

亲和数 是两个不同的数,其相关性使得每个数的真除数之和等于另一个数。例如,对 220 和 284 是友好的数字。 220的真因数之和为1+2+4+5+10+11+20+22+44+55+110=284,284的真因数之和为1+2+4+71+ 142=220。

一个数的适当除数是该数的一个正因数,除了数本身。

【问题讨论】:

  • @chux 是的,是的,先生。他对 findSumOfDevisors 有疑问
  • n =(int) sqrt((double)num); 可能无法提供所需的整数平方根,因为sqrt() 可能存在轻微的不准确性。建议只使用整数数学:for (i = 2; i &lt;= num/i; i++)
  • findSumOfDivisors *
  • OT:无论 Visual Studio 让程序员侥幸逃脱,main() 的返回类型始终是 int
  • OT:调用任何scanf() 系列函数时,始终检查返回值(不是参数值)以确保操作成功。

标签: c function output


【解决方案1】:

OP 的findSumOfDevisors() 有问题。也许其他代码也可以。

  1. sqrt() 没有义务给出一个完全正确的平方根 - 一些较弱的实现可能会导致一个值略高于或略低于预期的答案。再加上截断分数的(int),像 123.999999999... 这样的答案变成了 123 而不是 124。无论如何,这里不需要具有精度问题的浮点数学。

    建议只使用整数数学:

    // n =(int) sqrt((double)num);
    // for (i = 2; i <= n; i++)
    for (i = 2; i <= num/i; i++)
    
  2. 避免溢出。

    // for (i = 2; i*i <= num; i++)  `i*i` may overflow.
    for (i = 2; i <= num/i; i++)
    
  3. 对相同的值求和两次?当除数和商相同时,代码对它们都进行计数。我希望只有一个。

    sum += i;
    // sum += n / i;
    if (i != n/i) sum += n/i;
    
  4. 为了完整起见,我希望 findSumOfDevisors(0)findSumOfDevisors(1) 返回 0 而不是像 OP 的代码那样返回 1。负数是另一个未解决的问题。

    // int sum = 1;
    int sum = num > 1;
    
  5. 使用昂贵的/, % 还是获得 2 换 1?考虑以下代码。 %/ 每个都可能调用一些昂贵的余数和除法计算。然而,对于许多优秀的编译器,num/inum % i 将导致发出的代码在一次操作中计算这两者。为了清晰起见,最好编写代码,但如果需要考虑持久性,请研究良好优化编译器的结果。

    for (i = 2; i < num/i; i++) {
      if (num%i == 0) {
    

【讨论】:

    【解决方案2】:

    不建议在findSumOfDivisors 中使用浮点运算,试试这个:

    int findSumOfDivisors(int num) {
        int sum = 1, div, i;
    
        for (i = 2; i <= (div = num / i); i++) {
            if (num % i == 0) {
                sum += i;
                if (i == div)
                    break;
                sum += div;
            }
        }
        return sum;
    }
    

    另外,你的amicableNumbers函数太复杂了,你应该简单测试一下和的除数之和是不是原数:

    void amicableNumbers(int n) {
        int sum, i;
    
        for (i = 2; i <= n; i++) {
            sum = findSumOfDivisors(i);
            if (sum > i && sum <= n && findSumOfDivisors(sum) == i)
                printf("%d and %d\n", i, sum);
            }
        }
    }
    

    最后,main 不带参数的原型是int main(void),你应该测试scanf() 的返回值并从main 返回0

    这是一个改进的版本:

    #include <stdio.h>
    
    int findSumOfDivisors(int num) {
        int sum = 1, div, i;
    
        for (i = 2; i <= (div = num / i); i++) {
            if (num % i == 0) {
                sum += i;
                if (i == div)
                    break;
                sum += div;
            }
        }
        return sum;
    }
    
    void amicableNumbers(int n) {
        int sum, i;
    
        for (i = 2; i <= n; i++) {
            sum = findSumOfDivisors(i);
            if (sum > i && sum <= n && findSumOfDivisors(sum) == i)
                printf("%d and %d\n", i, sum);
            }
        }
    }
    
    int main(void) {
        int upperRange;
    
        printf("enter the value for upper range: ");
        if (scanf("%d", &upperRange) == 1) {
            printf("the amicable numbers between 2 and %d:\n", upperRange);
            amicableNumbers(upperRange);
        }
        return 0;
    }
    

    【讨论】:

    • 是的,这是我的第一种方式,但是正如@chux 所指出的那样,它可能会溢出。无论如何,+1 分享我的想法。
    • 另外,正如我在回答中提到的,if (sum &gt; i &amp;&amp; sum &lt;= n) 可以简化为i &lt; sum,所以if(i &lt; sum &amp;&amp; findSumOfDivisors(sum) == i) 更好
    • 是的,它可能,如果我们有机会克服这个可能性很小的问题,我们为什么要为此努力?
    • @snr:测试sum &lt;= n 确保您只生成两个元素在2n 之间的友好数字对。
    • @snr:更正了i * i &lt; n上的潜在溢出
    【解决方案3】:

    您的findSumOfDevisors 函数错误。此外,IMO sqrt() 功能对于性能来说是压倒性的。相反,您应该使用i &lt;= num / 2

    int findSumOfDevisors(int num)
    {
        int sum = 1, i,n;
    
        //n =(int) sqrt((double)num);
        for (i = 2; i <= num / 2; i++)
        {
            if ((num % i) == 0)
            {
                sum += i;
            }
        }
        return sum;
    }
    

    另外,if (sum &gt; i &amp;&amp; sum &lt;= n)可以简化为i &lt; sum

    【讨论】:

    • @snr,为什么我的 sqrt 函数会不堪重负?应该如何修复它而不是使用 i*i
    • 对于findSumOfDevisors(9),这段代码看起来像是在尝试对1+3+3+9求和。我希望1+3+9
    【解决方案4】:

    问题出在这个函数上

    int findSumOfDevisors(int num)
    

    您想输入 num 而不是 n 是输入错误 正确的功能是

    int findSumOfDevisors(int num)
    {
    int sum = 1, i,n;
    
     n =(int) sqrt((double)num);
    for (i = 2; i <= n; i++)
    {
        if ((num % i) == 0)
        {
            sum += i;
    
            // If divisors are equal, add only one 
            if (num /i != i)
            {
                sum += num / i;
            } 
        }
    }
    return sum;
    }
    

    【讨论】:

    • 这个函数还有一个bug:完全平方的平方根只能计算一次。
    • @chqrlie 你是对的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 2019-06-20
    相关资源
    最近更新 更多