Page 1 of 1

Programmeringsutfordring!

Posted: 18/11-2012 00:28
by Aleks855
Laga denne lille nøtta etter jeg fant ut at det finnes en tallgruppe som har en litt små-artig egenskap. Få høre hva dere syns! :)

En tallgruppe kalt "narsissistiske tall" har følgende egenskap:

For hvert siffer, opphøy sifferet i antall sifre i tallet, summer dem, og få samme tall som du startet med.


Eksempel:

[tex]153 \ \text{har 3 sifre} \ \rightarrow \ 1^3 + 5^3 + 3^3 = 153[/tex]

Finn det første narsissistiske tallet større enn 100.000.000.

Siden dette kan spores opp på nettet, vis også fremgangsmåte. Gjerne i form av en kodesnutt i valgfritt programmeringsspråk.

(Forvent at programmet ditt bruker et minutt eller to... eller flere, avhengig av hvordan ditt programmeringsspråk kompilerer kode.)

La meg høre hva dere syns om nøtta. Den burde være grei å kode, men jeg forventer ikke at folk skal få den til for hånd :lol:

-Aleks

Posted: 18/11-2012 00:50
by 2357
Hvis man ikke er opptatt av effektivitet, holder det med følgende linjer i Python.

Code: Select all

tall = 100000000

while sum([int(i)**len(str(tall)) for i in str(tall)]) != tall:
    tall += 1

print tall
Så var det det å huske nok syntaks til å lage et program som er effektivt nok til at man orker å kjøre det.

Posted: 18/11-2012 01:12
by Aleks855
Nesten så man hater Python for å bruke så få linjer. :lol:

Kunne vært interessant hvis du hadde lagt inn en timer som sjekker hvor lang tid den brukte på å finne det første tallet. :)

Posted: 18/11-2012 01:27
by Aleks855
Min brukte 209 sekunder. Det kan sikkert overgås ganske lett, siden jeg har litt debugging-prints og diverse O(n)-snadder som kanskje ikke er nødvendig.

Posted: 18/11-2012 03:49
by Gustav
Aleks855 wrote:Min brukte 209 sekunder. Det kan sikkert overgås ganske lett, siden jeg har litt debugging-prints og diverse O(n)-snadder som kanskje ikke er nødvendig.
Hvilket språk brukte du?

Matlab:

Code: Select all

tic
n = 10^8;
k = 0;
while k ~= n
    k = 0;
    n = n+1;
    for i = 0:8
        k = k + mod(floor(n/(10^i)),10)^9;
        if k > n
            break;
        end
    end
end
toc
fprintf('%d \n',n)


Elapsed time is 77.422856 seconds.

146511208

Posted: 18/11-2012 04:51
by Kork
Laget en raskt, jeg har bare grunnkurs i java så dette er ikke særlig elegant :D :D

Code: Select all

    int[] i = {1,0,0,0,0,0,0,0,0};
    int ii, sum, siffer = i.length;
    do {
      ii = 0; sum = 0;
      for(int j=siffer;j>0;j--) {
        ii+=i[j-1]*Math.pow(10,Math.abs(siffer-j));
        sum+=Math.pow(i[j-1], siffer);
      }
      boolean tallPlussEn = false;
      for(int j=siffer;j>0 && !tallPlussEn;j--) {
        if(i[j-1]!=9) {
          i[j-1]++;
          tallPlussEn=true;
          
          for(int jj=j;jj<siffer;jj++) {
            i[jj]=0;
          }
        }
      }
    } while(sum!=ii);
142 sekunder på en seig bærbar

Posted: 18/11-2012 05:54
by Gustav
Er jo en grei oppgave. Ligner på project Euler. Kanskje litt for enkel med mindre man setter krav til max kjøretid? Det burde være mulig å få kjøretida ned på et vesentlig lavere nivå.

Oppfølger:

Definisjon: Et positivt heltall n kalles en perfekt digital invariant (PDI) dersom det fins et positivt heltall m slik at summen av sifrene opphøyd i m er lik tallet selv.

F.eks. er [tex]n=4150=4^5+1^5+5^5+0^5[/tex] en PDI med [tex]m=5[/tex].

(Alle narsissistiske tall er også PDI med m=antall siffer i tallet)

Hva er minste PDI større enn [tex]10^7[/tex]?

Posted: 18/11-2012 16:01
by Emilga
Programmet (Python)

Code: Select all

def strip(n):
    a = []
    while n:
        a.append(n%10)
        n = n/10
    return a

n = 10**7+1
found = 0
while not found:
    a = strip(n)
    i = 1
    b = [x**i for x in a]
    while sum(b) <= n and sum(b) < sum([x**(i+1) for x in a]):
        if sum(b) == n:
            print(n,i)
            found = 1
        i+=1
        b = [x**i for x in a]
    n += 1
returnerer (n,m) = (14459929, 7). Programmet kan nok lages mer effektivt enn dette.

Posted: 18/11-2012 18:54
by Gustav
Ser bra ut.

Enda en oppfølger:

Definisjon: La en tallfølge [tex]\{H_n\}_{n\in\mathbb{N}\setminus \{0\}}[/tex] være gitt ved at

[tex]H_n = \sum_{i=1}^n \frac{1}{i^2}[/tex] for alle positive heltall n.

