Java большой для производительности - PullRequest
0 голосов
/ 18 апреля 2019

Мне нужно найти тысячное число (исключая числа 3, 4, 7), но требуется много времени, около 9 секунд, чтобы выяснить, как повысить производительность.

import java.sql.Date;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

public class FirstChallange {

    private static int removedNumbers;
    private static int numberQuantity;
    private static int lastNumberFound; 
    private static int cont;
    private static int pos3;
    private static int pos4;
    private static int pos7;    

    public static void main(String[] args) 
    { 

        long inicio = System.currentTimeMillis();  

        removedNumbers = 0;
        numberQuantity = 10000000;


        for (cont = 1; removedNumbers <= numberQuantity; cont++) {      
            String str = new String(); 
            str = String.valueOf(cont);     

            pos3 = str.indexOf("3");
            pos4 = str.indexOf("4");
            pos7 = str.indexOf("7");

            if((pos3 == -1) && (pos4 == -1) && (pos7 == -1)) {
                removedNumbers++; 
                if(removedNumbers == numberQuantity){ // can not find numbers (3, 4, 7)
                    lastNumberFound = cont; 
                }
            } 
        }       

        DecimalFormat dfmt = new DecimalFormat("0");

        System.out.println(dfmt.format(lastNumberFound)); 

        long fim  = System.currentTimeMillis();   
        System.out.println(new SimpleDateFormat("ss.SSS").format(new Date(fim - inicio)));  


    }
}

Является ли преобразование чисел в строку и удаление их с помощью indexOf лучшим режимом? или есть что-то лучше, чем indexOf, как RabinKarp?

Ожидаемый результат: 180999565 через 5/4 секунд

Ответы [ 4 ]

3 голосов
/ 18 апреля 2019

Вы должны думать о проблеме по-другому.

"Генерация всех чисел ниже числоКоличество , которые не содержат 3, 4, 7"

Или другими словами: Msgstr "Создать числа, содержащие только 0,1,2,5,6,8,9 в качестве цифр".

Это 7 разных цифр.

Таким образом, одним из подходов может быть увеличение счетчика, начиная с 1, и преобразование его в базу 7 представлений, где вы отображаете каждую цифру следующим образом:

0->0
1->1
2->2
3->5
4->6
5->8
6->9
2 голосов
/ 18 апреля 2019

Нужен ли поиск?Работая над комментариями выше, что есть 7 цифр, есть гораздо более эффективный прямой метод, который состоит в том, чтобы генерировать базовые 7 цифр, а затем переназначать их в соответствии со схемой исключения.

См. Полный список ниже.программный текст, который реализует три стратегии: более быстрый «поиск», который использует байтовый массив вместо строк.Это примерно так же, как оригинальный алгоритм, но должно быть намного быстрее.Преобразование base 7 с использованием форматирования java и прямое преобразование base 7, которое, скорее всего, лучше всего соответствует исходным требованиям задачи.

Вот только «Прямая» реализация.Эта реализация генерирует целевой номер как число 7, а затем перераспределяет цифры в соответствии со схемой для исключения цифр 3, 4 и 7:

public static int findNumberDirect(int index) {
    int base = 7;

    byte[] digits = new byte[10];  // Storage for the base 7 digits.

    // Convert to base 7 by generating digits for each power of 7.

    for ( int nextDigit = 0; index > 0; nextDigit++ ) {
        int nextRem = index % base;
        index = index / base;

        digits[nextDigit] = (byte) nextRem;
    }

    // Remap the digits from base 7 to base 10 with exclusions.
    // This could be done in the prior loop.  I've kept this as
    // a separate step for clarity.

    int number = 0;

    int numDigits = digits.length;
    for ( int nextDigit = 0; nextDigit < numDigits; nextDigit++ ) {
        number *= 10;
        number += DIGIT_MAPPING[ digits[ numDigits - nextDigit - 1 ] ];
    }

    return number;
}

Ниже приведены значения времени, выводимые программой:

