วิธีที่เร็วที่สุดในการแปลงเลขฐาน 10 เป็นฐานใน. NET


108

ฉันมีและเก่า (ish) วิธี C # ที่ฉันเขียนซึ่งใช้ตัวเลขและแปลงเป็นฐานใดก็ได้:

string ConvertToBase(int number, char[] baseChars);

ไม่ใช่ทั้งหมดที่รวดเร็วและเรียบร้อย มีวิธีที่ดีและเป็นที่รู้จักในการบรรลุสิ่งนี้ใน. NET หรือไม่?

ฉันกำลังมองหาสิ่งที่ช่วยให้ฉันใช้ฐานใดก็ได้ที่มีสตริงอักขระที่ต้องการใช้

อนุญาตเฉพาะฐาน 16, 10, 8 และ 2:

Convert.ToString(1, x);

ฉันต้องการใช้สิ่งนี้เพื่อให้ได้ฐานที่สูงมากโดยใช้ประโยชน์จากตัวเลขตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ทั้งหมด เช่นเดียวกับในหัวข้อนี้แต่สำหรับ C # ไม่ใช่ JavaScript

ไม่มีใครรู้วิธีที่ดีและมีประสิทธิภาพในการทำ C #?

คำตอบ:


136

Convert.ToString สามารถใช้เพื่อแปลงตัวเลขเป็นการแทนค่าสตริงที่เทียบเท่าในฐานที่ระบุ

ตัวอย่าง:

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

อย่างไรก็ตามตามที่ระบุไว้ในความคิดเห็น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);
    }
}

อัปเดต 2 (การปรับปรุงประสิทธิภาพ)

การใช้อาร์เรย์บัฟเฟอร์แทนการต่อสายอักขระเพื่อสร้างสตริงผลลัพธ์จะช่วยปรับปรุงประสิทธิภาพโดยเฉพาะในจำนวนมาก (ดูวิธีการIntToStringFast) ในกรณีที่ดีที่สุด (เช่นอินพุตที่ยาวที่สุด) วิธีนี้เร็วกว่าประมาณสามเท่า อย่างไรก็ตามสำหรับตัวเลข 1 หลัก (เช่น 1 หลักในฐานเป้าหมาย) IntToStringจะเร็วกว่า


5
ควรสังเกตว่าสิ่งนี้รองรับเฉพาะฐาน 2,8,10,16 เท่านั้นไม่ใช่ "ใด ๆ " ในคำถาม 'cos คุณไม่เคยรู้ว่าเมื่อคุณจะต้อง sexagesimal ;-P
Marc Gravell

46
Sexagesimal ฟังดูเหมือนสนุก
บังเหียน

ด้วย targetBase เป็น 60 และค่า 12345 บรรทัดนี้ในเมธอด IntToString: value = value / targetBase; จะทำให้ค่า = 203.75 ถูกต้องหรือไม่ ไม่ควรเก็บไว้เป็นจำนวนเต็ม?
Adam Harte

6
น่ากลัว แต่ฟังก์ชันผกผันอยู่ที่ไหน? : /
ashes999

2
ฉันมีฟังก์ชันผกผันการส่งครั้งแรกที่นี่: stackoverflow.com/questions/3579970/…
ashes999

78

ฉันเพิ่ง blogged เกี่ยวกับเรื่องนี้ การดำเนินงานของฉันไม่ได้ใช้การดำเนินงานสตริงใด ๆ ในระหว่างการคำนวณซึ่งจะทำให้มันเร็วมาก รองรับการแปลงเป็นระบบตัวเลขที่มีฐานตั้งแต่ 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;
}

ฉันได้ดำเนินการนอกจากนี้ยังมีฟังก์ชั่นผกผันอย่างรวดเร็วในกรณีที่คนต้องการมันมากเกินไป: พลให้เป็นทศนิยมตัวเลขระบบ


5
ฉันได้ทดสอบโซลูชันทั้งหมดในหน้านี้แล้วและนี่เป็นวิธีที่เร็วที่สุดเร็วกว่าโซลูชันสั้น ๆ ในตอนท้ายประมาณสองเท่า
Justin R.

