【问题标题】:Fastest way to separate the digits of an int into an array in .NET?将 int 的数字分隔到 .NET 中的数组的最快方法?
【发布时间】:2009-10-23 13:11:13
【问题描述】:

我想将一个整数(比如 12345)的数字分隔成一个字节数组 {1,2,3,4,5},但我想要最高效的方法来做到这一点,因为我的程序就是这样做的数百万次。

有什么建议吗?谢谢。

【问题讨论】:

  • 如果它真的很关键,那么尝试获得所有方法,并自己对它们进行基准测试,使用模拟真实环境的循环。
  • 好的,我不知道所有可能的方法,你能推荐一些吗?
  • 您真的是要分离出基数 10 位数字还是逐字节?
  • 你知道输入吗?例如,你知道整数总是正数吗?你知道它永远不会少于一百万吗?等等。如果你能缩小问题的范围,你就能得到很好的优化。
  • 这真的很有趣。我好奇。你为什么要这样做?

标签: c# .net performance arrays numbers


【解决方案1】:

怎么样:

public static int[] ConvertToArrayOfDigits(int value)
{
    int size = DetermineDigitCount(value);
    int[] digits = new int[size];
    for (int index = size - 1; index >= 0; index--)
    {
        digits[index] = value % 10;
        value = value / 10;
    }
    return digits;
}

private static int DetermineDigitCount(int x)
{
    // This bit could be optimised with a binary search
    return x < 10 ? 1
         : x < 100 ? 2
         : x < 1000 ? 3
         : x < 10000 ? 4
         : x < 100000 ? 5
         : x < 1000000 ? 6
         : x < 10000000 ? 7
         : x < 100000000 ? 8
         : x < 1000000000 ? 9
         : 10;
}

请注意,这无法处理负数...您需要它吗?

编辑:根据 Eric 的建议,这是一个记录 10000 以下结果的版本。如果您绝对保证不会更改返回数组的内容,则可以删除Clone 调用。它还具有减少检查次数以确定“大”数字长度的便利属性 - 无论如何,小数字只会通过该代码一次:)

private static readonly int[][] memoizedResults = new int[10000][];

public static int[] ConvertToArrayOfDigits(int value)
{
    if (value < 10000)
    {
        int[] memoized = memoizedResults[value];
        if (memoized == null) {
            memoized = ConvertSmall(value);
            memoizedResults[value] = memoized;
        }
        return (int[]) memoized.Clone();
    }
    // We know that value >= 10000
    int size = value < 100000 ? 5
         : value < 1000000 ? 6
         : value < 10000000 ? 7
         : value < 100000000 ? 8
         : value < 1000000000 ? 9
         : 10;

    return ConvertWithSize(value, size);
}

private static int[] ConvertSmall(int value)
{
    // We know that value < 10000
    int size = value < 10 ? 1
             : value < 100 ? 2
             : value < 1000 ? 3 : 4;
    return ConvertWithSize(value, size);
}

private static int[] ConvertWithSize(int value, int size)
{
    int[] digits = new int[size];
    for (int index = size - 1; index >= 0; index--)
    {
        digits[index] = value % 10;
        value = value / 10;
    }
    return digits;
}

请注意,目前这并没有尝试成为线程安全的。您可能需要添加内存屏障,以确保在单个结果中的写入可见之前,对记忆结果的写入不可见。除非万不得已,否则我已经放弃了对这些事情的推理。我相信您可以通过努力使其无锁,但如果您真的需要,您真的应该让一个非常聪明的人这样做。

编辑:我刚刚意识到“大”案例可以利用“小”案例 - 将大数字分成两个小数字并使用记忆的结果。晚饭后我会试一试,写一个基准......

编辑:好的,准备好编写大量代码了吗?我意识到,至少对于 均匀随机 数字,你会比小数字更频繁地获得“大”数字 - 所以你需要为此进行优化。当然,实际数据可能并非如此,但无论如何......这意味着我现在以相反的顺序进行尺寸测试,希望首先获得大数字。

我已经为原始代码、简单的记忆以及极其展开的代码制定了基准。

结果(以毫秒为单位):

Simple: 3168
SimpleMemo: 3061
UnrolledMemo: 1204

代码:

using System;
using System.Diagnostics;