Index [        1 ] Search [        1 ] [   2100 (ns) ] Convert [        1 ] [   4500 (ns) ] Direct [        1 ] [   1300 (ns) ] 
Index [        4 ] Search [        6 ] [   1400 (ns) ] Convert [        6 ] [   1900 (ns) ] Direct [        6 ] [   1000 (ns) ] 
Index [       10 ] Search [       15 ] [   1900 (ns) ] Convert [       15 ] [   2000 (ns) ] Direct [     15 ] [   1000 (ns) ] 
Index [      100 ] Search [      202 ] [  26300 (ns) ] Convert [      202 ] [   2100 (ns) ] Direct [      202 ] [   1000 (ns) ] 
Index [     1000 ] Search [     2929 ] [  98300 (ns) ] Convert [     2929 ] [   2100 (ns) ] Direct [     2929 ] [   1000 (ns) ] 
Index [    10000 ] Search [    61106 ] [ 694300 (ns) ] Convert [    61106 ] [   2100 (ns) ] Direct [    61106 ] [    900 (ns) ]

Полный текст программы:

package my.tests;

public class NumberCounter {

    // Find the thousandth number (excluding numbers 3, 4, 7).

    public static final String USAGE_TEXT = "Usage: " + NumberCounter.class.getName() + " index*";

    public static final int MAX_INDEX = 10000000;

    public static void main(String[] args) {
        for ( int argNo = 0; argNo < args.length; argNo++ ) {
            String indexText = args[argNo];
            int index = Integer.parseInt(indexText);
            if ( index <= 0 ) {
                System.out.println("Error: Index [ " + indexText + " ] is less than 1.");
                return;
            } else if ( index > MAX_INDEX ) {
                System.out.println("Error: Index [ " + indexText + " ] is greater than " + Integer.toString(MAX_INDEX) + ".");
                return;
            }

            long searchStart = System.nanoTime();
            int searchNumber = findNumberSearch(index);
            long searchEnd = System.nanoTime();

            long convertStart = System.nanoTime();
            int convertNumber = findNumberConvert(index);
            long convertEnd = System.nanoTime();

            long directStart = System.nanoTime();
            int directNumber = findNumberDirect(index);
            long directEnd = System.nanoTime();

            System.out.println("Index [ " + formatAmount((long) index) + " ]" +
                               " Search [ " + formatAmount(searchNumber) + " ] [ " + formatDuration(searchEnd, searchStart) + " ]" +
                               " Convert [ " + formatAmount(convertNumber) + " ] [ " + formatDuration(convertEnd, convertStart) + " ]" +
                               " Direct [ " + formatAmount(directNumber) + " ] [ " + formatDuration(directEnd, directStart) + " ]");                               
        }
    }

    public static String formatDuration(long end, long start) {
        return String.format("%6d (ns)", Long.valueOf(end - start));
    }

    public static String formatAmount(long amount) {
        return String.format("%8d", Long.valueOf(amount));
    }

    private final static byte[] DIGIT_MAPPING = { 0, 1, 2, 5, 6, 8, 9 };

    public static int findNumberSearch(int index) {
        byte[] digits = new byte[10];
        int numDigits = digits.length;

        for ( int nextNumber = 0; nextNumber < index; nextNumber++ ) {
            for ( int nextDigit = 0; nextDigit < numDigits; nextDigit++ ) {
                int digitOffset = numDigits - nextDigit - 1;
                byte digit = digits[digitOffset];
                if ( digit == 6 ) {
                    digit = 0;
                } else {
                    digit++;
                }
                digits[digitOffset] = digit;
                if ( digit != 0 ) {
                    break;
                }
            }
        }

        int number = 0;

        for ( int nextDigit = 0; nextDigit < numDigits; nextDigit++ ) {
            number *= 10;
            number += DIGIT_MAPPING[ digits[nextDigit] ];
        }

        return number;
    }

    public static int findNumberConvert(int index) {
        String numberText = Integer.toString(index, 7);

        int number = 0;

        int numDigits = numberText.length();
        for ( int nextDigit = 0; nextDigit < numDigits; nextDigit++ ) {
            number *= 10;
            number += DIGIT_MAPPING[ numberText.charAt(nextDigit) - '0' ];
        }

        return number;
    }

