Consider the fraction, n/d, where n and d are positive integers. If n<d and HCF(n,d)=1, it is called a reduced proper fraction.

If we list the set of reduced proper fractions for d ≤ 8 in ascending order of size, we get:

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

It can be seen that there are 21 elements in this set.

How many elements would be contained in the set of reduced proper fractions for d ≤ 1,000,000?

 

n, d가 양의 정수인 분수 n/d를 생각해 보자. n<d이고 HCF(n,d)=1인 것을 약분된 진분수라 부른다.

d가 8 이하인 약분된 진분수를 크기 순서로 정렬하면 다음과 같다.

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

이 집합에는 21개 원소가 있는 것을 알 수 있다.

d가 1,000,000 이하일 때, 약분된 진분수에는 몇 개의 원소가 있는가?

--------------------------------------------------------------------------

 

71번 문제에서 몇글자 다른데 전혀 다른 문제가 되어 있다. 71번과 마찬가지로 분자가 분모보다 작고, 분자와 분모의 최대공약수가 1이 되는 분수 집합을 전제로 하고, 이번 문제에서는 그런 약분된 진분수가 분모가 1,000,000 이하인 경우 몇 개 있는지 물어보고 있다.

 

71번 문제에서는 전체 분수 값이 정렬된 상태를 요구했지만, 이번 문제는 정렬하는 과정 없이 답을 구할 수 있으므로 조금 더 쉽게 접근이 가능할 것 같다. 특정 분모 숫자에 대한 공약수가 1인 분자의 갯수는 69번 문제, 70번 문제를 해결할 때 사용했던 Φ(n)을 구하는 것과 동일하다. 다만, 69, 70번은 특정 조건을 만족하는 소수를 대상으로 계산하면 되었지만, 이번 문제는 가능한 분모 전체를 대상으로 구해야 하기 때문에 시간을 더 많이 요구하고 있다.

 

기존 문제에 사용한 방법을 이용해서 계산해봤지만, 1만개 계산에 6분 이상 소요되면서 전체를 계산하려면 최소 10시간 이상이 필요할 것으로 전망되어 다시 난감함을 느끼게 되었다. 특정 숫자에 대한 파이값을 계산하는 공식을 구글링을 통해 찾아 적용했는데(예를 들어 3과 5가 약수인 Φ(75)=75*(1-1/3)*(1-1/5)), 그래도 생각보다 시간이 많이 소요되었다. 몇가지 최적화를 해서 2시간 이상 실행 끝에 오답을 구했다.

 

각 숫자를 소인수분해 해서 공식을 적용했는데 프로그램을 잘 못 구성했는지 오답이 나왔고, 발상의 전환을 해서 소수가 나오면 해당 수(i)의 모든 배수를 대상으로 미리 (i-1/i)를 곱하는 형태로 구현했더니 기존 시간에 비해 터무니없이 빠르게 정답을 구할 수 있었다.

 

51번 이후에는 문제들은 이해되고 해결방안을 구현 가능하지만 극심한 성능문제가 발생하는 경우가 많이 나오고 있다. 단순 알고리즘 구현보다는 정수론 같은 수학을 이해하고 그것을 기반으로 효율적인 알고리즘 구현이 필요한 상황이 나오고 있어서, 수학공부까지 하면서 파이썬 공부를 위해 오일러 프로젝트를 계속 할 것인지 고민하는 상황이 계속 나오고 있는 것 같다.

Consider the fraction, n/d, where n and d are positive integers. If n<d and HCF(n,d)=1, it is called a reduced proper fraction.

If we list the set of reduced proper fractions for d ≤ 8 in ascending order of size, we get:

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

It can be seen that there are 3 fractions between 1/3 and 1/2.

How many fractions lie between 1/3 and 1/2 in the sorted set of reduced proper fractions for d ≤ 12,000?

 

n과 d가 양의 정수인 분수 n/d를 생각해 보자. n<d이고, HCF(n,d)=1인 경우, 약분된 진분수라 부른다.

d가 8 이하인 경우, 크기가 커지는 순서로 약분된 진분수 목록을 구하면 다음과 같다.

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

1/3과 1/2 사이에는 분수가 3개 있는 것을 알 수 있다.

d가 12,000이하인 경우 1/3과 1/2 사이에는 정렬된 약분된 진분수가 몇 개 있는가?

--------------------------------------------------------------------------

 

