Parasittall

Parasittallene er et kjent fenomen og de kan finnes på forskjellige måter og i forskjellige former. Når jeg har tatt med dette som en algoritmisk øvelse er det fordi det er et godt eksempel på hvordan man kan bruke papir og blyant for å finne en generell metode som kan programmeres. Der også basert på litt notstalgi fra min tid som student.

For lenge siden, tror det var 1966, lange før internettet og før datamaskiner var allment tilgjengelige fikk jeg en utfordring av en medstudent. Hun vikarierte som lærer i ungdomsskolen og hadde fått en oppgave fra en elev (jeg gjetter på at det sto en matematikkkyndig forelder bak). Utfordringen var kort og godt slik:

Finn et naturlig tall som er slik at dersom det siste sifferet flyttes foran så blir tallet dobbelt så stort

Jeg hadde bare papir og blyant tilgjengelig og kjente, så vidt jeg husker, verken begrepet parasittall eller for den saks skyld algoritme. Jeg tok utfordringen og tilbrakte mesteparten av en dag på lesesalen for finne et slikt tall, og fant omsider en metode som fungerte. Det var den første gangen jeg arbeidet systematisk og bevisst for å finne en ikke-triviell algoritme. Jeg må innrømme at i løpet av prosessen dukket det opp en mistanke om at jeg var lurt opp i noe som ikke var mulig, men det gikk bra til slutt.

Mange år senere dukket denne anstrengelsen opp i hukommelsen og jeg forsøkte å rekonstruere dagen på lesesalen for å implementere algoritmen i Python og i Javascript. Det er denne rekonstruksjonen du finner nedenfor.

Vi starter med det bakerste sifferet (det som skal flyttes til å bli det første i summen). Vi velger et 2-tall. Det betyr altså at vi vil lete mot venstre etter et 2-tall som er det dobbelte av sifferet til høyre for seg.

Det neste blir 4 (2+2)42
Det neste blir 8 (4+4)842
Det neste blir 6 (8+8=16) Vi må ta med oss menta (1)6842
Det neste blir 3 (6+6+1=13) Vi må ta med oss menta36842
Det neste blir 7 (3+3+1)=7. Ingen mente736842
....
og slik fortsetter vi inntil vi får2105263157894736842
Resultatet 105263157894736842

Altså:

   105263157894736842
 + 105263157894736842
--------------------- 
 = 210526315789473684
 ====================

Det et par ting å merke seg:

Vi har altså med håndarbeid funnet et parasittall med 18 siffere. Så er utfordringen å programmere denne strategien (algoritmen) slik at vi kan finne flere tall.

Javascript

Vi ser først på en Javascript løsning. Denne er laget ved ganske slavisk å følge resonnementet over:

// finn en løsning med et gitt siste tall
function findIt(start){
  var siffere=new Array(); // tallrekka
  var ferdig = false;      // ferdig ?
  var mente=0;             // mente (0 eller 1)
  siffere.push(start);
  while (!ferdig){
    var sisteIx=siffere.length-1;   // det siste vi la til
    var t=siffere[sisteIx]*2+mente; // neste tall
    // vi er ferdig hvis:
    //	vi har flere enn to siffere og
    // 	vi ikke har noen mente og
    // 	det siste sifferet er det samme som vi startet med
    if((sisteIx > 2) && 
       (mente == 0 ) && 
       (siffere[sisteIx] == siffere[0])){
             ferdig=true;
    }
    else{ // legg til sifferet og husk  mente
      if(t >9){
       mente=1;
       siffere.push(t-10);
      }
      else{ 
        mente=0
        siffere.push(t);
       }
      }	
    }
    siffere.pop();     // ta bort det siste vi la til
    siffere.reverse(); // omvendt rekkefølge
    return siffere.join("");
}

Finn tall som slutter på





Python

Pythonkoden er litt mer omfattende. Her søker vi å finne alle parasittall som er som er kortere enn et gitt antall siffer.

#----------------------
# Produce numbers that is such that
# If you move the last digits in front it doubles
#---------------------------------------------
# Observe that for n+1 digits:
#
#    Dn-1 = 2*Dn % 10,         M = 2 * Dn / 10
#    ...
#    Di = (2 * Di+1 + M) % 10, M = (2 * Di+1 + M)/10
#    ...
#    Dn = (2 * D0 + M) % 10,   M = 0
#
# where M carries 0 or 1 to next digit addition
#-------------------------------------------------

# what we find
result=""

def CheckAndRemember(digits):
    # check the aritmetic, update result
    global result
    a=digits[0]
    for i in range(1,len(digits)):
        a=a*10+digits[i]
    b=digits[len(digits)-1]
    for i in range(0,len(digits)-1):
        b=b*10+digits[i]
    if 2*a == b:
        # it is ok, remember the number
        result=result+str(a)+"\n"
    

def makeDigits(n):
    # assume n digits in the soulution
    for d0 in range(1,5):
        # there are two possible last digits, dn
        for dn in range(2*d0,2*d0+2):
            D=[dn]
            M=0
            # calculate the others systematically right to left
            for i in range(1,n):
                v=2*D[0]+M
                D.insert(0,v%10)
                M=v/10
            CheckAndRemember(D)

def find(limit):
    # search for numbers smaller than limit
    for i in range(0,limit):
        makeDigits(i)
        
find(37)
# show what we found
print(result)

Hvis du (kopierer) og kjører denne koden kan du endre parameteren til find-funksjonen og du vil se at du finner ingen tall kortere enn 18 siffer og du vil se at hvis setter grensen til mer en 36 vil du se at siffersekvensene gjentar seg. F.eks slik med limit på 37:

105263157894736842
157894736842105263
210526315789473684
263157894736842105
315789473684210526
368421052631578947
421052631578947368
473684210526315789
105263157894736842105263157894736842
157894736842105263157894736842105263
210526315789473684210526315789473684
263157894736842105263157894736842105
315789473684210526315789473684210526
368421052631578947368421052631578947
421052631578947368421052631578947368
473684210526315789473684210526315789

Kanskje ikke så rart siden det første sifferet i en 18-sekvens ikke genererer noen mente.

Du vil finne stoff om parasittall på nettet og du vil se andre og smartere måter å beregne dem på. (parasitic numbers)