다음 코드는 내가 필요한 방식으로 작동하지만 못생긴, 과도한 또는 기타 여러 가지입니다. 나는 공식을 살펴보고 몇 가지 해결책을 쓰려고했지만 비슷한 양의 진술로 끝납니다.
이 경우 나에게 도움이되거나 16 개의 if 문이 허용되는 수학 공식이 있습니까?
코드를 설명하기 위해, 그것은 일종의 동시 턴 기반 게임을위한 것입니다. 이것이 도움이된다면 무엇이든 할당했습니다. 결과는 0 = 승리하지 않음, 1 = p1 승리, 2 = p2 승리, 3 = 두 승리입니다.
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;
}
답변
수식을 만들 수 없으면 제한된 수의 결과에 표를 사용할 수 있습니다.
final int[][] result = new int[][] {
{ 0, 0, 1, 2 },
{ 0, 0, 2, 1 },
{ 2, 1, 3, 3 },
{ 1, 2, 3, 3 }
};
return result[one][two];
답변
데이터 세트가 너무 작기 때문에 모든 것을 1 개의 긴 정수로 압축하여 수식으로 변환 할 수 있습니다
public int fightMath(int one,int two)
{
return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}
비트 별 변형 :
이것은 모든 것이 2의 배수라는 사실을 이용합니다.
public int fightMath(int one,int two)
{
return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}
마법 상수의 기원
내가 무엇을 말할 수 있습니까? 세상에는 마술이 필요합니다. 때로는 무언가가 창조를 요구할 수도 있습니다.
OP의 문제를 해결하는 함수의 본질은 2 개의 숫자 (1, 2), 도메인 {0,1,2,3}에서 {0,1,2,3} 범위까지의 맵입니다. 각 답변은 해당지도를 구현하는 방법에 접근했습니다.
또한 많은 답변에서 문제의 상태가 1 2 자리 기본 4 숫자 N (1, 2)의 맵으로 나타납니다. 여기서 1은 숫자 1, 2는 숫자 2, N = 4 * 1입니다. + 2; N = {0,1,2, …, 15}-16 개의 다른 값이 중요합니다. 함수의 출력은 1 자리의 기본 4 자리 숫자 {0,1,2,3}-4 개의 다른 값이며 중요합니다.
이제 1 자리 4 자리 숫자는 2 자리 2 자리 숫자로 표현할 수 있습니다. {0,1,2,3} = {00,01,10,11}이므로 각 출력은 2 비트로 만 인코딩 될 수 있습니다. 위에서는 16 개의 서로 다른 출력 만 가능하므로 전체 맵을 인코딩하는 데 필요한 것은 16 * 2 = 32 비트입니다. 이것은 모두 1 정수에 맞을 수 있습니다.
상수 M은 m (0)이 비트 M [0 : 1]로 인코딩되고 m (1)이 비트 M [2 : 3]으로 인코딩되고 m (n)이 비트로 인코딩되는 맵 m의 인코딩입니다. M [n * 2 : n * 2 + 1].
남아있는 것은 모두 상수의 오른쪽 부분을 인덱싱하고 반환하는 것입니다.이 경우 M을 2 * N 번 오른쪽으로 시프트하고 2 개의 최상위 비트를 가져갈 수 있습니다 (M >> 2 * N) & 0x3. 식 (하나 << 3)과 (두 << 1)은 2 * x = x << 1 및 8 * x = x << 3임을 지적하면서 여러 가지를 곱하는 것입니다.
답변
JAB를 제외하고 제시된 솔루션은 마음에 들지 않습니다. 다른 어떤 것도 코드를 쉽게 읽고 계산되고있는 것을 이해하지 못합니다 .
이 코드를 작성하는 방법은 다음과 같습니다. Java가 아닌 C # 만 알고 있지만 그림을 얻습니다.
const bool t = true;
const bool f = false;
static readonly bool[,] attackResult = {
{ f, f, t, f },
{ f, f, f, t },
{ f, t, t, t },
{ t, f, t, t }
};
[Flags] enum HitResult
{
Neither = 0,
PlayerOne = 1,
PlayerTwo = 2,
Both = PlayerOne | PlayerTwo
}
static HitResult ResolveAttack(int one, int two)
{
return
(attackResult[one, two] ? HitResult.PlayerOne : HitResult.Neither) |
(attackResult[two, one] ? HitResult.PlayerTwo : HitResult.Neither);
}
여기서 계산되는 내용이 훨씬 더 명확 해집니다. 이는 누가 우리가 어떤 공격에 타격을 받는지 계산하고 두 결과를 모두 반환한다는 것을 강조합니다.
그러나 이것은 더 나을 수 있습니다. 부울 배열은 다소 불투명합니다. 나는 테이블 룩업 접근법을 좋아하지만 의도 한 게임 의미가 무엇인지 명확히하는 방식으로 작성하는 경향이 있습니다. 즉, “제로의 공격과 하나의 방어는 명중되지 않음”보다는 코드가 “낮은 킥 공격과 낮은 블록 방어는 명중되지 않음”을보다 명확하게 암시하는 방법을 찾으십시오. 코드가 게임의 비즈니스 로직을 반영하도록합니다.
답변
결과가 포함 된 행렬을 만들 수 있습니다
int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};
가치를 얻고 싶을 때
public int fightMath(int one, int two) {
return this.results[one][two];
}
답변
다른 사람들은 이미 초기 아이디어 인 행렬 방법을 제안했지만 if 문을 통합하는 것 외에도 제공된 인수가 예상 범위에 있는지 확인하고 내부 반환 (일부 코딩)을 사용하여 일부 내용을 피할 수 있습니다 내가 한 함수에 대해 한 번의 종료 지점을 시행하는 것으로 보았지만 여러 리턴이 화살표 코딩을 피하는 데 매우 유용하며 Java에서 예외가 발생함에 따라 어쨌든 그러한 규칙을 엄격하게 시행하는 데는 아무런 의미가 없습니다. 메소드 내에서 발생하는 잡히지 않은 예외는 어쨌든 가능한 종료점이므로). 중첩 스위치 문은 가능하지만 여기에서 확인하는 작은 값 범위의 경우 문이 더 작고 성능 차이가 많이 발생하지 않는지 확인합니다.
public int fightMath(int one, int two) {
if (one > 3 || one < 0 || two > 3 || two < 0) {
throw new IllegalArgumentException("Result is undefined for arguments outside the range [0, 3]");
}
if (one <= 1) {
if (two <= 1) return 0;
if (two - one == 2) return 1;
return 2; // two can only be 3 here, no need for an explicit conditional
}
// one >= 2
if (two >= 2) return 3;
if (two == 1) return 1;
return 2; // two can only be 0 here
}
그렇지 않으면 입력-> 결과 매핑의 일부가 불규칙하기 때문에 읽기가 어렵습니다. 나는 그 단순성과 매트릭스가 시각적으로 의미있게 이해되도록 매트릭스를 선호하는 방법 때문에 매트릭스 스타일을 선호합니다.
int[][] results = {{0, 0, 1, 2},
{0, 0, 2, 1},
{2, 1, 3, 3},
{2, 1, 3, 3}};
업데이트 : 차단 / 타격에 대한 언급을 감안할 때 입력 및 결과에 대해 적절한 / 속성 보유 열거 유형을 사용하고 차단을 설명하기 위해 결과를 약간 수정하는 기능에 대한 근본적인 변화가 있습니다. 읽을 수있는 기능.
enum MoveType {
ATTACK,
BLOCK;
}
enum MoveHeight {
HIGH,
LOW;
}
enum Move {
// Enum members can have properties/attributes/data members of their own
ATTACK_HIGH(MoveType.ATTACK, MoveHeight.HIGH),
ATTACK_LOW(MoveType.ATTACK, MoveHeight.LOW),
BLOCK_HIGH(MoveType.BLOCK, MoveHeight.HIGH),
BLOCK_LOW(MoveType.BLOCK, MoveHeight.LOW);
public final MoveType type;
public final MoveHeight height;
private Move(MoveType type, MoveHeight height) {
this.type = type;
this.height = height;
}
/** Makes the attack checks later on simpler. */
public boolean isAttack() {
return this.type == MoveType.ATTACK;
}
}
enum LandedHit {
NEITHER,
PLAYER_ONE,
PLAYER_TWO,
BOTH;
}
LandedHit fightMath(Move one, Move two) {
// One is an attack, the other is a block
if (one.type != two.type) {
// attack at some height gets blocked by block at same height
if (one.height == two.height) return LandedHit.NEITHER;
// Either player 1 attacked or player 2 attacked; whoever did
// lands a hit
if (one.isAttack()) return LandedHit.PLAYER_ONE;
return LandedHit.PLAYER_TWO;
}
// both attack
if (one.isAttack()) return LandedHit.BOTH;
// both block
return LandedHit.NEITHER;
}
더 높은 높이의 블록 / 공격을 추가하고 싶다면 열거 형 만 추가하면 함수 자체를 변경할 필요조차 없습니다. 추가 유형의 이동을 추가하려면 아마도 기능을 수정해야합니다. 또한, EnumSet
들 예를 들어 주요 열거의 속성으로 추가 열거 형을 사용하는 것보다 더 확장 할 수 있습니다 EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);
후 attacks.contains(move)
가 아니라 move.type == MoveType.ATTACK
사용하지만, EnumSet
의 아마 직접 동등한 검사보다 약간 느립니다.
성공적인 블록이 카운터를 초래하는 경우를 들어, 대체 할 수 if (one.height == two.height) return LandedHit.NEITHER;
와 함께
if (one.height == two.height) {
// Successful block results in a counter against the attacker
if (one.isAttack()) return LandedHit.PLAYER_TWO;
return LandedHit.PLAYER_ONE;
}
또한 일부 if
문을 삼항 연산자 ( boolean_expression ? result_if_true : result_if_false
)로 대체 하면 코드가 더 간결 return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;
해지지 만 (예 : 앞의 블록에있는 코드가 될 수 있음 ) 읽기 어려운 원 라이너로 이어질 수 있습니다. ‘ 더 복잡한 분기에 권장하지 않습니다.
답변
왜 배열을 사용하지 않습니까?
처음부터 시작하겠습니다. 패턴이 보이면 값이 0에서 3으로 바뀌고 가능한 모든 값을 잡기를 원합니다. 이것은 당신의 테이블입니다 :
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
이 같은 테이블 바이너리를 보면 다음과 같은 결과가 나타납니다.
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
아마 당신은 이미 어떤 패턴을 보았지만 1과 2의 값을 결합하면 0000, 0001, 0010, ….. 1110과 1111의 모든 값을 사용하고 있음을 알 수 있습니다. 이제 값 1과 2를 결합하여 단일을 만들어 봅시다. 4 비트 정수
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
이것을 10 진수 값으로 다시 변환하면 하나와 두 개의 조합이 색인으로 사용될 수있는 매우 가능한 값 배열이 표시됩니다.
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
그런 다음 배열은 {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}
인덱스이며 단순히 하나와 두 개의 조합입니다.
나는 자바 프로그래머는 아니지만 모든 if 문을 제거하고 다음과 같이 쓸 수 있습니다.
int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two];
비트 시프트가 2만큼 곱셈보다 빠른지 모르겠습니다. 그러나 시도해 볼 가치가 있습니다.
답변
이것은 약간의 비트 매직을 사용합니다 (단일 정수로 두 비트의 정보 (낮음 / 높음 및 공격 / 차단)를 유지하여 이미하고 있습니다) :
나는 그것을 실행하지 않고 여기에만 입력했습니다. 다시 확인하십시오. 아이디어는 분명히 작동합니다.
편집 : 이제 모든 입력에 대해 테스트되었으며 정상적으로 작동합니다.
public int fightMath(int one, int two) {
if(one<2 && two<2){ //both players blocking
return 0; // nobody hits
}else if(one>1 && two>1){ //both players attacking
return 3; // both hit
}else{ // some of them attack, other one blocks
int different_height = (one ^ two) & 1; // is 0 if they are both going for the same height - i.e. blocker wins, and 1 if height is different, thus attacker wins
int attacker = one>1?1:0; // is 1 if one is the attacker, two is the blocker, and 0 if one is the blocker, two is the attacker
return (attacker ^ different_height) + 1;
}
}
아니면 두 비트의 정보를 별도의 변수로 분리하도록 제안해야합니까? 위와 같은 비트 연산을 기반으로 한 코드는 일반적으로 유지 관리하기가 어렵습니다.