Anta at vi skriver alle brøkene i følgen [tex]H_n[/tex] på redusert form, slik at største felles faktor til teller og nevner er 1. (Dersom [tex]H_n=\frac{a}{b}[/tex] så er [tex]gcd(a,b)=1[/tex], a og b er heltall)

Altså er

[tex]H_1 = 1[/tex],
[tex]H_2 = 1+\frac{1}{2^2} = \frac{5}{4}[/tex]
[tex]H_3 = 1+\frac{1}{2^2}+\frac{1}{3^2}=\frac{49}{36}[/tex] osv.

La [tex]W_n[/tex] være definert som telleren til [tex]H_n[/tex].

Altså får vi en heltallsfølge med

[tex]W_1 = 1[/tex]
[tex]W_2 = 5[/tex]
[tex]W_3 = 49[/tex]
osv.

Det første tallet i følgen [tex]\{W_n\}[/tex] som er primisk er derfor [tex]W_2=5[/tex].

Finn de neste 3 tallene i følgen [tex]W_n[/tex] som er primtall.

Posted: 18/11-2012 21:01
by Emilga
Her har du vært lur! Steinaldermetoden/brute force funker ikke:
Output:

Code: Select all

Leddet 2 har primtalls-teller: (5, 4)
Leddet 7 har primtalls-teller: (266681, 176400)

...
MemoryError
Kode:

Code: Select all

def gcd(a,b):
    # returnerer sfd(a,b)
    d = 1
    for i in range(1,min(a,b)+1):
        if not a%i and not b%i:
            d = i
    return d

def reduser(a):
    #input: touple a tolket som broek
    #output: touple (b,c) lik reduserte broek a
    d = gcd(a[0],a[1])
    b = a[0]/d
    c = a[1]/d
    return (b,c)

def isprime(n):
    # input: heltall n
    # output: 1 hvis n er primsk, 0 hvis n er sammensatt
    for i in range(2,n):
        if not n%i:
            return 0
    return 1

# H[k] er H_k som definert i oppgaveteksten
H = [(1,1),(1,1)]

# Antall primtall W_k funnet.
primtall = 0
# Finn 4 av dem
while primtall <= 4:
    # Legg til neste ledd i foelgen H ved aa benytte seg
    # av siste utregnede H-verdi
    l = len(H)-1
    Hl = H[l]
    Hl1 = (Hl[0]*(l+1)**2 + Hl[1], Hl[1]*(l+1)**2)
    Hl1 = reduser(Hl1)
    H.append(Hl1)
    # Sjekk om telleren er et primtall
    if isprime(Hl1[0]):
        print "Leddet %s har primtalls-teller: %s" % (l+1,Hl1)
        primtall += 1

Posted: 18/11-2012 21:51
by Gustav
Det er vel isprime-funksjonen din som bør utbedres endel..

Posted: 18/11-2012 22:07
by Emilga
Ja. Har endret isprime til å bruke at hvis et tall n er samensatt, så har det en faktor som er lavere enn sqrt(n). Bytter også ut range(..) med en while-løkke som bare sammenligner to heltall. Tror det er billigere, men er ikke sikker... Det tar dog fortsatt skrekkelig lang tid, så vet ikke om jeg ender opp med en løsning uansett. :)

Code: Select all

def gcd(a,b):
    # returnerer sfd(a,b)
    d = 1
    i = 1
    while i <= min(a,b)+1:
        if not a%i and not b%i:
            d = i
        i+=1
    return d

def reduser(a):
    #input: touple a tolket som broek
    #output: touple (b,c) lik reduserte broek a
    d = gcd(a[0],a[1])
    b = a[0]/d
    c = a[1]/d
    return (b,c)

def isprime(n):
    # input: heltall n
    # output: 1 hvis n er primsk, 0 hvis n er sammensatt
    i = 2
    while i <= int(n**.5)+1:
        if not n%i:
            return 0
        i+=1
    return 1

# H[k] er H_k som definert i oppgaveteksten
H = [(1, 1), (1, 1)]

#[(1, 1), (1, 1), (5, 4), (49, 36), (205, 144), (5269, 3600), (5369, 3600), (266681, 176400), (1077749, 705600), (9778141, 6350400), (1968329, 1270080), (239437889, 153679680)]

# Antall primtall W_k funnet.
primtall = 0
# Finn 4 av dem
while primtall <= 4:
    # Legg til neste ledd i foelgen H ved aa benytte seg
    # av siste utregnede H-verdi
    l = len(H)-1
    Hl = H[l]
    Hl1 = (Hl[0]*(l+1)**2 + Hl[1], Hl[1]*(l+1)**2)
    Hl1 = reduser(Hl1)
    H.append(Hl1)
    # Sjekk om telleren er et primtall
    if isprime(Hl1[0]):
        print "Leddet %s har primtalls-teller: %s" % (l+1,Hl1)
        primtall += 1
    else:
        print "Leddet %s har ikke primtalls-teller: %s" % (l+1,Hl1)


Posted: 18/11-2012 22:55
by Gustav
Ja, kan tenke meg at det tar litt tid.

Tips: Du kan korte ned kjøretiden med å bruke følgende teorem:

http://mathworld.wolfram.com/WolstenholmesTheorem.html

edit: Python-løsningen min tar for øvrig 200 sek. på en middels kjapp laptop