class DigitSplitting
{
    static void Main()        
    {
        Test(Simple);
        Test(SimpleMemo);
        Test(UnrolledMemo);
    }

    const int Iterations = 10000000;

    static void Test(Func<int, int[]> candidate)
    {
        Random rng = new Random(0);
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            candidate(rng.Next());
        }
        sw.Stop();
        Console.WriteLine("{0}: {1}",
            candidate.Method.Name, (int) sw.ElapsedMilliseconds);            
    }

    #region Simple
    static int[] Simple(int value)
    {
        int size = DetermineDigitCount(value);
        int[] digits = new int[size];
        for (int index = size - 1; index >= 0; index--)
        {
            digits[index] = value % 10;
            value = value / 10;
        }
        return digits;
    }

    private static int DetermineDigitCount(int x)
    {
        // This bit could be optimised with a binary search
        return x < 10 ? 1
             : x < 100 ? 2
             : x < 1000 ? 3
             : x < 10000 ? 4
             : x < 100000 ? 5
             : x < 1000000 ? 6
             : x < 10000000 ? 7
             : x < 100000000 ? 8
             : x < 1000000000 ? 9
             : 10;
    }
    #endregion Simple    

    #region SimpleMemo
    private static readonly int[][] memoizedResults = new int[10000][];

    public static int[] SimpleMemo(int value)
    {
        if (value < 10000)
        {
            int[] memoized = memoizedResults[value];
            if (memoized == null) {
                memoized = ConvertSmall(value);
                memoizedResults[value] = memoized;
            }
            return (int[]) memoized.Clone();
        }
        // We know that value >= 10000
        int size = value >= 1000000000 ? 10
                 : value >= 100000000 ? 9
                 : value >= 10000000 ? 8
                 : value >= 1000000 ? 7
                 : value >= 100000 ? 6
                 : 5;

        return ConvertWithSize(value, size);
    }

    private static int[] ConvertSmall(int value)
    {
        // We know that value < 10000
        return value >= 1000 ? new[] { value / 1000, (value / 100) % 10,
                                           (value / 10) % 10, value % 10 }
              : value >= 100 ? new[] { value / 100, (value / 10) % 10, 
                                         value % 10 }
              : value >= 10 ? new[] { value / 10, value % 10 }
              : new int[] { value };
    }

    private static int[] ConvertWithSize(int value, int size)
    {
        int[] digits = new int[size];
        for (int index = size - 1; index >= 0; index--)
        {
            digits[index] = value % 10;
            value = value / 10;
        }
        return digits;
    }
    #endregion

    #region UnrolledMemo
    private static readonly int[][] memoizedResults2 = new int[10000][];
    private static readonly int[][] memoizedResults3 = new int[10000][];
    static int[] UnrolledMemo(int value)
    {
        if (value < 10000)
        {
            return (int[]) UnclonedConvertSmall(value).Clone();
        }
        if (value >= 1000000000)
        {
            int[] ret = new int[10];
            int firstChunk = value / 100000000;
            ret[0] = firstChunk / 10;
            ret[1] = firstChunk % 10;
            value -= firstChunk * 100000000;
            int[] secondChunk = ConvertSize4(value / 10000);
            int[] thirdChunk = ConvertSize4(value % 10000);
            ret[2] = secondChunk[0];
            ret[3] = secondChunk[1];
            ret[4] = secondChunk[2];
            ret[5] = secondChunk[3];
            ret[6] = thirdChunk[0];
            ret[7] = thirdChunk[1];
            ret[8] = thirdChunk[2];
            ret[9] = thirdChunk[3];
            return ret;
        } 
        else if (value >= 100000000)
        {
            int[] ret = new int[9];
            int firstChunk = value / 100000000;
            ret[0] = firstChunk;
            value -= firstChunk * 100000000;
            int[] secondChunk = ConvertSize4(value / 10000);
            int[] thirdChunk = ConvertSize4(value % 10000);
            ret[1] = secondChunk[0];
            ret[2] = secondChunk[1];
            ret[3] = secondChunk[2];
            ret[4] = secondChunk[3];
            ret[5] = thirdChunk[0];
            ret[6] = thirdChunk[1];
            ret[7] = thirdChunk[2];
            ret[8] = thirdChunk[3];
            return ret;
        }
        else if (value >= 10000000)
        {
            int[] ret = new int[8];
            int[] firstChunk = ConvertSize4(value / 10000);
            int[] secondChunk = ConvertSize4(value % 10000);
            ret[0] = firstChunk[0];
            ret[1] = firstChunk[0];
            ret[2] = firstChunk[0];
            ret[3] = firstChunk[0];
            ret[4] = secondChunk[0];
            ret[5] = secondChunk[1];
            ret[6] = secondChunk[2];
            ret[7] = secondChunk[3];
            return ret;
        }
        else if (value >= 1000000)
        {
            int[] ret = new int[7];
            int[] firstChunk = ConvertSize4(value / 10000);
            int[] secondChunk = ConvertSize4(value % 10000);
            ret[0] = firstChunk[1];
            ret[1] = firstChunk[2];
            ret[2] = firstChunk[3];
            ret[3] = secondChunk[0];
            ret[4] = secondChunk[1];
            ret[5] = secondChunk[2];
            ret[6] = secondChunk[3];
            return ret;
        }
        else if (value >= 100000)
        {
            int[] ret = new int[6];
            int[] firstChunk = ConvertSize4(value / 10000);
            int[] secondChunk = ConvertSize4(value % 10000);
            ret[0] = firstChunk[2];
            ret[1] = firstChunk[3];
            ret[2] = secondChunk[0];
            ret[3] = secondChunk[1];
            ret[4] = secondChunk[2];
            ret[5] = secondChunk[3];
            return ret;
        }
        else
        {
            int[] ret = new int[5];
            int[] chunk = ConvertSize4(value % 10000);
            ret[0] = value / 10000;
            ret[1] = chunk[0];
            ret[2] = chunk[1];
            ret[3] = chunk[2];
            ret[4] = chunk[3];
            return ret;
        }
    }

    private static int[] UnclonedConvertSmall(int value)
    {
        int[] ret = memoizedResults2[value];
        if (ret == null)
        {
            ret = value >= 1000 ? new[] { value / 1000, (value / 100) % 10,
                                           (value / 10) % 10, value % 10 }
              : value >= 100 ? new[] { value / 100, (value / 10) % 10, 
                                         value % 10 }
              : value >= 10 ? new[] { value / 10, value % 10 }
              : new int[] { value };
            memoizedResults2[value] = ret;
        }
        return ret;
    }

    private static int[] ConvertSize4(int value)
    {
        int[] ret = memoizedResults3[value];
        if (ret == null)
        {
            ret = new[] { value / 1000, (value / 100) % 10,
                         (value / 10) % 10, value % 10 };
            memoizedResults3[value] = ret;
        }
        return ret;
    }
    #endregion UnrolledMemo
}

