将基数10转换为.NET中的任何基数的最快方法?

jos*_*ley 103 .net c# int base number-systems

我有和旧的(ish)C#方法我写了一个数字并将其转换为任何基数:

string ConvertToBase(int number, char[] baseChars);
Run Code Online (Sandbox Code Playgroud)

它不是那么超级快速和整洁.在.NET中有没有一种很好的,已知的方法来实现这一点?

我在寻找的东西,让我使用任何基地要使用的字符的任意字符串.

这只允许碱基16,10,8和2:

Convert.ToString(1, x);
Run Code Online (Sandbox Code Playgroud)

我想利用它来利用数字,全部小写和全部大写字母来实现高基数.就像在这个线程中一样,但对于C#而不是JavaScript.

有谁知道在C#中这样做的好方法?

Dir*_*mar 127

Convert.ToString 可用于将数字转换为指定基数中的等效字符串表示形式.

例:

string binary = Convert.ToString(5, 2); // convert 5 to its binary representation
Console.WriteLine(binary);              // prints 101
Run Code Online (Sandbox Code Playgroud)

然而,正如评论所指出的,Convert.ToString只支持以下有限的 - 但通常是足够的 - 基础集:2,8,10或16.

更新(以满足转换为任何基础的要求):

我不知道BCL中的任何方法能够将数字转换为任何基数,因此您必须编写自己的小实用程序函数.一个简单的示例看起来就像那样(请注意,通过替换字符串连接可以更快地完成此操作):

class Program
{
    static void Main(string[] args)
    {
        // convert to binary
        string binary = IntToString(42, new char[] { '0', '1' });

        // convert to hexadecimal
        string hex = IntToString(42, 
            new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                         'A', 'B', 'C', 'D', 'E', 'F'});

        // convert to hexavigesimal (base 26, A-Z)
        string hexavigesimal = IntToString(42, 
            Enumerable.Range('A', 26).Select(x => (char)x).ToArray());

