나는 Haskell이 신선하지 않기 때문에 Scala로 대답 할 것이므로 사람들은 이것이 일반적인 기능 프로그래밍 알고리즘 질문이라고 생각할 것입니다. 쉽게 전송할 수있는 데이터 구조와 개념을 고수하겠습니다.
collatz 시퀀스를 생성하는 함수로 시작할 수 있는데, 결과를 꼬리 재귀로 만들기 위해 인수로 결과를 전달할 필요가있는 경우를 제외하고는 비교적 간단합니다.
def collatz(n: Int, result: List[Int] = List()): List[Int] = {
if (n == 1) {
1 :: result
} else if ((n & 1) == 1) {
collatz(3 * n + 1, n :: result)
} else {
collatz(n / 2, n :: result)
}
}
이것은 실제로 시퀀스를 역순으로하지만, 다음 단계는 맵에 길이를 저장하는 데 적합합니다.
def calculateLengths(sequence: List[Int], length: Int,
lengths: Map[Int, Int]): Map[Int, Int] = sequence match {
case Nil => lengths
case x :: xs => calculateLengths(xs, length + 1, lengths + ((x, length)))
}
첫 번째 단계의 답변, 초기 길이 및 빈 맵과 같이 이것을 호출합니다 calculateLengths(collatz(22), 1, Map.empty))
. 이것이 결과를 기억하는 방법입니다. 이제 collatz
이것을 사용할 수 있도록 수정해야합니다 :
def collatz(n: Int, lengths: Map[Int, Int], result: List[Int] = List()): (List[Int], Int) = {
if (lengths contains n) {
(result, lengths(n))
} else if ((n & 1) == 1) {
collatz(3 * n + 1, lengths, n :: result)
} else {
collatz(n / 2, lengths, n :: result)
}
}
를 사용 n == 1
하여지도를 초기화 할 수 있기 때문에 검사를 제거 1 -> 1
하지만 1
내부에지도에 넣은 길이 를 추가해야합니다 calculateLengths
. 또한 되풀이가 중지 된 메모리 길이를 반환합니다. 초기화하는 데 사용할 수 있습니다 calculateLengths
.
val initialMap = Map(1 -> 1)
val (result, length) = collatz(22, initialMap)
val newMap = calculateLengths(result, lengths, initialMap)
이제 조각을 비교적 효율적으로 구현 했으므로 이전 계산의 결과를 다음 계산의 입력에 공급하는 방법을 찾아야합니다. 이것을이라고 fold
하며 다음과 같습니다.
def iteration(lengths: Map[Int, Int], n: Int): Map[Int, Int] = {
val (result, length) = collatz(n, lengths)
calculateLengths(result, length, lengths)
}
val lengths = (1 to 10).foldLeft(Map(1 -> 1))(iteration)
실제 답변을 찾으려면 주어진 범위 사이의 맵에서 키를 필터링하고 최대 값을 찾아 최종 결과를 얻습니다.
def answer(start: Int, finish: Int): Int = {
val lengths = (start to finish).foldLeft(Map(1 -> 1))(iteration)
lengths.filterKeys(x => x >= start && x <= finish).values.max
}
예제 입력과 같이 크기가 1000 정도 인 REPL에서 대답은 거의 즉시 반환됩니다.