检查三角形是否正确?

时间:2016-04-14 12:25:25

标签: c algorithm geometry computational-geometry

我试图检查三角形是否是C语言中的直角三角形。 abc是某个三角形边长。

int is_right_triangle(int a, int b, int c)
{
    return (a * a + b * b == c * c || a * a + c * c == b * b || b * b + c * c == a * a);
}

如何避免求和和乘法中的整数溢出?假设我们没有long long类型。

5 个答案:

答案 0 :(得分:2)

改进(优化)版本

  1. 找出最大的一面和最小面。 (不失一般性,让他们称之为C和A)。 B是第三方
  2. 现在C 2 - A 2 = B 2 为直角三角形。即(C + A)*(CA)= B 2 ,计算unsigned_sum = C + A和unsigned_diff = CA(保证unsigned int不会溢出int的总和)
  3. 将sum,diff和B除以sum和diff的gcd。如果它没有划分B,则它不是直角三角形。如果是这样的话,如果三角形是正确的,则sum和diff将是完美的正方形。
  4. 求sum和diff的整数平方根。如果根的乘积等于B,则三角形是正确的。
  5. int is_right_triangle(int a, int b, int c)
    {
      unsigned int sum, diff;
      int f = 2;  /* factor */
      unsigned int hcf, irts, irtd;
    
      /* sort */
      if(b > c) swap(&b, &c);
      if(a > b) swap(&a, &b);
      if(b > c) swap(&b, &c);
    
      sum = c;
      diff = c;
      sum += a;
      diff -= a;
    
      hcf = gcd(sum, diff);
      if(b % hcf != 0) return 0;
      sum /= hcf;
      diff /= hcf;
      b /= hcf;
    
      irts = isqrt(sum);
      if(irts * irts != sum || b % irts != 0) return 0;
      b /= irts;
      irtd = isqrt(diff);
      if(irtd * irtd != diff || b % irtd != 0) return 0;
      b /= irtd;
    
      return b == 1;
    }
    

    您可以找到isqrt @ Methods_of_computing_square_roots的算法或使用二进制搜索方法。

    #define NEXT(n, i)  (((n) + (i)/(n)) >> 1)  
    
    unsigned int isqrt(int number) {  
      unsigned int n  = 1;  
      unsigned int n1 = NEXT(n, number);  
    
      while(abs(n1 - n) > 1) {  
        n  = n1;  
        n1 = NEXT(n, number);  
      }  
      while(n1*n1 > number)  
        n1--;  
      return n1;  
    }
    

    isqrt在不更改codecodex

    的情况下复制

    旧答案

    1. 找出最大的一面和最小面。 (不失一般性,让他们称之为C和A)。 B是第三方
    2. 现在C 2 - A 2 = B 2 为直角三角形。即(C + A)*(CA)= B 2 ,计算unsigned_sum = C + A和unsigned_diff = CA(保证unsigned int不会溢出int的总和)
    3. 收集unsigned_sum和unsigned_diff的主要因子。如果它们不是2的倍数,则不是直角。如果因子是2的倍数,则保持划分copy_of_B = B,一次使用看到的素数因子。 (检查fac * fac< max(unsigned_sum,unsigned_dif),如果fac划分,请尝试与其他划分)
    4. 如果B = 1,则三角形是直角,否则不是。
    5. int is_right_triangle(int a, int b, int c)
      {
        unsigned int sum, diff;
        int f = 2;  /* factor */
      
        /* sort */
        if(b > c) swap(&b, &c);
        if(a > b) swap(&a, &b);
        if(b > c) swap(&b, &c);
      
        sum = c;
        diff = c;
        sum += a;
        diff -= a;
      
        while(f * f <= sum || f * f <= diff) {
          int count = 0;
          while(sum % f == 0) { sum /= f; ++count; }
          while(diff % f == 0) { diff /= f; ++count; }
          if(count % 2 == 1) return 0;
          while(count != 0) {
            b /= f;
            count -= 2;
          }
          ++f;  /* f = (f == 2 ? 3 : f + 2); */
        }
        return b == 1;
      }
      

      <强>优化
      1.如this comment中所述,您可以将unsigned_sum,unsigned_diff和b与gcd(unsigned_sum,unsigned_diff)分开,并且可以分别处理unsigned_sum和unsigned_diff。 2.在循环中,如果您可以随时检查sumdiff(以及b的平方)的产品是否保证不会溢出,则可以检查sum * diff == (unsigned)b * b并相应地中断。

答案 1 :(得分:2)

对于具有整数边a,b和b的所有直角三角形。 c,总有一对整数(m,n),这样;

g = gcd(a, gcd(b, c)); // greatest common divisor of a, b, c

baseA = a / g;
baseB = b / g;
baseC = c / g;

baseA = m * m - n * n;
baseB = 2 * m * n;
baseC = m * m + n * n;

