C#格式任意大BigInteger,用于无尽的游戏 [英] C# format arbitrarily large BigInteger for endless game

查看:243
本文介绍了C#格式任意大BigInteger,用于无尽的游戏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建无尽的游戏,例如Tap Titans,Clicker Heroes等.我有一个BigInteger类,只要能容纳在内存中,就可以表示任意大的整数.

现在,我有一个将BigInteger格式化为特定格式的类.对于较小的数字,它使用K(千),M(百万),B(十亿),T(万亿),Q(四十亿),但之后的缩写符号变得模棱两可且不直观.由于五位数,Q已​​经不明确了,但我可以接受.

在Q之后,我想从字母a开始.因此1000Q = 1.000a,然后1000a = 1.000b,依此类推.当达到1000z时,应将其格式化为1.000aa.那么1000aa = 1.000 ab,1000 az = 1.000 ba,1000 bz = 1.000 ca等.

到目前为止,我已经达到了上述要求,但是我的班级无法在1000zz之后格式化数字.我还无法提出一种自动确定需要多少个字符的通用算法(对于非常大的数字可能是aaaz).

我的课程如下:

public class NumericalFormatter : BigIntegerFormatter
{
    public string Format(BigInteger number)
    {
        return FormatNumberString(number.ToString());
    }

    private string FormatNumberString(string number)
    {
        if (number.Length < 5)
        {
            return number;
        }

        if (number.Length < 7)
        {
            return FormatThousands(number);
        }

        return FormatGeneral(number);
    }

    private string FormatThousands(string number)
    {
        string leadingNumbers = number.Substring(0, number.Length - 3);
        string decimals = number.Substring(number.Length - 3);

        return CreateNumericalFormat(leadingNumbers, decimals, "K");
    }

    private string CreateNumericalFormat(string leadingNumbers, string decimals, string suffix)
    {
        return String.Format("{0}.{1}{2}", leadingNumbers, decimals, suffix);
    }

    private string FormatGeneral(string number)
    {
        int amountOfLeadingNumbers = (number.Length - 7) % 3 + 1;
        string leadingNumbers = number.Substring(0, amountOfLeadingNumbers);
        string decimals = number.Substring(amountOfLeadingNumbers, 3);

        return CreateNumericalFormat(leadingNumbers, decimals, GetSuffixForNumber(number));
    }

    private string GetSuffixForNumber(string number)
    {
        int numberOfThousands = (number.Length - 1) / 3;

        switch (numberOfThousands)
        {
            case 1:
                return "K";
            case 2:
                return "M";
            case 3:
                return "B";
            case 4:
                return "T";
            case 5:
                return "Q";
            default:
                return GetProceduralSuffix(numberOfThousands - 5);
        }
    }

    private string GetProceduralSuffix(int numberOfThousandsAfterQ)
    {
        if (numberOfThousandsAfterQ < 27)
        {
            return ((char)(numberOfThousandsAfterQ + 96)).ToString();
        }

        int rightChar = (numberOfThousandsAfterQ % 26);
        string right = rightChar == 0 ? "z" : ((char)(rightChar + 96)).ToString();
        string left = ((char)(((numberOfThousandsAfterQ - 1) / 26) + 96)).ToString();

        return left + right;
    }
}

如您所见,getProceduralSuffix()方法无法处理将导致两个以上字符后缀的BigInteger.

我还有一个单元测试,它可以验证此类的功能(为进行一些侧面滚动做准备):

namespace UnitTestProject.BigIntegerTest
{
    [TestClass]
    public class NumericalformatterTest
    {
        [TestMethod]
        public void TestFormatReturnsNumericalFormat()
        {
            BigIntegerFormatter numericalFormatter = new NumericalFormatter();

            foreach (string[] data in DpNumbersAndNumericalFormat())
            {
                BigInteger number = new BigInteger(data[0]);
                string expectedNumericalFormat = data[1];

                Assert.AreEqual(expectedNumericalFormat, numericalFormatter.Format(number));
            }
        }

