J'ai vu des programmeurs utiliser la formule
mid = start + (end - start) / 2
au lieu d'utiliser la formule plus simple
mid = (start + end) / 2
pour trouver l'élément du milieu dans le tableau ou la liste.
Pourquoi utilisent-ils l'ancien?
Il y a trois raisons.
Tout d'abord, start + (end - start) / 2
fonctionne même si vous utilisez des pointeurs, tant que end - start
Ne déborde pas1.
int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2; // type error, won't compile
Deuxièmement, start + (end - start) / 2
ne débordera pas si start
et end
sont 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 expected
int mid = (start + end) / 2; // overflow... undefined
(Notez que end - start
Peut déborder, mais uniquement si start < 0
Ou end < 0
.)
Ou avec une 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 aussi longtemps que end >= start
.
unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2; // mid = 0x7ffffffe
Enfin, vous voudrez souvent arrondir vers l’élément start
.
int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2; // -1, surprise!
1 Selon la norme C, si le résultat de la soustraction de pointeur n'est pas représentable sous la forme d'un ptrdiff_t
, Le comportement est indéfini. Cependant, dans la pratique, cela nécessite d'allouer un tableau char
en utilisant au moins la moitié de l'espace d'adressage total.
Nous pouvons prendre un exemple simple pour démontrer ce fait. Supposons que dans un certain tableau large, nous essayons de trouver le point médian de la plage [1000, INT_MAX]
. Maintenant, INT_MAX
Est la plus grande valeur que le type de données int
puisse stocker. Même si 1
Est ajouté à cela, la valeur finale deviendra négative.
En outre, start = 1000
Et end = INT_MAX
.
En utilisant la formule: (start + end)/2
,
le milieu sera
(1000 + INT_MAX)/2
=-(INT_MAX+999)/2
, qui est négatif et peut donner l'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.
Pour ajouter à ce que d’autres ont déjà dit, le premier explique plus clairement sa signification à ceux qui ont un esprit mathématique:
mid = start + (end - start) / 2
se lit comme:
milieu égal à départ plus la moitié de la longueur.
tandis que:
mid = (start + end) / 2
se lit comme:
mi égale à la moitié du début plus la fin
Ce qui ne semble pas aussi clair que le premier, du moins lorsqu'il est exprimé comme ça.
comme l'a souligné Kos, on peut également lire:
mi est égal à la moyenne de début et de fin
Ce qui est plus clair mais toujours pas, du moins à mon avis, aussi clair que le premier.
start + (end-start)/2 peut éviter un débordement possible, par exemple start = 2 ^ 20 et end = 2 ^ 30