Calcola numeri pratici


18

Definizione

Un numero intero positivo nè un numero pratico (sequenza OEIS A005153 ) se tutti i numeri interi positivi più piccoli possono essere rappresentati come somme di divisori distinti di n.

Ad esempio, 18è un numero pratico: i suoi divisori sono 1, 2, 3, 6, 9 e 18 e gli altri numeri interi positivi inferiori a 18 possono essere formati come segue:

 4 = 1 + 3          5 = 2 + 3           7 = 1 + 6
 8 = 2 + 6          10 = 1 + 9         11 = 2 + 9
12 = 3 + 9 = 1 + 2 + 9 = 1 + 2 + 3 + 6
13 = 1 + 3 + 9      14 = 2 + 3 + 9      15 = 6 + 9
16 = 1 + 6 + 9      17 = 2 + 6 + 9

Ma 14non è un numero pratico: i suoi divisori sono 1, 2, 7 e 14 e non esiste alcun sottoinsieme di questi che si aggiunge a 4, 5, 6, 11, 12 o 13.

Sfida

Scrivere un programma, una funzione o verbo che prende in ingresso un numero intero positivo xe sia i ritorni o stampe il x th numero pratico, indicizzato da 1 per coerenza con OEIS. Il codice deve essere sufficientemente efficiente da poter gestire input fino a 250000 in meno di due minuti su un computer desktop ragionevole. (NB la mia implementazione di riferimento in Java gestisce 250000 in meno di 0,5 secondi e la mia implementazione di riferimento in Python lo gestisce in 12 secondi).

Casi test

Input        Expected output
1            1
8            18
1000         6500
250000       2764000
1000000      12214770
3000000      39258256

(IMHO) questo può anche essere interessante se vince il codice più veloce (per lingua?)
Visualizza nome

4
@SargeBorsch Quindi vedrai tabelle di 250.000 voci in tutte le risposte
Dr. belisarius,

@belisarius buon punto. ma penso che simili truffe possano essere facilmente bandite. Oppure il problema potrebbe richiedere risposte corrette per qualsiasi numero, ma ci sarebbero difficoltà nel farlo in una lingua senza numeri interi grandi nella libreria standard ...: /
Visualizza nome

Ho in mente un'ottimizzazione algoritmica, ma con le regole attuali sono troppo pigro per implementarlo: P
Visualizza nome

4
@SargeBorsch, se non vuoi giocare a golf il tuo codice non esitare a caricarlo su qualcosa come gist.github.com e rilascia un link in un commento qui o in chat. FWIW Preferisco il code golf con generosi limiti di prestazione al codice più veloce per due motivi: in primo luogo, la lunghezza del codice è misurabile in modo più oggettivo; in secondo luogo, introduce un elemento di compromesso: quali ottimizzazioni di velocità possono essere tralasciate per abbreviare il codice senza rovinare le prestazioni?
Peter Taylor,

Risposte:


5

J (99 caratteri)

f=:3 :0
'n c'=.0 1
while.c<y do.
'p e'=.__ q:n=.n+2
c=.c+*/(}.p)<:}:1+*/\(<:p^e+1)%<:p
end.
n+n=0
)

Poiché la dichiarazione del problema richiede un "programma, una funzione o un verbo ", qualcuno ha dovuto presentare una domanda J. La gente noterà che non ho davvero golf (!) O non l'ho ottimizzato. Come le altre voci, ho usato il teorema di Stewart, menzionato nel link OEIS, per verificare se ogni numero pari è pratico o meno.

Non ho accesso immediato a un "computer desktop ragionevole" con J installato. Sul mio netbook di sei anni f 250000calcola in 120,6 secondi, che non è del tutto inferiore a due minuti, ma presumibilmente su qualsiasi computer leggermente più ragionevole questo termina in tempo.


6

Mathematica, 126 121 caratteri

Grazie a Belisario.

Usando la formula su Wikipedia.

