P14
projecteuler.net

Longest Collatz Sequence

ℹ️Published on Friday, 5th April 2002, 06:00 pm; Solved by 244286;
Difficulty rating: 5%

The following iterative sequence is defined for the set of positive integers:

  • $n \to n/2$ ($n$ is even)
  • $n \to 3n + 1$ ($n$ is odd)

Using the rule above and starting with $13$, we generate the following sequence: $$13 \to 40 \to 20 \to 10 \to 5 \to 16 \to 8 \to 4 \to 2 \to 1.$$

It can be seen that this sequence (starting at $13$ and finishing at $1$) contains $10$ terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at $1$.

Which starting number, under one million, produces the longest chain?

NOTE: Once the chain starts the terms are allowed to go above one million.



Soluzione

Potremmo semplicemente scorrere sui numeri da uno a un milione, usare questa funzione per generare la sequenza di numeri prodotti, e guardare il numero che produce quella più lunga.

function simple_collatz(n; verbose=false)
	steps = 1
	if verbose println("(step=$steps) $n") end
	while true
		if iseven(n)
			n = div(n,2)
		else
			n = 3n+1
		end
		steps += 1

		if verbose println("(step=$steps) $n") end
		if n==1
			break
		end
	end
	return steps
end
simple_collatz(50, verbose=true)
(step=1) 50
(step=2) 25
(step=3) 76
(step=4) 38
(step=5) 19
(step=6) 58
(step=7) 29
(step=8) 88
(step=9) 44
(step=10) 22
(step=11) 11
(step=12) 34
(step=13) 17
(step=14) 52
(step=15) 26
(step=16) 13
(step=17) 40
(step=18) 20
(step=19) 10
(step=20) 5
(step=21) 16
(step=22) 8
(step=23) 4
(step=24) 2
(step=25) 1
25

E quindi fare così:

max_len = 1
max_idx = 1
@time for i in 1:1_000_000
	cur_len = simple_collatz(i)
	if cur_len > max_len
		global max_len = cur_len
		global max_idx = i
	end
end
@show max_len, max_idx
  0.223241 seconds (29 allocations: 464 bytes)
(max_len, max_idx) = (525, 837799)

Questo metodo in effetti funziona, ma diventerebbe molto lento se il limite fosse ben più alto di un milione. In tal caso, l'idea sarebbe di passare ad una versione ricorsiva: per calcolare la lunghezza della sequenza generata, ad esempio, da 50, consideriamo \(1 + \) la lunghezza della sequenza generata dal numero a cui conduce 50, ovvero 25. Se questo valore ci è già noto lo usiamo, altrimenti lo calcoliamo e ce lo salviamo (se c'è una cella disponibile per lui nell'array di appoggio, altrimenti lo calcoliamo al volo e basta) in modo che sarà eventualmente utile al calcolo della lunghezza delle sequenze generate dai numeri successivi.

function collatz(n, lens; verbose=false)
    if n <= length(lens) && lens[n] != 0
        if verbose println("    lui ci conduce a $n che sappiamo già produrre una sequenza lunga $(lens[n])") end
        return lens[n]
    end

    if verbose println("Incontriamo $n per la prima volta, continuiamo con la ricorsione") end
    nextn = iseven(n) ? div(n,2) : 3n+1
    val = 1 + collatz(nextn, lens; verbose=verbose)

    if n <= length(lens)
        lens[n] = val
    end
    return val
end
collatz (generic function with 1 method)

Esempio didattico:

lens = zeros(Int, 20)
lens[1] = 1
for i in 2:20
    collatz(i, lens; verbose=true)
end
Incontriamo 2 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 1 che sappiamo già produrre una sequenza lunga 1
Incontriamo 3 per la prima volta, continuiamo con la ricorsione
Incontriamo 10 per la prima volta, continuiamo con la ricorsione
Incontriamo 5 per la prima volta, continuiamo con la ricorsione
Incontriamo 16 per la prima volta, continuiamo con la ricorsione
Incontriamo 8 per la prima volta, continuiamo con la ricorsione
Incontriamo 4 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 2 che sappiamo già produrre una sequenza lunga 2
    lui ci conduce a 4 che sappiamo già produrre una sequenza lunga 3
    lui ci conduce a 5 che sappiamo già produrre una sequenza lunga 6
