[Python] 우선 순위 객체 정렬, 다양한 조건의 객체 정렬 - class, tuple, lambda, cmp_to_key

2023. 9. 14. 16:40파이썬(Python)

반응형

1. 우선순위 객체 정렬

1-1. 클래스를 이용한 객체 정렬

- 학생들의 국어, 수학, 영어 점수가 있을 때, 수학 점수를 기준으로 오름차순으로 정렬한다.

- 만약 수학 점수가 같다면, 국어 점수를 기준으로 오름차순으로 정렬한다.

 

- 이처럼 우선순위를 갖는 경우에는 lambda 함수의 반환값을 tuple 값으로 정의해준다.

print((3,5)>(2,8))
print((3,5)>(3,7))
print((3,5)>(4,2))
True
False
False

 

- 위와 같이 tuple끼리 비교하면 첫 번째 값을 기준으로 먼저 비교한다.

- 첫 번째 값이 동일하다면 두 번째 값을 기준으로 비교를 한다.

- 값이 2개뿐만 아니라 여러 개일 때도 마찬가지이다.

 

class Student:
    def __init__(self, kor, math, eng):
        self.kor = kor
        self.math = math
        self.eng = eng

n=int(input())
student_point=[]
for _ in range(n):
    kor,math,eng=tuple(map(int,input().split()))
    student_point.append(Student(kor,math,eng))

student_point.sort(key=lambda x:(x.math,x.eng))
for point in student_point:
    print(point.kor, point.math, point.eng)
>> 5
>> 20 50 60
>> 80 70 50
>> 30 50 90
>> 50 80 30
>> 70 50 90

# 국어, 수학, 영어 점수
20 50 60
30 50 90
70 50 90
80 70 50
50 80 30

 

- 수학 점수를 기준으로 오름차순을 한 후, 수학 점수가 같으면 영어 점수를 기준으로 오름차순으로 정렬한다.

- 영어 점수까지 같으면 우선순위 조건에 없는 값으로 오름차순으로 정렬한다. (index가 작은 값부터 오름차순 정렬)

 

- 만약 수학 점수가 같으면 국어 점수를 기준으로 내림차순으로 정렬하는 방법은 다음과 같다.

class Student:
    def __init__(self, kor, math, eng):
        self.kor = kor
        self.math = math
        self.eng = eng

n=int(input())
student_point=[]
for _ in range(n):
    kor,math,eng=tuple(map(int,input().split()))
    student_point.append(Student(kor,math,eng))

student_point.sort(key=lambda x:(x.math, -x.kor))
for point in student_point:
    print(point.kor, point.math, point.eng)

 

>> 5
>> 20 50 60
>> 80 70 50
>> 30 50 90
>> 50 80 30
>> 70 50 90

# 국어, 수학, 영어 점수
70 50 90
30 50 90
20 50 60
80 70 50
50 80 30

 

- 국어 점수를 기준으로 내림차순을 한다면 -x.kor을 하면 된다.

 

1-2. 튜플을 이용한 객체 정렬

- lambda 함수의 반환 값으로 tuple을 사용한다.

- 우선순위는 영어, 국어, 수학 점수이고 모두 내림차순으로 정렬하는 코드는 다음과 같다.

 

n=int(input())
student_points=[tuple(map(int,input().split())) for _ in range(n)]

student_points.sort(key=lambda x: (-x[2],-x[0], -x[1]))
for kor, math, eng in student_points:
    print(kor, math, eng)

 

>> 5
>> 20 50 60
>> 80 70 50
>> 50 50 90
>> 50 80 30
>> 70 50 90

# 국어, 수학, 영어 점수
70 50 90
50 50 90
20 50 60
80 70 50
50 80 30

 

2. 다양한 조건의 객체 정렬

2-1. 클래스를 이용한 객체 정렬

- 국어, 수학, 영어 점수를 가진 학생들의 정보를 점수의 합을 기준으로 내림차순으로 정렬한다.

- 이처럼 정렬 기준이 멤버 변수가 아니라면, lambda 함수에 기준 값을 반환해 주도록 설정한다.

 

class Student:
    def __init__(self, kor, math, eng):
        self.kor=kor
        self.math=math
        self.eng=eng
    
student_points=[]
n=int(input())
for _ in range(n):
    kor,math,eng=map(int,input().split())
    student_points.append(Student(kor,math,eng))

student_points.sort(key=lambda x:-(x.kor+x.math+x.eng))
for point in student_points:
    print(point.kor, point.math, point.eng, point.kor+point.math+point.eng)

 

>> 5
>> 20 50 60
>> 80 70 50
>> 50 50 90
>> 50 80 30
>> 70 50 90

# 국어, 수학, 영어, 총점
70 50 90 210
80 70 50 200
50 50 90 190
50 80 30 160
20 50 60 130

 

2-2. 튜플을 이용한 객체 정렬

n=int(input())
student_points=[tuple(map(int,input().split())) for _ in range(n)]
student_points.sort(key=lambda x:-(x[0]+x[1]+x[2]))

for kor,math,eng in student_points:
    print(kor,math,eng,kor+math+eng)

 

