[If-Statement] Zu viele 'if' Aussagen?



Answers

Da Ihr Datensatz so klein ist, können Sie alles in eine lange ganze Zahl komprimieren und daraus eine Formel machen

public int fightMath(int one,int two)
{
   return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}

Bitweise Variante:

Dies nutzt die Tatsache, dass alles ein Vielfaches von 2 ist

public int fightMath(int one,int two)
{
   return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}

Der Ursprung der magischen Konstante

Was kann ich sagen? Die Welt braucht Magie, manchmal muss etwas erschaffen werden.

Das Wesen der Funktion, die das Problem von OP löst, ist eine Karte von 2 Zahlen (eins, zwei), Domäne {0,1,2,3} zu der Reihe {0,1,2,3}. Jede der Antworten hat sich an die Implementierung dieser Karte angenähert.

Außerdem können Sie in einer Anzahl der Antworten eine Neuformulierung des Problems als eine Karte der 1 2-stelligen Basis 4 Nummer N (eins, zwei) sehen, wobei eins die Ziffer 1, 2 die Ziffer 2 und N = 4 * eins ist + zwei; N = {0,1,2, ..., 15} - sechzehn verschiedene Werte, das ist wichtig. Die Ausgabe der Funktion ist eine 1-stellige Basis 4 Nummer {0,1,2,3} - 4 verschiedene Werte, auch wichtig.

Jetzt kann eine 1-stellige Nummer der Basis 4 als eine 2-stellige Nummer der Basis 2 ausgedrückt werden; {0,1,2,3} = {00,01,10,11}, und so kann jeder Ausgang mit nur 2 Bits codiert werden. Von oben sind nur 16 verschiedene Ausgänge möglich, also ist nur 16 * 2 = 32 Bit notwendig, um die gesamte Karte zu kodieren; Dies kann alles in 1 Ganzzahl passen.

Die Konstante M ist eine Codierung der Karte m, wobei m (0) in Bits M [0: 1] codiert ist, m (1) in Bits M [2: 3] codiert ist und m (n) in Bits codiert ist M [n * 2: n * 2 + 1].

Es bleibt nur übrig, den rechten Teil der Konstanten zu indexieren und zurückzugeben, in diesem Fall können Sie M 2 * N mal rechts verschieben und die 2 niedrigstwertigen Bits nehmen, das heißt (M >> 2 * N) & 0x3. Die Ausdrücke (eins << 3) und (zwei << 1) multiplizieren nur Dinge, während 2 x x = x << 1 und 8 x x = x << 3.

Question

Der folgende Code funktioniert, wie ich es brauche, aber es ist hässlich, übertrieben oder eine Reihe anderer Dinge. Ich habe mir Formeln angesehen und versucht, ein paar Lösungen zu schreiben, aber am Ende bekomme ich eine ähnliche Menge an Aussagen.

Gibt es eine Art von mathematischer Formel, die mir in diesem Fall nutzen würde oder sind 16 Aussagen akzeptabel?

Um den Code zu erklären, ist es für eine Art simultan-rundenbasiertes Spiel. Zwei Spieler haben jeweils vier Aktionsschaltflächen und die Ergebnisse kommen von einem Array (0-3), aber die Variablen "Eins" und "Zwei" können sein zugewiesen, wenn das hilft. Das Ergebnis ist 0 = kein Gewinn, 1 = p1 gewinnt, 2 = p2 gewinnt, 3 = beide gewinnen.

public int fightMath(int one, int two) {

    if(one == 0 && two == 0) { result = 0; }
    else if(one == 0 && two == 1) { result = 0; }
    else if(one == 0 && two == 2) { result = 1; }
    else if(one == 0 && two == 3) { result = 2; }
    else if(one == 1 && two == 0) { result = 0; }
    else if(one == 1 && two == 1) { result = 0; }
    else if(one == 1 && two == 2) { result = 2; }
    else if(one == 1 && two == 3) { result = 1; }
    else if(one == 2 && two == 0) { result = 2; }
    else if(one == 2 && two == 1) { result = 1; }
    else if(one == 2 && two == 2) { result = 3; }
    else if(one == 2 && two == 3) { result = 3; }
    else if(one == 3 && two == 0) { result = 1; }
    else if(one == 3 && two == 1) { result = 2; }
    else if(one == 3 && two == 2) { result = 3; }
    else if(one == 3 && two == 3) { result = 3; }

    return result;
}



static int val(int i, int u){ int q = (i & 1) ^ (u & 1); return ((i >> 1) << (1 ^ q))|((u >> 1) << q); }




Warum nicht ein Array verwenden?

Ich werde von Anfang an beginnen. Ich sehe ein Muster, die Werte gehen von 0 bis 3 und Sie wollen alle möglichen Werte abfangen. Das ist dein Tisch:

