10 tabanlı bir sayıyı .NET'teki herhangi bir üsse dönüştürmenin en hızlı yolu?


108

Bir sayıyı alıp herhangi bir tabana dönüştüren yazdığım eski (ish) C # yöntemim var:

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

Hepsi o kadar hızlı ve düzgün değil. Bunu .NET'te elde etmenin iyi ve bilinen bir yolu var mı?

Herhangi bir tabanı rastgele bir karakter dizisi ile kullanmama izin veren bir şey arıyorum .

Bu yalnızca 16, 10, 8 ve 2 tabanlarına izin verir:

Convert.ToString(1, x);

Bunu, sayılardan, tümü küçük harflerden ve tümü büyük harflerden yararlanarak çok yüksek bir taban elde etmek için kullanmak istiyorum. Olduğu gibi bu konuya , ancak C # değil JavaScript için.

Bunu C # ile yapmanın iyi ve verimli bir yolunu bilen var mı?

Yanıtlar:


136

Convert.ToString bir sayıyı belirli bir tabandaki eşdeğer dize gösterimine dönüştürmek için kullanılabilir.

Misal:

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

Bununla birlikte, yorumlarda belirtildiği gibi, Convert.ToStringyalnızca aşağıdaki sınırlı - ancak tipik olarak yeterli - temel setini destekler: 2, 8, 10 veya 16.

Güncelleme (herhangi bir tabana dönüştürme gereksinimini karşılamak için):

BCL'de sayıları herhangi bir tabana dönüştürebilen herhangi bir yöntemin farkında değilim, böylece kendi küçük yardımcı program işlevinizi yazmanız gerekir. Basit bir örnek şöyle görünecektir (bunun kesinlikle dize birleştirme değiştirilerek daha hızlı yapılabileceğini unutmayın):

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

Güncelleme 2 (Performans İyileştirme)

Sonuç dizesini oluşturmak için dizi birleştirme yerine bir dizi tamponu kullanmak, özellikle büyük sayılarda bir performans artışı sağlar (yönteme bakın IntToStringFast). En iyi durumda (yani mümkün olan en uzun girdi) bu yöntem kabaca üç kat daha hızlıdır. Bununla birlikte, 1 basamaklı sayılar için (yani hedef tabandaki 1 basamaklı) IntToStringdaha hızlı olacaktır.


5
Bunun yalnızca 2,8,10,16 tabanlarını desteklediğine dikkat edilmelidir - sorudaki "herhangi birini" değil. çünkü ne zaman seksikine ihtiyaç duyacağınızı asla bilemezsiniz; -p
Marc Gravell

46
Altmışıncı kulağa eğlenceli geliyor.
dizgin

60 targetBase ve 12345 değeriyle, IntToString yöntemindeki bu satır: value = value / targetBase; değeri 203.75 yapar. Bu doğru mu? Tam sayı olarak tutması gerekmez mi?
Adam Harte

6
Harika. Ama ters fonksiyon nerede? : /
ashes999

2
Burada bir ilk geçiş ters fonksiyonuna
sahibim

78

Geçenlerde bunun hakkında blog yazdım . Uygulamam, hesaplamalar sırasında herhangi bir dizgi işlemi kullanmıyor, bu da onu çok hızlı hale getiriyor. 2'den 36'ya kadar tabana sahip herhangi bir sayı sistemine dönüştürme desteklenir:

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

Ayrıca, birinin de ihtiyacı olması durumunda hızlı bir ters işlev uyguladım: Keyfi - Ondalık Sayı Sistemi .


5
Bu sayfadaki tüm çözümleri mükemmel bir şekilde test ettim ve bu en hızlı olanı, sonunda kısa çözümden yaklaşık iki kat daha hızlı.
Justin R.

Bu result = "-" + resultnedir? Bu bir çeşit dolgu mı? Bir dolgu karakteri için yalnızca AZ veya 0-9 kullanacak şekilde kodu nasıl değiştirebilirim?
oscilatingcretin

"-"İçinde result = "-" + resultnegatif sayıların negatif bir işaret için standlar. Bu bir dolgu karakteri değil.
Pavel Vladov

2
Neden bu kabul edilen cevap değil? Bu dahice!
Avrohom Yisroel

