start + (end - start) / 2aussi porte une signification sémantique: (end - start)est la longueur, donc ce dit: start + half the length.
njzk2
2
@ LưuVĩnhPhúc: Cette question n'a-t-elle pas les meilleures réponses et le plus de votes? Si tel est le cas, les autres questions devraient probablement être fermées comme une dupe de celle-ci. L'âge des messages n'a pas d'importance.
Nisse Engström
Réponses:
218
Il y a trois raisons.
Tout d'abord, start + (end - start) / 2fonctionne même si vous utilisez des pointeurs, à condition qu'il end - startne déborde pas de 1 .
int*start =...,*end =...;int*mid = start +(end - start)/2;// works as expectedint*mid =(start + end)/2;// type error, won't compile
Deuxièmement, start + (end - start) / 2ne débordera pas si startet endsont de grands nombres positifs. Avec les opérandes signés, le débordement n'est pas défini:
int start =0x7ffffffe, end =0x7fffffff;int mid = start +(end - start)/2;// works as expectedint mid =(start + end)/2;// overflow... undefined
(Notez que cela end - startpeut déborder, mais seulement si start < 0ou end < 0.)
Ou avec l'arithmétique non signée, le débordement est défini mais vous donne la mauvaise réponse. Cependant, pour les opérandes non signés, start + (end - start) / 2ne débordera jamais tant que end >= start.
unsigned start =0xfffffffeu, end =0xffffffffu;unsigned mid = start +(end - start)/2;// works as expectedunsigned mid =(start + end)/2;// mid = 0x7ffffffe
Enfin, vous souhaitez souvent arrondir vers l' startélément.
int start =-3, end =0;int mid = start +(end - start)/2;// -2, closer to startint mid =(start + end)/2;// -1, surprise!
Notes de bas de page
1 Selon la norme C, si le résultat de la soustraction du pointeur n'est pas représentable comme a ptrdiff_t, alors le comportement est indéfini. Cependant, en pratique, cela nécessite d'allouer un chartableau utilisant au moins la moitié de l'espace d'adressage entier.
Le résultat de (end - start)dans le signed intcas n'est pas défini lorsqu'il déborde.
ensc
Pouvez-vous prouver que cela end-startne débordera pas? AFAIK si vous prenez un négatif, startil devrait être possible de le faire déborder. Bien sûr, la plupart du temps, lorsque vous calculez la moyenne, vous savez que les valeurs sont >= 0...
Bakuriu
12
@Bakuriu: Il est impossible de prouver quelque chose qui n'est pas vrai.
Dietrich Epp
4
C'est d'un intérêt particulier en C, car la soustraction du pointeur (selon la norme) est interrompue par conception. Les implémentations sont autorisées à créer des tableaux si grands qu'ils ne end - startsont pas définis, car les tailles des objets ne sont pas signées tandis que les différences de pointeur sont signées. Donc, end - start"fonctionne même en utilisant des pointeurs", à condition que vous gardiez également la taille du tableau ci-dessous PTRDIFF_MAX. Pour être juste avec la norme, ce n'est pas vraiment un obstacle sur la plupart des architectures car c'est la moitié de la taille de la carte mémoire.
Steve Jessop
3
@Bakuriu: Au fait, il y a un bouton "modifier" sur le message que vous pouvez utiliser pour suggérer des changements (ou les faire vous-même) si vous pensez que j'ai manqué quelque chose ou si quelque chose n'est pas clair. Je ne suis qu'un humain, et ce message a été vu par plus de deux mille paires de globes oculaires. Le genre de commentaire, "Vous devriez clarifier ..." me frotte vraiment dans le mauvais sens.
Dietrich Epp
18
Nous pouvons prendre un exemple simple pour démontrer ce fait. Supposons que dans un certain grand tableau, nous essayons de trouver le milieu de la plage [1000, INT_MAX]. Maintenant, INT_MAXest la plus grande valeur que le inttype de données peut stocker. Même si1 on ajoute à cela, la valeur finale deviendra négative.
Aussi, start = 1000et end = INT_MAX.
En utilisant la formule: (start + end)/2,
le point médian sera
(1000 + INT_MAX)/2= -(INT_MAX+999)/2, qui est négatif et peut donner une erreur de segmentation si nous essayons d'indexer en utilisant cette valeur.
Mais, en utilisant la formule (start + (end-start)/2), nous obtenons:
(1000 + (INT_MAX-1000)/2)= (1000 + INT_MAX/2 - 500)= (INT_MAX/2 + 500)qui ne débordera pas .
Si vous ajoutez 1 à INT_MAX, le résultat ne sera pas négatif, mais indéfini.
celtschk
@celtschk Théoriquement, oui. Pratiquement, cela se terminera la plupart du temps allant de INT_MAXà -INT_MAX. C'est une mauvaise habitude de se fier à cela.
Mât
17
Pour ajouter à ce que d'autres ont déjà dit, le premier explique sa signification plus clairement à ceux qui sont moins mathématiciens:
mid = start +(end - start)/2
se lit comme suit:
mi est égal au début plus la moitié de la longueur.
tandis que:
mid =(start + end)/2
se lit comme suit:
milieu est égal à la moitié du début et de la fin
Ce qui ne semble pas aussi clair que le premier, du moins exprimé ainsi.
comme Kos l'a souligné, il peut également lire:
mid est égal à la moyenne du début et de la fin
Ce qui est plus clair mais toujours pas, du moins à mon avis, aussi clair que le premier.
Je comprends votre point de vue, mais c'est vraiment exagéré. Si vous voyez «e - s» et pensez «longueur», alors vous voyez presque sûrement «(s + e) / 2» et pensez «moyen» ou «moyen».
djechlin
2
@djechlin Les programmeurs sont pauvres en mathématiques. Ils sont occupés à faire leur travail. Ils n'ont pas le temps d'assister aux cours de mathématiques.
Little Alien
1
start + (end-start) / 2 peut éviter un éventuel dépassement, par exemple start = 2 ^ 20 et end = 2 ^ 30
(start + end)
pourrait déborder, alors que(end - start)
ne le peut pas.start
etend
sont pointeur.start + (end - start) / 2
aussi porte une signification sémantique:(end - start)
est la longueur, donc ce dit:start + half the length
.Réponses:
Il y a trois raisons.
Tout d'abord,
start + (end - start) / 2
fonctionne même si vous utilisez des pointeurs, à condition qu'ilend - start
ne déborde pas de 1 .Deuxièmement,
start + (end - start) / 2
ne débordera pas sistart
etend
sont de grands nombres positifs. Avec les opérandes signés, le débordement n'est pas défini:(Notez que cela
end - start
peut déborder, mais seulement sistart < 0
ouend < 0
.)Ou avec l'arithmétique non signée, le débordement est défini mais vous donne la mauvaise réponse. Cependant, pour les opérandes non signés,
start + (end - start) / 2
ne débordera jamais tant queend >= start
.Enfin, vous souhaitez souvent arrondir vers l'
start
élément.Notes de bas de page
1 Selon la norme C, si le résultat de la soustraction du pointeur n'est pas représentable comme a
ptrdiff_t
, alors le comportement est indéfini. Cependant, en pratique, cela nécessite d'allouer unchar
tableau utilisant au moins la moitié de l'espace d'adressage entier.la source
(end - start)
dans lesigned int
cas n'est pas défini lorsqu'il déborde.end-start
ne débordera pas? AFAIK si vous prenez un négatif,start
il devrait être possible de le faire déborder. Bien sûr, la plupart du temps, lorsque vous calculez la moyenne, vous savez que les valeurs sont>= 0
...end - start
sont pas définis, car les tailles des objets ne sont pas signées tandis que les différences de pointeur sont signées. Donc,end - start
"fonctionne même en utilisant des pointeurs", à condition que vous gardiez également la taille du tableau ci-dessousPTRDIFF_MAX
. Pour être juste avec la norme, ce n'est pas vraiment un obstacle sur la plupart des architectures car c'est la moitié de la taille de la carte mémoire.Nous pouvons prendre un exemple simple pour démontrer ce fait. Supposons que dans un certain grand tableau, nous essayons de trouver le milieu de la plage
[1000, INT_MAX]
. Maintenant,INT_MAX
est la plus grande valeur que leint
type de données peut stocker. Même si1
on ajoute à cela, la valeur finale deviendra négative.Aussi,
start = 1000
etend = INT_MAX
.En utilisant la formule:
(start + end)/2
,le point médian sera
Mais, en utilisant la formule
(start + (end-start)/2)
, nous obtenons:la source
INT_MAX
, le résultat ne sera pas négatif, mais indéfini.INT_MAX
à-INT_MAX
. C'est une mauvaise habitude de se fier à cela.Pour ajouter à ce que d'autres ont déjà dit, le premier explique sa signification plus clairement à ceux qui sont moins mathématiciens:
se lit comme suit:
tandis que:
se lit comme suit:
Ce qui ne semble pas aussi clair que le premier, du moins exprimé ainsi.
comme Kos l'a souligné, il peut également lire:
Ce qui est plus clair mais toujours pas, du moins à mon avis, aussi clair que le premier.
la source
start + (end-start) / 2 peut éviter un éventuel dépassement, par exemple start = 2 ^ 20 et end = 2 ^ 30
la source