我试图检查三角形是否是C语言中的直角三角形。 a
,b
和c
是某个三角形边长。
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
类型。
答案 0 :(得分:2)
改进(优化)版本
int
的总和)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
旧答案
int
的总和)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.在循环中,如果您可以随时检查sum
和diff
(以及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;恐怕,这必须是强力的)他们是否也满足baseA
和baseC
的上述条件。
我认为,这个程序将处理溢出问题并断言任何中间计算步骤永远不会超过三角形的最长边; &#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}}位(B
是b
的{{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 < a
,0 < b
,0 < c
; a < b + c
,b < c + a
,c < a + b
; abs(a - b) < c
,abs(b - c) < a
,abs(c - a) < b
。否则他们不能成为三角形的腿。
a
&gt; b
&gt; c
。 Pythagorean theorem表示如果a * a = b * b + c * c
,则a
,b
和c
是直角三角形的边(a
是斜边)。
等式可以重写为a * a - c * c = b * b
或(a + c) * (a - c) = b * b
; sum = a + c
,diff = a - c
。 这是算法溢出的地方(a + c
)。b * b = sum * diff
。如果sum
和diff
不是mutually prime integers,那么他们的GCD也必须划分b
。计算GCD(sum, diff)
并检查它是否划分b
。如果它没有划分b
,则它不是方形三角形。sum
,diff
和b
除以GCD(sum, diff)
。如果原始三角形是直角三角形,则等式b * b = sum * diff
仍然存在(反之亦然)。sum
和diff
没有任何共同因素。如果方程式已经过验证,那么sum
和b
(以及diff
和b
)必须具有共同因素。设b1
= b2
= b
。等式为b1 * b2 = sum * diff
。b1
和sum
除以GCD;用他们的GCD划分b1
和diff
。 b2
和sum
除以GCD;用他们的GCD划分b2
和diff
。b1
,b2
,sum
和diff
的值较小,但它们可能仍然太大。但是,由于前面步骤中的划分,b1
和b2
并不与sum
和diff
共享因子。他们验证等式的唯一情况是b1 == b2 == sum == diff == 1
。代码非常冗长。您可以在this gist中找到它。它没有验证数字的基本条件是三角形的腿(在上面暴露),但实现了算法并使用注释#2(下面)来避免溢出。
另一种可能性是检查腿是否验证了elementary properties of Pythagorean triangles。大多数这些检查仅使用简单的数学运算,并且可以拒绝许多腿组合。对于通过的算法,请使用上面的算法。
上面列出的维基百科页面中的数学属性也可用于查找腿的常见因素或将它们组合起来的便捷方式,以便找到它们的共同因素并减少它们。
例如,如果所有的腿都是偶数,则可以在其他任何内容之前将它们除以2
。可以重复操作,同时所有操作均匀。
之后,如果斜边仍然是偶数,则它们不能是直角三角形的腿(参见列表中的属性)
鉴于上述算法中的斜边是a
,其他奇数可以选择为c
(而不是最小的数字)。这种方法可以保证sum
和diff
是偶数,sum
可以除以2
,而无需实际计算a + c
。
当a
和c
为奇数时,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;
}