5

Given range x, y. I need to count all numbers in between and are divisible by n.

I know the simple way to do this is by looping over the whole range

for(int i=x;i<=y;i++) if(i%n ==0) counter++; 

The counter holds the answer.

But this works so slow for big ranges. for example x=0 , and y=3,000,000,000.

I'm sure that there is some relation that I can use to reduce the number of iteration and optimizing this code for speed. I searched but i could not find it out. Can any one help me with that please.

Thanks so much.

Coder Stacker
  • 53
  • 1
  • 3

10 Answers10

7

This works: (e+1 - s) / d + (e%d < (s+d-1)%d). (It uses C semantics and integer arithmetic and assumes the start is non-negative. s is the start value, e is the end value [inclusive], and d is the divisor.)

Updated: A better solution is e/d - (s-1)/d. This was inspired by User448810. That requires s be positive; handling zero or negative s (or e) requires adjusting for the truncation toward zero (we would want toward negative infinity for this problem).

Update for negative values: The following works for any values of s and e in the range of their types, provided s <= e and 0 < d:

e = e <  0 ? (e+1)/d - 1 : e/d;
s = s <= 0 ? s/d - 1 : (s-1)/d;
return e-s;

Essentially, the first two statements are equivalent to e = e/d and s = (s-1)/d with the division modified to round toward -infinity instead of toward zero (so that -1/10 produces -1 rather than 0).

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Could you please explain it more. How did you reach there? – Coder Stacker Aug 04 '12 at 01:40
  • This is basically the same as the solution from @Peter except that this one properly takes into account the difference that the choice of end points makes. – Chaser324 Aug 04 '12 at 01:42
  • @EricPostpischil , It's not a home work. It was an interview question and I also found it onlinel – Coder Stacker Aug 04 '12 at 01:53
  • 2
    As you count through integers, the remainders modulo the divisor, d, step through the residues 0, 1, 2,... d-3, d-2, d-1, and then cycle, starting again at 0. From s to e, there is some whole number of these cycles (possibly zero). The number of whole cycles is `(e+1 - s)/d`, and each whole cycle contains exactly one number with a residue of zero (which means the number is a multiple of d). So we know there are that many multiples from the whole cycles, and we only need to figure out whether there are zero or one in the remaining partial cycle. [Continued in next comment.] – Eric Postpischil Aug 04 '12 at 01:57
  • We can suppose we have taken out the whole cycles and are considering just a partial cycle from s to e. If, in the course of going from s to e, we “go around” the residues, from d-1 to 0, then there is a multiple in the partial cycle. Otherwise, there is not. At first, we might just consider this: Is the residue of e (`e%d`) less than the residue of s? If so, we went around, since that is how you get from high residues to low residues. However, we must also consider the case where s is zero, in which case we are starting at a multiple and do not need to go around. [Continued.] – Eric Postpischil Aug 04 '12 at 02:01
  • To deal with that, we compare `e%d` to `(s+d-1)%d`. The latter is equivalent to `(s-1)%d` except that it adds d to ensure the result is positive, due to the annoying nature of the C’s remainder operator. You can examine the expression `e%d < (s+d-1)%d` to see that it is 1 if stepping from the residue of s to the residue of e starts at or goes through a multiple of d and 0 otherwise. – Eric Postpischil Aug 04 '12 at 02:07
2
(floor)(high/d) - (floor)(low/d) - (high%d==0)

Explanation:

There are a/d numbers divisible by d from 0 to a. (d!=0)

Therefore (floor)(high/d) - (floor)(low/d) will give numbers divisible in the range (low,high] (Note that low is excluded and high is included in this range)

Now to remove high from the range just subtract (high%d==0)

Use fmodf for floats.

cegprakash
  • 2,937
  • 33
  • 60
0

Only this implementation works for me (A, B in [0..2kkk]; K in [1..2kkk]):

