Segna la routine olimpica della vite olimpica di Tarzan


32

Gli scambisti olimpici svolgono la loro routine su alberi standard. In particolare, l'albero standard nha vertici verso l' 0alto n-1e gli spigoli che collegano ciascun vertice diverso da zero aal vertice n % asottostante. Quindi, ad esempio, l'albero standard 5 è simile al seguente:

3
|
2   4
 \ /
  1
  |
  0

perché il resto quando 5 è diviso per 3 è 2, il resto quando 5 è diviso per 2 o per 4 è 1, e il resto quando 5 è diviso per 1 è 0.

Quest'anno Tarzan difenderà il suo oro con nuove routine, ognuna delle quali inizia al vertice n - 1, oscilla verso il vertice n - 2, continua fino al vertice n - 3, ecc., Fino a quando alla fine non scende sul vertice 0.

Il punteggio per una routine è la somma dei punteggi per ogni swing (incluso lo smontaggio), e il punteggio per uno swing è la distanza all'interno dell'albero tra i suoi punti iniziale e finale. Pertanto, la routine di Tarzan su Standard Tree 5 ha un punteggio di 6:

  • un'oscillazione da 4a 3tre punti (giù, su, su),
  • uno swing da 3a 2segnare un punto (in basso),
  • un'oscillazione da 2a 1segna un punto (in basso), e
  • lo smontaggio da 1al 0ottiene un punto (verso il basso).

Scrivi un programma o una funzione che, dato un numero intero positivo n, calcola il punteggio della routine di Tarzan su Albero standard n. Ingressi e uscite di esempio:

 1 ->  0
 2 ->  1
 3 ->  2
 4 ->  6
 5 ->  6
 6 -> 12
 7 -> 12
 8 -> 18
 9 -> 22
10 -> 32
11 -> 24
12 -> 34
13 -> 34
14 -> 36
15 -> 44
16 -> 58
17 -> 50
18 -> 64
19 -> 60
20 -> 66
21 -> 78
22 -> 88
23 -> 68
24 -> 82

Le regole e il punteggio del codice sono come al solito per il .


9
Non riesco a trovare questa sequenza in OEIS. Bella domanda
Leaky Nun,

8
Specifiche eccellenti!
xnor