71번 문제72번 문제에 이어 또다른 형태로 변형되어 나왔다. 앞의 3줄은 같고 마지막 문제 부분만 바뀌어 있지만 그 한 줄을 풀기 위해서는 또다른 방식으로 접근해야 한다. 72번에서는 실행 시간을 줄이는 방법이 떠오르지 않는데, 이번 문제는 71번 문제 해결 방법을 조금만 변형하면 해결가능할 것 같아 먼저 해결해 보기로 했다.

 

분모 기준 1/3 주변의 분자값에서 시작해서 1/2 주변의 분자값까지 비교하는 형태로 해서 비교 범위를 줄인 덕분에 빠른 성능은 아니지만 brute force에 가까운 방식으로도 답을 구할 수 있다.

Consider the fraction, n/d, where n and d are positive integers. If n<d and HCF(n,d)=1, it is called a reduced proper fraction.

If we list the set of reduced proper fractions for d ≤ 8 in ascending order of size, we get:

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

It can be seen that 2/5 is the fraction immediately to the left of 3/7.

By listing the set of reduced proper fractions for d ≤ 1,000,000 in ascending order of size, find the numerator of the fraction immediately to the left of 3/7.

 

n과 d가 양의 정수인 분수 n/d를 생각해 보자. n<d이고 HCF(n,d)=1인 경우, 약분된 진분수라 부른다.

d가 8보다 작거나 같은 경우 크기가 커지는 순서로 약분된 진분수 목록을 구하면 다음과 같다.

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

여기에서 2/5는 3/7의 바로 왼쪽에 있는 것을 알 수 있다.

1,000,000 이하의 약분된 진분수를 크기가 커지는 순서로 나열할 때 3/7의 바로 왼쪽에 있는 분수의 분자는 무엇인가?

--------------------------------------------------------------------------

 

문제에서 요구하는 것을 다시 풀어보면, 분자가 분모보다 작고, 분자, 분모의 최대공약수가 1인 경우 약분된 진분수라 부른다고 정의하고 있으며, 진분수를 크기대로 나열했을 때, 3/7보다 작은 수가 무엇인지 물어보고 있다.

 

오래간만에 구현하기 간단한 문제가 나와 처음에는 분모가 2~1,000,000일때 까지의 약분된 진분수의 목록을 (분자, 분모, 나눈값)으로 구해서, 그것을 정렬하고, 3/7 바로 앞의 값을 찾는 형태로 구현했는데, 문제에서 제시한 예시를 구현했을 때에는 금방 동작했지만, 분모가 1,000,000일 때 까지 구하도록 하니 파이썬에서 처음 보는 '메모리 에러' 메시지와 함께 중단되었다. 진분수의 목록을 구하는 라인에서 에러가 발생한 것을 보니 수백만 개의 값이 있는 목록을 만드는 것에서 문제가 생긴 것 같다.

 

그래서, 각 분모값에 대해 분자를 1부터 키워나가면서 3/7에 근접하게 되는 값을 구하도록 접근 방법을 바꿨다. 리스트를 쓰지 않고 단순 계산만 하도록 몇 줄의 코드로 구현되어서 금방 값을 구할 것으로 예상했는데 실행 시간은 생각보다 너무 오래 걸려서 알고리즘 개선이 필요했다.

 

고민을 해 보니 분모가 커질수록 분자를 1부터 키워나가면서 3/7에 근접하는 값을 구하는 것에서 시간이 많이 소요되는 것을 찾아냈다. 분자가 시작하는 위치를 1에서 답에 가까운 것으로 바꾸었더니 1일 이상 필요할 것으로 예상되었던 시간이 줄어들어 4초 이내에 답을 구할 수 있었다. 더 빠르게 결과를 구하도록 개선 가능하겠지만 일단 이정도에서 만족하기로 했다.

 

정답 판정 화면에서 해결 과정을 구체적으로 쓰지 말기를 당부하면서 100번 이후 문제를 스포일하면 계정을 잠그겠다고 되어 있다. 해결과정을 조금 더 간결하게 써야겠다.

We hope that you enjoyed solving this problem. Please do not deprive others of going through the same process by publishing your solution outside of Project Euler. Members found to be spoiling problems beyond the first one-hundred problems will have their accounts locked.

If you are keen to share your insights and/or wish to see how other members have solved the problem, then please visit thread 71 in our private discussion forum.

+ Recent posts