web-dev-qa-db-fra.com

Comment trouver le nombre de valeurs dans une plage donnée divisibles par une valeur donnée?

J'ai trois nombres x, y, z.

Pour une plage entre les nombres x et y. 

Comment puis-je trouver le nombre total dont% avec z est 0 c'est-à-dire combien de nombres entre x et y sont divisibles par z?

19
Dexter

Cela peut être fait dans O (1): trouvez le premier, trouvez le dernier, trouvez le nombre de tous les autres.

Je suppose que la gamme est inclusive. Si vos gammes sont exclusives, ajustez les limites d'une unité:

  • trouve la première valeur après x qui est divisible par z. Vous pouvez rejeter x:

    x_mod = x % z;
    
    if(x_mod != 0)
      x += (z - x_mod);
    
  • trouve la dernière valeur avant y qui est divisible par y. Vous pouvez rejeter y:

    y -= y % z;
    
  • trouver la taille de cette gamme:

    if(x > y)
      return 0;
    else
      return (y - x) / z + 1;
    

Si des fonctions mathématiques floor et ceil sont disponibles, les deux premières parties peuvent être écrites plus facilement. La dernière partie peut également être compressée à l'aide de fonctions mathématiques:

 x = ceil  (x, z);
 y = floor (y, z);
 return max((y - x) / z + 1, 0);

s'il est garanti que l'entrée est une plage valide (x >= y), le dernier test ou max n'est pas nécessaire:

 x = ceil  (x, z);
 y = floor (y, z);
 return (y - x) / z + 1;
25
John Dvorak

( 2017, réponse réécrite grâce aux commentaires )
Le nombre de multiples de z dans un nombre n est simplement n / z

/ étant la division entière, ce qui signifie que les décimales pouvant résulter de la division sont simplement ignorées (par exemple 17/5 => 3 et non 3.4).

Maintenant, dans une plage de x à y , combien de multiples de z existe-t-il?

Voyons combien de multiples m nous avons jusqu’à y

0----------------------------------x------------------------y
-m---m---m---m---m---m---m---m---m---m---m---m---m---m---m---

Vous voyez où je vais ... pour obtenir le nombre de multiples dans la plage [ x, y ], obtenez le nombre de multiples de y puis soustrayez le nombre de multiples avant x , (x-1) / z

Solution: ( y / z ) - (( x - 1 ) / z )


Par programme, vous pouvez créer une fonction numberOfMultiples

function numberOfMultiples(n, z) {
   return n / z;
}

pour obtenir le nombre de multiples dans une plage [x, y]

numberOfMultiples(y) - numberOfMultiples(x-1)

La fonction est O(1) , il n’est pas nécessaire de faire une boucle pour obtenir le nombre de multiples.

Exemples de résultats que vous devriez trouver

  • [30, 90] ÷ 13 => 4
  • [1, 1000] ÷ 6 => 166
  • [100, 1000000] ÷ 7 => 142843
  • [777, 777777777] ÷ 7 => 111111001

Pour le premier exemple, 90 / 13 = 6, (30-1) / 13 = 2 et 6-2 = 4

---26---39---52---65---78---91--
     ^                      ^
     30<---(4 multiples)-->90
14
Ring Ø

J'ai aussi rencontré ceci sur la codilité. Il m'a fallu beaucoup plus de temps que je ne voudrais l'admettre pour trouver une bonne solution. J'ai donc pensé partager ce que je pense est une solution élégante!

Approche directe 1/2:

Solution temps O (N) avec une boucle et un compteur, irréaliste lorsque N = 2 milliards.

Superbe approche 3:

Nous voulons le nombre de chiffres dans une plage divisible par K.

Cas simple: supposons la plage [0 .. n * K], N = n * K

N/K représente le nombre de chiffres dans [0, N) qui sont divisibles par K, étant donné que N% K = 0 (ou N est divisible par K)

ex. N = 9, K = 3, Num digits = | {0 3 6} | = 3 = 9/3

De même,

N/K + 1 représente le nombre de chiffres dans [0, N] divisible par K