นี่มันอะไรกันresult = "-" + result? นั่นคือช่องว่างภายในบางประเภทหรือไม่? ฉันจะแก้ไขโค้ดเพื่อให้ใช้เฉพาะ AZ หรือ 0-9 สำหรับอักขระช่องว่างภายในได้อย่างไร
oscilatingcretin

"-"ในresult = "-" + resultอัฒจันทร์สำหรับสัญญาณเชิงลบของตัวเลขที่ติดลบ ไม่ใช่อักขระที่มีช่องว่างภายใน
Pavel Vladov

2
เหตุใดจึงไม่เป็นคำตอบที่ยอมรับ มันยอดเยี่ยม!
Avrohom Yisroel

ขอบคุณช่วยให้ฉันประหยัดเวลาได้มาก
NinjaLlama

15

เร็ว " จาก " และ " ถึง " ที่รวดเร็ว

ฉันไปงานปาร์ตี้สาย แต่ฉันรวบรวมคำตอบก่อนหน้านี้และปรับปรุงมากกว่าพวกเขา ฉันคิดว่าสองวิธีนี้เร็วกว่าวิธีอื่น ๆ ที่โพสต์ไว้ ฉันสามารถแปลงตัวเลข 1,000,000 จากและถึงฐาน 36 ในระยะ 400 มิลลิวินาทีในเครื่องแกนเดียว

ตัวอย่างด้านล่างเป็นฐาน 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;  
} 

แก้ไข (2018-07-12)

แก้ไขเพื่อแก้ไขกรณีมุมที่พบโดย @AdrianBotor (ดูความคิดเห็น) ที่แปลง 46655 เป็นฐาน 36 สิ่งนี้เกิดจากข้อผิดพลาดจุดลอยตัวเล็ก ๆ ที่คำนวณMath.Log(46656, 36)ซึ่งเท่ากับ 3 แต่. NET ส่งกลับ3 + 4.44e-16ซึ่งทำให้เกิดอักขระพิเศษในบัฟเฟอร์เอาต์พุต .


@AdrianBotor ไม่สามารถ repro ปัญหาของคุณ:BaseToLong(LongToBase(46655)) == 46655
Diego

2
@Diego ขออภัยที่ตอบช้า อาร์เรย์การเริ่มต้นกับ Let 's และความคุ้มค่าแปลง0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 46655ผลที่ควรจะเป็นแต่ในการดีบักฉันได้รับZZZ เฉพาะค่านี้ได้รับเพิ่มเติม\0ZZZ \0ยกตัวอย่างเช่นค่าแปลงอย่างถูกต้องเพื่อ46654 ZZY
Adrian Botor

@AdrianBotor จับดี. การแก้ไขโดยการปรับเปลี่ยนคำสั่งกลับในLongToBaseการreturn new string(buffer, (int) i, buffer.Length - (int)i);
ดิเอโก

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

4

ไปงานปาร์ตี้สายนี้ แต่ฉันเพิ่งเขียนคลาสผู้ช่วยต่อไปนี้สำหรับโปรเจ็กต์ที่ทำงาน ได้รับการออกแบบมาเพื่อแปลงสตริงสั้น ๆ เป็นตัวเลขและย้อนกลับอีกครั้ง ( ฟังก์ชันแฮชที่สมบูรณ์แบบที่เรียบง่าย) แต่จะทำการแปลงตัวเลขระหว่างฐานโดยพลการ Base10ToStringดำเนินการวิธีการตอบคำถามที่ถูกโพสต์

shouldSupportRoundTrippingธงส่งผ่านไปยังตัวสร้างการเรียนเป็นสิ่งจำเป็นเพื่อป้องกันการสูญเสียตัวเลขชั้นนำจากสตริงจำนวนในระหว่างการแปลงฐาน 10 และกลับมาอีกครั้ง (ที่สำคัญได้รับความต้องการของฉัน!) ส่วนใหญ่แล้วการสูญเสีย 0 นำหน้าจากสตริงตัวเลขอาจไม่ใช่ปัญหา

