Tävlingsprogrammering/Uppgifter/Hageltal

Från Wikibooks


Hageltal

Tänk dig att du skriver upp alla positiva heltal på ett oändligt stort papper. Från varje tal n>1 ritar du nu en pil till talet

  • n/2 om n är jämnt.
  • 3n+1 om n är udda.

Ett avsnitt ur den graf som bildas visas i den här serierutan. De talföljder man får genom att följa pilarna kallas ibland för hageltal eftersom de likt hagelkorn driver upp och ner längs tallinjen innan de slutligen faller ner till marken (talet 1). Det intressanta är att det fortfarande inte har bevisats att man verkligen alltid når talet 1, men det har verifierats för alla tal upp till 1019 så man förmodar det, vilket brukar kallas Collatz förmodan (conjecture).

Skriv ett program som, givet två olika heltal beräknar hur långt ifrån varandra (antal pilar, oavsett riktning) de är i grafen.

Indata

En rad med två olika heltal A och B, där 1≤A,B≤1000.

Utdata

En rad med ett heltal, antal steg mellan A och B i grafen.

Fem körningsexempel

3 20      ger 2
24 10      ger 4
1 5         ger 5
22 32     ger 12
702 703    ger 236

Lösning[redigera]

Eftersom vi vet att det inte finns några cykler i grafen kan uppgiften kan omformuleras på följande sätt:

  • Generera hageltalföljden från A till 1.
  • Generera hageltalföljden från B till 1.
  • Hitta det första talet C i den ena talföljden som förekommer i den andra.
  • Positionen (indexet) för C i vardera sekvensen beskriver avståndet mellan A och C respektive B och C. Avståndet mellan A och B ges därför av summan av dessa index.

Genom att köra igenom de möjliga startvärdena ser man att inga sekvenser blir mer än 200 steg långa, så det mest tidskrävande steget (att kolla om varje tal från den ena sekvensen finns i den andra), kan göras nästan hur naivt som helst. I ett högnivåspråk som Python blir denna algoritm extra enkel att skriva.


import sys
 
def seq(n):    #Rekursiv funktion som genererar sekvensen
    if n==1:        return [n]                  # n=1: avsluta sekvensen
    elif n%2==0:    return [n]+seq(n/2)         # n är jämnt
    else:           return [n]+seq(n*3+1)       # n är udda
 
tokens=sys.stdin.readline().split()
s1=seq(int(tokens[0]))   #Generera sekvens från A
s2=seq(int(tokens[1]))   #Generera sekvens från B
 
for a in s1:     # För varje tal i ena sekvensen
    if a in s2:        # Kolla om det förekommer i den andra sekvensen
        print s1.index(a) + s2.index(a)    #Skriv ut avståndet
        break


Och en lösning i Haskell (av Anton Fors Hurdén):

hageltal 1 = [1]
hageltal n = n : hageltal (if odd n then 3*n+1 else quot n 2)

main = do
	line <- getLine
	let [s1, s2] = map (hageltal . read) $ words line
	print $ sum [1 | n <- s1 ++ s2, elem n s1 /= elem n s2]