Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么更喜欢start+;(结束-开始)/2在计算数组中间时是否结束(开始和结束)/2?_C_Algorithm - Fatal编程技术网

C 为什么更喜欢start+;(结束-开始)/2在计算数组中间时是否结束(开始和结束)/2?

C 为什么更喜欢start+;(结束-开始)/2在计算数组中间时是否结束(开始和结束)/2?,c,algorithm,C,Algorithm,我见过程序员使用这个公式 mid = start + (end - start) / 2 而不是使用更简单的公式 mid = (start + end) / 2 用于在数组或列表中查找中间元素 他们为什么使用前者?有三个原因 首先,start+(end-start)/2即使在使用指针时也能工作,只要end-start不溢出1即可 其次,如果start和end是大正数,start+(end-start)/2不会溢出。对于有符号操作数,溢出未定义: int start = 0x7ffffffe,

我见过程序员使用这个公式

mid = start + (end - start) / 2
而不是使用更简单的公式

mid = (start + end) / 2
用于在数组或列表中查找中间元素


他们为什么使用前者?

有三个原因

首先,
start+(end-start)/2即使在使用指针时也能工作,只要
end-start
不溢出1即可

其次,如果
start
end
是大正数,
start+(end-start)/2
不会溢出。对于有符号操作数,溢出未定义:

int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2;         // overflow... undefined
(请注意,
end-start
可能会溢出,但仅当
start<0
end<0
时才会溢出)

或者使用无符号算术,定义了溢出,但给出了错误的答案。但是,对于无符号操作数,
start+(end-start)/2
只要
end>=start
就永远不会溢出

unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2;         // mid = 0x7ffffffe
最后,您通常希望向
start
元素取整

int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2;         // -1, surprise!
脚注
1根据C标准,如果指针减法的结果不能表示为
ptrdiff\u t
,则该行为未定义。然而,在实践中,这需要使用至少一半的整个地址空间来分配
char
数组。

我们可以举一个简单的例子来说明这一事实。假设在某个大型数组中,我们试图找到范围
[1000,INT\u MAX]
的中点。现在,
INT\u MAX
INT
数据类型可以存储的最大值。即使将
1
添加到此值,最终值也将变为负值

另外,
start=1000
end=INT\u MAX

使用以下公式:
(开始+结束)/2

中点将是

(1000+INT\u MAX)/2
=
-(INT\u MAX+999)/2
,它是负的如果我们尝试使用此值进行索引,可能会导致分段错误

但是,使用公式,
(start+(end start)/2)
,我们得到:

(1000+(INT\u MAX-1000)/2)
=
(1000+INT\u MAX/2-500)
=
(INT\u MAX/2+500)
不会溢出


为了补充其他人已经说过的话,第一条对那些不太懂数学的人更清楚地解释了它的含义:

mid = start + (end - start) / 2
全文如下:

中间等于起点加一半长度

鉴于:

mid = (start + end) / 2
全文如下:

中点等于起点加终点的一半

这似乎不像第一条那么清楚,至少在这样表达的时候

正如科斯指出的,它也可以是:

mid等于开始和结束的平均值


这更清楚,但至少在我看来,还不如第一个清楚。

start+(end-start)/2可以避免可能的溢出,例如start=2^20和end=2^30

猜测:
(start+end)
可能溢出,而
(end-start)
不能。因为当
start
end
是指针时,后者不起作用。
start+(end-start)/2
也有语义含义:
(end-start)
是长度,所以说:
start+长度的一半
@LưuVĩnhPhúc:这个问题不是有最好的答案和最多的选票吗?如果是这样的话,其他问题可能应该作为这个问题的一个重复来结束。帖子的年龄无关。
(end-start)
签名int
案例中的
结果溢出时未定义。你能证明
end-start
不会溢出吗?AFAIK如果您使用负数
start
则可能使其溢出。当然,大多数情况下,当你计算平均值时,你知道值是
=0
..@Bakuriu:不可能证明某些不正确的东西。这在C中特别有意义,因为指针减法(根据标准)被设计打破了。由于对象大小是无符号的,而指针差异是有符号的,因此允许实现创建大到未定义
end-start
的数组。因此
end-start
“即使使用指针也能工作”,前提是您还以某种方式将数组的大小保持在
PTRDIFF_MAX
以下。公平地说,这对大多数体系结构来说并不是什么障碍,因为它只有内存映射的一半大小。@Bakuriu:顺便说一下,如果你认为我遗漏了什么,或者有什么不清楚的地方,在帖子上有一个“编辑”按钮,你可以用它来建议修改(或者自己修改)。我只是一个人,这篇文章已经被两千多对眼球看到了。“你应该澄清……”这类评论真的让我很恼火。如果你在
INT\u MAX
中加1,结果将不是否定的,而是未定义的。@celtschk理论上是的。实际上,从
INT\u MAX
-INT\u MAX
,它将在很多时间内运行。不过,依赖它是一种坏习惯。我明白你的意思,但这确实是一种延伸。如果你看到“e-s”而想到“length”,那么你几乎肯定会看到“(s+e)/2”而想到“average”或“mid”。@djechlin程序员的数学很差。他们正忙着做他们的工作。他们没有时间上数学课。
mid = (start + end) / 2