อย่างไรก็ตามนี่คือรหัส:

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflow
{
    /// <summary>
    /// Contains methods used to convert numbers between base-10 and another numbering system.
    /// </summary>
    /// <remarks>
    /// <para>
    /// This conversion class makes use of a set of characters that represent the digits used by the target
    /// numbering system. For example, binary would use the digits 0 and 1, whereas hex would use the digits
    /// 0 through 9 plus A through F. The digits do not have to be numerals.
    /// </para>
    /// <para>
    /// The first digit in the sequence has special significance. If the number passed to the
    /// <see cref="StringToBase10"/> method has leading digits that match the first digit, then those leading
    /// digits will effectively be 'lost' during conversion. Much of the time this won't matter. For example,
    /// "0F" hex will be converted to 15 decimal, but when converted back to hex it will become simply "F",
    /// losing the leading "0". However, if the set of digits was A through Z, and the number "ABC" was
    /// converted to base-10 and back again, then the leading "A" would be lost. The <see cref="System.Boolean"/>
    /// flag passed to the constructor allows 'round-tripping' behaviour to be supported, which will prevent
    /// leading digits from being lost during conversion.
    /// </para>
    /// <para>
    /// Note that numeric overflow is probable when using longer strings and larger digit sets.
    /// </para>
    /// </remarks>
    public class Base10Converter
    {
        const char NullDigit = '\0';

        public Base10Converter(string digits, bool shouldSupportRoundTripping = false)
            : this(digits.ToCharArray(), shouldSupportRoundTripping)
        {
        }

        public Base10Converter(IEnumerable<char> digits, bool shouldSupportRoundTripping = false)
        {
            if (digits == null)
            {
                throw new ArgumentNullException("digits");
            }

            if (digits.Count() == 0)
            {
                throw new ArgumentException(
                    message: "The sequence is empty.",
                    paramName: "digits"
                    );
            }

            if (!digits.Distinct().SequenceEqual(digits))
            {
                throw new ArgumentException(
                    message: "There are duplicate characters in the sequence.",
                    paramName: "digits"
                    );
            }

            if (shouldSupportRoundTripping)
            {
                digits = (new[] { NullDigit }).Concat(digits);
            }

            _digitToIndexMap =
                digits
                .Select((digit, index) => new { digit, index })
                .ToDictionary(keySelector: x => x.digit, elementSelector: x => x.index);

            _radix = _digitToIndexMap.Count;

            _indexToDigitMap =
                _digitToIndexMap
                .ToDictionary(keySelector: x => x.Value, elementSelector: x => x.Key);
        }

        readonly Dictionary<char, int> _digitToIndexMap;
        readonly Dictionary<int, char> _indexToDigitMap;
        readonly int _radix;

        public long StringToBase10(string number)
        {
            Func<char, int, long> selector =
                (c, i) =>
                {
                    int power = number.Length - i - 1;

                    int digitIndex;
                    if (!_digitToIndexMap.TryGetValue(c, out digitIndex))
                    {
                        throw new ArgumentException(
                            message: String.Format("Number contains an invalid digit '{0}' at position {1}.", c, i),
                            paramName: "number"
                            );
                    }

                    return Convert.ToInt64(digitIndex * Math.Pow(_radix, power));
                };

            return number.Select(selector).Sum();
        }

        public string Base10ToString(long number)
        {
            if (number < 0)
            {
                throw new ArgumentOutOfRangeException(
                    message: "Value cannot be negative.",
                    paramName: "number"
                    );
            }

            string text = string.Empty;

            long remainder;
            do
            {
                number = Math.DivRem(number, _radix, out remainder);

                char digit;
                if (!_indexToDigitMap.TryGetValue((int) remainder, out digit) || digit == NullDigit)
                {
                    throw new ArgumentException(
                        message: "Value cannot be converted given the set of digits used by this converter.",
                        paramName: "number"
                        );
                }

                text = digit + text;
            }
            while (number > 0);

            return text;
        }
    }
}

นอกจากนี้ยังสามารถเป็นคลาสย่อยเพื่อรับตัวแปลงตัวเลขที่กำหนดเอง:

namespace StackOverflow
{
    public sealed class BinaryNumberConverter : Base10Converter
    {
        public BinaryNumberConverter()
            : base(digits: "01", shouldSupportRoundTripping: false)
        {
        }
    }

    public sealed class HexNumberConverter : Base10Converter
    {
        public HexNumberConverter()
            : base(digits: "0123456789ABCDEF", shouldSupportRoundTripping: false)
        {
        }
    }
}

และรหัสจะถูกใช้ในลักษณะนี้:

using System.Diagnostics;