ex. N = 9, K = 3, Num digits = | {0 3 6 9} | = 4 = 9/3 + 1

Je pense vraiment que comprendre le fait ci-dessus est la partie la plus délicate de cette question, je ne peux pas expliquer exactement pourquoi cela fonctionne . Le reste se résume à prefix sums et traitant des cas particuliers.


Maintenant, nous n’avons pas toujours une plage commençant par 0 et nous ne pouvons pas supposer que les deux bornes seront divisibles par K . Mais attendez! Nous pouvons résoudre ce problème en calculant nos propres limites supérieure et inférieure de Nice et en utilisant de la magie de soustraction :)

  1. Commencez par trouver les valeurs supérieure et inférieure les plus proches dans la plage [A, B] divisibles par K.

    • Limite supérieure (plus facile): ex. B = 10, K = 3, new_B = 9 ... le motif est B - B% K
    • Limite inférieure: ex. A = 10, K = 3, new_A = 12 ... essayez un peu plus et vous verrez que le motif est A - A% K + K
  2. Calculez ensuite les éléments suivants en utilisant la technique ci-dessus:

    • Détermine le nombre total de chiffres X entre [0, B] qui sont divisibles par K
    • Détermine le nombre total de chiffres Y entre [0, A) qui sont divisibles par K
  3. Calculez le nombre de chiffres entre [A, B] divisibles par K en temps constant par l'expression X - Y

Site Web: https://codility.com/demo/take-sample-test/count_div/

class CountDiv {
    public int solution(int A, int B, int K) {
        int firstDivisible = A%K == 0 ? A : A + (K - A%K);
        int lastDivisible = B%K == 0 ? B : B - B%K; //B/K behaves this way by default.
        return (lastDivisible - firstDivisible)/K + 1;
    }
}

C'est la première fois que j'explique une telle approche. Les commentaires sont très appréciés :)

12
Lori-Ann

C'est l'une des questions de la leçon 3 sur la codilité. Pour cette question, l'entrée est garantie d'être dans une plage valide. Je lui ai répondu en utilisant Javascript:

function solution(x, y, z) {
    var totalDivisibles =  Math.floor(y / z),
        excludeDivisibles = Math.floor((x - 1) / z),
        divisiblesInArray = totalDivisibles - excludeDivisibles;
   return divisiblesInArray;
}

https://codility.com/demo/results/demoQX3MJC-8AP/

