Подсчет конечных нулей чисел, полученных из факториала

Я пытаюсь подсчитать конечные нули чисел, которые являются результатом факториалов (что означает, что числа становятся довольно большими). Следующий код принимает число, вычисляет факториал числа и подсчитывает конечные нули. Однако, когда число составляет около 25!, numZeros не работают.

public static void main(String[] args) {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    double fact;
    int answer;

    try {
        int number = Integer.parseInt(br.readLine());
        fact = factorial(number);
        answer = numZeros(fact);
    }
    catch (NumberFormatException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static double factorial (int num) {
    double total = 1;
    for (int i = 1; i <= num; i++) {
        total *= i;
    }
    return total;
}   

public static int numZeros (double num) {
    int count = 0;
    int last = 0;   

    while (last == 0) {
        last = (int) (num % 10);
        num = num / 10;
        count++;
    }

    return count-1;
}

Я не беспокоюсь об эффективности этого кода, и я знаю, что есть несколько способов сделать эффективность данного кода лучше. Что я пытаюсь понять out-вот почему подсчет конечных нулей чисел, которые больше 25! не работающий.

какие идеи?

10 ответов


ваша задача-вычислить не факториал, а количество нулей. Хорошее решение использует формулу изhttp://en.wikipedia.org/wiki/Trailing_zeros (что вы можете попытаться доказать)

def zeroes(n):
    i = 1
    result = 0
    while n >= i:
        i *= 5
        result += n/i  # (taking floor, just like Python or Java does)
    return result

надеюсь, вы можете перевести это на Java. Это просто вычисляет [n / 5] + [n / 25] + [n / 125] + [n / 625]+... и останавливается, когда делитель становится больше n.

Не используйте BigIntegers. Это бузосорт. Такие решения требуют секунд времени для больших числа.


вам действительно нужно знать, сколько 2s и 5s есть в продукте. Если вы подсчитываете конечные нули, то вы на самом деле подсчитываете "сколько раз десять делит это число?". если вы представляете Н! как q*(2^a)*(5^b), где q не делится на 2 или 5. Тогда, просто взяв минимум a и b во втором выражении, вы получите, сколько раз 10 делит число. На самом деле умножение-это перебор.

Edit: подсчет двоек также является излишним, поэтому тебе нужны только пятерки.

и для некоторых python, я думаю, это должно работать:

def countFives(n):
    fives = 0   
    m = 5
    while m <= n:
        fives = fives + (n/m)
        m = m*5
    return fives

тип double имеет ограниченную точность, поэтому если вы работаете слишком большой двойной будет только приблизительным. Чтобы обойти это, вы можете использовать что-то вроде BigInteger, чтобы заставить его работать для сколь угодно больших целых чисел.


вы можете использовать DecimalFormat для форматирования больших чисел. Если вы отформатируете свой номер таким образом, вы получите номер в научная нотация тогда каждое число будет равно 1.4567E7 это значительно облегчит вашу работу. Потому что число после E-количество символов позади . я думаю, это количество конечных нулей.

Я не знаю, является ли это точным шаблоном. Вы можете увидеть, как сформировать шаблоны здесь

DecimalFormat formater = new DecimalFormat("0.###E0");

мои 2 цента: избегайте работы с double, поскольку они подвержены ошибкам. лучшим типом данных в этом случае является BigInteger, и здесь есть небольшой метод, который поможет вам:

public class CountTrailingZeroes {

    public int countTrailingZeroes(double number) {
        return countTrailingZeroes(String.format("%.0f", number));
    }

    public int countTrailingZeroes(String number) {
        int c = 0;
        int i = number.length() - 1;

        while (number.charAt(i) == '0') {
            i--;
            c++;
        }

        return c;

    }

    @Test
    public void 8() {
        assertEquals(0, countTrailingZeroes("128"));
    }

    @Test
    public void 0() {
        assertEquals(1, countTrailingZeroes("120"));
    }

    @Test
    public void 00() {
        assertEquals(2, countTrailingZeroes("1200"));
    }

    @Test
    public void 000() {
        assertEquals(3, countTrailingZeroes("12000"));
    }

    @Test
    public void 0000() {
        assertEquals(4, countTrailingZeroes("120000"));
    }

    @Test
    public void 2350000() {
        assertEquals(4, countTrailingZeroes("102350000"));
    }

    @Test
    public void 23500000() {
        assertEquals(5, countTrailingZeroes(1023500000.0));
    }
}

вот как я это сделал, но с большим > 25 факториалом длинной емкости недостаточно и следует использовать класс Biginteger, с ведьмой я еще не знаком:)

public static void main(String[] args) {
    // TODO Auto-generated method stub
    Scanner in = new Scanner(System.in);
    System.out.print("Please enter a number : ");
    long number = in.nextLong();
    long numFactorial = 1;

    for(long i = 1; i <= number; i++) {
        numFactorial *= i;
    }
    long result = 0;
    int divider = 5;
    for( divider =5; (numFactorial % divider) == 0; divider*=5) {
         result += 1;
    }

    System.out.println("Factorial of n is: " + numFactorial);
    System.out.println("The number contains " + result + " zeroes at its end.");

    in.close();

 }

}