牢记这一点(see primitive Pythagorean triples on this page),您可以将(baseB / 2)baseB为偶数整数)分解为其因子,然后检查因子对(m, n)(I& #39;恐怕,这必须是强力的)他们是否也满足baseAbaseC的上述条件。

我认为,这个程序将处理溢出问题并断言任何中间计算步骤永远不会超过三角形的最长边; &#34; C&#34;在这种情况下。

编辑:@anatolyg是对的。 a,b&amp; c应分为最大公约数g;但是,这种修正仍然没有违反对解决方案的接受,对于向下缩放,所有三个整数确实有助于断言非溢出约束。

上次编辑:我认为下面的代码有效(测试为32位整数限制,并使用gcc编译)。

#include <stdio.h>
#include <stdlib.h>

//  linked list definition for the set of divisors
struct divisorSet {
    int divisor;
    struct divisorSet *next;
};

//  swaps two integers
void swap(int *a, int *b) {
    int t;

    t = *a;
    *a = *b;
    *b = t;

    return;
}

//  computes the greatest common divisor
int gcd(int a, int b) {
    int t;

    if (a < b) {
        swap(&a, &b);
    }

    while (b) {
        t = a % b;
        a = b;
        b = t;
    }

    return (a);
}

//  sorts a, b & c as follows:
//  a < c
//  b is the even integer regardless of its magnitude
int sort(int *a, int *b, int *c) {
    int oneEven;

    oneEven = 0;

    if (*b % 2) {
        if (*a % 2) {
            if (*c % 2) {
                oneEven = 1;
            }
            else {
                swap(b, c);
            }
        }
        else {
            swap(a, b);
        }
    }

    if (*a > *c) {
        swap(a, c);
    }

    return (oneEven);
}

//  creates the divisor set as linked list
struct divisorSet *createDivisorSet(int b) {
    struct divisorSet *dSet, *dTmp, *dBase;
    int l, i, k;

    k = sizeof(struct divisorSet);
    l = b / 2;
    dBase = malloc(k);
    dSet = dBase;
    i = 1;
    dSet->divisor = i;

    while (i++ < l) {
        if (!(b % i)) {
            dTmp = dSet;
            dSet = malloc(k);
            dSet->divisor = i;
            dTmp->next = dSet;
        }
    }

    dSet->next = 0;

    return (dBase);
}

//  frees allocated memory
void releaseMem(struct divisorSet *dSet) {
    struct divisorSet *dTmp;

    while (dSet->next) {
        dTmp = dSet->next;
        free(dSet);
        dSet = dTmp;
    }

    free(dSet);
    return;
}

//  test if pythagorean triangle or not
int isRightTriangle(int a, int b, int c) {
    struct divisorSet *dSet, *dTmp, *dBase;
    int g, baseA, baseB, baseC, m, n;

    g = gcd(a, gcd(b, c));
    baseA = a / g;
    baseB = b / g;
    baseC = c / g;

    if (sort(&baseA, &baseB, &baseC)) return (0);

    dSet = createDivisorSet(baseB / 2);
    dBase = dSet;

    while (dSet->next) {
        n = dSet->divisor * dSet->divisor;
        dTmp = dSet;

        while (dTmp->next) {
            dTmp = dTmp->next;
            m = dTmp->divisor * dTmp->divisor;

            if (m - n == baseA && m + n == baseC) {
                releaseMem(dBase);
                return (1);
            }
        }

        dSet = dSet->next;
    }

    releaseMem(dBase);
    return (0);
}

void scaleSides(int *a, int *b, int *c, int s) {
    *a *= s;
    *b *= s;
    *c *= s;
    return;
}

int main(void) {
    int a, b, c, s;

    s = 7040900;

    a = 160;
    b = 231;
    c = 281; // (right triangle)
    printf("a = %10d   b = %10d   c = %10d   rightTriangle = %d\n", a, b, c, isRightTriangle(a, b, c));

    scaleSides(&a, &b, &c, s); // testing for overflow (right triangle)
    printf("a = %10d   b = %10d   c = %10d   rightTriangle = %d\n", a, b, c, isRightTriangle(a, b, c));

    b += 2; // testing for overflow (not right triangle)
    printf("a = %10d   b = %10d   c = %10d   rightTriangle = %d\n", a, b, c, isRightTriangle(a, b, c));

    a = 207;
    b = 224;
    c = 305; // (right triangle)
    printf("a = %10d   b = %10d   c = %10d   rightTriangle = %d\n", a, b, c, isRightTriangle(a, b, c));

    scaleSides(&a, &b, &c, s); // testing for overflow (right triangle)
    printf("a = %10d   b = %10d   c = %10d   rightTriangle = %d\n", a, b, c, isRightTriangle(a, b, c));

    b += 2; // testing for overflow (not right triangle)
    printf("a = %10d   b = %10d   c = %10d   rightTriangle = %d\n", a, b, c, isRightTriangle(a, b, c));

    printf("press <enter> to exit...\n");
    getchar();
    return (0);
}

答案 2 :(得分:1)

您可以通过扩展算法增加可以处理的值的范围。

如果您将数字X分为最多b位的两部分,则会得到X = B X' + X",其中X'X"最多b {1}}位(Bb的{​​{1}}次幂。2。然后平方X² = B²X'² + 2BX'X" + X"²,其中三个部分最多有b+1个有效位。

如果以b位的部分分解产品,则重写为

B²(B [X'²]' + [X'²]") + 2B(B [X'X"]' + [X'X"]") + B[X"²]' + [X"²]"

并获取

B³ [X'²]' + B² ([X'²]" + 2[X'X"]') + B (2[X'X"]" + [X"²]') + [X"²]"

小心谨慎,保留一些保护位并处理进位,你可以用两个单词来获得结果。

扩展添加很简单。

答案 3 :(得分:1)

在其他任何事情之前,让我们假设所有腿的值都是正整数。他们必须验证基本三角关系:

  • 0 < a0 < b0 < c;
  • a < b + cb < c + ac < a + b;
  • abs(a - b) < cabs(b - c) < aabs(c - a) < b

否则他们不能成为三角形的腿。

一种解决方案,它改进了@ mohit-jain提出的解决方案:

  1. 让腿为a&gt; b&gt; cPythagorean theorem表示如果a * a = b * b + c * c,则abc是直角三角形的边(a是斜边)。 等式可以重写为a * a - c * c = b * b(a + c) * (a - c) = b * b;
  2. 分配sum = a + cdiff = a - c这是算法溢出的地方(a + c
  3. 现在b * b = sum * diff。如果sumdiff不是mutually prime integers,那么他们的GCD也必须划分b。计算GCD(sum, diff)并检查它是否划分b。如果它没有划分b,则它不是方形三角形。
  4. sumdiffb除以GCD(sum, diff)。如果原始三角形是直角三角形,则等式b * b = sum * diff仍然存在(反之亦然)。
  5. 现在sumdiff没有任何共同因素。如果方程式已经过验证,那么sumb(以及diffb)必须具有共同因素。设b1 = b2 = b。等式为b1 * b2 = sum * diff
  6. b1sum除以GCD;用他们的GCD划分b1diff
  7. b2sum除以GCD;用他们的GCD划分b2diff
  8. 现在b1b2sumdiff的值较小,但它们可能仍然太大。但是,由于前面步骤中的划分,b1b2并不与sumdiff共享因子。他们验证等式的唯一情况是b1 == b2 == sum == diff == 1
  9. 代码非常冗长。您可以在this gist中找到它。它没有验证数字的基本条件是三角形的腿(在上面暴露),但实现了算法并使用注释#2(下面)来避免溢出。

    备注#1

    另一种可能性是检查腿是否验证了elementary properties of Pythagorean triangles。大多数这些检查仅使用简单的数学运算,并且可以拒绝许多腿组合。对于通过的算法,请使用上面的算法。

    备注#2

    上面列出的维基百科页面中的数学属性也可用于查找腿的常见因素或将它们组合起来的便捷方式,以便找到它们的共同因素并减少它们。

    例如,如果所有的腿都是偶数,则可以在其他任何内容之前将它们除以2。可以重复操作,同时所有操作均匀。 之后,如果斜边仍然是偶数,则它们不能是直角三角形的腿(参见列表中的属性)

    鉴于上述算法中的斜边是a,其他奇数可以选择为c(而不是最小的数字)。这种方法可以保证sumdiff是偶数,sum可以除以2,而无需实际计算a + c

    ac为奇数时,sum / 2可以计算为a / 2 + c / 2 + 1。这样可以完全消除溢出的风险,算法的其余部分会使每个步骤中所涉及的所有数字变得越来越小。

答案 4 :(得分:0)

如果a ^ 2 + b ^ 2 = c ^ 2(modulo p_i)对于某些素数p1,p2,...,那么你知道a ^ 2 + b ^ 2 = c ^ 2(模p1 * p2 * ...)。通过选择素数以使中间总和不会溢出,您可以检查任意范围。

这里的代码适用于c ^ 2到1e22,这意味着它对a,b,c全部为32位整数有效。如果这还不够,您可以使用更多的基数(或更大的质数,小心确保中间计算不会溢出您的整数)。

void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

int bases[] = {5051, 5059, 5077, 5081, 5087, 5099};

int is_rightangled(int a, int b, int c) {
  if (a > c) swap(&a, &c);
  if (b > c) swap(&b, &c);
  for (int i = 0; i < sizeof(bases)/sizeof(bases[0]); i++) {
    int p = bases[i];
    int ap = a % p, bp = b % p, cp = c % p;
    if (((ap * ap) + (bp * bp)) % p != (cp * cp) % p) return 0;
  }
  return 1;
}