1
@LeakyNun Dovrebbe essere aggiunto però. È una sequenza molto originale! (Anche senza il
retroscena

Risposte:


12

C, 98 97 byte

F(i){int c[i],t=i-2,n=0,p;for(;++n<i;)for(p=c[n]=n;p=i%p;c[p]=n)t+=c[p]<n-1;return i>2?t*2:i-1;}

Questo calcola la distanza tra ogni coppia di punti con la seguente formula:

  • Aggiungi la distanza dalla radice al nodo A
  • Aggiungi la distanza dalla radice al nodo B
  • Sottrai 2 * la lunghezza della radice comune di A e B

Questo ha il vantaggio che, quando applicato a tutte le coppie, è lo stesso di:

  • Aggiungi 2 * la distanza dalla radice a ciascun nodo
  • Sottrai 2 * la lunghezza della radice comune di ciascuna coppia di nodi
  • Sottrai la distanza dalla radice al primo nodo
  • Sottrai la distanza dalla radice all'ultimo nodo

Per semplificare la logica, supponiamo di passare dal nodo 0 al nodo n-1, anziché da n-1 a 0 come afferma la domanda. La distanza dal nodo radice al nodo 0 è ovviamente 0 (sono gli stessi). E possiamo vedere che per (la maggior parte) degli alberi, la distanza dall'ultimo nodo alla radice è 2:

                    n+1 % n = 1  for all n > 1
and:                  n % 1 = 0  for all n >= 0
therefore:  n % (n % (n-1)) = 0  for all n > 2

Questo significa che abbiamo alcuni casi speciali (n <2) ma possiamo spiegarli abbastanza facilmente.

Abbattersi:

F(i){                               // Types default to int
    int c[i],                       // Buffer for storing paths
        t=i-2,                      // Running total score
        n=0,                        // Loop index
        p;                          // Inner loop variable
    for(;++n<i;)                    // Loop through all node pairs (n-1, n)
        for(p=c[n]=n;p=i%p;c[p]=n)  //  Recurse from current node (n) to root
            t+=c[p]<n-1;            //   Increase total unless this is a common
                                    //   node with the previous path
    return i>2?   :i-1;             // Account for special cases at 1 and 2
               t*2                  // For non-special cases, multiply total by 2
}

Grazie @feersum per 1 byte salvato


Bonus: alberi!

Ho scritto un programma rapido e sporco per vedere che aspetto hanno questi alberi. Ecco alcuni dei risultati:

6:

5 4  
| |  
1 2 3
 \|/ 
  0  

8:

  5      
  |      
7 3   6  
|  \ /   
1   2   4
'--\|/--'
    0    

13:

   08              
    |              
11 05   10 09 07   
 |   \ /    |  |   
02   03    04 06 12
 '-----\  /---'--' 
        01         
         |         
        00         

19:

   12                       
    |                       
   07   14                  
     \ /                    
     05    15 11            
       \  /    |            
17      04    08 16 13 10   
 |       '-\  /--'   |  |   
02          03      06 09 18
 '---------\ |/-----'--'--' 
            01              
             |              
            00              

49:

                         31                                                    
                          |                                                    
           30            18   36                                               
            |              \ /                                                 
           19   38 27      13    39 29    32                                   
             \ /    |        \  /    |     |                                   
   26        11    22 44      10    20 40 17   34                              
    |         '-\  /--'        '-\  /--'    \ /                                
47 23   46       05               09        15    45 43 41 37 33 25    35 28   
 |   \ /          '--------------\ |/-------'-----'   |  |  |  |  |     |  |   
02   03                           04                 06 08 12 16 24 48 14 21 42
 '----'--------------------------\ |/----------------'--'--'--'--'--'    \ |/  
                                  01                                      07   
                                   '-----------------\  /-----------------'    
                                                      00                       

Ci sono alcune parentesi superflue nella dichiarazione di ritorno.
feersum

@feersum d'oh! Non erano sempre superflui, ma poi ho cambiato la gestione dei casi speciali. Grazie!
Dave,

3
Adoro le visualizzazioni!
Edward,

7

Python 2, 85 byte

def f(a,i=1):h=lambda n:n and{n}|h(a%n)or{0};return i<a and len(h(i)^h(i-1))+f(a,i+1)

7

Perl, 65 59 55 54 byte

Include +2 per -ap

Esegui con la dimensione dell'albero su STDIN:

for i in `seq 24`; do echo -n "$i: "; vines.pl <<< $i; echo; done

vines.pl:

#!/usr/bin/perl -ap
$_=map{${"-@F"%$_}|=$_=$$_|$"x$p++.1;/.\b/g}1-$_..-1

Spiegazione

Se riscrivi l'albero

3
|
2   4
 \ /
  1
  |
  0

a uno in cui ciascun nodo contiene l'insieme di tutti i suoi antenati e se stesso:

 {3}
  |
{2,3}   {4}
   \    /
    \  /
  {1,2,3,4}
      |
 {0,1,2,3,4}

Quindi possiamo descrivere ad esempio tutti i nodi il percorso da 4 a 3 come:

  • Tutti i nodi che contengono 3 ma non 4 (scendendo da 3)
  • Tutti i nodi che contengono 4 ma non 3 (scendendo da 4)
  • Il nodo più alto che contiene sia 3 che 4 (il join)

Il numero di bordi è uno in meno del numero di nodi, quindi possiamo usarlo per ignorare il punto di unione, quindi il numero di bordi sul percorso da 4 a 3 è 3 perché:

  • Il numero di nodi che contengono 3 ma non 4: 2 nodi
  • Il numero di nodi che contengono un nodo 4 ma non 3: 1

Si noti che questo funziona anche per un percorso che scende direttamente alla sua destinazione, ad esempio per il percorso da 3 a 2 il numero di bordi è 1 perché:

  • Il numero di nodi che contengono 2 ma non 3: 0 nodi
  • Il numero di nodi che contengono un nodo 3 ma non 2: 1

Possiamo quindi riassumere tutte queste combinazioni.

Se invece guardi solo un nodo, ad esempio nodo 2 con set di antenati {2,3}. Questo nodo contribuirà una volta durante l'elaborazione del percorso 2 to 1perché contiene un 2 ma non un 1. Non contribuirà nulla per il percorso 3 to 2poiché ha sia 2 che 3, ma contribuirà una volta durante l'elaborazione del percorso 4 to 3poiché ha 3 ma no 4. In generale un numero nell'insieme di antenati di un nodo contribuirà uno per ciascun vicino (uno più basso di più alto) che non è nell'insieme. Ad eccezione dell'elemento massimo (4 in questo caso) che contribuisce solo per il vicino basso 3 poiché non esiste alcun percorso5 to 4. Lo 0 similare è unilaterale, ma poiché 0 è sempre alla radice dell'albero e contiene tutti i numeri (è il join finale e non contiamo i join) non c'è mai alcun contributo da 0, quindi è più facile lasciare il nodo 0 del tutto fuori.

Quindi possiamo anche risolvere il problema osservando il set di antenati per ciascun nodo, contando i contributi e sommando tutti i nodi.

Per elaborare facilmente i vicini, rappresenterò i set di antenati come una stringa di spazi e 1 dove ogni 1 nella posizione p rappresenta che n-1-p è un antenato. Quindi, ad esempio, nel nostro caso di n=5un 1 nella posizione 0 indica che 4 è un antenato. Lascerò fuori gli spazi finali. Quindi la rappresentazione reale dell'albero che costruirò è:

" 1"
  |
" 11"   "1"
   \    /
    \  /
   "1111"

Si noti che ho lasciato fuori il nodo 0 che sarebbe rappresentato dal "11111"perché ignorerò il nodo 0 (non contribuisce mai).

Gli antenati senza vicini inferiori sono ora rappresentati dalla fine di una sequenza di 1. Gli antenati senza vicini più alti sono ora rappresentati dall'inizio di una sequenza di 1, ma dovremmo ignorare qualsiasi inizio di una sequenza all'inizio di una stringa poiché ciò rappresenterebbe il percorso 5 to 4che non esiste. Questa combinazione è esattamente abbinata al regex /.\b/.

La creazione delle stringhe dell'antenato viene eseguita elaborando tutti i nodi nell'ordine n-1 .. 1e impostando un 1 nella posizione per il nodo stesso e facendo un "o" nel discendente.

Con tutto ciò che il programma è abbastanza facile da capire:

-ap                                                  read STDIN into $_ and @F

   map{                                    }1-$_..-1 Process from n-1 to 1,
                                                     but use the negative
                                                     values so we can use a
                                                     perl sequence.
                                                     I will keep the current
                                                     ancestor for node $i in
                                                     global ${-$i} (another
                                                     reason to use negative
                                                     values since $1, $2 etc.
                                                     are read-only
                       $$_|$"x$p++.1                 "Or" the current node
                                                     position into its ancestor
                                                     accumulator
                    $_=                              Assign the ancestor string
                                                     to $_. This will overwrite
                                                     the current counter value
                                                     but that has no influence
                                                     on the following counter
                                                     values
       ${"-@F"%$_}|=                                 Merge the current node
                                                     ancestor string into the
                                                     successor
                                                     Notice that because this
                                                     is an |= the index
                                                     calculation was done
                                                     before the assignment
                                                     to $_ so $_ is still -i.
                                                     -n % -i = - (n % i), so
                                                     this is indeed the proper
                                                     index
                                     /.\b/g          As explained above this
                                                     gives the list of missing
                                                     higher and lower neighbours
                                                     but skips the start
$_=                                                  A map in scalar context
                                                     counts the number of
                                                     elements, so this assigns
                                                     the grand total to $_.
                                                     The -p implicitly prints

Si noti che la sostituzione /.\b/dal /\b/risolve la versione andata e ritorno di questo problema dove tarzan prende anche il percorso0 to n-1

Alcuni esempi di come appaiono le stringhe degli antenati (fornite in ordine n-1 .. 1):

n=23:
1
 1
  1
   1
    1
     1
      1
       1
        1
         1
          1
          11
         1  1
        1    1
       1      1
      11      11
     1          1
    11  1    1  11
   1              1
  1111  11  11  1111
 111111111  111111111
1111111111111111111111
edges=68

n=24:
1
 1
  1
   1
    1
     1
      1
       1
        1
         1
          1
           1
          1 1
         1   1
        1     1
       1       1
      1         1
     1  1     1  1
    1             1
   11    1   1    11
  1   1         1   1
 1        1 1        1
1                     1
edges=82

Spiacenti, non mi ero reso conto che la tua modifica fosse vecchia di pochi secondi. Comunque, approccio e spiegazione molto accurati!
FryAmTheEggman,

@FryAmTheEggman Nessun problema, stavamo solo risolvendo lo stesso identico problema di layout. Comunque, sì, sono abbastanza contento di come tutti i pezzi si sono riuniti in questo programma. Al momento non vedo alcun grasso da tagliare ..
Ton Hospel

3

Mathematica, 113 103 102 byte

(r=Range[a=#-1];Length@Flatten[FindShortestPath[Graph[Thread[r<->Mod[a+1,r]]],#,#2]&@@{#,#-1}&/@r]-a)&

-10 byte grazie a @feersum; -1 byte grazie a @MartinEnder

Quanto segue è molto più veloce (ma purtroppo più lungo, a 158 byte ):

(a=#;If[a<4,Part[-{1,1,1,-6},a],If[EvenQ@a,-2,1]]+a+4Total[Length@Complement[#,#2]&@@#&/@Partition[NestWhileList[Mod[a,#]&,#,#!=0&]&/@Range@Floor[a/2],2,1]])&

Credo che tu possa assegnare le cose senza usare With. Inoltre sembra che ogni volta che Rangeviene utilizzato, aè l'argomento, in modo che possa essere preso in considerazione.
feersum,

1
r=Range[a=#-1]salva un byte.
Martin Ender,

2

J, 37 byte

[:+/2(-.+&#-.~)/\|:@(]|~^:(<@>:@[)i.)

Uso:

   f=.[:+/2(-.+&#-.~)/\|:@(]|~^:(<@>:@[)i.)
   f 10
32
   f every 1+i.20
0 1 2 6 6 12 12 18 22 32 24 34 34 36 44 58 50 64 60 66

Provalo online qui.


Sarei interessato a vedere una ripartizione di come funziona. Anche il servizio tryj.tk sembra non funzionare ("impossibile leggere localStorage ..." e "$ (...) .terminal non è una funzione")
Dave,

@Dave quel sito non funziona anche per me su Chrome, ma funziona se provo a utilizzare IE o Edge, tuttavia ti consiglio di installare J ( link ) se ti interessa!
miglia

@miles Weird, per me funziona con tutti i browser (FF, Chrome, IE).
randomra,

Ha funzionato per me utilizzando Chrome, ma ha smesso di funzionare alcuni mesi fa e ha iniziato a rispondere con un messaggio di errore simile a quello di Dave
miglia

@Edward Lo farà quando trovo un po 'di tempo.
randomra,

1

JavaScript (ES6), 118 116 byte

n=>[...Array(n)].map(g=(_,i)=>i?[...g(_,n%i),i]:[],r=0).reduce(g=(x,y,i)=>x.map(e=>r+=!y.includes(e))&&i?g(y,x):x)|r

La mancanza di una funzione di differenza impostata fa davvero male, ma una certa ricorsione creativa riduce un po 'il conteggio dei byte. Modifica: salvato 2 byte rimuovendo un parametro non necessario.

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.