лучшим с логарифмической сложностью времени является следующее:

public int trailingZeroes(int n) {
    if (n < 0)
        return -1;

    int count = 0;
    for (long i = 5; n / i >= 1; i *= 5) {
        count += n / i;
    }

    return count;
}

бесстыдно скопировано из http://www.programcreek.com/2014/04/leetcode-factorial-trailing-zeroes-java/


у меня была такая же проблема для решения в Javascript, и я решил ее так:

var number = 1000010000;
var str = (number + '').split(''); //convert to string
var i = str.length - 1; // start from the right side of the array
var count = 0; //var where to leave result
for (;i>0 && str[i] === '0';i--){
    count++;
}
console.log(count) // console shows 4

это решение дает вам количество конечных нулей.

var number = 1000010000;
var str = (number + '').split(''); //convert to string
var i = str.length - 1; // start from the right side of the	array
var count = 0; //var where to leave result
for (;i>0 && str[i] === '0';i--){
	count++;
}
console.log(count)

Java удваивает Макс на немного более 9 * 10 ^ 18, где как 25! составляет 1,5 * 10 ^ 25. Если вы хотите иметь факториалы, которые вы можете использовать BigInteger (подобно BigDecimal, но не делает десятичных знаков).


это я писал очень быстро, я думаю, что это точно решает вашу проблему. Я использовал класс BigInteger, чтобы избежать этого приведения от double к integer, которое может вызывает у тебя проблемы. Я протестировал его на нескольких больших числах более 25, таких как 101, которые точно вернули 24 нуля.

идея метода заключается в том, что если вы берете 25! тогда первое вычисление будет 25 * 24 = 600, поэтому вы можете сразу выбить два нуля, а затем сделать 6 * 23 = 138. Так что вычисляет факториал, удаляющий нули.

public static int count(int number) {
    final BigInteger zero = new BigInteger("0");
    final BigInteger ten = new BigInteger("10");
    int zeroCount = 0;
    BigInteger mult = new BigInteger("1");
    while (number > 0) {
        mult = mult.multiply(new BigInteger(Integer.toString(number)));
        while (mult.mod(ten).compareTo(zero) == 0){
            mult = mult.divide(ten);
            zeroCount += 1;
        }
        number -= 1;
    }
    return zeroCount;
}

Так как вы сказали, что вас не волнует время выполнения вообще (не то, что мой первый был особенно эффективным, просто немного больше), это просто делает факториал, а затем подсчитывает нули, поэтому это cenceptually проще:

public static BigInteger factorial(int number) {
    BigInteger ans = new BigInteger("1");
    while (number > 0) {
        ans = ans.multiply(new BigInteger(Integer.toString(number)));
        number -= 1;
    }
    return ans;
}

public static int countZeros(int number) {
    final BigInteger zero = new BigInteger("0");
    final BigInteger ten = new BigInteger("10");
    BigInteger fact = factorial(number);
    int zeroCount = 0;
    while (fact.mod(ten).compareTo(zero) == 0){
        fact = fact.divide(ten);
        zeroCount += 1;
    }
}