Stampa n numeri strani


12

Un numero strano è un numero che la somma dei divisori propri è maggiore del numero stesso e nessun sottoinsieme di divisori propri somma a quel numero.

Esempi:

70 è un numero strano perché i suoi divisori propri (1, 2, 5, 7, 10, 14 e 35) si sommano a 74, che è maggiore di 70 e nessuna combinazione di questi numeri si somma a 70.

18 non è un numero strano perché i suoi propri divisori (1, 2, 3, 4, 6, 9) si sommano a 25, che è maggiore di 18, ma 3, 6 e 9 si sommano a 18.

Il tuo compito è quello di scrivere il programma più breve che immetta tramite std-in qualsiasi numero n e calcola e stampa su un file o std-out i primi n numeri strani con separazione newline. Non è consentita la codifica rigida delle risposte (scusate per non averlo specificato all'inizio).

Per ulteriori esempi, consultare questa pagina: http://mathworld.wolfram.com/WeirdNumber.html


1
Quando questa domanda era nella sandbox, non ho commentato che dovresti aggiungere una regola "no hard-coding" perché è già presente nella parola "calcola". Incoraggio le persone a votare in basso e contrassegnare come non una risposta o di bassa qualità qualsiasi risposta che non tenti di calcolare il risultato. ( Meta discussione pertinente ).
Peter Taylor,

Risposte:


2

Mathematica 99 94 87

Spazi non necessari. Lento!:

j = i = 0;
While[j<#, i++; If[Union@Sign[Tr /@ Subsets@Most@Divisors@i-i]=={-1, 1}, j++; Print@i]]&

A scapito di pochi caratteri, questa è una versione più veloce che controlla solo numeri pari e salta multipli di quelli 6che non sono mai strani:

j = i = 0;
While[j < #, 
      i += 2; If[Mod[i, 6] != 0 && Union@Sign[Tr /@ Subsets@Most@Divisors@i - i] == {-1, 1}, 
                 j++; Print@i]] &@3

è ancora troppo lento per qualsiasi scopo utile. Trova i primi due in pochi secondi ma diventa sempre più lento con l'aumentare del numero di divisori.


Stavo cercando una soluzione simile sfruttando il fatto che numeri strani non sono pseudoperfetti, ma hai ottenuto molto più golfistico di quanto non fossi ancora riuscito. Molto bella!
Jonathan Van Matre,

@JonathanVanMatre Grazie :)
Dr. belisarius,

4

Haskell - 129

Sono sicuro che c'è molto da golf qui, ma dato che la competizione sembra bassa per ora, lo lancerò.

Non provare a eseguire questo, però, sono riuscito ad aspettare solo i due primi elementi, il terzo inizierà a prendere minuti.

(%)=filter
w n=take n$e%[1..]
e x=let d=((==0).mod x)%[1..x-1]in sum d>x&&all((/=x).sum)(i d)
i[]=[[]]
i(y:z)=map(y:)(i z)++(i z)

1
È la seconda volta che qualcuno a Haskell è meglio di me a Sage, accidenti: D
yo

2

Python 2.7 (255 byte)

import itertools as t
a=int(raw_input())
n=1
while a>0:
    d=[i for i in range(1,n/2+1) if not n%i]
    if all([n not in map(sum,t.combinations(d,i)) for i in range(len(d))]+[sum(d)>n]):
        print n
        a-=1
    n+=1

1

PHP, 267 byte

$n=$x=0;while($n<$argv[1]){$x++;for($i=1,$s=0,$d=array();$i<$x;$i++){if($x%$i){continue;}$s+=$i;$d[]=$i;}if($s<$x){continue;}$t=pow(2,$m=count($d));for($i=0;$i<$t;$i++){for($j=0,$s=0;$j<$m;$j++){if(pow(2,$j)&$i){$s+=$d[$j];}}if($s==$x){continue 2;}}$n++;print"$x\n";}

Ed ecco il codice sorgente originale:

$n = 0;
$x = 0;

while ($n < $argv[1]) {
    $x++;

    for ($i = 1, $sum = 0, $divisors = array(); $i < $x; $i++) {
        if ($x % $i) {
            continue;
        }

        $sum += $i;
        $divisors[] = $i;
    }

    if ($sum < $x) {
        continue;
    }

    $num = count($divisors);
    $total = pow(2, $num);

    for ($i = 0; $i < $total; $i++) {  
        for ($j = 0, $sum = 0; $j < $num; $j++) { 
            if (pow(2, $j) & $i) {
                $sum += $divisors[$j];
            }
        }

        if ($sum == $x) {
            continue 2;
        }
    }

    print "$x\n";
}

Noterai che ci vuole del tempo per emettere i numeri mentre sta eseguendo una verifica della forza bruta (dovresti arrivare a 70 abbastanza velocemente, però).


1

R, 164

r=0;x=1;n=scan();while(r<n){i=which(!x%%(2:x-1));if(sum(i)-1&&!any(unlist(lapply(2:sum(i|T),function(o)colSums(combn(i,o))==x)))&sum(i)>x){r=r+1;cat(x,"\n")};x=x+1}

Versione senza golf:

r = 0
x = 1
n = scan()
while(r < n) {
  i = which(!x %% (2:x - 1))
  if( sum(i) - 1 &&
       !any(unlist(lapply(2:sum(i | T),
                          function(o) colSums(combn(i, o)) == x))) &
       sum(i) > x
     ){ r = r + 1
        cat(x, "\n")
  }
  x = x + 1
}

Questo richiede del tempo a causa della forza bruta.


1

Rubino - 152

x=2;gets.to_i.times{x+=1 while((a=(1..x/2).find_all{|y|x%y==0}).reduce(:+)<=x||(1..a.size).any?{|b|a.combination(b).any?{|c|c.reduce(:+)==x}});p x;x+=1}

Ruby With ActiveSupport - 138

x=2;gets.to_i.times{x+=1 while((a=(1..x/2).find_all{|y|x%y==0}).sum<=x||(1..a.size).any?{|b|a.combination(b).any?{|c|c.sum==x}});p x;x+=1}

Davvero lento e sono quasi sicuro che ci sia ancora spazio per giocare a golf ...


1

Smalltalk, 143

((1to:(Integer readFrom:Stdin))reject:[:n||d|d:=(1to:n//2)select:[:d|(n\\d)=0].d sum<n or:[(PowerSet for:d)contains:[:s|s sum=n]]])map:#printCR

ingresso:

1000

produzione:

70
836

1

SageMath: 143 131 byte

x=1
def w():
 l=x.divisors()
 return 2*x>=sum(l)or max(2*x==sum(i)for i in subsets(l))
while n:
 while w():x+=1
 print x;n-=1;x+=1

Inoltre, non è nemmeno golfato, non c'è comunque molto da golf nel codice. La cosa più grande è che dovresti fare 2*x>=sum(l)prima il test , risparmierebbe molto tempo di calcolo. Bisogna rendersi conto che maxsui booleani è la orseconda cosa che w(x)è Falseper numeri strani e Trueper numeri non strani. Versione non golfata:

def w(x) :
 Divisors = x.divisors()
 return 2*x >= sum(Divisors) or max ( sum(SubS) == 2*x for SubS in subsets(Divisors) )

x=1

for k in xrange(n) :
 while w(x) : x += 1
 print x
 x += 1

1

C ++ - 458

Questa non è tutta la mia soluzione come ho dovuto chiedere a SO per aiuto nel calcolo della somma dei sottoinsiemi, ma tutto il resto è mio:

#include<iostream>
#include<vector>
using namespace std;
#define v vector<int>
#define r return
#define c const_iterator
v x(int i){v d;for(int k=1;k<i;k++)if(i%k==0)d.push_back(k);r d;}bool u(v::c i,v::c e,int s){if(s==0)r 0;if(i==e)r 1;r u(i+1,e,s-*i)&u(i+1,e,s);}bool t(v&d,int i){bool b=u(d.begin(),d.end(),i);if(b)cout<<i<<endl;r b;}int main(){v d;int n;cin>>n;for(int i=2,j=0;j<n;i++){d=x(i);int l=0;for(int k=0;k<d.size();k++)l+=d[k];if(l>i)if(t(d,i))j++;}}

Versione lunga:

#include<iostream>
#include<vector>
using namespace std;

vector<int> divisors(int i) {

    vector<int> divs;
    for(int k = 1; k < i; k++)
        if(i%k==0)
            divs.push_back(k);
    return divs;
}

bool u(vector<int>::const_iterator vi, vector<int>::const_iterator end, int s) {

    if(s == 0) return 0;
    if(vi == end) return 1;
    return u(vi + 1, end, s - *vi) & u(vi + 1, end, s);
}

bool t(vector<int>&d, int i) {

    bool b = u(d.begin(), d.end(), i);
    if(b) cout<< i << endl;
    return b;
}

int main() {

    vector<int> divs;
    int n;
    cin>>n;

    for(int i = 2, j = 0; j < n; i++) {
        divs = divisors(i);

        int sum_divs = 0;
        for(int k = 0; k < divs.size(); k++)
            sum_divs += divs[k];

        if(sum_divs > i)
            if(t(divs, i))
                j++;
    }
}

Attualmente ha calcolato solo i primi due (70 e 836). L'ho ucciso dopo.


Sarebbe bello pubblicare anche una versione leggibile, soprattutto dal momento che la rendi una sola riga;)
yo

@tohecz Certo, fammi modificare.

@tohecz ho finito.

1

Perl, 173

Vorrei aggiungere un'altra soluzione inutile. Questa soluzione è così lenta che non può nemmeno produrre nulla oltre il primo numero strano. Oserei dire che è la più lenta di tutte le soluzioni qui.

$n=<>;$i=2;while($n){$b=qr/^(?=(.+)\1{2}$)((.+)(?=.*(?(2)(?=\2$)\3.+$|(?=\1$)\3.+$))(?=.*(?=\1$)\3+$))+/;$_='x'x3x$i;if(/$b/&&($+[0]>$i)&&!/$b\1{2}$/){print"$i\n";$n--}$i++}

dimostrazione

Lo stesso codice scritto in Java (di cui mi sento più a mio agio) non è nemmeno in grado di riconoscere il 2o numero strano (836) e ho già inserito il numero direttamente nel metodo di verifica (invece di eseguire il ciclo e controllare ogni numero).

Il nucleo di questa soluzione sta nella regex:

^(?=(.+)\1{2}$)((.+)(?=.*(?(2)(?=\2$)\3.+$|(?=\1$)\3.+$))(?=.*(?=\1$)\3+$))+

E come la stringa è impostata per essere 3 volte il numero che stiamo controllando.

La lunghezza della stringa è impostata per essere 3 volte il numero che stiamo controllando i: il primo 2 iserve per abbinare la somma dei fattori e l'ultimo 1 iè riservato per verificare se un numero è un fattore di i.

(?=(.+)\1{2}$) viene utilizzato per acquisire il numero che stiamo verificando.

((.+)(?=.*(?(2)(?=\2$)\3.+$|(?=\1$)\3.+$))(?=.*(?=\1$)\3+$))+corrisponde ai fattori del numero. L'iterazione successiva corrisponderà a un fattore inferiore rispetto a un'iterazione precedente.

  • Possiamo vedere che queste 2 parti (.+)e (?=.*(?=\1$)\3+$)insieme selezionano un fattore del numero da controllare.
  • (?=.*(?(2)(?=\2$)\3.+$|(?=\1$)\3.+$)) si assicura che il fattore selezionato sia più piccolo del numero che viene controllato nella prima iterazione e sia più piccolo del fattore precedente nelle iterazioni successive.

Il regex cerca di far corrispondere quanti più fattori del numero può entro il limite di 2 i. Ma non ci interessa il valore effettivo della somma dei divisori, ci importa solo se il numero è abbondante.

Quindi la seconda regex, che è la prima regex con \1{2}$aggiunta. Di conseguenza, regex si assicura che la somma di (alcuni) fattori del numero da verificare sia uguale al numero stesso:

^(?=(.+)\1{2}$)((.+)(?=.*(?(2)(?=\2$)\3.+$|(?=\1$)\3.+$))(?=.*(?=\1$)\3+$))+\1{2}$

Il vincolo aggiunto farà sì che il motore regex esegua una ricerca di backtracking su tutti i possibili sottogruppi di fattori, quindi sarà estremamente lento.


1

Perl, 176 174 byte

$n=<>;$i=9;X:while($n){@d=grep{!($i%$_)}1..$i-1;$l=0;map{$a=$_;$s=0;$s+=$d[$_]for grep{2**$_&$a}0..@d-1;$i++,next X if$s==$i;$l=1 if$s>$i}0..2**@d-1;$n--,print$i,$/if$l;$i++}

Il numero di numeri strani è previsto in STDIN e i numeri trovati vengono stampati su STDOUT.

Versione Ungolfed

#!/usr/bin/env perl
use strict;
$^W=1;

# read number from STDIN
my $n=<>;
# $i is the loop variable that is tested for weirdness
my $i=9; # better start point is 70, the smallest weird number
# $n is the count of numbers to find
X: while ($n) {
    # find divisors and put them in array @divisors
    my @divisors = grep{ !($i % $_) } 1 .. $i-1; # better: 1 .. int sqrt $i
    # $large remembers, if we have found a divisor sum greater than the number
    my $large = 0;
    # looping through all subsets. The subset of divisors is encoded as
    # bit mask for the divisors array.
    map {
        my $subset = $_;
        # calculate the sum for the current subset of divisors
        my $sum = 0;
        map { $sum += $divisors[$_] }
            grep { 2**$_ & $subset }
                0 .. @divisors-1;
        # try next number, if the current number is pseudoperfect
        $i++, next X if $sum == $i; # better: $i+=2 to skip even numbers
        $large = 1 if $sum > $i;
    } 0 .. 2**@divisors - 1;
    # print weird number, if we have found one
    $n--, print "$i\n" if $large;
    $i++; # better: $i+=2 to skip even numbers
}
__END__

limitazioni

  • Forza lenta e bruta.
  • Il conteggio dei divisori per un numero è limitato al "testimone" di numeri interi in Perl.
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.