>> 5
>> 20 50 60
>> 80 70 50
>> 50 50 90
>> 50 80 30
>> 70 50 90

# 국어, 수학, 영어, 총점
70 50 90 210
80 70 50 200
50 50 90 190
50 80 30 160
20 50 60 130

 

2-3. cmp_to_key 함수 사용

- lambda로 처리하기 어려운 경우에는 cmp_to_key 함수를 사용한다.

- functools 내에서 cmp_to_key를 import 한다.

- compare라는 비교하는 함수를 만들어서 cmp_to_key(compare) 식으로 사용한다.

 

■ compare 함수

- compare 함수에는 인자를 2개 설정한다. (이를 x, y라 하겠다.)

- x가 앞에 있는 원소, y가 뒤에 있는 원소이다.

- 정렬 기준은 3가지가 있고 이에 대한 반환값은 다음과 같다.

1. 조건에 부합하는 순서라면 음수

2. 조건과 반대되는 순서라면 양수

3. 우선순위가 같으면 0

 

- 만약 compare(x, y)를 하여 양수가 반환되면 x와 y 순서를 바꾼다.

 

Q) 점수 Custom 정렬

- 학생들은 국어, 수학, 영어 점수를 가지고 있다.

- 정렬 기준은 다음과 같다.

- 국어 점수와 수학 점수를 더한 값이 3의 배수인 학생 점수를 제일 우선순위로 둔다.

- 그중 국어와 수학 점수의 합이 더 높은 사람을 우선으로 한다.

- 국어와 수학 점수의 합이 3의 배수가 아니면 수학과 영어 점수의 합이 큰 사람을 우선순위로 둔다.

 

from functools import cmp_to_key

n=int(input())
student_points=[tuple(map(int,input().split())) for _ in range(n)]

def compare_two_points(x,y,a,b):
    if x[a]+x[b]<y[a]+y[b]:
        return 1
    elif x[a]+x[b]>y[a]+y[b]:
        return -1
    else:
        return 0

def compare(x,y):
    if (x[0]+x[1])%3==0 and (y[0]+y[1])%3!=0:
        return -1
    elif (y[0]+y[1])%3==0 and (x[0]+x[1])%3!=0:
        return 1
    else:
        if (x[0]+x[1])%3==0 and (y[0]+y[1])%3==0:
            return compare_two_points(x,y,0,1)
        elif (x[0]+x[1])%3!=0 and (y[0]+y[1])%3!=0:
            return compare_two_points(x,y,1,2)

student_points.sort(key=cmp_to_key(compare))

for kor,math,eng in student_points:
    print(kor,math,eng)

 

>> 5
>> 40 50 60  # 학생 1
>> 80 70 50  # 학생 2
>> 50 50 90  # 학생 3
>> 50 80 30  # 학생 4
>> 70 50 90  # 학생 5

80 70 50
70 50 90
40 50 60
50 50 90
50 80 30

 

- 학생 1과 학생 2를 비교하면 국어와 수학의 합이 각각 40+50=90, 80+70=150 이므로 학생 2가 앞에 와야 한다.

- 학생 4와 학생 5를 비교하면 국어와 수학의 합이 각각 50+80=130, 70+50=120이고 학생 5의 합인 120이 3의 배수이므로 학생 4보다 앞에 와야 한다.

- 학생 3과 학생 4를 비교하면 국어와 수학의 합이 각각 50+50=100, 50+80=130 이므로 두 학생 모두 3의 배수가 아니다.

- 학생 3과 학생 4는 수학과 영어의 합이 각각 50+90=140, 80+30=110 이므로 학생 3이 학생 4보다 먼저 와야 한다.

 

- 현재 학생들의 점수는 튜플 값으로 되어 있어 0, 1, 2번째 index는 각각 국어, 수학, 영어 점수이다.

- compare 함수는 다음과 같다.

- 두 학생의 점수 x, y에서 x가 y보다 앞에 있다.

- x의 국어와 수학의 합이 3의 배수이고 y의 국어와 수학의 합이 3의 배수가 아니면 x가 y보다 먼저 와야 하므로 음수 (-1)을 반환한다.

- x의 국어와 수학의 합이 3의 배수가 아니고 y의 국어와 수학의 합이 3의 배수이면 y가 x보다 먼저 와야 하므로 양수 (1)을 반환한다.

- 위 두 조건이 아니면 compare_two_points를 수행하는데 다음과 같다.

- x, y는 각각 학생의 점수이고 a, b는 x 또는 y의 index이다.

- x, y의 국어와 수학의 합이 3의 배수이면 국어와 수학의 합이 더 큰 사람이 먼저 와야 하므로 compare_two_points의 a, b는 각각 0, 1이 되어 반환한다.

- x, y의 국어와 수학의 합이 3의 배수가 아니면 수학과 영어의 합이 더 큰 사람이 먼저 와야 하므로 compare_two_points의 a, b는 각각 1, 2가 되어 반환한다. 

 

- sort를 할 때 lambda 대신 cmp_to_key를 사용하고 조건에 만족하는 함수 compare를 인자값으로 사용한다.