Teşekkür ederim, bu bana çok zaman kazandırdı.
NinjaLlama

15

HIZLI " NEDEN " VE " TO " YÖNTEMLERİ

Partiye geç kaldım, ancak önceki cevapları birleştirdim ve onları geliştirdim. Bu iki yöntemin şimdiye kadar yayınlananlardan daha hızlı olduğunu düşünüyorum. Tek çekirdekli bir makinede 1.000.000 sayıyı 400 ms'nin altında 36 tabanından ve 36 tabanına dönüştürebildim.

Aşağıdaki örnek 62 tabanı içindir . BaseCharsDiğer tabandan ve diğer tabana dönüştürülecek diziyi değiştirin .

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

DÜZENLEME (2018-07-12)

@AdrianBotor (açıklamalara bakın) tarafından 46655'i taban 36'ya dönüştürürken bulunan köşe durumunu ele almak için düzeltildi. Bu Math.Log(46656, 36), tam olarak 3 olan küçük bir kayan nokta hesaplamasından kaynaklanır, ancak .NET 3 + 4.44e-16, çıktı tamponunda fazladan bir karaktere neden olan .


@AdrianBotor, sorununuzu yeniden çözemez:BaseToLong(LongToBase(46655)) == 46655
Diego

2
@Diego, geç cevap için özür dilerim. Diziyi 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZdeğerle başlatalım ve değeri dönüştürelim 46655. Sonuç olmalı ZZZama hata ayıklayıcıda alıyorum \0ZZZ. Sadece bu değer ilave edilir \0. Örneğin, değer 46654doğru şekilde ZZY.
Adrian Botor

@AdrianBotor İyi yakaladı. Dönüş deyimi değişiklikler yaparak Ele LongToBaseiçinreturn new string(buffer, (int) i, buffer.Length - (int)i);
Diego

7

Ayrıca kabul edilenin biraz değiştirilmiş halini kullanabilir ve temel karakter dizesini ihtiyaçlarına göre ayarlayabilirsiniz:

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

Bu konuda partiye çok geç kaldım, ancak geçenlerde işteki bir proje için aşağıdaki yardımcı sınıfı yazdım. Kısa dizeleri sayılara dönüştürmek ve tekrar geri dönüştürmek için tasarlanmıştır (basit bir mükemmel karma işlevi), ancak aynı zamanda keyfi tabanlar arasında sayı dönüştürme de gerçekleştirecektir. Base10ToStringYöntem uygulaması başlangıçta gönderilmiş soruyu cevaplar.

shouldSupportRoundTrippingSınıf yapıcısı geçirilen bayrağı (benim gereksinimleri göz önüne alındığında, önemli!) Tekrar baz-10 ve dönüştürme sırasında sayı dizesi önde gelen basamak kaybını önlemek için gereklidir. Çoğu zaman sayı dizisindeki baştaki 0'ların kaybı muhtemelen bir sorun olmayacaktır.

Her neyse, işte kod:

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

Bu ayrıca, özel sayı dönüştürücüleri türetmek için alt sınıflara ayrılabilir:

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)
        {
        }
    }
}

Ve kod şu şekilde kullanılacaktır:

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

Bu forum gönderisindeki bu sınıf size yardımcı olabilir mi?

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; 

} 

}

Tamamen denenmemiş ... işe yarayıp yaramadığını bana bildirin! (Forum gönderisinin kaybolması veya başka bir şey olması durumunda kopyalayıp yapıştırın ...)


Kapat .. Daha sonra bir oyun oynarım. Herhangi bir karakteri alabilmek için biraz çalışmanız gerekecek, ancak bu doğru yönde atılmış bir adım. Hızı kendi yöntemimle karşılaştıracağım!
joshcomley

İyileştirirseniz buradaysa paylaşmayı unutmayın. Başka biri de ot isteyebilir =)
Svish

@joshcomley Hafta sonu nasıl geçti? ;)
Mikkel R. Lund

3
Uzun bir hafta sonuydu: D
joshcomley

1