0 & 0 = 0
0 & 1 = 0
0 & 2 = 1
0 & 3 = 2
1 & 0 = 0
1 & 1 = 0
1 & 2 = 2
1 & 3 = 1
2 & 0 = 2
2 & 1 = 1
2 & 2 = 3
2 & 3 = 3
3 & 0 = 2
3 & 1 = 1
3 & 2 = 3
3 & 3 = 3

Wenn wir uns dieselbe Tabelle ansehen, sehen wir folgende Ergebnisse:

00 & 00 = 00
00 & 01 = 00
00 & 10 = 01
00 & 11 = 10
01 & 00 = 00
01 & 01 = 00
01 & 10 = 10
01 & 11 = 01
10 & 00 = 10
10 & 01 = 01
10 & 10 = 11
10 & 11 = 11
11 & 00 = 10
11 & 01 = 01
11 & 10 = 11
11 & 11 = 11

Jetzt siehst du vielleicht schon ein Muster, aber wenn ich den Wert eins und zwei kombiniere, sehe ich, dass du alle Werte 0000, 0001, 0010, ... 1110 und 1111 verwendest. Nun lass uns den Wert eins und zwei kombinieren, um einen einzigen zu bilden 4-Bit-Ganzzahl.

0000 = 00
0001 = 00
0010 = 01
0011 = 10
0100 = 00
0101 = 00
0110 = 10
0111 = 01
1000 = 10
1001 = 01
1010 = 11
1011 = 11
1100 = 10
1101 = 01
1110 = 11
1111 = 11

Wenn wir das in Dezimalwerte zurückübersetzen, sehen wir ein sehr mögliches Array von Werten, wo die Eins und Zwei kombiniert als Index verwendet werden könnten:

0 = 0
1 = 0
2 = 1
3 = 2
4 = 0
5 = 0
6 = 2
7 = 1
8 = 2
9 = 1
10 = 3
11 = 3
12 = 2
13 = 1
14 = 3
15 = 3

Das Array ist dann {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3} , wobei sein Index einfach eins und zwei kombiniert ist.

Ich bin kein Java-Programmierer, aber Sie können alle if-Anweisungen loswerden und es einfach so schreiben:

int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two]; 

Ich weiß nicht, ob eine Bitverschiebung um 2 schneller ist als eine Multiplikation. Aber es könnte einen Versuch wert sein.




Ich hoffe, ich verstehe die Logik richtig. Wie wäre es mit etwas wie:

public int fightMath (int one, int two)
{
    int oneHit = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : 0;
    int twoHit = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : 0;

    return oneHit+twoHit;
}

Ein Treffer hoch oder ein Treffer niedrig wird nicht geblockt und das gleiche gilt für Spieler zwei.

Edit: Algorithmus wurde nicht vollständig verstanden, "Treffer" beim Blockieren vergeben, was ich nicht realisiert habe (Thx elias):

public int fightMath (int one, int two)
{
    int oneAttack = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : (one >= 2) ? 2 : 0;
    int twoAttack = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : (two >= 2) ? 1 : 0;

    return oneAttack | twoAttack;
}



Sie können eine Matrix erstellen, die Ergebnisse enthält

int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};

Wenn du Wert bekommen willst, wirst du es benutzen

public int fightMath(int one, int two) {
  return this.results[one][two]; 
}



Thanks to @Joe Harper as I ended up using a variation of his answer. To slim it down further as 2 results per 4 were the same I slimmed it down further.

I may come back to this at some point, but if there's no major resistance caused by multiple if -statements then I'll keep this for now. I will look into the table matrix and switch statement solutions further.

public int fightMath(int one, int two) {

    if       (one == 0) {

        if       (two == 2) {
            result = 1;
        } else if(two == 3) {
            result = 2;
        } else {
            result = 0; }

    } else if(one == 1) {
        if       (two == 2) {
            result = 2;
        } else if(two == 3) {
            result = 1;
        } else {
            result = 0; }

    } else if(one == 2) {

        if       (two == 0) {
            result = 2;
        } else if(two == 1) {
            result = 1;
        } else {
            result = 3; }

    } else if(one == 3) {

        if       (two == 0) {
            result = 1;
        } else if(two == 1) {
            result = 2;
        } else {
            result = 3; }
    }

    return result;
}