        // convert to sexagesimal
        string xx = IntToString(42, 
            new char[] { '0','1','2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
            'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x'});
    }

    public static string IntToString(int value, char[] baseChars)
    {
        string result = string.Empty;
        int targetBase = baseChars.Length;

        do
        {
            result = baseChars[value % targetBase] + result;
            value = value / targetBase;
        } 
        while (value > 0);

        return result;
    }

    /// <summary>
    /// An optimized method using an array as buffer instead of 
    /// string concatenation. This is faster for return values having 
    /// a length > 1.
    /// </summary>
    public static string IntToStringFast(int value, char[] baseChars)
    {
        // 32 is the worst cast buffer size for base 2 and int.MaxValue
        int i = 32;
        char[] buffer = new char[i];
        int targetBase= baseChars.Length;

        do
        {
            buffer[--i] = baseChars[value % targetBase];
            value = value / targetBase;
        }
        while (value > 0);

        char[] result = new char[32 - i];
        Array.Copy(buffer, i, result, 0, 32 - i);

        return new string(result);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新2(性能改进)

使用数组缓冲区而不是字符串连接来构建结果字符串可以提高性能,特别是对于大数字(参见方法IntToStringFast).在最好的情况下(即最长的输入),这种方法大约快三倍.但是,对于1位数字(即目标基数中的1位数字),IntToString会更快.

  • Sexagesimal听起来很有趣. (41认同)
  • 真棒.但是反函数在哪里?:/ (6认同)
  • 应该指出的是,这仅支持基础2,8,10,16 - 而不是问题中的"任何".'因为你永远不知道什么时候你需要sexagesimal ;-p (3认同)
  • 负面数字失败. (3认同)
  • 我在这里有第一遍反函数:http://stackoverflow.com/questions/3579970/whats-an-efficient-inversion-of-the-convert-to-arbitrary-base-c-function/3579977#3579977 (2认同)

Pav*_*dov 72

我最近在博客上写了这个.我的实现在计算过程中不使用任何字符串操作,这使得它非常快.支持转换为基数为2到36的任何数字系统:

/// <summary>
/// Converts the given decimal number to the numeral system with the
/// specified radix (in the range [2, 36]).
/// </summary>
/// <param name="decimalNumber">The number to convert.</param>
/// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
/// <returns></returns>
public static string DecimalToArbitrarySystem(long decimalNumber, int radix)
{
    const int BitsInLong = 64;
    const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    if (radix < 2 || radix > Digits.Length)
        throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());

    if (decimalNumber == 0)
        return "0";

    int index = BitsInLong - 1;
    long currentNumber = Math.Abs(decimalNumber);
    char[] charArray = new char[BitsInLong];

    while (currentNumber != 0)
    {
        int remainder = (int)(currentNumber % radix);
        charArray[index--] = Digits[remainder];
        currentNumber = currentNumber / radix;
    }

    string result = new String(charArray, index + 1, BitsInLong - index - 1);
    if (decimalNumber < 0)
    {
        result = "-" + result;
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

我也实现了一个快速反函数,以防任何人需要它: 任意到十进制数字系统.

  • 我在本页面上对所有解决方案进行了性能测试,这是最快的,大约是最后的短解决方案的两倍. (5认同)
  • 为什么这不是公认的答案?这个棒极了! (2认同)

Die*_*ego 9

快速" "和" "方法

我迟到了,但是我把以前的答案复合了,并对它们进行了改进.我认为这两种方法比目前为止发布的其他方法都快.我能够在单核心机器中将400,000个数字转换为400以下的基数36.

以下示例适用于基座62.更改BaseChars阵列以从任何其他基础转换为.

private static readonly char[] BaseChars = 
         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
private static readonly Dictionary<char, int> CharValues = BaseChars
           .Select((c,i)=>new {Char=c, Index=i})
           .ToDictionary(c=>c.Char,c=>c.Index);

public static string LongToBase(long value)
{
   long targetBase = BaseChars.Length;
   // Determine exact number of characters to use.
   char[] buffer = new char[Math.Max( 
              (int) Math.Ceiling(Math.Log(value + 1, targetBase)), 1)];

   var i = buffer.Length;
   do
   {
       buffer[--i] = BaseChars[value % targetBase];
       value = value / targetBase;
   }
   while (value > 0);

   return new string(buffer, i, buffer.Length - i);
}

public static long BaseToLong(string number) 
{ 
    char[] chrs = number.ToCharArray(); 
    int m = chrs.Length - 1; 
    int n = BaseChars.Length, x;
    long result = 0; 
    for (int i = 0; i < chrs.Length; i++)
    {
        x = CharValues[ chrs[i] ];
        result += x * (long)Math.Pow(n, m--);
    }
    return result;  
} 
Run Code Online (Sandbox Code Playgroud)

编辑(2018-07-12)

修复以解决由@AdrianBotor(请参阅注释)将46655转换为基数36所发现的极端情况.这是由一个小的浮点错误计算引起的,它Math.Log(46656, 36)正好是3,但.NET返回3 + 4.44e-16,这会在输出缓冲区中产生额外的字符.

  • @Diego,很抱歉迟到的回复.让我们使用`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`初始化数组并转换值"46655".结果应该是`ZZZ`但在调试器中我得到`\ 0ZZZ`.只有这个值才能得到额外的`\ 0`.例如,值"46654"正确转换为"ZZY". (2认同)

Kre*_*mir 7

也可以使用已接受的版本的略微修改版本,并根据需要调整基本字符串:

public static string Int32ToString(int value, int toBase)
{
    string result = string.Empty;
    do
    {
        result = "0123456789ABCDEF"[value % toBase] + result;
        value /= toBase;
    }
    while (value > 0);

    return result;
}
Run Code Online (Sandbox Code Playgroud)