f=(i=j=1;While[j<#,If[And@@Thread[#[[;;,1]]<2+Most@DivisorSum[FoldList[#Power@@#2&,1,#],#&]&@FactorInteger@++i],j++]];i)&

Esempi:

f[1]

1

f[8]

18

f[250000]

2764000

Ci sono voluti 70 anni per calcolare f[250000]sul mio computer.


3
Penso che puoi ottenere prestazioni migliori a scapito di un carattere bypassando numeri dispari
Dr. belisarius

1
Nel ridurre il codice dall'invio di OEIS, hai rallentato l'esecuzione di 10 volte. Mi chiedo solo "perché pensi che il tuo codice sia molto più lento dell'esempio OEIS?"
DavidC

@belisarius Il tuo suggerimento riduce il tempo a metà, come previsto.
DavidC

2
Lo stesso in 119 caratteri:(i=j=1;While[j<#,If[And@@Thread[#[[;;,1]]<2+Most@DivisorSum[FoldList[#Power@@#2&,1,#],#&]&@FactorInteger@++i],j++]];i)&
Dr. belisarius

3

Haskell - 329

s 1=[]
s n=p:(s$div n p)where d=dropWhile((/=0).mod n)[2..ceiling$sqrt$fromIntegral n];p=if null d then n else head d
u=foldr(\v l@((n,c):q)->if v==n then(n,c+1):q else(v,1):l)[(0,1)]
i z=(z<2)||(head w==2)&&(and$zipWith(\(n,_)p->n-1<=p)(tail n)$scanl1(*)$map(\(n,c)->(n*n^c-1)`div`(n-1))n)where w=s z;n=u w
f=((filter i[0..])!!)

Esempi:

> f 1
1
> f 13
32
> f 1000
6500

Ecco una piccola suite di test (anteporre a quanto sopra):

import Data.Time.Clock
import System.IO

test x = do
    start <- getCurrentTime
    putStr $ (show x) ++ " -> " ++ (show $ f x)
    finish <- getCurrentTime
    putStrLn $ " [" ++ (show $ diffUTCTime finish start) ++ "]"

main = do
    hSetBuffering stdout NoBuffering
    mapM_ test [1, 8, 1000, 250000, 1000000, 3000000]

Risultati del test dopo essere stati compilati con ghc -O3:

1 -> 1 [0.000071s]
8 -> 18 [0.000047s]
1000 -> 6500 [0.010045s]
250000 -> 2764000 [29.084049s]
1000000 -> 12214770 [201.374324s]
3000000 -> 39258256 [986.885397s]

Quando provo questo in ghci si lamenta parse error on input `='. Devo usare qualche bandiera o altro?
Peter Taylor,

1
@PeterTaylor Non è possibile incollare definizioni di funzioni in ghci in quel modo. La cosa più semplice che puoi fare è salvarla asdf.hsed eseguirla ghci asdf.hs, quindi da lì potresti accederef
mniip

@PeterTaylor ghc --make -O3 [filename]. Potresti anche caricarlo in ghci con :l [filename]ma dato i vincoli temporali compilati è probabilmente il migliore. :)
Jonathan Van Matre,

@JonathanVanMatre come visto nel commento sopra, ghcicarica i file specificati nei suoi argomenti
mniip

Ah ok. Nel frattempo ce l'ho fatta funzionare con il tuo framework di test e ghc. Il tuo computer è più veloce del mio, ma è ancora profondamente all'interno del criterio di prestazione sul mio computer a 98 secondi.
Peter Taylor,

2

Javascript, 306 307 282B

function y(r){for(n=r-1,k=1;n;k++)if(p=[],e=[],c=0,P=s=1,!((x=k)%2|1==x)){while(x>1){for(f=x,j=2;j<=Math.sqrt(f);j++)if(f%j==0){f=j;break}f!=p[c-1]?(p.push(f),e.push(2),c++):e[c-1]++,x/=f}for(i=0;c>i;i++){if(p[i]>P+1){s=0;break}P*=(Math.pow(p[i],e[i])-1)/(p[i]-1)}s&&n--}return k-1}

250k circa 6s sul mio laptop.

Codice non golfato commentato: http://jsfiddle.net/82xb9/3/ ora con migliori test sigma e una migliore condizione if (grazie commenti)

Versioni pre-modifica: http://jsfiddle.net/82xb9/ http://jsfiddle.net/82xb9/1/


La domanda richiede una funzione o un programma (JS non ha verbi), quindi piuttosto che non contare la prima riga dovresti avvolgere la seconda riga in una dichiarazione di funzione e sostituire la finale k--;con return k-1. Sebbene ciò aumenti leggermente il conteggio dei byte, è possibile salvarne alcuni con elementi come la sostituzione p[i]>=P+2con p[i]>P+1(e probabilmente rimuovendo la chiamata di funzione interna e utilizzando breakinvece un ).
Peter Taylor,

Penso che la parte "testing sigma" possa essere riscritta sia per dimensioni che per velocità: jsfiddle.net/3DTSa . Sebbene questa soluzione JS sia molto veloce così com'è.
user2846289
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.