        private string[][] DpNumbersAndNumericalFormat()
        {
            return new string[][]
            {
                new string[] { "0", "0" },
                new string[] { "1", "1" },
                new string[] { "15", "15" },
                new string[] { "123", "123" },
                new string[] { "999", "999" },
                new string[] { "1000", "1000" },
                new string[] { "9999", "9999" },
                new string[] { "10000", "10.000K" },
                new string[] { "78456", "78.456K" },
                new string[] { "134777", "134.777K" },
                new string[] { "999999", "999.999K" },
                new string[] { "1000000", "1.000M" },
                new string[] { "12345000", "12.345M" },
                new string[] { "999999000", "999.999M" },
                new string[] { "1000000000", "1.000B" },
                new string[] { "12345678900", "12.345B" },
                new string[] { "123345678900", "123.345B" },
                new string[] { "1233000000000", "1.233T" },
                new string[] { "9999000000000", "9.999T" },
                new string[] { "12233000000000", "12.233T" },
                new string[] { "99999000000000", "99.999T" },
                new string[] { "100000000000000", "100.000T" },
                new string[] { "456789000000000", "456.789T" },
                new string[] { "999999000000000", "999.999T" },
                new string[] { "1000000000000000", "1.000Q" },
                new string[] { "10000000000000000", "10.000Q" },
                new string[] { "100000000000000000", "100.000Q" },
                new string[] { "999999000000000000", "999.999Q" },
                new string[] { "1000000000000000000", "1.000a" },
                new string[] { "10000000000000000000", "10.000a" },
                new string[] { "100000000000000000000", "100.000a" },
                new string[] { "1000000000000000000000", "1.000b" },
                new string[] { "1000000000000000000000000", "1.000c" },
                new string[] { "1000000000000000000000000000", "1.000d" },
                new string[] { "1000000000000000000000000000000", "1.000e" },
                new string[] { "1000000000000000000000000000000000", "1.000f" },
                new string[] { "1000000000000000000000000000000000000", "1.000g" },
                new string[] { "1000000000000000000000000000000000000000", "1.000h" },
                new string[] { "1000000000000000000000000000000000000000000", "1.000i" },
                new string[] { "1000000000000000000000000000000000000000000000", "1.000j" },
                new string[] { "1000000000000000000000000000000000000000000000000", "1.000k" },
                new string[] { "1000000000000000000000000000000000000000000000000000", "1.000l" },
                new string[] { "1000000000000000000000000000000000000000000000000000000", "1.000m" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000", "1.000n" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000", "1.000o" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000", "1.000p" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000", "1.000q" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000", "1.000r" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000", "1.000s" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000t" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000u" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000v" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000w" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000x" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000y" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000z" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aa" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ab" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ac" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ad" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ae" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000af" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ag" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ah" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ai" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aj" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ak" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000al" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000am" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000an" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ao" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ap" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aq" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ar" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000as" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000at" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000au" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000av" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aw" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ax" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ay" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000az" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ba" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bb" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bc" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bd" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000be" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bf" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bg" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bh" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bi" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bj" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bt" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000by" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bz" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ca" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cb" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cc" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cd" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ce" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ct" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cy" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cz" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000da" },
                new string[] { "1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.234da" },
                new string[] { "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "123.456da" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000db" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000dr" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000dz" },
            };
        }
    }
}

以上所有测试目前都通过了.缺少的测试是检查1000 ^ (26 ^ 3 + 5)(+ 5是因为a格式在Q之后开始)的一种,可以将其格式化为"1.000aaa".

如何使用上述方法格式化大整数.

解决方案

在数字符号世界中,这实际上是一个已解决的问题.也就是说,您可以改为使用科学符号来表示这些特别大的数字.科学表示法是紧凑的,允许尾数具有任意精度,并且易于理解.就个人而言,这就是我要采取的方法.

为了便于讨论,让我们看看您还有哪些其他选择


从表面上看,您的请求可以归结为一个直接的数字基值,以进行文本转换.就像我们可以将数字值转换为以2为基数,以10为基数,以16为基数等形式的文本表示形式一样,我们也可以使用以26为基数的数字(仅使用字母az作为数字.

然后您的GetProceduralSuffix()方法将如下所示:

static string GetProceduralSuffix(int value)
{
    StringBuilder sb = new StringBuilder();

    while (value > 0)
    {
        int digit = value % 26;

        sb.Append((char)('a'+ digit));
        value /= 26;
    }

    if (sb.Length == 0)
    {
        sb.Append('a');
    }

    sb.Reverse();
    return sb.ToString();
}

Reverse()扩展方法是这样的:

public static void Reverse(this StringBuilder sb)
{
    for (int i = 0, j = sb.Length - 1; i < sb.Length / 2; i++, j--)
    {
        char chT = sb[i];

        sb[i] = sb[j];
        sb[j] = chT;
    }
}

但是,上面有一个小问题.在以这种方式表示的基数26中,数字a对应于0,因此您的后缀将永远不会以字母a开头,至少不会在第一个字母之后(这是特殊情况,就像使用十进制时一样)表示法,我们单独使用数字0表示零值).相反,例如,您将在z之后得到ba,在zz之后得到baa.

我个人认为这很好.它会排除像aaaz这样的后缀,但这只是因为后缀表示法系统是逻辑,可预测且易于逆转的(即给定后缀,弄清楚这在数字上的含义是微不足道的).

但是,如果您坚持使用azaazzaaazzzaaaa…依此类推,您可以使用基数27而不是26,并为0数字使用除az以外的其他字符,并预先计算后缀,以使您跳过后缀为0的值,并且然后索引结果.例如:

List<string> _hackedValues = new List<string>();

static void PrecomputeValues()
{
    // 531441 = 27 ^ 4, i.e. the first 5-digit base 27 number.
    // That's a large enough number to ensure that the output
    // include "aaaz", and indeed almost all of the 4-digit
    // base 27 numbers
    for (int i = 0; i < 531441; i++)
    {
        string text = ToBase27AlphaString(i);

        if (!text.Contains('`'))
        {
            _hackedValues.Add(text);
        }
    }
}

static string GetProceduralSuffix(int value)
{
    if (hackedValues.Count == 0)
    {
        PrecomputeValues();
    }

    return _hackedValues[value];
}

static string ToBase27AlphaString(int value)
{
    StringBuilder sb = new StringBuilder();

    while (value > 0)
    {
        int digit = value % 27;

        sb.Append((char)('`'+ digit));
        value /= 27;
    }

    if (sb.Length == 0)
    {
        sb.Append('`');
    }

    sb.Reverse();
    return sb.ToString();
}

这是一个完整的程序,说明了这两种技术,显示了有趣"输入的文本(即输出中字符数发生变化的地方):

class Program
{
    static void Main(string[] args)
    {
        int[] values = { 0, 25, 26, 675, 676 };

        foreach (int value in values)
        {
            Console.WriteLine("{0}: {1}", value, ToBase26AlphaString(value));
        }

        Console.WriteLine();

        List<Tuple<int, string>> hackedValues = new List<Tuple<int, string>>();

        for (int i = 0; i < 531441; i++)
        {
            string text = ToBase27AlphaString(i);

            if (!text.Contains('`'))
            {
                hackedValues.Add(Tuple.Create(i, text));
            }
        }

        Tuple<int, string> prev = null;

        for (int i = 0; i < hackedValues.Count; i++)
        {
            Tuple<int, string> current = hackedValues[i];

            if (prev == null || prev.Item2.Length != current.Item2.Length)
            {
                if (prev != null)
                {
                    DumpHackedValue(prev, i - 1);
                }
                DumpHackedValue(current, i);
            }

            prev = current;
        }
    }