    public static int findNumberDirect(int index) {
        int base = 7;

        byte[] digits = new byte[10];

        for ( int nextDigit = 0; index > 0; nextDigit++ ) {
            int nextRem = index % base;
            index = index / base;

            digits[nextDigit] = (byte) nextRem;
        }

        int number = 0;

        int numDigits = digits.length;
        for ( int nextDigit = 0; nextDigit < numDigits; nextDigit++ ) {
            number *= 10;
            number += DIGIT_MAPPING[ digits[ numDigits - nextDigit - 1 ] ];
        }

        return number;
    }
}
0 голосов
/ 23 апреля 2019

Следуя ответу MrSmith , вы можете заметить, что после перевода ожидаемого результата 180999565, используя таблицы поиска и функцию ниже ...

                       // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
static int b10_to_b7[] = {0, 1, 2,-1,-1, 3, 4,-1, 5, 6}; // avoids 3, 4 and 7
static int b7_to_b10[] = {0, 1, 2, 5, 6, 8, 9,-1,-1,-1}; // undoes above permutation 

// extracts digits in base b, replacing them according to p
static long permuteDigits(long input, long base, int[] p) {
    long output = 0;
    long shift = 1;
    while (input > 0) {
       int digit = (int)(input % base);
       input /= base;
       output += p[digit] * shift;
       shift *= base;
    }
    return output;
}

// converts input digits in base ibase into obase
static long changeBase(long input, long ibase, long obase) {
    long output = 0;
    long shift = 1;
    while (input > 0) {
       int digit = (int)(input % ibase);
       input /= ibase;
       output += digit * shift;
       shift *= obase;
    }
    return output;
}


permuteDigits(180999565L, 10, b10_to_b7); // 150666343
changeBase(150666343L, 10, 7);            // 10000000  !!

Итак, используя те же функции, вы можете найти свой результат напрямую, поменяв местами операции:

changeBase(10000000L, 7, 10);             // 150666343
permuteDigits(150666343L, 10, b7_to_b10); // 180999565

И это занимает всего O(log(n)) операций, что намного быстрее, чем любой итеративный подход. Ваш оригинальный код был O(n log(n))

Обратите внимание, что вы можете оптимизировать мой код, объединив обе функции. Но это даст вам выигрыш только в улучшении скорости x2: ничто так радикально, как улучшение x10M, избежать итерации.

0 голосов
/ 18 апреля 2019

Каждый раз, когда вы выполняете indexOf, вы зацикливаетесь внутри строки, чтобы увидеть, содержит ли она символ.

Затем вы выполняете 3 "indexOf", поэтому при каждом цикле цикла ваша обработка в этой частиумножается на 3.

Мы можем рассмотреть возможность объединения этих трех проверок в одном цикле и, таким образом, повысить производительность, например, путем создания метода, который эмулирует indexOf, но при этом вы передаете 3 проверки:

import java.sql.Date;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

public class FirstChallange {

    private static int removedNumbers=0;
    private static int numberQuantity;
    private static int lastNumberFound; 
    private static int cont;

    private static char three = '3';
    private static char four = '4';
    private static char seven = '7';

    private static SimpleDateFormat sf = new SimpleDateFormat("ss.SSS");
    private static DecimalFormat dfmt = new DecimalFormat("0");

    public static void main(String[] args) 
    { 

        long inicio = System.currentTimeMillis();  

        numberQuantity = 10000000;

        String str; 

        for (cont = 1; removedNumbers <= numberQuantity; cont++) {      

            //Faster way to create String
            str = "" + cont;

            if(!contains(str.toCharArray())) {
                removedNumbers++; 
                if(removedNumbers == numberQuantity){ // can not find numbers (3, 4, 7)
                    lastNumberFound = cont; 
                }
            } 
        }

        System.out.println(dfmt.format(lastNumberFound)); 

        long fim  = System.currentTimeMillis();
        System.out.println(sf.format(new Date(fim - inicio)));  

    }

    public static boolean contains(char[] chars) {

        for (int i = 0; i < chars.length; i++) {
             if(chars[i] == three) {
                return true;
            } else if (chars[i] == four) {
                return true;
            } else if(chars[i] == seven) {
                return true;
            }
        }
        return false;
    }

}

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

Я уменьшил производительность на 3 или 4 секунды.

...