Incontriamo 6 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 3 che sappiamo già produrre una sequenza lunga 8
Incontriamo 7 per la prima volta, continuiamo con la ricorsione
Incontriamo 22 per la prima volta, continuiamo con la ricorsione
Incontriamo 11 per la prima volta, continuiamo con la ricorsione
Incontriamo 34 per la prima volta, continuiamo con la ricorsione
Incontriamo 17 per la prima volta, continuiamo con la ricorsione
Incontriamo 52 per la prima volta, continuiamo con la ricorsione
Incontriamo 26 per la prima volta, continuiamo con la ricorsione
Incontriamo 13 per la prima volta, continuiamo con la ricorsione
Incontriamo 40 per la prima volta, continuiamo con la ricorsione
Incontriamo 20 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 10 che sappiamo già produrre una sequenza lunga 7
    lui ci conduce a 8 che sappiamo già produrre una sequenza lunga 4
Incontriamo 9 per la prima volta, continuiamo con la ricorsione
Incontriamo 28 per la prima volta, continuiamo con la ricorsione
Incontriamo 14 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 7 che sappiamo già produrre una sequenza lunga 17
    lui ci conduce a 10 che sappiamo già produrre una sequenza lunga 7
    lui ci conduce a 11 che sappiamo già produrre una sequenza lunga 15
Incontriamo 12 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 6 che sappiamo già produrre una sequenza lunga 9
    lui ci conduce a 13 che sappiamo già produrre una sequenza lunga 10
    lui ci conduce a 14 che sappiamo già produrre una sequenza lunga 18
Incontriamo 15 per la prima volta, continuiamo con la ricorsione
Incontriamo 46 per la prima volta, continuiamo con la ricorsione
Incontriamo 23 per la prima volta, continuiamo con la ricorsione
Incontriamo 70 per la prima volta, continuiamo con la ricorsione
Incontriamo 35 per la prima volta, continuiamo con la ricorsione
Incontriamo 106 per la prima volta, continuiamo con la ricorsione
Incontriamo 53 per la prima volta, continuiamo con la ricorsione
Incontriamo 160 per la prima volta, continuiamo con la ricorsione
Incontriamo 80 per la prima volta, continuiamo con la ricorsione
Incontriamo 40 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 20 che sappiamo già produrre una sequenza lunga 8
    lui ci conduce a 16 che sappiamo già produrre una sequenza lunga 5
    lui ci conduce a 17 che sappiamo già produrre una sequenza lunga 13
Incontriamo 18 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 9 che sappiamo già produrre una sequenza lunga 20
Incontriamo 19 per la prima volta, continuiamo con la ricorsione
Incontriamo 58 per la prima volta, continuiamo con la ricorsione
Incontriamo 29 per la prima volta, continuiamo con la ricorsione
Incontriamo 88 per la prima volta, continuiamo con la ricorsione
Incontriamo 44 per la prima volta, continuiamo con la ricorsione
Incontriamo 22 per la prima volta, continuiamo con la ricorsione
    lui ci conduce a 11 che sappiamo già produrre una sequenza lunga 15
    lui ci conduce a 20 che sappiamo già produrre una sequenza lunga 8

Vera soluzione:

lens = zeros(Int, 1_000_000)
lens[1] = 1
@time for i in 2:1_000_000
    collatz(i, lens)
end
  0.052356 seconds (1.00 M allocations: 15.278 MiB, 6.70% compilation time)
risp = findmax(lens)
@show risp
risp = (525, 837799)
println("La catena più lunga parte da n=$(risp[2]) ed è lunga $(risp[1])")
La catena più lunga parte da n=837799 ed è lunga 525

Last modified: September 20, 2025. Website built with Franklin.jl and the lovely Julia programming language.