Ben de ondalık sayıyı [2..36] aralığında başka bir tabana çevirmenin hızlı bir yolunu arıyordum, bu yüzden aşağıdaki kodu geliştirdim. İzlemesi kolaydır ve karakter bazında indeksleyebileceğimiz bir karakter tamponu için vekil olarak bir Stringbuilder nesnesi kullanır. Kod, alternatiflere kıyasla çok hızlı ve bir karakter dizisindeki tek tek karakterleri başlatmaktan çok daha hızlı görünüyor.

Kendi kullanımınız için şunları tercih edebilirsiniz: 1 / Bir istisna atmak yerine boş bir dize döndür. 2 / yöntemin daha da hızlı çalışmasını sağlamak için radix kontrolünü kaldırın 3 / Stringbuilder nesnesini 32 '0'larla başlatın ve satır sonucunu kaldırın. (0, i); Bu, dizenin önde gelen sıfırlarla döndürülmesine neden olacak ve hızı daha da artıracaktır. 4 / Stringbuilder nesnesini sınıf içinde statik bir alan yapın, böylece DecimalToBase yöntemi kaç kez çağrılırsa adlandırılsın Stringbuilder nesnesi yalnızca bir kez başlatılır. Bu değişikliği yaparsanız, yukarıdaki 3 artık işe yaramayacaktır.

Umarım birisi bunu yararlı bulur :)

AtomicParadox

        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

Bunu bir Kılavuzu daha kısa bir dize olarak saklamak için kullanıyordum (ancak 106 karakterle sınırlıydı). Burada ilgilenen biri varsa, dizeyi sayısal değere geri döndürmek için kodumdur (bu durumda, bir Int128 kodlamak yerine Guid değeri için 2 ulong kullandım (4.0 değil 3.5'te olduğum için). 106 benzersiz karakter içeren string const. ConvertLongsToBytes oldukça heyecan verici.

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

Benzer bir ihtiyacım vardı, ancak "sayılar" üzerinde de matematik yapmam gerekiyordu. Buradaki bazı önerileri aldım ve tüm bu eğlenceli şeyleri yapacak bir sınıf oluşturdum. Herhangi bir unicode karakterin bir sayıyı temsil etmek için kullanılmasına izin verir ve ondalık sayılarla da çalışır.

Bu sınıfın kullanımı oldukça kolaydır. Tür olarak bir sayı oluşturun New BaseNumber, birkaç özelliği ayarlayın ve kapatın. Rutinler otomatik olarak 10 tabanı ve x tabanı arasında geçiş yapmaya özen gösterir ve ayarladığınız değer, ayarladığınız tabanda korunur, bu nedenle hiçbir doğruluk kaybı olmaz (yani dönüşüme kadar, ancak o zaman bile hassasiyet kaybı çok az olmalıdır çünkü bu rutin kullanımlar Doubleve Longmümkün olan yerlerde).

Bu rutinin hızına hükmedemiyorum. Muhtemelen oldukça yavaştır, bu yüzden soruyu soran kişinin ihtiyaçlarını karşılayıp karşılayamayacağından emin değilim, ama kesinlikle esnektir, bu yüzden umarım başka biri bunu kullanabilir.

Excel'de bir sonraki sütunu hesaplamak için bu koda ihtiyaç duyan herkes için, bu sınıftan yararlanan kullandığım döngü kodunu ekleyeceğim.

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

Ve şimdi kodun Excel sütunlarından geçmesi için:

    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 bölümünün önemli kısmının, yeniden tabanlı numarada 0'ın bir @ ile tanımlanması olduğunu fark edeceksiniz. Bu yüzden, içinde @ olan tüm sayıları filtreliyorum ve doğru sırayı (A, B, C, ..., Z, AA, AB, AC, ...) elde ediyorum.


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


       }
    }
}

Bu, 1'den 10'a kadar olan temel sayılar içindir.
Martin Dimitrov

0

Bir VB seçeneği arayan biri varsa, bu Pavel'in cevabına dayanıyordu:

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

Bu, bunu yapmanın oldukça basit bir yoludur, ancak en hızlı yolu olmayabilir. Oldukça güçlü çünkü bir araya getirilebilir.

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

Bunu bu basit genişletme yöntemiyle birleştirin ve herhangi bir temeli elde etmek artık mümkün:

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

Şu şekilde kullanılabilir:

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

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

Çıktı:

10111
11X
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.