Conversion de base avec des chaînes

16

introduction

Nous avons eu quelques défis de conversion de base ici dans le passé, mais pas beaucoup conçus pour s'attaquer aux nombres de longueur arbitraire (c'est-à-dire, les nombres suffisamment longs pour dépasser le type de données entier), et parmi ceux-ci, la plupart se sentaient un peu compliqué. Je suis curieux de voir comment un changement de code de base comme celui-ci peut se produire.

Défi

Écrivez un programme ou une fonction dans la langue de votre choix qui peut convertir une chaîne d'une base en chaîne d'une autre base. L'entrée doit être le nombre à convertir (chaîne), de la base (numéro base-10), à la base (numéro base-10) et le jeu de caractères (chaîne). La sortie doit être le nombre converti (chaîne).

Voici quelques détails et règles supplémentaires:

  • Le nombre à convertir sera un entier non négatif (car -et .peut être dans le jeu de caractères). Il en sera de même pour la sortie.
  • Les zéros non significatifs (le premier caractère du jeu de caractères) doivent être tronqués. Si le résultat est zéro, un seul chiffre zéro doit rester.
  • La plage de base minimale prise en charge est comprise entre 2 et 95, composée des caractères ascii imprimables.
  • L'entrée du nombre à convertir, le jeu de caractères et la sortie doivent tous être du type de données chaîne. Les bases doivent être du type de données entier de base 10 (ou des nombres entiers flottants).
  • La longueur de la chaîne de numéro d'entrée peut être très grande. Il est difficile de quantifier un minimum raisonnable, mais attendez-vous à ce qu'il soit capable de gérer au moins 1000 caractères et de compléter 100 caractères en moins de 10 secondes sur une machine décente (très généreux pour ce genre de problème, mais je ne veux pas vitesse pour être au centre).
  • Vous ne pouvez pas utiliser les fonctions intégrées de changement de base.
  • L'entrée du jeu de caractères peut être dans n'importe quel arrangement, pas seulement le 0-9a-z typique, etc.
  • Supposons que seule une entrée valide sera utilisée. Ne vous inquiétez pas de la gestion des erreurs.

Le gagnant sera déterminé par le code le plus court qui remplit les critères. Ils seront sélectionnés dans au moins 7 jours sur 10, ou si / quand il y a eu suffisamment de soumissions. En cas d'égalité, le code qui s'exécute le plus vite sera le vainqueur. Si elle est suffisamment proche en vitesse / performances, la réponse qui est venue plus tôt l'emporte.

Exemples

Voici quelques exemples d'entrée et de sortie que votre code devrait être capable de gérer:

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
Mwr247
la source
Nous en avons eu un conçu pour lutter contre les nombres de longueur arbitraire.
Peter Taylor
@PeterTaylor Eh bien dang, en quelque sorte manqué celui-ci dans ma recherche. Pourtant, je dirais qu'ils sont suffisamment différents. L'autre implique un jeu de caractères par défaut, des séquences multi-octets, la gestion des erreurs et la conversion de séquence en séquence. Tout cela ajoute à un ballonnement beaucoup plus important dans les réponses et se concentre sur différentes optimisations. Ce défi est beaucoup plus réduit et se traduira par un code complètement différent de l'autre défi (court de l'algorithme de base).
Mwr247
@PeterTaylor Plus, l'autre question a été posée il y a 4 ans et n'a reçu que deux réponses valides (et avec une déjà acceptée, peu de raisons de se cogner). Je suis prêt à parier que la communauté apprécierait ce défi, avec peu d'impact par rapport au précédent, ou des sentiments de «répétitivité».
Mwr247
7
Bien que ce défi soit très similaire au précédent, je serais en fait favorable à la fermeture du précédent en tant que dupe de celui-ci. Ce défi est beaucoup plus clair et de meilleure qualité que l'ancien.
Mego
Pourriez-vous développer un peu You cannot use built in change-of-base functions to convert the entire input string/number at once? Plus précisément, pourrais-je utiliser un intégré pour convertir l'entrée en une base intermédiaire? Puis-je ensuite utiliser un intégré pour convertir en la base cible? Souhaitez-vous quelque chose 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

Réponses:

5

CJam, 34 octets

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

Le format d'entrée est input_N alphabet input_B output_Bchacun sur une ligne distincte.

