让我们说我们有x和y,两者都是C中的有符号整数,我们如何才能找到两者之间最准确的平均值?
我更喜欢不利用任何机器/编译器/工具链特定工作的解决方案。
我提出的最好的是:(a / 2) + (b / 2) + !!(a % 2) * !!(b %2)
是否有更准确的解决方案?快点?简单?
如果我们知道一个是否比另一个先验大,该怎么办?
感谢。
d
编者注:请注意,当输入值接近C int
类型的最大绝对边界时,OP需要不受整数溢出影响的答案。这在原始问题中没有说明,但在给出答案时很重要。
答案 0 :(得分:6)
接受回答后(4年)
我希望函数int average_int(int a, int b)
能够:
1.针对[INT_MIN..INT_MAX]
和a
的组合,在b
的整个范围内工作
2.与(a+b)/2
具有相同的结果,就像使用更宽的数学一样。
当int2x存在时,@Santiago Alessandri方法效果很好。
int avgSS(int a, int b) {
return (int) ( ((int2x) a + b) / 2);
}
其他@AProgrammer的变体:
int avgC(int a, int b) {
if ((a < 0) == (b < 0)) { // a,b same sign
return a/2 + b/2 + (a%2 + b%2)/2;
}
return (a+b)/2;
}
solution包含更多测试但没有%
所有以下解决方案&#34;工作&#34;当溢出没有发生时,在(a+b)/2
的1之内,但我希望找到一个与所有(a+b)/2
匹配int
的。{/ p>
@Santiago Alessandri只要int
的范围小于long long
的范围(通常),解决方案就会有效。
((long long)a + (long long)b) / 2
接受答案的 @AProgrammer在1/4的时间内无法匹配(a+b)/2
。示例输入,例如a == 1, b == -2
a/2 + b/2 + (a%2 + b%2)/2
@Guy Sirton,解决方案大约1/8的时间无法匹配(a+b)/2
。示例输入,例如a == 1, b == 0
int sgeq = ((a<0)==(b<0));
int avg = ((!sgeq)*(a+b)+sgeq*(b-a))/2 + sgeq*a;
@R..,解决方案在1/4的时间内无法匹配(a+b)/2
。示例输入,例如a == 1, b == 1
return (a-(a|b)+b)/2+(a|b)/2;
@MatthewD,现在已删除的解决方案在约5/6的时间内无法匹配(a+b)/2
。示例输入,例如a == 1, b == -2
unsigned diff;
signed mean;
if (a > b) {
diff = a - b;
mean = b + (diff >> 1);
} else {
diff = b - a;
mean = a + (diff >> 1);
}
答案 1 :(得分:3)
如果(a^b)<=0
您可以使用(a+b)/2
而不必担心溢出。
否则,请尝试(a-(a|b)+b)/2+(a|b)/2
。 -(a|b)
的幅度至少与a
和b
的幅度相同,并且符号相反,因此可以避免溢出。
我从头顶快速做到这一点,所以可能会有一些愚蠢的错误。请注意,此处有没有机器特定的黑客。 所有行为完全由C标准确定,并且它需要有符号值的二进制补码,一补码或符号幅度表示,并指定按位运算符在逐位表示中工作。 不,a|b
的相对大小取决于表示......
修改:如果符号相同,您也可以使用a+(b-a)/2
。请注意,这会偏向a
。您可以撤消它并偏向b
。另一方面,如果我没有弄错的话,我上面的解决方案会偏向零。
另一种尝试:一种标准方法是(a&b)+(a^b)/2
。在两个补码中,无论符号如何,它都有效,但我认为如果a
和b
具有相同的符号,它也可以在补码或符号量级中起作用。小心检查一下?
答案 2 :(得分:3)
a/2 + b/2 + (a%2 + b%2)/2
似乎最简单的一个适用于实现特性的假设(它依赖于C99,它指定/的结果为“截断为0”,而它是依赖于C90的实现)。
它的优点是没有测试(因此没有昂贵的跳转),并且所有的分区/余数都是2,所以编译器可以使用位错误技术。
答案 3 :(得分:1)
只是一些可能有帮助的观察结果:
“最准确”并不一定是整数唯一的。例如。对于1和4,2和3是同样“最准确”的答案。数学上(不是C整数):
(a+b)/2 = a+(b-a)/2 = b+(a-b)/2
让我们试着打破这个:
您准备优化什么?不同的处理器架构可能有不同的最佳解决方案例如,在您的代码中,用AND替换乘法可以提高性能。同样在二进制补码架构中,您可以简单地(a&amp; b&amp; 1)。
我只是要抛出一些代码,不要看得太快,但也许有人可以使用和改进:
int sgeq = ((a<0)==(b<0));
int avg = ((!sgeq)*(a+b)+sgeq*(b-a))/2 + sgeq*a
答案 4 :(得分:1)
对于无符号整数,平均值是(x + y)/ 2的最低值。但是签名整数也是如此。对于总和为奇数的整数,该公式失败,因为它的最低值比其平均值小1。
您可以在第2.5节
中的Hacker's Delight阅读更多内容计算没有溢出的2个有符号整数的平均值的代码是
int t = (a & b) + ((a ^ b) >> 1)
unsigned t_u = (unsigned)t
int avg = t + ( (t_u >> 31 ) & (a ^ b) )
我已使用Z3 SMT solver
检查了它的正确性答案 5 :(得分:-1)
我会这样做,将两者转换为long long(64位有符号整数)加起来,这不会溢出然后将结果除以2:
((long long)a + (long long)b) / 2
如果您想要小数部分,请将其存储为double。
重要的是要注意结果将适合32位整数。
如果您使用的是最高等级的整数,则可以使用:
((double)a + (double)b) / 2
答案 6 :(得分:-2)
这个答案适合任意数量的整数:
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
decimal avg = 0;
for (int i = 0; i < array.Length; i++){
avg = (array[i] - avg) / (i+1) + avg;
}
此测试需要avg == 5.0