Come ottimizzare le prestazioni dei controlli delle condizioni sequenziali in Java?

1

Sto scrivendo un programma che cerca una soluzione di un'equazione diophantine. Il programma sta pedalando

for (int d = 0; d <= max; d++) {
    for (int c = 0; c < d; c++) {
        boolean possibleSolution = true;  
        possibleSolution =test1(c,d);
        if(possibleSolution) {possibleSolution =test2(c,d);}
        ...
        if(possibleSolution) {possibleSolution =test30(c,d);}
        if(possibleSolution) solutionFound(c,d);
    }
}

I miei metodi testN sono ragionevolmente ottimizzati. La maggior parte delle soluzioni viene rimossa da un semplice test1 , ma il programma deve eseguire 30 controlli pointless if(possibleSolution) . C'è un modo per andare direttamente a un nuovo ciclo di testN rendimento false . A questo scopo può essere adottato un blocco try ... catch o una struttura simile?

    
posta sixtytrees 08.07.2016 - 16:47
fonte

5 risposte

1

In base alla tua domanda, break; non funzionerà per te. Vuoi saltare tutti gli ulteriori passaggi in questo ciclo, fai c++ e vai per un nuovo round. Questo viene fatto usando continue . Break termina il ciclo interno do d++ e avvia il ciclo interno da 0.

    
risposta data 09.07.2016 - 03:02
fonte
5

Misura prima invece di fare supposizioni.

La sequenza di 30 test "if (possibleSolution)" è qualcosa che un buon compilatore dovrebbe essere in grado di ottimizzare. Al primo "if (possibleSolution)" un buon compilatore genera un codice che non salta solo una chiamata, ma rileva immediatamente che il secondo, il terzo e così via, se anche falliscono, genererebbe un codice che salta subito dopo la chiamata a solutionFound ().

Un modo brutto ma efficace di farlo a mano sta usando il buon vecchio goto se i tuoi standard di lingua / codifica lo supportano.

if (! test1 (c, d)) goto nosolution;
if (! test2 (c, d)) goto nosolution;
...
if (! test30 (c, d)) goto nosolution;
solutionfound (c, d);
nosolution: ;

Un modo quasi ugualmente brutto per evitare goto è questo:

do {
    if (! test1 (c, d)) break;
    if (! test2 (c, d)) break;
    ...
    if (! test30 (c, d)) break;
    solutionfound (c, d);
} while (0);

Ma per velocizzarlo, vuoi ridurre al minimo il tempo di esecuzione totale di tutto il test che stai facendo. Per fare ciò, misurare per ogni test per quanto tempo è in esecuzione e quanto è probabile che sia decisiva, cioè determina che non c'è soluzione. Quindi si ordina il test, facendo quello con il più piccolo rapporto tra il tempo di esecuzione diviso per la probabilità che il test decida il risultato. Se un test richiede 5 microsecondi e ha una probabilità del 10% di fallire, e un altro richiede 7 microsecondi con una probabilità del 20% di fallire, facciamo il secondo per primo. (Questo ha scarso effetto se è altamente probabile che tutti e 30 i test superino).

Questo diventa più complicato se i test non sono indipendenti.

    
risposta data 08.07.2016 - 18:31
fonte
2

Sostituisci

 If(pass) pass=testN(c,d);

Con

  If(!testN(c,d)) {break;}

In questo modo arrivi subito a un nuovo ciclo.

    
risposta data 08.07.2016 - 17:06
fonte
1

Non sono sicuro se questo è ciò che intendi, ma puoi fare una cosa del genere con un break / continue etichettato ( link ):

main: for (int d = 0; d <= max; d++) {
    subMain: for (int c = 0; c < d; c++) {
        boolean possibleSolution = test1(c,d);
        if(!possibleSolution)
            continue main;
        possibleSolution = test2(c,d);
        ...
        if(possibleSolution)
            break main;
        else continue subMain;
        ...etc.
    }
}
    
risposta data 09.07.2016 - 03:54
fonte
1

Potresti usare gli stream di java 8, che causeranno cortocircuiti sotto il cofano per te.

Per aggirare un po 'di boxe / unboxing puoi definire questa interfaccia interna:

private interface SolutionTest {
    boolean test(int a, int b);
}

Con questo metodo è possibile introdurre questi metodi di supporto:

private boolean isPossibleSolution(int c, int d) {
    return allTests().anyMatch(solutionTest -> solutionTest.test(c, d));
}

private Stream<SolutionTest> allTests() {
    return Stream.of(
                this::test1,
                this::test2,
                //...

                this::test30
        );
}

Che permetterà al codice principale di essere semplice come:

IntStream.rangeClosed(0, max)
        .forEach(d -> IntStream.range(0, d)
                .filter(c -> isPossibleSolution(c, d))
                .findFirst()
                .ifPresent(c -> solutionFound(c, d)));

anyMatch() e findFirst() sono i metodi che fanno tutto il cortocircuito per te. E come bonus è tutto molto meno prolisso e molto più leggibile.

    
risposta data 09.07.2016 - 17:25
fonte

Leggi altre domande sui tag