    private static void DumpHackedValue(Tuple<int, string> hackedValue, int i)
    {
        Console.WriteLine("{0}: {1} (actual value: {2})", i, hackedValue.Item2, hackedValue.Item1);
    }

    static string ToBase26AlphaString(int value)
    {
        return ToBaseNAlphaString(value, 'a', 26);
    }

    static string ToBase27AlphaString(int value)
    {
        return ToBaseNAlphaString(value, '`', 27);
    }

    static string ToBaseNAlphaString(int value, char baseChar, int numericBase)
    {
        StringBuilder sb = new StringBuilder();

        while (value > 0)
        {
            int digit = value % numericBase;

            sb.Append((char)(baseChar + digit));
            value /= numericBase;
        }

        if (sb.Length == 0)
        {
            sb.Append(baseChar);
        }

        sb.Reverse();
        return sb.ToString();
    }
}

static class Extensions
{
    public static void Reverse(this StringBuilder sb)
    {
        for (int i = 0, j = sb.Length - 1; i < sb.Length / 2; i++, j--)
        {
            char chT = sb[i];

            sb[i] = sb[j];
            sb[j] = chT;
        }
    }
}

I am trying to create an endless game such as Tap Titans, Clicker Heroes, etc. I have a BigInteger class that is capable of representing arbitrarily large integers as long as they fit into memory.

Now I have a class that formats a BigInteger into a specific format. It uses K (thousand), M (million), B (billion), T (trillion), Q (quadrillion) for the 'smaller' numbers, but after that the short hand notations become ambiguous and unintuitive. Q is already ambiguous due to Quintillion, but I can live with that.

After Q, I want to start from the letter a. So 1000Q = 1.000a, then 1000a = 1.000b etc. When 1000z is reached, it should be formatted to 1.000aa. Then 1000aa = 1.000 ab, 1000 az = 1.000 ba, 1000 bz = 1.000 ca, etc.

So far I have achieved the above, however my class is unable to format a number after 1000zz. I have not been able to come up with a generic algorithm that automatically determines how many characters are needed (could be aaaz for extremely large numbers).

My class looks as follows:

public class NumericalFormatter : BigIntegerFormatter
{
    public string Format(BigInteger number)
    {
        return FormatNumberString(number.ToString());
    }