function solution(A, B, K) {
    if(B < K) return A === 0 ? 1 : 0;
    if(A === B) return A % K === 0 ? 1 : 0;

    var pre = A === 0 ? 2 : 1;
    var min = A < K ? K : A + A % K;
    var max = B - B % K;

    return (max - min) / K + pre;
}
Roman Malieiev
  • 205
  • 4
  • 10
-1

I'm pretty sure you can do this:

public static int numDivisible(int low, int high, int test) {
    return (high-low)/test;
}

There you go. A constant-time solution. Since you don't need to know exactly which numbers are divisible, you don't need to bother iterating through all of them.

EDIT: Change this to the following, as per @Chaser324.

public static float numDivisible(float low, float high, float test) {
    return Math.ceil((high-low)/test);
}

EDIT: A small typo ie., changed text to test

Sri Harsha Chilakapati
  • 11,744
  • 6
  • 50
  • 91
Peter
  • 1,349
  • 11
  • 21
  • 4
    Not quite, try range(3-10) and test = 3 and you will get 2 but he answer is 3. You need to somehow determine the cases to add 1 – Nate Aug 04 '12 at 00:57
  • For (6, 10, 3), this returns one ((10-6)/3 produces 1) but clearly the interval contains two multiples of three (six and nine). – Eric Postpischil Aug 04 '12 at 00:58
  • 2
    This solution seems like it could work and be by far the fastest with a little modification. Would a cast to floats and a ceil() call to round up possibly work? I haven't run through enough cases to see if that would always work. – Chaser324 Aug 04 '12 at 01:00
  • 2
    The new formula, ceil((high-low)/text), produces zero for (4, 4, 2), even though four is clearly divisible by two. – Eric Postpischil Aug 04 '12 at 01:12
  • Can I know please how do you get this formula ? hi-lo/test ? – Coder Stacker Aug 04 '12 at 01:12
  • 1
    @Nate, @Eric Postpischil -- I think this does require a bit of reworking. @Coder Stacker -- Since you don't need to know which numbers are divisible by `test`, I figured you could do it by just workng out how many numbers are divisble by `test` in that range. Obvisouly I wasn't right at first, but the idea is possible. – Peter Aug 04 '12 at 02:02
-1

Rather than iterating over each and every number, you could try

public int test(int floor, int ceil, int n) {
    int a = (floor / n) + 1;
    int counter = 0;
    while (a * n <= ceil) {
        counter++;
        a++;
    }
    return counter;
}

and only use multiples of the divisor. Now you're not doing repeated division (slow), you're doing repeated multiplication (faster).

gobernador
  • 5,659
  • 3
  • 32
  • 51
-1
public static int solution(int low,int high, int n) {
    boolean h0=high%n==0;
    boolean l0=low%n==0;

    int k1=l0?low/n:(low+n-low%n)/n;
    int k2=h0?high/n:(high+n-high%n)/n;

    //                 |-----------|
    // ---------------k1----------k2---------------

    if(k2*n>high) k2--;

    return k2-k1+1;

}
-1

You asked for a count of the number of integers between x and y (both x and y are included in the range) that are divisible by n. There is no need for any looping, and only two divisions are necessary to compute the answer. Let's consider a simple example: for the range 100 to 200, how many integers are divisible by 7?

Start at the low end of the range: 100 / 7 = 14 with a remainder of 2. Now the divisor 7 minus the remainer 2 is 5, so the smallest number on the range that is divisible by 7 is 100 + 5 = 105.

Now go to the high end of the range: 200 / 7 = 28 with a remainder of 4, so the largest number on the range that is divisible by 7 is 200 - 4 = 196.

Thus, the numbers on the range that are divisible by 7 are 105, 112, 119, 126, 133, 140, 147, 154, 161, 168, 175, 182, 189, and 196. There are 14 of them, which you can calculate in a couple of ways. Take the quotient at both ends and subtract them: 28 - 14 = 14. Or take the difference of the two endpoints, divide by the divisor, and add 1: 196 - 105 = 91, 91 / 7 = 13, 13 + 1 = 14. But be careful when one of the endpoints is divisble by the divisor; I'll leave it to you to work out the details, and also to write the program.

