도구스개발

플로이드 순환 검출 계산기 (토끼와 거북)

한 칸씩 가는 거북과 두 칸씩 가는 토끼로 수열의 고리를 찾습니다. 둘이 만나는 자리부터 꼬리 길이 μ와 고리 길이 λ를 구하는 두 번째 단계까지 걸음마다 보이고, 지나온 자리를 적어 두는 방식과 메모리를 견줍니다.

x ← (5·x + 3) mod 1000

상태가 1,000가지입니다.

고리 길이 λ

8

꼬리 길이 μ는 3이고, 고리에 들어가는 자리는 218입니다. 출발해서 3걸음을 걸으면 고리에 들어가고, 그 뒤로는 8걸음마다 같은 자리로 돌아옵니다.

꼬리 길이 μ3걸음
고리 길이 λ8걸음
고리에 들어가는 값218
토끼와 거북이 만난 값593
만났을 때 거북이 걸은 수8걸음 (λ의 1배)
상태 수1,000가지 (μ+λ = 11)

메모리를 얼마나 쓰나

플로이드 — 기억하는 값2개 (포인터 둘)
지나온 자리를 적어 두면11개
플로이드가 포인터를 옮긴 횟수37번

지나온 자리를 전부 적어 두면 첫 중복이 곧 답이라 훨씬 간단하지만, 꼬리와 고리를 합친 만큼(μ+λ) 적어 둘 자리가 필요합니다. 플로이드는 포인터 두 개만 쓰고도 같은 답을 냅니다 — 걸음 수는 몇 배 더 걷지만 메모리가 입력 크기와 무관하게 일정합니다. 이 맞바꿈이 이 알고리즘을 쓰는 유일한 이유입니다.

1단계 — 만날 때까지

걸음거북 (1칸)토끼 (2칸)
1843
24393
3218343
493593
5468843
634393
7718343
8593593

2단계 — 진입점 찾기

걸음출발점에서만난 자리에서
01593
18968
243843
3218218

한 포인터를 출발점으로 되돌리고 둘을 한 칸씩 나란히 보냅니다. 만난 시점에 거북이 걸은 수 t는 고리 길이 λ의 배수(여기서는 8 = 8 × 1)이므로, 만난 자리에서 μ걸음을 더 가면 고리를 정수 바퀴 돌아 진입점에 섭니다. 출발점 쪽 포인터도 μ걸음이면 진입점입니다. 그래서 둘이 처음 다시 만나는 자리가 곧 진입점이고, 그때까지 걸은 수가 μ입니다.

수열의 모양

꼬리 3개1 → 8 → 43
고리 8개218 → 93 → 468 → 343 → 718 → 593 → 968 → 843 → 218

열 개까지만 보입니다. 꼬리를 지나 고리에 들어가면 진입점(값 218)으로 다시 돌아옵니다. 이 「꼬리 + 고리」 모양이 그리스 문자 ρ를 닮았다고 해서 순환 마디를 로(rho)라 부릅니다.

상태가 유한하고 다음 자리가 하나로 정해지면 반드시 고리에 빠집니다. 갈 곳이 유한한데 끝없이 가야 하니 언젠가 왔던 자리를 다시 밟을 수밖에 없습니다(비둘기집 원리). 그래서 이 계산기에 「고리 없음」이라는 답은 나오지 않습니다.
토끼를 두 칸씩 보내는 데에는 이유가 있습니다. 둘이 고리 안에 들어온 뒤 간격이 매 걸음 정확히 1씩 줄어드는데, 1씩 줄어드는 값은 0을 건너뛸 수 없어 반드시 같은 자리가 됩니다. 세 칸씩 보내면 간격이 2씩 줄어 이 성질이 깨지고, 고리 길이가 짝수일 때 영영 못 만날 수 있습니다.

사용 방법

  1. 1수열을 식으로 줄지 목록으로 줄지 고릅니다.
  2. 2식이라면 x ← (a·x + c) mod m의 a·c·m과 출발값을 넣습니다.
  3. 3목록이라면 i번 자리에서 갈 다음 자리를 차례로 적습니다. 0번에서 출발합니다.
  4. 41단계 표에서 거북과 토끼가 같은 값이 되는 줄을 찾습니다.
  5. 52단계 표에서 한 포인터를 출발점으로 되돌린 뒤 둘이 다시 만나는 자리(진입점)를 확인합니다.