namespace StackOverflow
{
    class Program
    {
        static void Main(string[] args)
        {
            {
                var converter = new Base10Converter(
                    digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz",
                    shouldSupportRoundTripping: true
                    );

                long number = converter.StringToBase10("Atoz");
                string text = converter.Base10ToString(number);
                Debug.Assert(text == "Atoz");
            }

            {
                var converter = new HexNumberConverter();

                string text = converter.Base10ToString(255);
                long number = converter.StringToBase10(text);
                Debug.Assert(number == 255);
            }
        }
    }
}

2

ชั้นเรียนนี้จากโพสต์ในฟอรัมนี้ช่วยคุณได้ไหม

public class BaseConverter { 

public static string ToBase(string number, int start_base, int target_base) { 

  int base10 = this.ToBase10(number, start_base); 
  string rtn = this.FromBase10(base10, target_base); 
  return rtn; 

} 

public static int ToBase10(string number, int start_base) { 

  if (start_base < 2 || start_base > 36) return 0; 
  if (start_base == 10) return Convert.ToInt32(number); 

  char[] chrs = number.ToCharArray(); 
  int m = chrs.Length - 1; 
  int n = start_base; 
  int x; 
  int rtn = 0; 

  foreach(char c in chrs) { 

    if (char.IsNumber(c)) 
      x = int.Parse(c.ToString()); 
    else 
      x = Convert.ToInt32(c) - 55; 

    rtn += x * (Convert.ToInt32(Math.Pow(n, m))); 

    m--; 

  } 

  return rtn; 

} 

public static string FromBase10(int number, int target_base) { 

  if (target_base < 2 || target_base > 36) return ""; 
  if (target_base == 10) return number.ToString(); 

  int n = target_base; 
  int q = number; 
  int r; 
  string rtn = ""; 

  while (q >= n) { 

    r = q % n; 
    q = q / n; 

    if (r < 10) 
      rtn = r.ToString() + rtn; 
    else 
      rtn = Convert.ToChar(r + 55).ToString() + rtn; 

  } 

  if (q < 10) 
    rtn = q.ToString() + rtn; 
  else 
    rtn = Convert.ToChar(q + 55).ToString() + rtn; 

  return rtn; 

} 

}

ยังไม่ทดลองทั้งหมด ... แจ้งให้เราทราบหากได้ผล! (คัดลอกวางในกรณีที่โพสต์ในกระดานสนทนาหายไปหรือมีบางอย่าง ... )


ปิด.. เดี๋ยวจะมาเล่น ต้องใช้เวลาเล็กน้อยเพื่อให้สามารถใช้ตัวอักษรใด ๆ ได้ แต่เป็นขั้นตอนในทิศทางที่ถูกต้อง ฉันจะเปรียบเทียบความเร็วกับวิธีการของฉันเอง!
joshcomley

อย่าลืมแบ่งปันหากคุณปรับปรุงที่นี่ คนอื่นอาจต้องการ ot ด้วย =)
Svish

@joshcomley สุดสัปดาห์ที่ผ่านมาเป็นอย่างไรบ้าง? ;)
Mikkel R. Lund

3
เป็นวันหยุดยาว: D
joshcomley

1

ฉันก็กำลังมองหาวิธีที่รวดเร็วในการแปลงเลขฐานสิบเป็นฐานอื่นในช่วง [2..36] ดังนั้นฉันจึงพัฒนาโค้ดต่อไปนี้ ง่ายต่อการติดตามและใช้วัตถุ Stringbuilder เป็นพร็อกซีสำหรับบัฟเฟอร์อักขระที่เราสามารถทำดัชนีอักขระตามอักขระ โค้ดดูเหมือนจะเร็วมากเมื่อเทียบกับทางเลือกอื่นและเร็วกว่าการเริ่มต้นอักขระแต่ละตัวในอาร์เรย์อักขระ