    private string FormatNumberString(string number)
    {
        if (number.Length < 5)
        {
            return number;
        }

        if (number.Length < 7)
        {
            return FormatThousands(number);
        }

        return FormatGeneral(number);
    }

    private string FormatThousands(string number)
    {
        string leadingNumbers = number.Substring(0, number.Length - 3);
        string decimals = number.Substring(number.Length - 3);

        return CreateNumericalFormat(leadingNumbers, decimals, "K");
    }

    private string CreateNumericalFormat(string leadingNumbers, string decimals, string suffix)
    {
        return String.Format("{0}.{1}{2}", leadingNumbers, decimals, suffix);
    }

    private string FormatGeneral(string number)
    {
        int amountOfLeadingNumbers = (number.Length - 7) % 3 + 1;
        string leadingNumbers = number.Substring(0, amountOfLeadingNumbers);
        string decimals = number.Substring(amountOfLeadingNumbers, 3);

        return CreateNumericalFormat(leadingNumbers, decimals, GetSuffixForNumber(number));
    }

    private string GetSuffixForNumber(string number)
    {
        int numberOfThousands = (number.Length - 1) / 3;

        switch (numberOfThousands)
        {
            case 1:
                return "K";
            case 2:
                return "M";
            case 3:
                return "B";
            case 4:
                return "T";
            case 5:
                return "Q";
            default:
                return GetProceduralSuffix(numberOfThousands - 5);
        }
    }

    private string GetProceduralSuffix(int numberOfThousandsAfterQ)
    {
        if (numberOfThousandsAfterQ < 27)
        {
            return ((char)(numberOfThousandsAfterQ + 96)).ToString();
        }

        int rightChar = (numberOfThousandsAfterQ % 26);
        string right = rightChar == 0 ? "z" : ((char)(rightChar + 96)).ToString();
        string left = ((char)(((numberOfThousandsAfterQ - 1) / 26) + 96)).ToString();

        return left + right;
    }
}

As you can see the getProceduralSuffix() method cannot handle BigIntegers that would result in more than two character suffixes.

I also have a unit test that verifies the functionality of this class (prepare for some side scrolling):

namespace UnitTestProject.BigIntegerTest
{
    [TestClass]
    public class NumericalformatterTest
    {
        [TestMethod]
        public void TestFormatReturnsNumericalFormat()
        {
            BigIntegerFormatter numericalFormatter = new NumericalFormatter();

            foreach (string[] data in DpNumbersAndNumericalFormat())
            {
                BigInteger number = new BigInteger(data[0]);
                string expectedNumericalFormat = data[1];

                Assert.AreEqual(expectedNumericalFormat, numericalFormatter.Format(number));
            }
        }