자주 묻는 질문

간격이 매 걸음 정확히 1씩 줄어들게 하기 위해서입니다. 둘이 고리 안에 들어온 뒤 간격이 1씩 줄면 0을 건너뛸 수 없어 반드시 같은 자리가 됩니다. 세 칸씩 보내면 간격이 2씩 줄어 이 성질이 깨지고, 고리 길이가 짝수일 때 영영 못 만날 수 있습니다.

만난 시점에 거북이 걸은 수 t가 고리 길이 λ의 배수이기 때문입니다. 토끼는 2t를 걸었고 둘이 같은 자리에 있으므로 두 걸음 수의 차 t도 고리를 정수 바퀴 돈 것이 됩니다. 이제 한 포인터를 출발점으로 되돌려 둘을 한 칸씩 보내면, 출발점 쪽은 μ걸음에 진입점에 닿고 만난 자리 쪽은 t + μ를 걸어 역시 진입점에 섭니다. 그래서 처음 다시 만나는 자리가 진입점입니다.

더 간단하지만 메모리를 훨씬 많이 씁니다. 첫 중복이 곧 답이라 코드는 짧지만 꼬리와 고리를 합친 만큼(μ+λ) 적어 둘 자리가 필요합니다. 플로이드는 포인터 두 개만 쓰므로 입력이 아무리 커도 메모리가 일정합니다. 걸음 수는 몇 배 더 걷는 대신 메모리를 아끼는 맞바꿈입니다.

상태가 유한하고 다음 자리가 하나로 정해지면 고리가 없을 수 없습니다. 갈 곳이 유한한데 끝없이 가야 하니 언젠가 왔던 자리를 다시 밟게 되기 때문입니다(비둘기집 원리). 그래서 이 계산기에 「고리 없음」이라는 답은 나오지 않으며, 최악이라도 μ + λ가 상태 수를 넘지 않습니다.

μ는 출발점에서 고리에 들어가기까지의 걸음 수(꼬리 길이)이고, λ는 고리를 한 바퀴 도는 걸음 수(고리 길이)입니다. 수열을 그림으로 그리면 꼬리 뒤에 고리가 달린 그리스 문자 ρ 모양이 되는데, 순환 마디를 「로(rho)」라 부르는 것이 여기서 왔습니다.

연결 리스트에 사이클이 있는지 판정하는 문제로 가장 널리 알려져 있습니다. 그 밖에 난수 생성기의 주기를 재거나, 폴라드 로 소인수분해처럼 「같은 값이 다시 나오는 순간」을 메모리 없이 잡아야 하는 곳에 쓰입니다. 값을 전부 적어 둘 수 없을 만큼 상태가 클 때 값어치가 큽니다.

전송되지 않습니다. 모든 계산은 브라우저 안에서 이뤄지고, 입력값은 이 기기에만 남습니다.

알아두면 좋은 점

  • 검증은 무작위 함수 그래프 2,000벌과 무작위 선형 합동 생성기 800벌을 돌려, 지나온 자리를 전부 적어 두는 방식이 낸 μ·λ·진입점과 모두 일치하는지 대조해 했습니다.
  • 만난 시점에 거북이 걸은 수가 항상 고리 길이의 배수인지, 만난 자리가 실제로 고리 안에 있는지도 무작위 입력으로 확인했습니다.
  • 꼬리 4,999에 고리 1인 수열에서 적어 두는 방식이 5,000개를 기억해야 할 때 플로이드는 포인터 둘로 끝나는 것을 테스트로 고정해 두었습니다.
  • 표는 60줄까지만 보입니다. 계산 자체는 끝까지 돌며, 걸음 수가 그보다 많으면 실제 걸음 수를 따로 적습니다.
  • 모듈러스는 100,000까지, 자리 목록은 60개까지 다룹니다.

함께 보면 좋은 도구

마지막 검증: 2026년 9월 1일 · 결과는 참고용 추정치입니다.