맞는데 왜 틀릴까..?

알고리즘 문제/기하학

[Java] 기하학 (CCW)

안도일 2023. 2. 2. 02:43

https://www.acmicpc.net/problem/11758

 

11758번: CCW

첫째 줄에 P1의 (x1, y1), 둘째 줄에 P2의 (x2, y2), 셋째 줄에 P3의 (x3, y3)가 주어진다. (-10,000 ≤ x1, y1, x2, y2, x3, y3 ≤ 10,000) 모든 좌표는 정수이다. P1, P2, P3의 좌표는 서로 다르다.

www.acmicpc.net

 

다시 한번 내 머리가 좋지 않다는 것을 깨닫게 해 준 문제.

애초에 종이랑 펜 없이 머리만 굴려서 절대 풀 수 없었다.

여차저차 나름대로 풀이법을 생각해서 돌려봤지만 4연 실패를 안겨주었다.

 

뭐가 문제였을까 찾아본 결과 아무리 double형이라도 그 수가 크다면 부동 소수점 정밀도 문제가 생겨 틀리는 경우가 있다고 했다. 전공자 주제에 이런 건 생각지도 못한 변수였다. 당연히 double을 쓰면 실수의 나눗셈이 정확하게 떨어질 것이라고 생각했었는데 그게 아니었던 것이다.

 

그래도 푼 게 아까우니까 일단 내가 풀었던 코드를 올려놔야지. 아마 부동 소수점 문제가 아니라 다른 게 문제일 수 도 있을 수도 있다.

 

public class Main {

    static double[][] xy = new double[3][2];
    static double first_radian, second_radian;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        for(int i=0; i<3; i++){
            st = new StringTokenizer(br.readLine());
            xy[i][0] = Double.parseDouble(st.nextToken());
            xy[i][1] = Double.parseDouble(st.nextToken());
        }


        setFirst_radian();
        setSecond_radian();

        if(second_radian == first_radian) System.out.println(0); //기울기가 같으면 직선
        else if ((Math.PI-second_radian) == first_radian) System.out.println(0);
        else if ((Math.PI+second_radian) == first_radian) System.out.println(0);
        else if( second_radian >first_radian ) { //second_radian이 first_radian 보다 크고 first_radian+180도 보다 작으면 CCW
            System.out.println(1);
        }
        else {//CW
            System.out.println(-1);
        }

    }

    static public void setFirst_radian(){
        if(xy[1][0] == xy[0][0]) {first_radian = Math.PI/2; return;}


        first_radian = (xy[1][1]-xy[0][1]) / (xy[1][0]-xy[0][0]);

        if(first_radian<0 && ( (xy[1][1]-xy[0][1]) >  0) ) first_radian = Math.PI+first_radian; //2사분면
        else if(first_radian>0 && ( (xy[1][1]-xy[0][1]) <  0)) first_radian = Math.PI+first_radian; //3사분면
      
    }

    static public void setSecond_radian(){
        if(xy[2][0] == xy[1][0]) {second_radian = Math.PI/2; return;}

        second_radian = (xy[2][1]-xy[1][1]) / (xy[2][0]-xy[1][0]);

        if(second_radian<0 && ( (xy[2][1]-xy[1][1]) >  0) ) second_radian = Math.PI+second_radian; //2사분면
        else if(second_radian>0 && ( (xy[2][1]-xy[1][1]) <  0)) second_radian = Math.PI+second_radian; //3사분면
     
    }

}

 

 

해당 문제 풀이법을 찾아보니 이미 정형화된 기하학 필수 문제였었고 공식까지 존재했었다.

대체 이따위 근본 없는 공식은 어떻게 유도된 걸까. 유도하지 않고 공식을 쓰는 걸 굉장히 싫어하기 때문에 공식을 한번 증명해 보자. - 증명 실패함 진짜 이거 어떻게 나온 거냐? 아마 벡터 내적 외적 뭐 그런 거 같은데 고등학교 기벡 배운 지 어언 5년 대학교 선행대수학 배운 지 어언 4년이 지난 지금 1도 모르겠다. 

 

 

 

class Point{
    int x,y;

    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}
public class Main {

    static int x,y;
    static Point[] point = new Point[3];
    static int result;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        for(int i=0; i<3; i++) {
            st = new StringTokenizer(br.readLine());
            x = Integer.parseInt(st.nextToken());
            y = Integer.parseInt(st.nextToken());
            point[i] = new Point(x, y);
        }

        result = (point[0].x * point[1].y) +  (point[1].x * point[2].y) +  (point[2].x * point[0].y) -
                (  (point[1].x * point[0].y) +  (point[2].x * point[1].y) +  (point[0].x * point[2].y) );

        if(result==0) System.out.println(0);
        else if (result>0)  System.out.println(1);
        else System.out.println(-1);

    }

}

 

아래 참고하자 

 

https://www.acmicpc.net/blog/view/27

 

점 3개의 방향성을 나타내는 CCW

세 점 P1(x1, y1), P2(x2, y2), P3(x3, y3)가 있을 떄, 점 3개를 이은 선분은 어떤 방향성을 나타내게 될까요? 11758번 문제: CCW 가능한 경우의 수는 총 3가지가 있습니다. 반시계 방향, 시계 방향, 일직선. 시

www.acmicpc.net