สำหรับการใช้งานของคุณเองคุณอาจต้องการ: 1 / ส่งคืนสตริงว่างแทนที่จะโยนข้อยกเว้น 2 / ลบการตรวจสอบรัศมีเพื่อให้เมธอดทำงานได้เร็วยิ่งขึ้น 3 / เริ่มต้นอ็อบเจ็กต์ Stringbuilder ด้วย 32 '0 และลบผลลัพธ์บรรทัดลบ (0, i);. สิ่งนี้จะทำให้สตริงถูกส่งกลับด้วยเลขศูนย์นำหน้าและเพิ่มความเร็วขึ้นอีก 4 / ทำให้อ็อบเจกต์ Stringbuilder เป็นฟิลด์สแตติกภายในคลาสดังนั้นไม่ว่าเมธอด DecimalToBase จะเรียกว่ากี่ครั้งอ็อบเจ็กต์ Stringbuilder จะเริ่มต้นเพียงครั้งเดียว หากคุณทำการเปลี่ยนแปลง 3 ข้อข้างต้นจะใช้ไม่ได้อีกต่อไป

ฉันหวังว่าจะมีคนพบว่าสิ่งนี้มีประโยชน์ :)

ปรมาณูพาราด็อกซ์

        static string DecimalToBase(int number, int radix)
    {
        // Check that the radix is between 2 and 36 inclusive
        if ( radix < 2 || radix > 36 )
            throw new ArgumentException("ConvertToBase(int number, int radix) - Radix must be between 2 and 36.");

        // Create a buffer large enough to hold the largest int value represented in binary digits 
        StringBuilder result = new StringBuilder("                                ");  // 32 spaces

        // The base conversion calculates the digits in reverse order so use
        // an index to point to the last unused space in our buffer
        int i = 32; 

        // Convert the number to the new base
        do
        {
            int remainder = number % radix;
            number = number / radix;
            if(remainder <= 9)
                result[--i] = (char)(remainder + '0');  // Converts [0..9] to ASCII ['0'..'9']
            else
                result[--i] = (char)(remainder + '7');  // Converts [10..36] to ASCII ['A'..'Z']
        } while ( number > 0 );

        // Remove the unwanted padding from the front of our buffer and return the result
        // Note i points to the last unused character in our buffer
        result.Remove( 0, i );
        return (result.ToString());
    }

0

