简体   繁体   中英

Wrong results for bigger values

I was trying a programming problem, the statement is If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Like this we have to find the sum of multiples for 't' test cases with 'n' value each, I have tried to find the solution and my code is

import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class Solution {

    public static void main(String[] args) {

        long t,n,sum;
        Scanner in=new Scanner(System.in);
        t=in.nextLong();
        for(int i=0;i<t;i++)
        {
            sum=0;
            n=in.nextLong();
            long l3=0,l5=0,l15=0;
            for(int j=3;j>0;j--)
                if((n-j)%3==0&&j<n)
                {
                l3=n-j;
                break;
            }
            for(int j=5;j>0;j--)
                if((n-j)%5==0&&j<n)
                {
                l5=n-j;
                break;
            }
            for(int j=15;j>0;j--)
                if((n-j)%15==0&&j<n)
                {
                l15=n-j;
                break;
            }

            sum+=(float)(((float)l3/(float)3)/(float)2)*(float)(l3+3);

            sum+=(float)(((float)l5/(float)5)/(float)2)*(float)(l5+5);

            sum-=(float)(((float)l15/(float)15)/(float)2)*(float)(l15+15);
            System.out.println(sum);    

        }
    }
} 

And the input I gave was,

12
10
11
12
13
1000
1001
1002
1003
100000000
100000001
100000002
100000003

Here 12 is the number of test cases.

And the output I got was

23
33
33
45
233168
234168
234168
235170
2333333593784320
2333333593784320
2333333593784320
2333333593784320

The problem here is the answer is correct for values in the test case 10,11,12,13,1000,1001,1002,1003 but the output is wrong for remaining bigger inputs. I cant find what i am missing. Could you please help me on why I am getting this kind of wrong result and how to rectify it.

Without deeper analysis of your code I would guess the problem is that you are using float, which has a quite short value range. Could you try double instead? Not sure what the correct answer would be, but at least you get different results (I just tried)

The solution looks too complex for such a task. I don't understand why do you need to perform the divisions at the end. You can try the following code:

int t = <some number> // the upper bound
int[] dividers = [...] // those are divisors against which we have to test
long sum = 0;
// check all the number up the bound
for (int number = 1; number < t; number++) {
    for (int i = 0; i < dividers.length; i++) {
        if (number % dividers[i] == 0) {
             // the number is divisible without remainder -> add it to the sum
             sum += number;
             break;
        }
    }    
}

The idea is to iterate all the numbers you want to check and see if they are divisible by some of the N dividers. If you find a number, you add it to the sum and continue with the next one.

Edit: After the clarifications from OP, I came up with another way to do this.

int t = <some number> // the upper bound
int dividers = [3, 5];
int dividerProduct = dividers[0] * dividers[1];
long sum = calculateSumForDivider(dividers[0], t) + calculateSumForDivider(dividers[1], t) - calculateSumForDivider(dividerProduct, t);

public static int calculateSumForDivider(int divider, int number) {
    int n = number / divider;
    return divider * n * (n + 1) / 2;
}

What is the logic behind all this? By dividing the target number, we can calculate how many times does the divider "fit" in the target. This is also the number of numbers in the interval [1, number] that are divisible by the divider. Let's see an example:

t = 10, divider = 3
10 / 3 = 3, so they are 3 numbers in the interval [1, 10], divisible by 3
the numbers are: 1 * 3, 2 * 3, 3 * 3
if we calculate the sum we get 1 * 3 + 2 * 3 + 3 * 3 = 3 * (1 + 2 + 3) = 18

analogically, for = 10, divider = 5
10 / 5 = 2
1 * 5 + 2 * 5 = 5 * (1 + 2) = 15

As a conclusion, we have the following formula for the sum:

sum = divider * n * (n + 1) / 2

where n is the result of the division.

The gotcha here is that numbers, divisible by both 3 and 5 (in other words divisible by 15) are going to be added twice to the sum. To correct this we use the same formula as above to calculate their sum and subtract it from the resulting some, reaching the result.

This solution will only work well for 2 dividers, since with multiple the number of numbers that will be added multiple times to the sum will grow exponentially. Fe if we want to divide be 3, 4 or 5, we will need to take care of 12, 15, 20, 60, etc.

This will also not work if the two one of the dividers is a power of the other, like 3 and 9. In that case we only need the numbers, divisible by 3.

You can get a higher precision and a larger numbers by using BigDecimal and BigInteger:

package test;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

public class Solution {

    public static void main(String[] args) {

        long t,n;
        BigInteger sum;
        Scanner in=new Scanner(System.in);
        t=in.nextLong();
        for(int i=0;i<t;i++)
        {
            sum = BigInteger.ZERO;
            n=in.nextLong();
            long l3=0,l5=0,l15=0;
            for(int j=3;j>0;j--)
                if((n-j)%3==0&&j<n)
                {
                    l3=n-j;
                    break;
                }
            for(int j=5;j>0;j--)
                if((n-j)%5==0&&j<n)
                {
                    l5=n-j;
                    break;
                }
            for(int j=15;j>0;j--)
                if((n-j)%15==0&&j<n)
                {
                    l15=n-j;
                    break;
                }

            BigDecimal x = BigDecimal.valueOf(l3)
            .divide(BigDecimal.valueOf(6))
            .multiply(BigDecimal.valueOf(l3+3));
            sum=sum.add(x.toBigIntegerExact());
            x = BigDecimal.valueOf(l5)
            .divide(BigDecimal.valueOf(10))
            .multiply(BigDecimal.valueOf(l5+5));
            sum=sum.add(x.toBigIntegerExact());
            x = BigDecimal.valueOf(l15)
            .divide(BigDecimal.valueOf(30))
            .multiply(BigDecimal.valueOf(l15+15));
            sum=sum.subtract(x.toBigIntegerExact());

            System.out.println(sum);    
        }
    }
} 

I am not sure about your algorithm because the simplest one should be like:

 sum=0;
     n=100100000;

     for(int j=1;j<n;j++)
         if(j%3==0 || j%5==0)
         {
             sum+=j;
         }
     System.out.println(sum);   
     System.out.println(n*n);  

And to get a better idea on what should be the result, it is always less than n*n .

The output I found was:

2338002249916668
10020010000000000

So, as your results where less than n*n , if you are sure about you algorithm, then they are correct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM