Math 用基本算术运算实现等式函数
给定正整数输入Math 用基本算术运算实现等式函数,math,arithmetic-expressions,integer-arithmetic,Math,Arithmetic Expressions,Integer Arithmetic,给定正整数输入x和y,如果x=y或0,是否存在返回1的数学公式?不幸的是,我不得不使用一个只允许我使用以下符号的工具:数字0-9;小数点;括号(和);以及四种基本算术运算+,-,/和* 目前,我依靠的是一个事实,即计算除法为零的工具是零。(我不知道这是一个bug还是一个特性。)因此,我能够使用((x-y)/(y-x))+1。显然,这是丑陋和不公平的,特别是当它是一个bug并且他们在将来的版本中修复它时。所以问题是,如果他们修复了零除法,这意味着你不能再使用任何包含输入变量的除法(您必须检查除数!
x
和y
,如果x
=y
或0
,是否存在返回1
的数学公式?不幸的是,我不得不使用一个只允许我使用以下符号的工具:数字0
-9
;小数点
;括号(
和)
;以及四种基本算术运算+
,-
,/
和*
目前,我依靠的是一个事实,即计算除法为零的工具是零。(我不知道这是一个bug还是一个特性。)因此,我能够使用
((x-y)/(y-x))+1
。显然,这是丑陋和不公平的,特别是当它是一个bug并且他们在将来的版本中修复它时。所以问题是,如果他们修复了零除法,这意味着你不能再使用任何包含输入变量的除法(您必须检查除数!=0,实现该检查将解决原始的x-y==0问题!);因此,除法根本不能使用
因此,只有
+
,-
,*
和关联运算符()
可以使用。不难看出,仅使用这些运算符无法实现所需的行为。利用C中的整数除法向0截断,下面的运算效果良好。没有乘法溢出。为所有“正整数输入x
和y
”定义良好
(x/y)*(y/x)
#include <stdio.h>
#include <limits.h>
void etest(unsigned x, unsigned y) {
unsigned ref = x == y;
unsigned z = (x/y) * (y/x);
if (ref != z) {
printf("%u %u %u %u\n", x,y,z,ref);
}
}
void etests(void) {
unsigned list[] = { 1,2,3,4,5,6,7,8,9,10,100,1000, UINT_MAX/2 , UINT_MAX - 1, UINT_MAX };
for (unsigned x = 0; x < sizeof list/sizeof list[0]; x++) {
for (unsigned y = 0; y < sizeof list/sizeof list[0]; y++) {
etest(list[x], list[y]);
}
}
}
int main(void) {
etests();
printf("Done\n");
return 0;
}
如果除法是截断的,且数字不太大,则:
((x - y) ^ 2 + 2) / ((x - y) ^ 2 + 1) - 1
如果x=y,则除法的值为2,否则将截断为1
(这里x^2是x*x的缩写。)
如果(x-y)^2溢出,此操作将失败。在这种情况下,您需要单独检查x/k=y/k
和x%k=y%k
,其中(k-1)*(k-1)
不会溢出(如果k是ceil(sqrt(INT_MAX))
。x%k
可以计算为x-k*(x/k)
和A&B
只是A*B
这将适用于范围[-k*k,k*k]
内的任何x和y
一个稍微不正确的计算,使用了大量中间值,它假设x-y
不会溢出(或者至少溢出不会产生假0)
或全部写出来:
((((x-y)/K)*((x-y)/K)+2)/(((x-y)/K)*((x-y)/K)+1)-1)*
((((x-y)-K*((x-y)/K))*((x-y)-K*((x-y)/K))+2)/
(((x-y)-K*((x-y)/K))*((x-y)-K*((x-y)/K))+1)-1)
(对于有符号31位整数,使用K=46341;对于无符号32位整数,使用65536。)
使用@chux的测试线束进行检查,添加0案例:和负值
在一个平台上,整数减法可能会产生除2s补码外的其他结果,可以使用类似的技术,但将数字分成三部分而不是两部分。不。数学不是这样工作的(你的表达式,
((x-y)/(y-x))+如果x
和y
相等,1
实际上应该抛出一个被零除的错误。@Draco18s不是在数学中,而是在OP的工具中;参见OP的最后一段!@MarcusMüller我没有注意到。如果是这样,那么((x-y)/(y-x))+1
将是唯一的方法。嗯,你使用的是什么很棒的工具?@MarcusMüller输入是整数。使用DP是可选的。“你必须检查除数!=0,”-->已知x,y
为正。“不难看出,仅使用这些运算符无法实现所需的行为。”显然,这个命题并不像看上去的那么明显:——)@rici我假设所涉及的数学几乎在有理数域上“正常”运行,除了零除法例外,而不是整数算术。事实上,OP从未说过这样的话,所以我坚持这个假设,直到OP解释他的意思:)非常好。我不知道我的解决方案的复杂性是否合理,因为即使x或y为0,它也能工作。@rici谢谢,听起来不像x,y==0
很重要。使用((x+1)/(y+1))*((y+1)/(x+1))
在这里很容易调整,但现在不必使用UINT_MAX
。这只会将问题从0转移到-1:)使用差的平方对任何值都有效,只要您可以处理溢出。次要细节:符号delta=x-y代码>可能导致签名溢出46341
取决于int
是否为32位。@chux:Nits noted.)我确实使用cpp生成了正确的表达式,但这很可笑,所以我把它作为练习。
int delta = x - y;
int delta_hi = delta / K;
int delta_lo = delta - K * delta_hi;
int equal_hi = (delta_hi * delta_hi + 2) / (delta_hi * delta_hi + 1) - 1;
int equal_lo = (delta_lo * delta_lo + 2) / (delta_lo * delta_lo + 1) - 1;
int equals = equal_hi * equal_lo;
((((x-y)/K)*((x-y)/K)+2)/(((x-y)/K)*((x-y)/K)+1)-1)*
((((x-y)-K*((x-y)/K))*((x-y)-K*((x-y)/K))+2)/
(((x-y)-K*((x-y)/K))*((x-y)-K*((x-y)/K))+1)-1)