ฉันใช้สิ่งนี้เพื่อจัดเก็บ Guid เป็นสตริงที่สั้นกว่า (แต่ จำกัด ให้ใช้ 106 อักขระ) หากใครสนใจนี่คือรหัสของฉันสำหรับการถอดรหัสสตริงกลับไปเป็นค่าตัวเลข (ในกรณีนี้ฉันใช้ 2 ulongs สำหรับค่า Guid แทนที่จะเข้ารหัส Int128 (เนื่องจากฉันอยู่ใน 3.5 ไม่ใช่ 4.0) เพื่อความชัดเจน CODE คือ a สตริง const ที่มี 106 ตัวอักษรที่ไม่ซ้ำกัน ConvertLongsToBytes ค่อนข้างไม่น่าตื่นเต้น

private static Guid B106ToGuid(string pStr)
    {
        try
        {
            ulong tMutl = 1, tL1 = 0, tL2 = 0, targetBase = (ulong)CODE.Length;
            for (int i = 0; i < pStr.Length / 2; i++)
            {
                tL1 += (ulong)CODE.IndexOf(pStr[i]) * tMutl;
                tL2 += (ulong)CODE.IndexOf(pStr[pStr.Length / 2 + i]) * tMutl;
                tMutl *= targetBase;
            }
            return new Guid(ConvertLongsToBytes(tL1, tL2));
        }
        catch (Exception ex)
        {
            throw new Exception("B106ToGuid failed to convert string to Guid", ex);
        }
    }

0

ฉันมีความต้องการคล้าย ๆ กันยกเว้นว่าฉันจำเป็นต้องทำคณิตศาสตร์เกี่ยวกับ "ตัวเลข" ด้วย ฉันได้รับคำแนะนำบางส่วนที่นี่และสร้างชั้นเรียนที่จะทำเรื่องสนุก ๆ ทั้งหมดนี้ ช่วยให้สามารถใช้อักขระ Unicode เพื่อแสดงตัวเลขและทำงานร่วมกับทศนิยมได้เช่นกัน

คลาสนี้ค่อนข้างใช้งานง่าย เพียงสร้างตัวเลขเป็นประเภทNew BaseNumberตั้งค่าคุณสมบัติบางอย่างและปิดของคุณ กิจวัตรจะดูแลการสลับระหว่างฐาน 10 และฐาน x โดยอัตโนมัติและค่าที่คุณตั้งไว้จะถูกเก็บไว้ในฐานที่คุณตั้งไว้ดังนั้นจึงไม่มีการสูญเสียความแม่นยำ (จนกว่าจะมีการแปลงที่เป็นอยู่ แต่ถึงอย่างนั้นการสูญเสียความแม่นยำก็ควรน้อยมากเนื่องจากสิ่งนี้ การใช้งานประจำDoubleและLongที่ที่เป็นไปได้)

ฉันไม่สามารถสั่งความเร็วของกิจวัตรนี้ได้ มันอาจจะค่อนข้างช้าดังนั้นฉันไม่แน่ใจว่าจะเหมาะกับความต้องการของผู้ถามคำถามหรือไม่ แต่แน่นอนว่ามีความยืดหยุ่นดังนั้นหวังว่าจะมีคนอื่นใช้สิ่งนี้ได้

สำหรับใครก็ตามที่อาจต้องการรหัสนี้ในการคำนวณคอลัมน์ถัดไปใน Excel ฉันจะรวมรหัสการวนซ้ำที่ฉันใช้ซึ่งใช้ประโยชน์จากคลาสนี้

Public Class BaseNumber

    Private _CharacterArray As List(Of Char)

    Private _BaseXNumber As String
    Private _Base10Number As Double?

    Private NumberBaseLow As Integer
    Private NumberBaseHigh As Integer

    Private DecimalSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
    Private GroupSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator

    Public Sub UseCapsLetters()
        'http://unicodelookup.com
        TrySetBaseSet(65, 90)
    End Sub

    Public Function GetCharacterArray() As List(Of Char)
        Return _CharacterArray
    End Function

    Public Sub SetCharacterArray(CharacterArray As String)
        _CharacterArray = New List(Of Char)
        _CharacterArray.AddRange(CharacterArray.ToList)

        TrySetBaseSet(_CharacterArray)
    End Sub

    Public Sub SetCharacterArray(CharacterArray As List(Of Char))
        _CharacterArray = CharacterArray
        TrySetBaseSet(_CharacterArray)
    End Sub

    Public Sub SetNumber(Value As String)
        _BaseXNumber = Value
        _Base10Number = Nothing
    End Sub

    Public Sub SetNumber(Value As Double)
        _Base10Number = Value
        _BaseXNumber = Nothing
    End Sub

    Public Function GetBaseXNumber() As String
        If _BaseXNumber IsNot Nothing Then
            Return _BaseXNumber
        Else
            Return ToBaseString()
        End If
    End Function

    Public Function GetBase10Number() As Double
        If _Base10Number IsNot Nothing Then
            Return _Base10Number
        Else
            Return ToBase10()
        End If
    End Function

    Private Sub TrySetBaseSet(Values As List(Of Char))
        For Each value As Char In _BaseXNumber
            If Not Values.Contains(value) Then
                Throw New ArgumentOutOfRangeException("The string has a value, " & value & ", not contained in the selected 'base' set.")
                _CharacterArray.Clear()
                DetermineNumberBase()
            End If
        Next

        _CharacterArray = Values

    End Sub

    Private Sub TrySetBaseSet(LowValue As Integer, HighValue As Integer)

        Dim HighLow As KeyValuePair(Of Integer, Integer) = GetHighLow()

        If HighLow.Key < LowValue OrElse HighLow.Value > HighValue Then
            Throw New ArgumentOutOfRangeException("The string has a value not contained in the selected 'base' set.")
            _CharacterArray.Clear()
            DetermineNumberBase()
        End If

        NumberBaseLow = LowValue
        NumberBaseHigh = HighValue

    End Sub

    Private Function GetHighLow(Optional Values As List(Of Char) = Nothing) As KeyValuePair(Of Integer, Integer)
        If Values Is Nothing Then
            Values = _BaseXNumber.ToList
        End If

        Dim lowestValue As Integer = Convert.ToInt32(Values(0))
        Dim highestValue As Integer = Convert.ToInt32(Values(0))

        Dim currentValue As Integer

        For Each value As Char In Values

            If value <> DecimalSeparator AndAlso value <> GroupSeparator Then
                currentValue = Convert.ToInt32(value)
                If currentValue > highestValue Then
                    highestValue = currentValue
                End If
                If currentValue < lowestValue Then
                    currentValue = lowestValue
                End If
            End If
        Next

        Return New KeyValuePair(Of Integer, Integer)(lowestValue, highestValue)

    End Function

    Public Sub New(BaseXNumber As String)
        _BaseXNumber = BaseXNumber
        DetermineNumberBase()
    End Sub

    Public Sub New(BaseXNumber As String, NumberBase As Integer)
        Me.New(BaseXNumber, Convert.ToInt32("0"c), NumberBase)
    End Sub

    Public Sub New(BaseXNumber As String, NumberBaseLow As Integer, NumberBaseHigh As Integer)
        _BaseXNumber = BaseXNumber
        Me.NumberBaseLow = NumberBaseLow
        Me.NumberBaseHigh = NumberBaseHigh
    End Sub

    Public Sub New(Base10Number As Double)
        _Base10Number = Base10Number
    End Sub

    Private Sub DetermineNumberBase()
        Dim highestValue As Integer

        Dim currentValue As Integer

        For Each value As Char In _BaseXNumber

            currentValue = Convert.ToInt32(value)
            If currentValue > highestValue Then
                highestValue = currentValue
            End If
        Next

        NumberBaseHigh = highestValue
        NumberBaseLow = Convert.ToInt32("0"c) 'assume 0 is the lowest

    End Sub

    Private Function ToBaseString() As String
        Dim Base10Number As Double = _Base10Number

        Dim intPart As Long = Math.Truncate(Base10Number)
        Dim fracPart As Long = (Base10Number - intPart).ToString.Replace(DecimalSeparator, "")

        Dim intPartString As String = ConvertIntToString(intPart)
        Dim fracPartString As String = If(fracPart <> 0, DecimalSeparator & ConvertIntToString(fracPart), "")

        Return intPartString & fracPartString

    End Function

    Private Function ToBase10() As Double
        Dim intPartString As String = _BaseXNumber.Split(DecimalSeparator)(0).Replace(GroupSeparator, "")
        Dim fracPartString As String = If(_BaseXNumber.Contains(DecimalSeparator), _BaseXNumber.Split(DecimalSeparator)(1), "")

        Dim intPart As Long = ConvertStringToInt(intPartString)
        Dim fracPartNumerator As Long = ConvertStringToInt(fracPartString)
        Dim fracPartDenominator As Long = ConvertStringToInt(GetEncodedChar(1) & String.Join("", Enumerable.Repeat(GetEncodedChar(0), fracPartString.ToString.Length)))

        Return Convert.ToDouble(intPart + fracPartNumerator / fracPartDenominator)

    End Function

    Private Function ConvertIntToString(ValueToConvert As Long) As String
        Dim result As String = String.Empty
        Dim targetBase As Long = GetEncodingCharsLength()

        Do
            result = GetEncodedChar(ValueToConvert Mod targetBase) & result
            ValueToConvert = ValueToConvert \ targetBase
        Loop While ValueToConvert > 0

        Return result
    End Function

    Private Function ConvertStringToInt(ValueToConvert As String) As Long
        Dim result As Long
        Dim targetBase As Integer = GetEncodingCharsLength()
        Dim startBase As Integer = GetEncodingCharsStartBase()

        Dim value As Char
        For x As Integer = 0 To ValueToConvert.Length - 1
            value = ValueToConvert(x)
            result += GetDecodedChar(value) * Convert.ToInt32(Math.Pow(GetEncodingCharsLength, ValueToConvert.Length - (x + 1)))
        Next

        Return result

    End Function

    Private Function GetEncodedChar(index As Integer) As Char
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray(index)
        Else
            Return Convert.ToChar(index + NumberBaseLow)
        End If
    End Function

    Private Function GetDecodedChar(character As Char) As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray.IndexOf(character)
        Else
            Return Convert.ToInt32(character) - NumberBaseLow
        End If
    End Function

    Private Function GetEncodingCharsLength() As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray.Count
        Else
            Return NumberBaseHigh - NumberBaseLow + 1
        End If
    End Function

    Private Function GetEncodingCharsStartBase() As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return GetHighLow.Key
        Else
            Return NumberBaseLow
        End If
    End Function
End Class

และตอนนี้สำหรับโค้ดที่จะวนซ้ำคอลัมน์ Excel:

    Public Function GetColumnList(DataSheetID As String) As List(Of String)
        Dim workingColumn As New BaseNumber("A")
        workingColumn.SetCharacterArray("@ABCDEFGHIJKLMNOPQRSTUVWXYZ")

        Dim listOfPopulatedColumns As New List(Of String)
        Dim countOfEmptyColumns As Integer

        Dim colHasData As Boolean
        Dim cellHasData As Boolean

        Do
            colHasData = True
            cellHasData = False
            For r As Integer = 1 To GetMaxRow(DataSheetID)
                cellHasData = cellHasData Or XLGetCellValue(DataSheetID, workingColumn.GetBaseXNumber & r) <> ""
            Next
            colHasData = colHasData And cellHasData

            'keep trying until we get 4 empty columns in a row
            If colHasData Then
                listOfPopulatedColumns.Add(workingColumn.GetBaseXNumber)
                countOfEmptyColumns = 0
            Else
                countOfEmptyColumns += 1
            End If

            'we are already starting with column A, so increment after we check column A
            Do
                workingColumn.SetNumber(workingColumn.GetBase10Number + 1)
            Loop Until Not workingColumn.GetBaseXNumber.Contains("@")

        Loop Until countOfEmptyColumns > 3

        Return listOfPopulatedColumns

    End Function

คุณจะสังเกตว่าส่วนที่สำคัญของส่วน Excel คือ 0 ถูกระบุโดย @ ในหมายเลขอ้างอิงซ้ำ ดังนั้นฉันแค่กรองตัวเลขทั้งหมดที่มี @ อยู่ในนั้นและฉันจะได้ลำดับที่เหมาะสม (A, B, C, ... , Z, AA, AB, AC, ... )


0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConvertToAnyBase
{
   class Program
    {
        static void Main(string[] args)
        {
            var baseNumber = int.Parse(Console.ReadLine());
            var number = int.Parse(Console.ReadLine());
            string conversion = "";


            while(number!=0)
            {

                conversion += Convert.ToString(number % baseNumber);
                number = number / baseNumber;
            }
            var conversion2 = conversion.ToArray().Reverse();
            Console.WriteLine(string.Join("", conversion2));


       }
    }
}

นั่นคือตัวเลขฐานตั้งแต่ 1 ถึง 10
Martin Dimitrov

0

หากใครกำลังมองหาตัวเลือก VB นี่เป็นไปตามคำตอบของ Pavel:

Public Shared Function ToBase(base10 As Long, Optional baseChars As String = "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ") As String

    If baseChars.Length < 2 Then Throw New ArgumentException("baseChars must be at least 2 chars long")

    If base10 = 0 Then Return baseChars(0)

    Dim isNegative = base10 < 0
    Dim radix = baseChars.Length
    Dim index As Integer = 64 'because it's how long a string will be if the basechars are 2 long (binary)
    Dim chars(index) As Char '65 chars, 64 from above plus one for sign if it's negative

    base10 = Math.Abs(base10)


    While base10 > 0
        chars(index) = baseChars(base10 Mod radix)
        base10 \= radix

        index -= 1
    End While

    If isNegative Then
        chars(index) = "-"c
        index -= 1
    End If

    Return New String(chars, index + 1, UBound(chars) - index)

End Function

0

นี่เป็นวิธีที่ค่อนข้างตรงไปตรงมา แต่อาจไม่ใช่วิธีที่เร็วที่สุด มันค่อนข้างทรงพลังเพราะมันประกอบได้

public static IEnumerable<int> ToBase(this int x, int b)
{
    IEnumerable<int> ToBaseReverse()
    {
        if (x == 0)
        {
            yield return 0;
            yield break;
        }
        int z = x;
        while (z > 0)
        {
            yield return z % b;
            z = z / b;
        }
    }

    return ToBaseReverse().Reverse();
}

รวมสิ่งนี้เข้ากับวิธีการขยายแบบง่ายๆนี้และตอนนี้การรับฐานใด ๆ ก็เป็นไปได้:

public static string ToBase(this int number, string digits) =>
    String.Concat(number.ToBase(digits.Length).Select(x => digits[x]));

สามารถใช้งานได้ดังนี้:

var result = 23.ToBase("01");
var result2 = 23.ToBase("012X");

Console.WriteLine(result);
Console.WriteLine(result2);

ผลลัพธ์คือ:

10111
11X
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.