Conversione di base con stringhe


16

introduzione

Abbiamo avuto alcune sfide di conversione di base qui in passato, ma non molte progettate per affrontare numeri di lunghezza arbitraria (vale a dire numeri che sono abbastanza lunghi da superare il tipo di dati intero), e di questi, la maggior parte si è sentita un po ' complicato. Sono curioso di sapere come può arrivare un cambiamento di codice base come questo.

Sfida

Scrivi un programma o una funzione nella lingua che preferisci in grado di convertire una stringa di una base in una stringa di un'altra base. L'input deve essere il numero da convertire (stringa), dalla base (numero base-10), alla base (numero base-10) e il set di caratteri (stringa). L'output dovrebbe essere il numero convertito (stringa).

Alcuni ulteriori dettagli e regole sono i seguenti:

  • Il numero da convertire sarà un numero intero non negativo (poiché -e .potrebbe essere nel set di caratteri). Così sarà anche l'output.
  • Gli zeri iniziali (il primo carattere nel set di caratteri) devono essere tagliati. Se il risultato è zero, dovrebbe rimanere una singola cifra zero.
  • L'intervallo di base minimo supportato è compreso tra 2 e 95, costituito dai caratteri ASCII stampabili.
  • L'input per il numero da convertire, il set di caratteri e l'output devono essere tutti del tipo di dati stringa. Le basi devono essere del tipo di dati intero base-10 (o numeri interi float).
  • La lunghezza della stringa del numero di input può essere molto grande. È difficile quantificare un minimo ragionevole, ma mi aspetto che sia in grado di gestire almeno 1000 caratteri e completare l'inserimento di 100 caratteri in meno di 10 secondi su una macchina decente (molto generoso per questo tipo di problema, ma non voglio velocità per essere al centro).
  • Non è possibile utilizzare le funzioni di cambio di base integrate.
  • L'input del set di caratteri può essere in qualsiasi disposizione, non solo nel tipico 0-9a-z ... ecc.
  • Supponiamo che verrà utilizzato solo un input valido. Non preoccuparti della gestione degli errori.

Il vincitore sarà determinato dal codice più breve che soddisfa i criteri. Saranno selezionati in almeno 7 giorni base-10 o se / quando ci sono stati abbastanza contributi. In caso di pareggio, il codice che corre più veloce sarà il vincitore. Se abbastanza vicino in termini di velocità / prestazioni, la risposta che è arrivata prima vince.

Esempi

Ecco alcuni esempi di input e output che il tuo codice dovrebbe essere in grado di gestire:

F("1010101", 2, 10, "0123456789")
> 85

F("0001010101", 2, 10, "0123456789")
> 85

F("85", 10, 2, "0123456789")
> 1010101

F("1010101", 10, 2, "0123456789")
> 11110110100110110101

F("bababab", 2, 10, "abcdefghij")
> if

F("10", 3, 2, "0123456789")
> 11

F("<('.'<)(v'.'v)(>'.'>)(^'.'^)", 31, 2, "~!@#$%^v&*()_+-=`[]{}|';:,./<>? ")
> !!~~~~~~~!!!~!~~!!!!!!!!!~~!!~!!!!!!~~!~!~!!!~!~!~!!~~!!!~!~~!!~!!~~!~!!~~!!~!~!!!~~~~!!!!!!!!!!!!~!!~!~!~~~~!~~~~!~~~~~!~~!!~~~!~!~!!!~!~~

F("~~~~~~~~~~", 31, 2, "~!@#$%^v&*()_+-=`[]{}|';:,./<>? ")
> ~

F("9876543210123456789", 10, 36, "0123456789abcdefghijklmnopqrstuvwxyz")
> 231ceddo6msr9

