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)