【讨论】:

  • @Thorarin:好主意,但还有更好的 :)
  • 实际上,我不确定它是否“更好”。理论上应该是这样,但是 for (int i = 0; i &lt; size; i++) 被 JIT 编译器优化为比 for (int i = size - 1; i &gt;= 0; i--) 更快。有点愚蠢,我知道 :( 我主要是为了可读性。
  • 你可以通过记忆结果来平均提高速度。
  • 我曾想过将数字分成两半并实现它,它确实比你现在的解决方案更快。
  • 第一个解决方案具有“难以超越”的性能(摆脱函数调用来改进它)。第二个解决方案...太多的错别字和 Clone() 将扼杀大部分性能提升。
【解决方案2】:

1 + Math.Log10(num) 将给出位数而无需任何搜索/循环:

public static byte[] Digits(int num)
{
    int nDigits = 1 + Convert.ToInt32(Math.Floor(Math.Log10(num)));
    byte[] digits = new byte[nDigits];
    int index = nDigits - 1;
    while (num > 0) {
        byte digit = (byte) (num % 10);
        digits[index] = digit;
        num = num / 10;
        index = index - 1;
    }
    return digits;
}

编辑:可能更漂亮:

public static byte[] Digits(int num)
{
    int nDigits = 1 + Convert.ToInt32(Math.Floor(Math.Log10(num)));
    byte[] digits = new byte[nDigits];

    for(int i = nDigits - 1; i != 0; i--)
    {
        digits[i] = (byte)(num % 10);
        num = num / 10;
    }
    return digits;
} 

【讨论】:

    【解决方案3】:

    将整数转换为字符串,然后使用 String.Chars[]

    【讨论】:

    • 我不相信这是最快的方法。
    • 每当有人使用字符串进行简单算术运算时,一只小猫就会死去。
    • “硬件很便宜,程序员很贵。”
    【解决方案4】:

    百万次并不多。

    // input: int num >= 0
    List<byte> digits = new List<byte>();
    while (num > 0)
    {
       byte digit = (byte) (num % 10);
       digits.Insert(0, digit);  // Insert to preserve order
       num = num / 10;
    }
    
    // if you really want it as an array
    byte[] bytedata = digits.ToArray();
    

    请注意,如果您将 byte 更改为 sbyte 并测试 num != 0,则可以更改此设置以处理负数。

    【讨论】:

    • 这里有几个昂贵的操作:分配一个列表,插入一个列表,转换为一个数组。它可以加速至少一百个数量级。
    • 最好先以相反的顺序填充一个包含 10 个项目的数组,然后再将数字复制到一个新数组中。
    【解决方案5】:

    “将”与“会”?我非常喜欢在编写、分析代码后优化代码,并且它已被确定为瓶颈。

    【讨论】:

    • 为什么投反对票? +!过早优化是万恶之源
    • 您的回答是非回答。你怎么知道他没有编写和分析代码,并且他知道这是瓶颈。
    • @cdiggins:是的,谢谢,这是我程序逻辑的瓶颈。
    • 他说我的代码会,不会。他推断未来时态,是的,如前所述。过早的优化是万恶之源。
    【解决方案6】:

    只是为了好玩,这里有一种仅使用一个 C# 语句来分隔所有数字的方法。它是这样工作的:正则表达式使用数字的字符串版本,将其数字拆分为字符串数组,最后外部 ConvertAll 方法从字符串数组创建一个 int 数组。

        int num = 1234567890;
    
        int [] arrDigits = Array.ConvertAll<string, int>(
            System.Text.RegularExpressions.Regex.Split(num.ToString(), @"(?!^)(?!$)"),
            str => int.Parse(str)
            );
    
        // resulting array is [1,2,3,4,5,6,7,8,9,0]
    

    效率方面?...与我在这里看到的其他一些快速答案相比,我不确定。必须有人对其进行测试。

    【讨论】:

    • 存在正则表达式的好处是您可以修改模式以排除特殊的数字相关符号,例如 . $ E 等,如果您只想在拆分之前提取数字。
    【解决方案7】:

    也许是一个小循环展开?

    int num = 147483647;
    int nDigits = 1 + Convert.ToInt32(Math.Floor(Math.Log10(num)));
    byte[] array = new byte[10] {
                (byte)(num / 1000000000 % 10),
                (byte)(num / 100000000 % 10),
                (byte)(num / 10000000 % 10),
                (byte)(num / 1000000 % 10),
                (byte)(num / 100000 % 10),
                (byte)(num / 10000 % 10),
                (byte)(num / 1000 % 10),
                (byte)(num / 100 % 10),
                (byte)(num / 10 % 10),
                (byte)(num % 10)};
    byte[] digits;// = new byte[nDigits];
    digits = array.Skip(array.Length-nDigits).ToArray();
    

    感谢上面的 Log10 东西.. ;)

    有一些关于基准测试的讨论...

    我已经完全展开了循环,并与公认的 Jons 记忆变体相比,我得到了更快的时间:-

        static int[] ConvertToArrayOfDigits_unrolled(int num)
        {
            if (num < 10)
            {
                return new int[1] 
                {
                    (num % 10) 
                };
            }
            else if (num < 100)
            {
                return new int[2] 
                {
                    (num / 10 % 10),
                    (num % 10)
                };
            }
            else if (num < 1000)
            {
                return new int[3] {
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else if (num < 10000)
            {
                return new int[4] {
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else if (num < 100000)
            {
                return new int[5] {
                (num / 10000 % 10),
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else if (num < 1000000)
            {
                return new int[6] {
                (num / 100000 % 10),
                (num / 10000 % 10),
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else if (num < 10000000)
            {
                return new int[7] {
                (num / 1000000 % 10),
                (num / 100000 % 10),
                (num / 10000 % 10),
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else if (num < 100000000)
            {
                return new int[8] {
                (num / 10000000 % 10),
                (num / 1000000 % 10),
                (num / 100000 % 10),
                (num / 10000 % 10),
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else if (num < 1000000000)
            {
                return new int[9] {
                (num / 100000000 % 10),
                (num / 10000000 % 10),
                (num / 1000000 % 10),
                (num / 100000 % 10),
                (num / 10000 % 10),
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
            else
            {
                return new int[10] {
                (num / 1000000000 % 10),
                (num / 100000000 % 10),
                (num / 10000000 % 10),
                (num / 1000000 % 10),
                (num / 100000 % 10),
                (num / 10000 % 10),
                (num / 1000 % 10),
                (num / 100 % 10),
                (num / 10 % 10),
                (num % 10)};
            }
        }
    

    可能是我在某个地方搞砸了 - 我没有太多时间去娱乐和玩游戏,但我将这个时间安排得快了 20%。

    【讨论】:

    • 不过,这对小数字做了很多多余的工作......即使你跳过它们,它仍然会计算值。
    • 确实,乔恩,平均而言,这并不比您的解决方案快。
    • 虽然多余的工作都是数学,但它保存了一个循环条件和一组赋值,但编译器很可能会得到相同的代码。 @Joren 这是来自基准,还是只是估计?
    • 我对前几亿个整数进行了基准测试,给出了大约 5:3 的速度比,有利于 Jon 的代码。如果您不对不存在的数字进行任何算术运算,我认为您的代码很有可能更快。不经过测试很难确定。
    • 完全展开我可以保存数组重新调整大小,并且收获很多。我将其基准为比 Jon Skeets 2 接受的答案快 20%。
    【解决方案8】:

    如果您可以使用前导零,那就容易多了。

        void Test()
        { 
            // Note: 10 is the maximum number of digits.
            int[] xs = new int[10];
            System.Random r = new System.Random();
            for (int i=0; i < 10000000; ++i)
                Convert(xs, r.Next(int.MaxValue));
        }
    
        // Notice, I don't allocate and return an array each time.
        public void Convert(int[] digits, int val)
        {
            for (int i = 0; i < 10; ++i)
            {
                digits[10 - i - 1] = val % 10;
                val /= 10;
            }
        }
    

    编辑:这是一个更快的版本。在我的电脑上,它比 Jon Skeet 的两种算法测试得更快,除了他的记忆版本:

    static void Convert(int[] digits, int val)
    {
      digits[9] = val % 10; val /= 10;
      digits[8] = val % 10; val /= 10;
      digits[7] = val % 10; val /= 10;
      digits[6] = val % 10; val /= 10;
      digits[5] = val % 10; val /= 10;
      digits[4] = val % 10; val /= 10;
      digits[3] = val % 10; val /= 10;
      digits[2] = val % 10; val /= 10;
      digits[1] = val % 10; val /= 10;
      digits[0] = val % 10; val /= 10;     
    } 
    

    【讨论】:

    • 在返回之前通过将相关数字复制到新数组中来消除零当然很容易。好的,它再次遍历数组,但是对于一个有 10 个条目长的数组,我真的看不出有什么问题。
    • 额外的迭代是完全正确的。我们唯一可能遇到的是数百万个数组分配。更快地预分配 10 个静态数组,然后选择正确的。
    【解决方案9】:

    divide 和 mod 往往是缓慢的操作。我想知道使用乘法和减法的解决方案是否会更快,并且似乎(在我的计算机上):

        public static void ConvertToArrayOfDigits2(int value, int[] digits)
        {
            double v = value;
            double vby10 = v * .1;
    
            for (int index = digits.Length - 1; index >= 0; index--)
            {
                int ivby10 = (int)vby10;
                digits[index] = (int)(v)- ivby10* 10;
                v = ivby10;
                vby10 = ivby10 * .1;
            }       
        }
    

    我传入一个数组而不是每次都分配它,以将内存分配器和长度排除在等式之外。如果数组长于数字,此版本将产生前导零。与 Jon 示例的类似转换版本相比:

        public static void ConvertToArrayOfDigits(int value, int[] digits){
    
            for (int index = digits.Length - 1; index >= 0; index--)    { 
                digits[index] = value % 10;    
                value = value / 10;  
            }   
        }
    

    无除法/mod 版本需要大约 50 多时间来生成给定数量的所有数组。我也尝试过使用浮点数,它只慢了大约 5-10%(双倍版本比浮点版本快)。

    只是因为它让我感到困扰,这里是一个展开的版本,它再次稍微快一点:

            public static void ConvertToArrayOfDigits3(int value, int[] digits)
        {
            double v = value;
            double vby10 = v * .1;
            int ivby10;
    
            switch(digits.Length -1){
                default:
                    throw new ArgumentOutOfRangeException();
                case 10:
                    ivby10 = (int)vby10;
                    digits[10] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 9;
                case 9:
                    ivby10 = (int)vby10;
                    digits[9] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 8;
                case 8:
                    ivby10 = (int)vby10;
                    digits[8] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 7;
                case 7:
                    ivby10 = (int)vby10;
                    digits[7] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 6;
                case 6:
                    ivby10 = (int)vby10;
                    digits[6] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 5;
                case 5:
                    ivby10 = (int)vby10;
                    digits[5] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 4;
                case 4:
                    ivby10 = (int)vby10;
                    digits[4] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 3;
                case 3:
                    ivby10 = (int)vby10;
                    digits[3] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 2;
                case 2:
                    ivby10 = (int)vby10;
                    digits[2] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 1;
                case 1:
                    ivby10 = (int)vby10;
                    digits[1] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 0;
                case 0:
                    ivby10 = (int)vby10;
                    digits[0] = (int)(v) - ivby10 * 10;
                    break;
            }
    
        }
    

    【讨论】:

      【解决方案10】:

      根据我的测试,每次分配一个新的 int[] 都会占用大量时间。如果您知道这些值将被使用一次并在下一次调用之前被丢弃,那么您可以重用静态数组以显着提高速度:

          private static readonly int[] _buffer = new int[10];
          public static int[] ConvertToArrayOfDigits(int value)
          {
              for (int index = 9; index >= 0; index--)
              {
                  _buffer[index] = value % 10;
                  value = value / 10;
              }
              return _buffer;
          }
      

      为了使代码更小,我为较小的数字返回尾随零,但这可以通过使用 9 个不同的静态数组(或数组数组)轻松更改。

      或者,可以提供 2 个单独的 ConvertToArrayOfDigits 方法,一个采用预先创建的 int 数组作为额外参数,另一个没有在调用第一个方法之前创建结果缓冲区的方法。

          public static void ConvertToArrayOfDigits(int value, int[] digits) { ... }
          public static int[] ConvertToArrayOfDigits(int value)
          {
              int size = DetermineDigitCount(value);
              int[] digits = new int[size];
              ConvertToArrayOfDigits(value, digits);
              return digits;
          }
      

      这样,如果他们的用例允许,调用者可能会创建一个静态可重用缓冲区。

      【讨论】:

        【解决方案11】:

        我没有对此或任何东西进行基准测试,但我认为这将是最简单的答案。如果我错了,请纠正我。

            Dim num As Integer = 147483647
            Dim nDigits As Integer = 1 + Convert.ToInt32(Math.Floor(Math.Log10(num)))
            Dim result(nDigits - 1) As Integer
        
            For a As Integer = 1 To nDigits
                result(a - 1) = Int(num / (10 ^ (nDigits - a))) Mod 10
            Next
        

        ** 编辑 **

        修改了函数,因为指数似乎非常昂贵。

        Private Function Calc(ByVal num As Integer) As Integer()
            Dim nDigits As Int64 = 1 + Convert.ToInt64(Math.Floor(Math.Log10(num)))
            Dim result(nDigits - 1) As Integer
            Dim place As Integer = 1
        
            For a As Integer = 1 To nDigits
                result(nDigits - a) = Int(num / place) Mod 10
                place = place * 10
            Next
        
            Return result
        End Function
        

        这个基准大约为 775k/秒(对于 9 位或更少的数字)。将最大位数降至 7,它以 885k/s 的速度运行。 5 位,1.1m/s。

        【讨论】:

        • 我每秒返回 400k 到 500k 个随机整数(从 1 到 9 位)。
        猜你喜欢
        • 1970-01-01
        • 2020-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-24
        • 2012-02-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多