Tu stattdessen so etwas

   public int fightMath(int one, int two) {
    return Calculate(one,two)

    }


    private int Calculate(int one,int two){

    if (one==0){
        if(two==0){
     //return value}
    }else if (one==1){
   // return value as per condtiion
    }

    }



Um ganz ehrlich zu sein, jeder hat seinen eigenen Code. Ich hätte nicht gedacht, dass die Performance zu sehr beeinflusst würde. Wenn Sie das besser verstehen als eine Switchcase-Version, dann verwenden Sie diese weiter.

Sie könnten die IFS verschachteln, also könnte es möglicherweise zu einer leichten Leistungssteigerung für Ihre letzten if-Prüfungen kommen, da sie nicht so viele if-Anweisungen durchlaufen hätte. Aber in Ihrem Kontext eines grundlegenden Java-Kurses wird es wahrscheinlich nicht profitieren.

else if(one == 3 && two == 3) { result = 3; }

Also, anstatt ...

if(one == 0 && two == 0) { result = 0; }
else if(one == 0 && two == 1) { result = 0; }
else if(one == 0 && two == 2) { result = 1; }
else if(one == 0 && two == 3) { result = 2; }

Du würdest tun ...

if(one == 0) 
{ 
    if(two == 0) { result = 0; }
    else if(two == 1) { result = 0; }
    else if(two == 2) { result = 1; }
    else if(two == 3) { result = 2; }
}

Und formatiere es einfach so, wie du es vorziehst.

Dies lässt den Code zwar nicht besser aussehen, beschleunigt ihn aber möglicherweise etwas, glaube ich.




Here's a fairly concise version, similar to JAB's response . This utilises a map to store which moves triumph over others.

public enum Result {
  P1Win, P2Win, BothWin, NeitherWin;
}

public enum Move {
  BLOCK_HIGH, BLOCK_LOW, ATTACK_HIGH, ATTACK_LOW;

  static final Map<Move, List<Move>> beats = new EnumMap<Move, List<Move>>(
      Move.class);

  static {
    beats.put(BLOCK_HIGH, new ArrayList<Move>());
    beats.put(BLOCK_LOW, new ArrayList<Move>());
    beats.put(ATTACK_HIGH, Arrays.asList(ATTACK_LOW, BLOCK_LOW));
    beats.put(ATTACK_LOW, Arrays.asList(ATTACK_HIGH, BLOCK_HIGH));
  }

  public static Result compare(Move p1Move, Move p2Move) {
    boolean p1Wins = beats.get(p1Move).contains(p2Move);
    boolean p2Wins = beats.get(p2Move).contains(p1Move);

    if (p1Wins) {
      return (p2Wins) ? Result.BothWin : Result.P1Win;
    }
    if (p2Wins) {
      return (p1Wins) ? Result.BothWin : Result.P2Win;
    }

    return Result.NeitherWin;
  }
} 

Beispiel:

System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));

Prints:

P1Win






The first thing that occurred to me was essentially the same answer given by Francisco Presencia, but optimized somewhat:

public int fightMath(int one, int two)
{
    switch (one*10 + two)
    {
    case  0:
    case  1:
    case 10:
    case 11:
        return 0;
    case  2:
    case 13:
    case 21:
    case 30:
        return 1;
    case  3:
    case 12:
    case 20:
    case 31:
        return 2;
    case 22:
    case 23:
    case 32:
    case 33:
        return 3;
    }
}

You could further optimize it by making the last case (for 3) the default case:

    //case 22:
    //case 23:
    //case 32:
    //case 33:
    default:
        return 3;

The advantage of this method is that it is easier to see which values for one and two correspond to which return values than some of the other suggested methods.




A good point would be to define the rules as text, you can easier derive the correct formula then. This is extracted from laalto's nice array representation:

{ 0, 0, 1, 2 },
{ 0, 0, 2, 1 },
{ 2, 1, 3, 3 },
{ 1, 2, 3, 3 }

And here we go with some general comments, but you should describe them in rule terms:

if(one<2) // left half
{
    if(two<2) // upper left half
    {
        result = 0; //neither hits
    }
    else // lower left half
    {
        result = 1+(one+two)%2; //p2 hits if sum is even
    }
}
else // right half
{
    if(two<2) // upper right half
    {
        result = 1+(one+two+1)%2; //p1 hits if sum is even
    }
    else // lower right half
    {
        return 3; //both hit
    }
}

You could of course crunch this down to less code, but it is generally a good idea to understand what you code rather than finding a compact solution.

if((one<2)&&(two<2)) result = 0; //top left
else if((one>1)&&(two>1)) result = 3; //bottom right
else result = 1+(one+two+((one>1)?1:0))%2; //no idea what that means

Some explanation on the complicated p1/p2 hits would be great, looks interesting!




Da Sie lieber verschachtelte Bedingungen bevorzugen, hier ein anderer Weg.
Beachten Sie, dass das result nicht verwendet wird und kein Status geändert wird.

public int fightMath(int one, int two) {
    if (one == 0) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 1; }
      if (two == 3) { return 2; }
    }   
    if (one == 1) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 2; }
      if (two == 3) { return 1; }
    }
    if (one == 2) {
      if (two == 0) { return 2; }
      if (two == 1) { return 1; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    if (one == 3) {
      if (two == 0) { return 1; }
      if (two == 1) { return 2; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    return DEFAULT_RESULT;
}



Links