user448810
  • 17,381
  • 4
  • 34
  • 59
  • This is close but has errors because it accounts for the starting point incorrectly. Simply taking the quotients at the ends and subtracting fails. E.g., for (2, 2, 1), it produces 0 (2/2-2/2 = 0), but 1 is correct. In the latter case, adjust when one of the endpoints is a multiple is insufficient because there are cases where neither endpoint is a multiple but there is an extra multiple in the interval. – Eric Postpischil Aug 04 '12 at 02:14
-1

Kindly provide comments on algorithm below: Suppose the range is [R1,R2] and number to be divided is n.

Find the smallest number starting from R1 divisible by n.Call it small.

Find the largest number starting from R2 divisible by n. Call it large.

Total numbers that are divisible= (large-small)/n + 1.

Worst case is still O(n) but might be efficient for large difference between R1 and R2.Hopefully I have covered all cases. Kindly suggest.

int numofDivisible(int R1, int R2, int n) {

int small = R1, large= R2;

if ((R1 > R2) || (n==0)) return 0;

if (R1 == R2) {
    if (R1 % n == 0) 
        return 1;
    else 
        return 0;
}

while(small <= R2){

     if(small % n == 0)
         break;

      ++small;
}

if (small > R2)
    return 0;


while (large > small) {

    if (large % n == 0)
       break;

    --large;
}

if (large == small)
   return 1;

return ((large-small)/n + 1);

}
Gaurang
  • 144
  • 1
  • 1
  • 9
  • It works, unlike most of the answers here, but it uses iteration to find the points inside the interval where multiples of the divisor begin and end. As we have seen in other answers, iteration is not necessary; we can calculate these points directly. – Eric Postpischil Aug 04 '12 at 02:38
  • Hi Eric, Thanks for feedback. Agree that we don't need to use iteration. I was posting my solution by the time the other solution got posted. – Gaurang Aug 04 '12 at 02:57
-1
 public int myfunc(int low, int high, int test) {   
    int count = 0;
    if(low % test == 0)
       count++;
    count +=(high-low)/test;
    return count;
 }
vandervagos
  • 113
  • 3
  • 7
-2

Well, I am not a university professor so I don't hold some amazing formula for you, but really as far as I know the only way to check over a list of numbers like that is to iterate over them, in the end you will have to evaluate the variable to check wether it is divisible, now there is a way to optimise your algorithm, sort the numbers first! This will initially be expensive, but any successive needs to check the numbers would be much faster,

I suggest looking at sorting algorithms, the one I would use would be bubble sort, which will come up on google quite a bit.

As for what you can do with the sorting, you could sort numbers into lists of of multiples, for instance 2 4 6 8 are all multiples of 2 so you could basically check the first in the list, and the rest would be instantly known to also be divisible

Keep in mind there may be a much more efficient way of doing this, just offering my 2 cents

user1294021
  • 322
  • 3
  • 11
  • How do you think sorting the numbers will help in solving this ? – Coder Stacker Aug 04 '12 at 00:54
  • You sort numbers first, test first element. Then, every next dividable element is current + N. Something like that. From there you can either get those numbers, or just count without even iterating :) –  Aug 04 '12 at 00:55
  • Sorry I actually added a bit more to be more precise – user1294021 Aug 04 '12 at 00:57
  • I still don't get where sorting is helpful. The most optimal algorithm does not use iteration at all, but the iterative algorithms are **starting** with a sorted set `R1, R1+1, R1+2, ... , R2-2, R2-1, R2`. What are you sorting? – emory Aug 04 '12 at 03:35
  • I must have misunderstood what the OP was trying to do, I was under the impression he had a list of numbers that he needed to check for a range that was divisible by a number, by sorting this list into sub lists that are divisible by the number, he could check the first in a list, and already know the rest of the numbers are also divisible. I read through the post, but simply didn't understand it, also I did say "keep in mind there may be a much more efficient way of doing this" – user1294021 Aug 04 '12 at 13:56