带字符串的基数转换


16

介绍

过去,我们在这里遇到了一些基本的转换挑战,但是设计用来处理任意长度数字(即足够长以至于它们溢出整数数据类型的数字)的设计并不多,其中大多数感到有些不足。复杂。我很好奇这样的基础代码更改如何获得成功。

挑战

用您选择的语言编写一个程序或函数,该程序或函数可以将一个基数的字符串转换为另一基数的字符串。输入应该是要转换的数字(字符串),从基数(以10为基数),以基数(以10为基数)和字符集(字符串)。输出应为转换后的数字(字符串)。

一些进一步的细节和规则如下:

  • 要转换的数字将是一个非负整数(因为-.可能在字符集中)。输出也将如此。
  • 前导零(字符集中的第一个字符)应被修剪。如果结果为零,则应保留一个零数字。
  • 支持的最小基本范围是2到95,由可打印的ASCII字符组成。
  • 要转换的数字的输入,字符集和输出必须全部为字符串数据类型。基数必须是以10为底的整数数据类型(或整数浮点数)。
  • 输入数字字符串的长度可能非常大。很难量化一个合理的最小值,但是期望它能够处理至少1000个字符,并在像样的机器上在不到10秒的时间内完成100个字符的输入(对于这种问题非常慷慨,但是我不想速度成为重点)。
  • 您不能使用内置的基准变更功能。
  • 字符集输入可以采用任何排列方式,而不仅仅是典型的0-9a-z ...等。
  • 假设将仅使用有效输入。不用担心错误处理。

获胜者将由符合条件的最短代码确定。至少会在7个基准10天之内,或者是否/是否有足够的提交内容,来选择它们。如果出现平局,则运行速度更快的代码将是赢家。如果在速度/性能上足够接近,则较早出现的答案为准。

例子

这是您的代码应能够处理的一些输入和输出示例:

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

我们已经有一个旨在解决任意长度的数字。
彼得·泰勒

@PeterTaylor Well dang,以某种方式错过了我的搜索。不过,我认为它们足够不同。另一个涉及默认字符集,多字节序列,错误处理和序列到序列的转换。所有这些都使答案更加larger肿,并专注于不同的优化。这个挑战要少得多,并且将导致与另一个挑战(缺少核心算法)完全不同的代码。
Mwr247'1

@PeterTaylor Plus,另一个问题是4年前提出的,仅收到两个有效答案(并且已经被接受,没有什么理由要碰碰)。我愿意打赌,社区将乐于接受这一挑战,而不会受到前一个挑战的影响,也不会带来“重复性”的感觉。
Mwr247'1

7
尽管此挑战与上一个挑战非常相似,但实际上我还是倾向于将上一个挑战作为对此挑战的复制。与旧的挑战相比,这一挑战更加清晰,质量更高。
Mego 2016年

您能详细说明一下You cannot use built in change-of-base functions to convert the entire input string/number at once吗?具体来说,我可以使用内置方法将输入转换为中间基数吗?然后可以使用内置的转换为目标库吗?会这样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

Answers:


5

CJam,34个字节

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

输入格式input_N alphabet input_B output_B分别在单独的行上。

运行所有测试用例。

说明

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.

这适用于相同的字节数:

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

唯一的区别是我们正在构建一个字符串,而不是收集堆栈中的所有内容并将其反转。


7

Python 2中115个 114 106 105 94字节

欢迎打高尔夫球。在线尝试!

编辑: -9字节,感谢mbomb007。-2个字节感谢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]

取消高尔夫:

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/=t保存9个字节。
mbomb007 '16

您可以将z=0s=''放在函数声明中以保存字节。
FlipTack

使用print,而不是return默认允许
FlipTack

6

认真地,50个字节

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

十六进制转储:

30bb2cbc2c32bf2c33bf2c34bfbe6034c0a1bd32c02a2bbb60
4d58bdeebb573b33c0402534c045bd402bbb33c0405c5758bd

尽管它很长,我还是为此感到骄傲。为什么?因为它在第二次尝试中效果很好。我编写并在10分钟内对其进行了调试。通常调试一个认真的程序是一个小时的工作。

说明:

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

带有GMP库的 C(函数),260

结果比我希望的要长,但是无论如何,这里还是。这些mpz_*东西真的吃了很多字节。我尝试了#define M(x) mpz_##x,但是净增加了10个字节。

#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);}

该功能F()是入口点。它将输入字符串mpz_tfrom-base 相乘并在数字列表中添加给定数字的索引,将输入字符串转换为。

功能 O()是递归输出函数。每个递归divmod mpz_t通过-base进行to。因为这会产生相反顺序的输出数字,所以递归有效地允许将数字存储在堆栈中并以正确的顺序输出。

测试驱动程序:

添加了换行符和缩进以提高可读性。

#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个字节

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

与@ Mwr247的代码不同(我的代码每次使用base-f算法将s除以t,并随其收集每个余数),我使用base-t算法每次将答案乘以f,并随即添加s的每个数字。

取消高尔夫:

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

Ruby,113 112 105 98 97 95 87字节

我有点重复了我的Python答案(某种程度上),所以这是一个Ruby答案。多亏了manatwork,又增加了七个字节,另一个字节感谢马丁布特内尔和8更感谢字节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]}

取消高尔夫:

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

s=d[z%t]+s;z/=t而不是使用z,m=z.divmod t;s=d[m]+s呢?
cia_rana

3

APL,10个字节

{⍺⍺[⍵⍵⍳⍵]}

这是一个APL运营商。在APL中,用于传递值,而⍵⍵⍺⍺通常用于传递函数。我在这里滥用这个有3个论点。⍺⍺是左参数,⍵⍵是“内部”右参数,是“外部”右参数。

基本上: ⍺(⍺⍺{...}⍵⍵)⍵

然后,所需要做的就是在“ from”表中找到输入字符串的位置,然后使用[]这些位置索引到“ to”表中。

例:

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

2

JavaScript(ES6),175个字节

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

现在已经足够长了,我可以提交我创建的示例了。稍后我可能会尝试将其打倒一些。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.