Exécutez tous les cas de test.

Explication

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.

Cela fonctionne pour le même nombre d'octets:

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

La seule différence est que nous construisons une chaîne au lieu de collecter tout sur la pile et de l'inverser.

Martin Ender
la source
7

Python 2 , 115 114 106 105 94 bytes

Suggestions de golf bienvenues. Essayez-le en ligne!

Edit: -9 octets grâce à mbomb007. -2 octets grâce à 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]

Non golfé:

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]
Sherlock9
la source
1
while z:s=d[z%t]+s;z/=tenregistre 9 octets.
mbomb007
Vous pouvez mettre z=0et s=''dans la déclaration de fonction pour enregistrer les octets.
FlipTack
l'utilisation printau lieu de returnest autorisée par défaut .
FlipTack
6

Sérieusement, 50 octets

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

Vidage hexadécimal:

30bb2cbc2c32bf2c33bf2c34bfbe6034c0a1bd32c02a2bbb60
4d58bdeebb573b33c0402534c045bd402bbb33c0405c5758bd

Je suis fier de celui-ci malgré sa longueur. Pourquoi? Parce que cela a parfaitement fonctionné au deuxième essai. Je l'ai écrit et débogué en 10 minutes. Le débogage d'un programme Serious représente généralement une heure de travail.

Explication:

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.
quintopie
la source
3

C (fonction) avec bibliothèque GMP , 260

Cela s'est avéré plus long que je ne l'avais espéré, mais le voici quand même. Le mpz_*truc mange vraiment beaucoup d'octets. J'ai essayé #define M(x) mpz_##x, mais cela a donné un gain net de 10 octets.

#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 fonction F()est le point d'entrée. Il convertit la chaîne d'entrée en une mpz_tmultiplication successive par la frombase et l'ajout de l'index du chiffre donné dans la liste des chiffres.

La fonction O()est une fonction de sortie récursive. Chaque divmod de récursion le mpz_tpar leto base. Parce que cela donne les chiffres de sortie dans l'ordre inverse, la récursivité permet effectivement aux chiffres d'être stockés sur la pile et de sortir dans le bon ordre.

Pilote de test:

Ajout de nouvelles lignes et de retraits pour plus de lisibilité.

#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;
}
Traumatisme numérique
la source
3

JavaScript (ES6), 140 octets

(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``

Contrairement au code de @ Mwr247 (qui utilise l'arithmétique base-f pour diviser s par t à chaque fois, en collectant chaque reste au fur et à mesure), j'utilise l'arithmétique base-t pour multiplier la réponse par f à chaque fois, en ajoutant chaque chiffre de s au fur et à mesure.

Non golfé:

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;
}
Neil
la source
3

Ruby, 113 112 105 98 97 95 87 octets

J'ai en quelque sorte posté ma réponse Python (en quelque sorte), alors voici une réponse Ruby. Sept octets supplémentaires grâce à Manatwork , un autre octet grâce à Martin Büttner et 8 octets supplémentaires grâce à 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]}

Non golfé:

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
Sherlock9
la source
Que diriez-vous d'utiliser s=d[z%t]+s;z/=tau lieu de z,m=z.divmod t;s=d[m]+s?
cia_rana
3

APL, 10 octets

{⍺⍺[⍵⍵⍳⍵]}

Il s'agit d'un opérateur APL. Dans APL, et sont utilisés pour transmettre des valeurs, tandis que ⍵⍵et ⍺⍺sont généralement utilisés pour transmettre des fonctions. J'en abuse ici pour avoir 3 arguments. ⍺⍺est l'argument gauche, ⍵⍵l'argument droit "intérieur" et l'argument droit "extérieur".

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

Ensuite, tout ce qui est nécessaire est de trouver les positions de la chaîne d'entrée dans la table "from", puis d'utiliser []pour indexer dans la table "to" avec ces positions.

Exemple:

    ('012345'{⍺⍺[⍵⍵⍳⍵]}'abcdef')'abcabc'
012012
Ven
la source
2

JavaScript (ES6), 175 octets

(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``')

Je pensais que cela faisait assez longtemps maintenant que je pouvais soumettre celui que j'avais fait pour créer les exemples. Je peux essayer de jouer au golf un peu mieux plus tard.

Mwr247
la source