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 central d'un tableau ou d'une liste.
Pourquoi utilisent-ils la première ?
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 central d'un tableau ou d'une liste.
Pourquoi utilisent-ils la première ?
Il y a trois raisons.
Tout d'abord, start + (end - start) / 2
fonctionne même si vous utilisez des pointeurs, du moment que end - start
ne déborde pas 1 .
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 des opérandes signés, le dépassement est indé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 seulement si start < 0
ou end < 0
.)
Ou avec l'arithmétique non signée, le dépassement 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 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 souhaitez souvent arrondir vers le start
élément.
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 d'un pointeur n'est pas représentable en tant que ptrdiff_t
alors le comportement est indéfini. Cependant, en pratique, cela nécessite l'allocation d'un objet char
utilisant au moins la moitié de l'espace d'adressage.
Pouvez-vous prouver que end-start
ne débordera pas ? AFAIK si vous prenez une valeur négative 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
...
Nous pouvons prendre un exemple simple pour démontrer ce fait. Supposons que dans un certain grand site nous essayons de trouver le point médian de l'intervalle [1000, INT_MAX]
. Maintenant, INT_MAX
est la plus grande valeur que l int
que le type de données peut stocker. Même si 1
est ajouté à cela, la valeur finale deviendra négative.
Aussi, start = 1000
et 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 lieu à un défaut de segmentation si on essaie d'indexer en utilisant cette valeur.
Mais, en utilisant la formule, (start + (end-start)/2)
on obtient :
(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, la première explique sa signification plus clairement aux personnes ayant moins d'esprit mathématique :
mid = start + (end - start) / 2
se lit comme :
Le milieu est égal au début plus la moitié de la longueur.
alors que :
mid = (start + end) / 2
se lit comme :
le milieu est égal à la moitié du début plus la fin
Ce qui ne semble pas aussi clair que le premier, du moins lorsqu'il est exprimé de cette manière.
comme l'a souligné Kos, il peut aussi être lu :
le milieu 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 vois ce que vous voulez dire, mais c'est vraiment tiré par les cheveux. Si vous voyez "e - s" et pensez "longueur", vous voyez presque certainement "(s+e)/2" et pensez "moyen" ou "médian".
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
54 votes
Supposition hasardeuse :
(start + end)
pourrait déborder, tandis que(end - start)
ne peut pas.32 votes
Car ce dernier ne fonctionne pas lorsque
start
etend
sont des pointeurs.25 votes
Extra, Extra - Lisez tout sur le sujet : Presque toutes les recherches binaires et les triages par fusion sont cassés.
21 votes
start + (end - start) / 2
est également porteur de sens sémantique :(end - start)
est la longueur, donc ça dit :start + half the length
.0 votes
@JonathanLeffler pourquoi quelqu'un utiliserait un signé Un nombre entier comme index de tableau me dépasse. J'utilise normalement
size_t
pour eux ces jours-ci si vous trouvez un cas où c'est incorrect, dites-le, mais je pense que cela fonctionne pour les tableaux de caractères, les tableaux de pointeurs et les tableaux de structs.0 votes
@mirabilos malheureusement, ce n'est tout simplement pas le type d'un indice de tableau. les indices de tableau en c natif sont signés et de type ptrdiff_t
0 votes
@SteveCox hrm, ok. Mais
size_t
est généralement la même chose queptrdiff_t
Il s'agit simplement d'un code non signé (ce qui le rend plus facile à manipuler) et plus portable (disponible sur un plus grand nombre de plateformes, notamment les plus anciennes).0 votes
@mirabilos vous pouvez comme, size_t mieux, mais
a[-2]
est une expression C valide indexant dans un tableau. Ce n'est pas vraiment une question de préférence. L'indexation des tableaux C est signée, et un type non signé n'est pas suffisant pour cette tâche.0 votes
@SteveCox
a[-2]
n'est pas un index valide dans un tableau défini comme suita[]
seulement sichar b[]; char *a = b + 2;
ou quelque chose comme ça, oui. Mais s'enrouler autour est défini pour les types non signés, donc cela fonctionnerait au moins pour les types char, mais probablement pour les autres aussi0 votes
Quelques autres doublons Recherche binaire utilisant des itérateurs, pourquoi utiliser "(end - begin)/2" ? , Problèmes de débordement lors de l'application de formules mathématiques . En fait, cela devrait être expliqué dans toutes les questions sur la recherche binaire.
2 votes
@LuuVinhPhúc : Cette question n'a-t-elle pas les meilleures réponses et le plus de votes ? Si c'est le cas, les autres questions devraient probablement être fermées en double de celle-ci. L'âge des messages n'est pas pertinent.
0 votes
Peut-on aussi faire mid= (début)/2+(fin)/2 ?