        private string[][] DpNumbersAndNumericalFormat()
        {
            return new string[][]
            {
                new string[] { "0", "0" },
                new string[] { "1", "1" },
                new string[] { "15", "15" },
                new string[] { "123", "123" },
                new string[] { "999", "999" },
                new string[] { "1000", "1000" },
                new string[] { "9999", "9999" },
                new string[] { "10000", "10.000K" },
                new string[] { "78456", "78.456K" },
                new string[] { "134777", "134.777K" },
                new string[] { "999999", "999.999K" },
                new string[] { "1000000", "1.000M" },
                new string[] { "12345000", "12.345M" },
                new string[] { "999999000", "999.999M" },
                new string[] { "1000000000", "1.000B" },
                new string[] { "12345678900", "12.345B" },
                new string[] { "123345678900", "123.345B" },
                new string[] { "1233000000000", "1.233T" },
                new string[] { "9999000000000", "9.999T" },
                new string[] { "12233000000000", "12.233T" },
                new string[] { "99999000000000", "99.999T" },
                new string[] { "100000000000000", "100.000T" },
                new string[] { "456789000000000", "456.789T" },
                new string[] { "999999000000000", "999.999T" },
                new string[] { "1000000000000000", "1.000Q" },
                new string[] { "10000000000000000", "10.000Q" },
                new string[] { "100000000000000000", "100.000Q" },
                new string[] { "999999000000000000", "999.999Q" },
                new string[] { "1000000000000000000", "1.000a" },
                new string[] { "10000000000000000000", "10.000a" },
                new string[] { "100000000000000000000", "100.000a" },
                new string[] { "1000000000000000000000", "1.000b" },
                new string[] { "1000000000000000000000000", "1.000c" },
                new string[] { "1000000000000000000000000000", "1.000d" },
                new string[] { "1000000000000000000000000000000", "1.000e" },
                new string[] { "1000000000000000000000000000000000", "1.000f" },
                new string[] { "1000000000000000000000000000000000000", "1.000g" },
                new string[] { "1000000000000000000000000000000000000000", "1.000h" },
                new string[] { "1000000000000000000000000000000000000000000", "1.000i" },
                new string[] { "1000000000000000000000000000000000000000000000", "1.000j" },
                new string[] { "1000000000000000000000000000000000000000000000000", "1.000k" },
                new string[] { "1000000000000000000000000000000000000000000000000000", "1.000l" },
                new string[] { "1000000000000000000000000000000000000000000000000000000", "1.000m" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000", "1.000n" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000", "1.000o" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000", "1.000p" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000", "1.000q" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000", "1.000r" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000", "1.000s" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000t" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000u" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000v" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000w" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000x" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000y" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000z" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aa" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ab" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ac" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ad" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ae" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000af" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ag" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ah" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ai" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aj" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ak" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000al" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000am" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000an" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ao" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ap" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aq" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ar" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000as" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000at" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000au" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000av" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000aw" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ax" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ay" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000az" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ba" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bb" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bc" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bd" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000be" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bf" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bg" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bh" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bi" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bj" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bt" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000by" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000bz" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ca" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cb" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cc" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cd" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ce" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000ct" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cy" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000cz" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000da" },
                new string[] { "1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.234da" },
                new string[] { "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "123.456da" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000db" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000dr" },
                new string[] { "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "1.000dz" },
            };
        }
    }
}

All the tests above pass at the moment. The tests that are missing is one that checks if 1000 ^ (26 ^ 3 + 5) (the +5 is because the a formatting starts after Q) can be formatted into "1.000aaa".

How do I go about formatting procedurally large integers in the way I have described above.

解决方案

In the world of numerical notation, this is actually a solved problem. That is, you could instead use scientific notation to represent these especially large numbers. Scientific notation is compact, allows arbitrary precision for the mantissa, and readily understandable. Personally, that's the approach I'd take.

For the sake of discussion, let's look at what other alternatives you have…


On the face of it, your request boils down to a straight numeric base value to text conversion. Just as we can convert a numeric value to its textual representation in, for example, base 2, base 10, base 16, etc. we can convert a numeric value to a textual representation using base 26, using just the letters a through z as the digits.

Then your GetProceduralSuffix() method would look something like this:

static string GetProceduralSuffix(int value)
{
    StringBuilder sb = new StringBuilder();

    while (value > 0)
    {
        int digit = value % 26;

        sb.Append((char)('a'+ digit));
        value /= 26;
    }

    if (sb.Length == 0)
    {
        sb.Append('a');
    }

    sb.Reverse();
    return sb.ToString();
}

where the Reverse() extension method is this:

public static void Reverse(this StringBuilder sb)
{
    for (int i = 0, j = sb.Length - 1; i < sb.Length / 2; i++, j--)
    {
        char chT = sb[i];

        sb[i] = sb[j];
        sb[j] = chT;
    }
}

However, there's a slight problem with the above. In base 26 represented this way, the digit a corresponds to 0, and so your suffixes will never start with the letter a, at least not after the first one (that's a special case, just like when using decimal notation we use the digit 0 by itself to represent the value of zero). Instead, for example, you'll get ba after z and baa after zz.

Personally, I think that's fine. It would preclude suffixes like aaaz, but only because the suffix notation system would be logical, predictable, and easily reversed (i.e. given a suffix, it's trivial to figure out what that means numerically).

However, if you insist on a sequence like az, aazz, aaazzz, aaaa… and so on, you can use base 27 instead of 26, with a character other than az for the 0 digit, and precompute the suffixes skipping values that would have a 0 digit as you go, and then index the result. For example:

List<string> _hackedValues = new List<string>();

static void PrecomputeValues()
{
    // 531441 = 27 ^ 4, i.e. the first 5-digit base 27 number.
    // That's a large enough number to ensure that the output
    // include "aaaz", and indeed almost all of the 4-digit
    // base 27 numbers
    for (int i = 0; i < 531441; i++)
    {
        string text = ToBase27AlphaString(i);

        if (!text.Contains('`'))
        {
            _hackedValues.Add(text);
        }
    }
}

static string GetProceduralSuffix(int value)
{
    if (hackedValues.Count == 0)
    {
        PrecomputeValues();
    }

    return _hackedValues[value];
}

static string ToBase27AlphaString(int value)
{
    StringBuilder sb = new StringBuilder();

    while (value > 0)
    {
        int digit = value % 27;

        sb.Append((char)('`'+ digit));
        value /= 27;
    }

    if (sb.Length == 0)
    {
        sb.Append('`');
    }

    sb.Reverse();
    return sb.ToString();
}

Here is a complete program that illustrates both techniques, showing the text for "interesting" input (i.e. where the number of characters in the output changes):

class Program
{
    static void Main(string[] args)
    {
        int[] values = { 0, 25, 26, 675, 676 };

        foreach (int value in values)
        {
            Console.WriteLine("{0}: {1}", value, ToBase26AlphaString(value));
        }

        Console.WriteLine();

        List<Tuple<int, string>> hackedValues = new List<Tuple<int, string>>();

        for (int i = 0; i < 531441; i++)
        {
            string text = ToBase27AlphaString(i);

            if (!text.Contains('`'))
            {
                hackedValues.Add(Tuple.Create(i, text));
            }
        }

        Tuple<int, string> prev = null;

        for (int i = 0; i < hackedValues.Count; i++)
        {
            Tuple<int, string> current = hackedValues[i];

            if (prev == null || prev.Item2.Length != current.Item2.Length)
            {
                if (prev != null)
                {
                    DumpHackedValue(prev, i - 1);
                }
                DumpHackedValue(current, i);
            }

            prev = current;
        }
    }

    private static void DumpHackedValue(Tuple<int, string> hackedValue, int i)
    {
        Console.WriteLine("{0}: {1} (actual value: {2})", i, hackedValue.Item2, hackedValue.Item1);
    }

    static string ToBase26AlphaString(int value)
    {
        return ToBaseNAlphaString(value, 'a', 26);
    }

    static string ToBase27AlphaString(int value)
    {
        return ToBaseNAlphaString(value, '`', 27);
    }

    static string ToBaseNAlphaString(int value, char baseChar, int numericBase)
    {
        StringBuilder sb = new StringBuilder();

        while (value > 0)
        {
            int digit = value % numericBase;

            sb.Append((char)(baseChar + digit));
            value /= numericBase;
        }

        if (sb.Length == 0)
        {
            sb.Append(baseChar);
        }

        sb.Reverse();
        return sb.ToString();
    }
}

static class Extensions
{
    public static void Reverse(this StringBuilder sb)
    {
        for (int i = 0, j = sb.Length - 1; i < sb.Length / 2; i++, j--)
        {
            char chT = sb[i];

            sb[i] = sb[j];
            sb[j] = chT;
        }
    }
}

这篇关于C#格式任意大BigInteger,用于无尽的游戏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