Какая эффективная инверсия функции С# для преобразования в произвольную базу?

Мне нужно преобразовать целое число в представление base64-character. Я использую ответ OxA3 в этой теме: Самый быстрый способ конвертировать базовый номер 10 в любую базу в .NET?

Как мне инвертировать это, чтобы вернуть исходное целое число, учитывая строку?

Ответ 1

Ответ Джоэля Мюллера должен помочь вам в случае с базой 64.

В ответ на предварительный код, который вы указали в свой собственный ответ, вы можете определенно повысить его эффективность, изменив код, чтобы выполнить то, что ваш цикл for (эффективно O (N) IndexOf), чтобы использовать хэш-поиск (который должен сделать его O (1)).

Я основываю это на предположении, что baseChars - это поле, которое вы инициализируете в своем конструкторе класса. Если это правильно, выполните следующую настройку:

private Dictionary<char, int> baseChars;

// I don't know what your class is called.
public MultipleBaseNumberFormatter(IEnumerable<char> baseCharacters)
{
    // check for baseCharacters != null and Count > 0

    baseChars = baseCharacters
        .Select((c, i) => new { Value = c, Index = i })
        .ToDictionary(x => x.Value, x => x.Index);
}

Затем в вашем методе StringToInt:

char next = encodedString[currentChar];

// No enumerating -- we've gone from O(N) to O(1)!
if (!characterIndices.TryGetValue(next, out nextCharIndex))
{
    throw new ArgumentException("Input includes illegal characters.");
}

Ответ 2

У меня есть первый проход рабочей версии здесь, хотя я не уверен, насколько он эффективен.

public static int StringToInt(string encodedString)
    {
        int result = 0;
        int sourceBase = baseChars.Length;
        int nextCharIndex = 0;

        for (int currentChar = encodedString.Length - 1; currentChar >= 0; currentChar--)
        {
            char next = encodedString[currentChar];

            // For loop gets us: baseChar.IndexOf(char) => int
            for (nextCharIndex = 0; nextCharIndex < baseChars.Length; nextCharIndex++)
            {
                if (baseChars[nextCharIndex] == next)
                {
                    break;
                }
            }

            // For character N (from the end of the string), we multiply our value
            // by 64^N. eg. if we have "CE" in hex, F = 16 * 13.
            result += (int)Math.Pow(baseChars.Length, encodedString.Length - 1 - currentChar) * nextCharIndex;
        }

        return result;
    }

Ответ 3

Ниже приведена версия с использованием функциональности Linq и расширения Zip для .NET Framework 4.0 для выполнения расчета.

public static int StringToInt(string encodedString, char[] baseChars) {
    int sourceBase = baseChars.Length;

    var dict = baseChars
        .Select((c, i) => new { Value = c, Index = i })
        .ToDictionary(x => x.Value, x => x.Index);

    return encodedString.ToCharArray()
        // Get a list of positional weights in descending order, calcuate value of weighted position
        .Zip(Enumerable.Range(0,encodedString.Length).Reverse(), (f,s) => dict[f] * (int)Math.Pow(sourceBase,s)) 
        .Sum();
}

FYI, вычисление словаря за пределами функции будет более эффективным для большой партии преобразований.

Ответ 4

Если base-64 действительно то, что вам нужно, а не "любая база", тогда все, что вам нужно, уже встроено в фреймворк:

int orig = 1337;
byte[] origBytes = BitConverter.GetBytes(orig);
string encoded = Convert.ToBase64String(origBytes);
byte[] decoded = Convert.FromBase64String(encoded);
int converted = BitConverter.ToInt32(decoded, 0);
System.Diagnostics.Debug.Assert(orig == converted);

Ответ 5

Здесь представлено полное решение, которое преобразует число base10 в baseK и обратно:

public class Program
{
    public static void Main()
    {
        int i = 100;

        Console.WriteLine("Int:               " + i);

        // Default base definition. By moving chars around in this string, we can further prevent
        // users from guessing identifiers.
        var baseDefinition = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        //var baseDefinition = "WBUR17GHO8FLZIA059M4TESD2VCNQKXPJ63Y"; // scrambled to minimize guessability

        // Convert base10 to baseK
        var newId = ConvertToBaseK(i, baseDefinition);
        Console.WriteLine(string.Format("To base{0} (short): {1}", baseDefinition.Length, newId));

        // Convert baseK to base10
        var convertedInt2 = ConvertToBase10(newId, baseDefinition);
        Console.WriteLine(string.Format("Converted back:    {0}", convertedInt2));
    }

    public static string ConvertToBaseK(int val, string baseDef)
    {
        string result = string.Empty;
        int targetBase = baseDef.Length;

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

        return result;
    }

    public static int ConvertToBase10(string str, string baseDef)
    {
        double result = 0;
        for (int idx = 0; idx < str.Length; idx++)
        {
            var idxOfChar = baseDef.IndexOf(str[idx]);
            result += idxOfChar * System.Math.Pow(baseDef.Length, (str.Length-1) - idx);
        }

        return (int)result;
    }
}