(Je voulais en fait poser des questions sur certains des autres commentaires de cette page mais je n'ai pas encore assez de points de repères).

10
tercerojista

Divisez y-x par z en arrondissant le bas. Ajoutez-en un si y%z < x%z ou si x%z == 0.

Aucune preuve mathématique, à moins que quelqu'un ne veuille en fournir une, mais des cas de test en Perl:

#!Perl
use strict;
use warnings;
use Test::More;

sub multiples_in_range {
  my ($x, $y, $z) = @_;
  return 0 if $x > $y;
  my $ret = int( ($y - $x) / $z);
  $ret++ if $y%$z < $x%$z or $x%$z == 0;
  return $ret;
}   

for my $z (2 .. 10) {
  for my $x (0 .. 2*$z) {
    for my $y (0 .. 4*$z) {
      is multiples_in_range($x, $y, $z),
         scalar(grep { $_ % $z == 0 } $x..$y),
         "[$x..$y] mod $z";
    }
  }
}

done_testing;

Sortie:

$ prove divrange.pl 
divrange.pl .. ok      
All tests successful.
Files=1, Tests=3405,  0 wallclock secs ( 0.20 usr  0.02 sys +  0.26 cusr  0.01 csys =  0.49 CPU)
Result: PASS
5
hobbs

Soit [A;B] un intervalle de nombres entiers positifs, y comprisAetBtel que 0 <= A <= B,Ksoit le diviseur.

Il est facile de voir qu'il existe des facteurs N(A) = ⌊A / K⌋ = floor(A / K) deKdans l'intervalle [0;A]:

         1K       2K       3K       4K       5K
●········x········x··●·····x········x········x···>
0                    A

De même, il existe N(B) = ⌊B / K⌋ = floor(B / K) facteurs deKdans l'intervalle [0;B]:

         1K       2K       3K       4K       5K
●········x········x········x········x···●····x···>
0                                       B

Alors N = N(B) - N(A) est égal au nombre deK(nombre d'entiers divisibles parK) compris dans la plage (A;B]. Le pointA n'est pas inclus, car soustrait N(A) inclut ce point. Par conséquent, le résultat doit être incrémenté de un, si A mod K est égal à zéro:

N := N(B) - N(A)

if (A mod K = 0)
  N := N + 1

Implémentation dans PHP

function solution($A, $B, $K) {
  if ($K < 1)
    return 0;

  $c = floor($B / $K) - floor($A / $K);

  if ($A % $K == 0)
    $c++;

  return (int)$c;
}

En PHP, l’effet de la fonction floor peut être obtenu en convertissant le type entier:

$c = (int)($B / $K) - (int)($A / $K);

qui, je pense, est plus rapide.

3
Ruslan Osmanov

Voici ma solution courte et simple en C++ qui a obtenu 100/100 sur la codilité. :) S'exécute dans le temps O(1). J'espère que ce n'est pas difficile à comprendre.

int solution(int A, int B, int K) {
    // write your code in C++11
    int cnt=0;
    if( A%K==0 or B%K==0)
        cnt++;
    if(A>=K)
        cnt+= (B - A)/K;
    else
        cnt+=B/K;

    return cnt;
}
3
piyush121
(floor)(high/d) - (floor)(low/d) - (high%d==0)

Explication:

Il y a un nombre/d divisible par d de 0.0 à a. (d! = 0)

Par conséquent (plancher) (haut/d) - (plancher) (bas/d) donnera des nombres divisibles dans la plage (bas, haut) (notez que bas est exclu et haut est inclus dans cette plage)

Maintenant, pour supprimer haut de la plage, il suffit de soustraire (haut% d == 0)

Fonctionne pour les entiers, les flottants ou autre (Utilisez la fonction fmodf pour les flottants)

2
cegprakash

La complexité temporelle de la solution sera linéaire.

Extrait de code :

int countDiv(int a, int b, int m)
{
    int mod = (min(a, b)%m==0);
    int cnt  = abs(floor(b/m) - floor(a/m))  +  mod;
    return cnt;
}
0
rashedcs

Ne cherchera pas une solution o(1), ce congé pour une personne plus intelligente :) Juste, croyez-vous que c'est un scénario d'utilisation parfait pour la programmation de fonctions. Simple et simple. 

 > x,y,z=1,1000,6
 => [1, 1000, 6] 
 > (x..y).select {|n| n%z==0}.size
 => 166 

EDIT: après avoir lu la solution de O(1) d’autre. Je me sens honteux. La programmation rend les gens paresseux pour penser ... 

0
pierrotlefou

Division (a/b = c) par définition - prenant un ensemble de taille a et formant des groupes de taille b. Le nombre de groupes de cette taille pouvant être formés, c, est le quotient de a et b. - n’est rien d’autre que le nombre d’entiers compris dans la plage/l’intervalle] 0..a] (non compris zéro, mais y compris a) qui sont divisibles par b.

donc par définition: 
Y/Z - nombre d’entiers dans] 0..Y] qui sont divisibles par Z
et
X/Z - nombre d’entiers dans] 0..X] qui sont divisibles par Z

ainsi:
resultat = [Y/Z] - [X/Z] + x (où x = 1 si et seulement si X est divisible par Y sinon 0 - en supposant que la plage donnée [X..Y] inclut X)

exemple :
pour (6, 12, 2) nous avons 12/2 - 6/2 + 1 (sous forme de 6% 2 == 0) = 6 - 3 + 1 = 4 // {6, 8, 10, 12}
pour (5, 12, 2) nous avons 12/2 - 5/2 + 0 (sous forme de 5% 2! = 0) = 6 - 2 + 0 = 4 // {6, 8, 10, 12}

0
Legna