F("ALLYOURBASEAREBELONGTOUS", 62, 10, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
> 6173180047113843154028210391227718305282902

F("howmuchwoodcouldawoodchuckchuckifawoodchuckcouldchuckwood", 36, 95, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+=[{]}\\|;:'\",<.>/? ")
> o3K9e(r_lgal0$;?w0[`<$n~</SUk(r#9W@."0&}_2?[n

F("1100111100011010101010101011001111011010101101001111101000000001010010100101111110000010001001111100000001011000000001001101110101", 2, 95, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+=[{]}\\|;:'\",<.>/? ")
> this is much shorter

Ne abbiamo progettato uno per affrontare numeri di lunghezza arbitrari.
Peter Taylor,

@PeterTaylor Beh, dang, in qualche modo ha perso quello nella mia ricerca. Tuttavia, direi che sono abbastanza diversi. L'altro riguarda un set di caratteri predefinito, sequenze multi-byte, gestione degli errori e conversione da sequenza a sequenza. Tutto ciò si aggiunge a un gonfiamento molto più ampio nelle risposte e si concentra su diverse ottimizzazioni. Questa sfida è molto più semplice e si tradurrà in un codice completamente diverso dall'altra (a parte l'algoritmo core).
Mwr247,

@PeterTaylor Plus, l'altra domanda è stata posta 4 anni fa e ha ricevuto solo due risposte valide (e con una già accettata, poche ragioni per urtare). Sono disposto a scommettere che la community apprezzerebbe questa sfida, con scarso impatto dalla precedente, o sentimenti di "ripetitività".
Mwr247,

7
Mentre questa sfida è molto simile alla precedente, in realtà sarei a favore di chiudere la precedente come un duplicato di questa. Questa sfida è molto più chiara e di qualità superiore a quella precedente.
Mego

Potresti approfondire un po ' You cannot use built in change-of-base functions to convert the entire input string/number at once? In particolare, è possibile utilizzare un built-in per convertire l'input in una base intermedia? Posso quindi utilizzare un built-in per convertire nella base di destinazione? Ti piacerebbe qualcosa convert input with canonical form for given base; convert to base 10; convert to target base; convert back to specified character set with string replacement?
Mego

Risposte:


5

CJam, 34 byte

0ll:Af#lif{@*+}~li:X;{XmdA=\}h;]W%

Il formato di input è input_N alphabet input_B output_Bciascuno su una riga separata.

Esegui tutti i casi di test.

Spiegazione

0     e# Push a zero which we'll use as a running total to build up the input number.
l     e# Read the input number.
l:A   e# Read the alphabet and store it in A.
f#    e# For each character in the input number turn it into its position in the alphabet,
      e# replacing characters with the corresponding numerical digit value.
li    e# Read input and convert to integer.
f{    e# For each digit (leaving the base on the stack)...
  @*  e#   Pull up the running total and multiply it by the base.
  +   e#   Add the current digit.
}
~     e# The result will be wrapped in an array. Unwrap it.
li:X; e# Read the output base, store it in X and discard it.
{     e# While the running total is not zero yet...
  Xmd e#   Take the running total divmod X. The modulo gives the next digit, and
      e#   the division result represents the remaining digits.
  A=  e#   Pick the corresponding character from the alphabet.
  \   e#   Swap the digit with the remaining value.
}h
;     e# We'll end up with a final zero on the stack which we don't want. Discard it.
]W%   e# Wrap everything in an array and reverse it, because we've generated the 
      e# digits from least to most significant.

Funziona con lo stesso numero di byte:

L0ll:Af#lif{@*+}~li:X;{XmdA=@+\}h;

L'unica differenza è che stiamo costruendo una stringa invece di raccogliere tutto nello stack e invertirlo.


7

Python 2 , 115 114 106 105 94 byte

Suggerimenti di golf benvenuti. Provalo online!

Modifica: -9 byte grazie a mbomb007. -2 byte grazie a FlipTack.

def a(n,f,t,d,z=0,s=''):
 for i in n:z=z*f+d.find(i)
 while z:s=d[z%t]+s;z/=t
 print s or d[0]

Ungolfed:

def arbitrary_base_conversion(num, b_from, b_to, digs, z=0, s=''):
    for i in num:
        z = z * b_from + digs.index(i)
    while z:
        s = digs[z % b_to] + s
        z = z / t
    if s:
        return s
    else:
        return d[0]

1
while z:s=d[z%t]+s;z/=tsalva 9 byte.
mbomb007,

È possibile inserire z=0e s=''nella dichiarazione di funzione per salvare byte.
FlipTack

l'utilizzo printinvece di returnè consentito per impostazione predefinita .
FlipTack

6

Scherzi a parte, 50 byte

0╗,╝,2┐,3┐,4┐╛`4└í╜2└*+╗`MX╜ε╗W;3└@%4└E╜@+╗3└@\WX╜

Dump esadecimale:

30bb2cbc2c32bf2c33bf2c34bfbe6034c0a1bd32c02a2bbb60
4d58bdeebb573b33c0402534c045bd402bbb33c0405c5758bd

Sono orgoglioso di questo nonostante la sua lunghezza. Perché? Perché ha funzionato perfettamente al secondo tentativo. L'ho scritto e debug letteralmente in 10 minuti. Di solito il debug di un programma Seriamente richiede un'ora di lavoro.

Spiegazione:

0╗                                                  Put a zero in reg0 (build number here)
  ,╝,2┐,3┐,4┐                                       Put evaluated inputs in next four regs
             ╛                                      Load string from reg1
              `         `M                          Map over its chars
               4└                                   Load string of digits
                 í                                  Get index of char in it.
                  ╜                                 Load number-so-far from reg0
                   2└*                              Multiply by from-base
                      +                             Add current digit.
                       ╗                            Save back in reg0
                          X                         Discard emptied string/list.
                           ╜                        Load completed num from reg0
                            ε╗                      Put empty string in reg0
                              W                W    While number is positive
                               ;                    Duplicate
                                3└@%                Mod by to-base.
                                    4└E             Look up corresponding char in digits
                                       ╜@+          Prepend to string-so-far.
                                                      (Forgetting this @ was my one bug.)
                                          ╗         Put it back in reg0
                                           3└@\     integer divide by to-base.
                                                X   Discard leftover 0
                                                 ╜  Load completed string from reg0
                                                    Implicit output.

3

C (funzione) con libreria GMP , 260

Questo è risultato più lungo di quanto sperassi, ma eccolo qui. Il mpz_*materiale consuma davvero molti byte. Ho provato #define M(x) mpz_##x, ma questo ha dato un guadagno netto di 10 byte.

#include <gmp.h>
O(mpz_t N,int t,char*d){mpz_t Q,R;mpz_inits(Q,R,0);mpz_tdiv_qr_ui(Q,R,N,t);mpz_sgn(Q)&&O(Q,t,d);putchar(d[mpz_get_ui(R)]);}F(char*n,int f,int t,char*d){mpz_t N;mpz_init(N);while(*n)mpz_mul_ui(N,N,f),mpz_add_ui(N,N,strchr(d,*n++)-d);O(N,t,d);}

La funzione F()è il punto di ingresso. Converte la stringa di input in una mpz_tper moltiplicazioni successive per from-base e aggiunta dell'indice della cifra indicata nell'elenco delle cifre.

La funzione O()è una funzione di uscita ricorsiva. Ogni ricorsione divmodifica mpz_tper la tobase. Poiché ciò produce le cifre di output in ordine inverso, la ricorsione consente effettivamente di memorizzare le cifre nello stack e di produrre nell'ordine corretto.

Test driver:

Newline e rientri aggiunti per la leggibilità.

#include <stdio.h>
#include <string.h>

#include <gmp.h>
O(mpz_t N,int t,char*d){
  mpz_t Q,R;
  mpz_inits(Q,R,0);
  mpz_tdiv_qr_ui(Q,R,N,t);
  mpz_sgn(Q)&&O(Q,t,d);
  putchar(d[mpz_get_ui(R)]);
}
F(char*n,int f,int t,char*d){
  mpz_t N;
  mpz_init(N);
  while(*n)
    mpz_mul_ui(N,N,f),mpz_add_ui(N,N,strchr(d,*n++)-d);
  O(N,t,d);
}

int main (int argc, char **argv) {
  int i;

  struct test_t {
    char *n;
    int from_base;
    int to_base;
    char *digit_list;
  } test[] = {
    {"1010101", 2, 10, "0123456789"},
    {"0001010101", 2, 10, "0123456789"},
    {"85", 10, 2, "0123456789"},
    {"1010101", 10, 2, "0123456789"},
    {"bababab", 2, 10, "abcdefghij"},
    {"10", 3, 2, "0123456789"},
    {"<('.'<)(v'.'v)(>'.'>)(^'.'^)", 31, 2, "~!@#$%^v&*()_+-=`[]{}|';:,./<>? "},
    {"~~~~~~~~~~", 31, 2, "~!@#$%^v&*()_+-=`[]{}|';:,./<>? "},
    {"9876543210123456789", 10, 36, "0123456789abcdefghijklmnopqrstuvwxyz"},
    {"ALLYOURBASEAREBELONGTOUS", 62, 10, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"},
    {"howmuchwoodcouldawoodchuckchuckifawoodchuckcouldchuckwood", 36, 95, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+=[{]}\\|;:'\",<.>/? "},
    {"1100111100011010101010101011001111011010101101001111101000000001010010100101111110000010001001111100000001011000000001001101110101", 2, 95, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`!@#$%^&*()_-+=[{]}\\|;:'\",<.>/? "},
    {0}
  };

  for (i = 0; test[i].n; i++) {
    F(test[i].n, test[i].from_base, test[i].to_base, test[i].digit_list);
    puts("");
  }

  return 0;
}

3

JavaScript (ES6), 140 byte

(s,f,t,m)=>[...s].map(c=>{c=m.indexOf(c);for(i=0;c||i<r.length;i++)r[i]=(n=(r[i]|0)*f+c)%t,c=n/t|0},r=[0])&&r.reverse().map(c=>m[c]).join``

A differenza del codice di @ Mwr247 (che usa l'aritmetica base-f per dividere ogni volta s per t, raccogliendo ogni resto mentre va) Uso l'aritmetica base-t per moltiplicare la risposta per f ogni volta, aggiungendo ogni cifra di s mentre procedo.

Ungolfed:

function base(source, from, to, mapping) {
    result = [0];
    for (j = 0; j < s.length; s++) {
        carry = mapping.indexOf(s[j]);
        for (i = 0; carry || i < result.length; i++) {
            next = (result[i] || 0) * from + carry;
            result[i] = next % to;
            carry = Math.floor(next / to);
         }
    }
    string = "";
    for (j = result.length; j --> 0; )
        string += mapping[result[j]];
    return string;
}

3

Rubino, 113 112 105 98 97 95 87 byte

Ho postato due volte la mia risposta Python (in qualche modo), quindi ecco una risposta di Ruby. Altri sette byte grazie a manatwork , un altro byte grazie a Martin Büttner e altri 8 byte grazie a cia_rana .

->n,f,t,d{z=0;s='';n.chars{|i|z=z*f+d.index(i)};(s=d[z%t]+s;z/=t)while z>0;s[0]?s:d[0]}

Ungolfed:

def a(n,f,t,d)
  z=0
  s=''
  n.chars do |i|
    z = z*f + d.index(i)
  end
  while z>0 
    s = d[z%t] + s
    z /= t
  end
  if s[0]   # if n not zero
    return s
  else
    return d[0]
  end
end

Che ne dici di usare s=d[z%t]+s;z/=tinvece di z,m=z.divmod t;s=d[m]+s?
cia_rana,

3

APL, 10 byte

{⍺⍺[⍵⍵⍳⍵]}

Questo è un operatore APL. In APL, e sono utilizzati per passare valori, mentre ⍵⍵e ⍺⍺vengono normalmente utilizzati per passare funzioni. Sto abusando di questo qui per avere 3 argomenti. ⍺⍺è l'argomento di sinistra, ⍵⍵è l'argomento di destra "interiore", ed è l'argomento di destra "esterno".

Fondamentalmente: ⍺(⍺⍺{...}⍵⍵)⍵

Quindi tutto ciò che serve è trovare le posizioni della stringa di input nella tabella "da" e quindi utilizzare []per indicizzare la tabella "a" con queste posizioni.

Esempio:

    ('012345'{⍺⍺[⍵⍵⍳⍵]}'abcdef')'abcabc'
012012

2

JavaScript (ES6), 175 byte

(s,f,t,h)=>eval('s=[...s].map(a=>h.indexOf(a));n=[];while(s.length){d=m=[],s.map(v=>((e=(c=v+m*f)/t|0,m=c%t),e||d.length?d.push(e):0)),s=d,n.unshift(m)}n.map(a=>h[a]).join``')

Ho pensato che è stato abbastanza a lungo ora che posso presentare quello che ho fatto per creare gli esempi. Potrei provare a giocarci un po 'meglio dopo.


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.