因此,有两个问题提示了这个问题。


我在源代码控件中发现了一些执行此类操作的代码。
这些SO问题:


https://stackoverflow.com/questions/297213/translate-an-index-into-an-excel-column-name
https://stackoverflow.com/questions/837155/fastest- function-to-generate-excel-column-letters-in-c-sharp
https://stackoverflow.com/questions/4075656/how-to-get-continuous-characters-in-c/4077835#4077835
https://stackoverflow.com/questions/1011732/iterating-through-the-alphabet-c-sharp-a-caz



所以当我想到这个问题,这几乎立刻就浮现在脑海。

 class Util
{
    private static string[] alphabetArray = { string.Empty, "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" };
    public static IEnumerable<string> alphaList = alphabetArray.Cast<string>();

    public static string IntToAA(int value)
    {
        while (Util.alphaList.Count() -1 < value)
        {
            Util.IncreaseList();
        }

        return Util.alphaList.ElementAt(value);
    }

    private static void IncreaseList()
    {
        Util.alphaList = Util.alphabetArray.Take(1).Union(
            Util.alphaList.SelectMany(currentLetter =>
               Util.alphabetArray.Skip(1).Select(innerLetter => currentLetter + innerLetter)
            )
        );
    }
}
 


我的问题是:这种方法是否是更好的解决方案(在性能方面)?
还是递归/计算值更好(例如,此答案)?

评论

反复使用LINQ进行琐碎的转换始终会损害您的性能。如果您特别想要性能,请远离LINQ。

@JeffMercado jeff我看不到超过50的值,所以只会被敲两次。那我应该具体改变什么?

#1 楼

通常,调用任何函数都会给您带来很小的(微妙的)性能影响。不能内联递归函数(AFAIK),因此无法对其进行优化。 LINQ围绕调用其他函数展开,因此如果要编写性能良好的代码,这是最糟糕的选择。我之前已经说过,LINQ不是用于编写快速代码,而是用于编写简洁的代码。这是一个简单的算法,不需要为此而烦恼。

*如果您没有正确使用LINQ,这会有所帮助,因为您有很多不可以代码。

如果您想要最快的方法(不求助于映射内存中所有可能的值),则可以远离这些并进行更直接的转换。为了绝对最快,您必须停止使用低级代码并使用unsafe(非托管)代码。我不会去那里。另一方面,如果您想要最快的托管代码,则需要迭代地执行此操作。无论如何,我的提议并不是声称是最快的实施,它很可能不是,但我不知道,您必须对其进行概要分析以找出答案。将以10为底的数字转换为以26为底的“数字”。我不知道是否有更快的算法可以执行此操作,但这是使用StringBuilder作为缓冲区的直接转换。另一方面,由于您输入了被限制为最多50'ish,则可以只使用数组(而不是IEnumerable<>)进行查找。

const int ColumnBase = 26;
const int DigitMax = 7; // ceil(log26(Int32.Max))
const string Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string IndexToColumn(int index)
{
    if (index <= 0)
        throw new IndexOutOfRangeException("index must be a positive number");

    if (index <= ColumnBase)
        return Digits[index - 1].ToString();

    var sb = new StringBuilder().Append(' ', DigitMax);
    var current = index;
    var offset = DigitMax;
    while (current > 0)
    {
        sb[--offset] = Digits[--current % ColumnBase];
        current /= ColumnBase;
    }
    return sb.ToString(offset, DigitMax - offset);
}


评论


\ $ \ begingroup \ $
我很困惑。因此,如果将数字映射到内存字段中的速度更快...为什么这会比我建议的方法更快?
\ $ \ endgroup \ $
–詹姆斯·科里(James Khoury)
2012年6月27日下午5:45

\ $ \ begingroup \ $
你正在做一种记忆形式(很奇怪,我可能会补充一点)。调用某些输入后,您的代码实际上变成了(线性)查找。但是,您要付出很多代价才能使该备忘录生效。为了能够获得索引50的相应列,您需要计算从1到49的所有值(这将导致您的第一个调用的点击率更高)。现在,如果您要以递增的顺序使用索引来访问它(忽略使用LINQ的成本),这可能会很好,对于实用程序方法有如此有限的用途,我会质疑它的存在。
\ $ \ endgroup \ $
–杰夫·梅卡多(Jeff Mercado)
2012年6月27日5:59



\ $ \ begingroup \ $
那么将其保留为列表而不是IEnumerable会更好,因为它是线性查找? (假设值是预先计算的?)
\ $ \ endgroup \ $
–詹姆斯·科里(James Khoury)
2012年6月27日7:28



\ $ \ begingroup \ $
@JamesKhoury几乎所有内容都比线性查找要好。
\ $ \ endgroup \ $
– svick
2012年6月27日上午9:39

\ $ \ begingroup \ $
@JeffMercado我相信LINQ可用于编写性能良好的代码,我当然不会将其称为“最差的选择”。是的,确实有一些开销,但是在大多数情况下这可以忽略不计。
\ $ \ endgroup \ $
– svick
2012年6月27日9:42

#2 楼

数学!简单的数学当然是最好的方法,没有列表要处理,只是老式的ASCII和数学。如果您希望能够切换此方法的大小写,只需使用像isCapital ? 'A' : 'a'这样的三元运算符,我只是将其保留为大写,因为这是OP似乎想要的方式。 Jeff Mercado的答案足够好地解释了计算式,递归式与此类之间的区别...我主要想提供一个不涉及使用列表的简单计算式答案。

public static string IntToLetters(int value)
{
    string result = string.Empty;
    while (--value >= 0)
    {
        result = (char)('A' + value % 26 ) + result;
        value /= 26;
    }
    return result;
}


编辑:为了满足A为1而不是0的要求,我在while循环条件中添加了--,并从循环结束处删除了该值-如果有人出于个人目的将其设为0,您可以撤消更改,或者只需在整个方法的开头添加value++;

评论


\ $ \ begingroup \ $
您的版本将'A'设置为0而不是1。您打算如何以一种简单的方式实现这一目标?
\ $ \ endgroup \ $
–詹姆斯·科里(James Khoury)
2014年3月12日,下午1:26

\ $ \ begingroup \ $
@JamesKhoury对此感到抱歉,没意识到...只是价值-;在整个方法的开始,我实际上花了很多时间使它以0为底。
\ $ \ endgroup \ $
– BenVlodgi
2014年3月12日在1:32

\ $ \ begingroup \ $
太好了。此实现是否比其他实现提供了性能提升?
\ $ \ endgroup \ $
–詹姆斯·科里(James Khoury)
2014年3月12日22:39

\ $ \ begingroup \ $
以26为基础的弦,这简直更好
\ $ \ endgroup \ $
–图恩·范(Tuyen Pham)
2015年5月6日在4:15

\ $ \ begingroup \ $
您可能要考虑使用StringBuilder并将其初始化为value / 26 + 1。
\ $ \ endgroup \ $
– HerpDerpington
15/09/14在18:07