简体   繁体   English

在C#中使剩余计数达到下一个上限10的最快方法

[英]Fastest way to get remaining count to next ceiling ten in C#

I'm doing a operation in a very large set of numbers (13 digit length each). 我正在执行一组非常大的数字(每个数字13位长度)的操作。

I need to validate each number with checksum. 我需要用校验和验证每个数字。 Checksum digit will tell how far number is it to next ten, so ie: 校验和数字将告诉它到下一个十的距离是多少,即:

checksum    checksum digit
      20                 0
      21                 9
      22                 8
      33                 7
      34                 6
      35                 5
      36                 4
      37                 3
     208                 2
       9                 1

The numbers are EAN-13 format. 数字为EAN-13格式。 So, the maximum digits sum = 217 (999999999999: no checksum validation). 因此,最大位数总和= 217(999999999999:无校验和验证)。

By far, the fastest way I have think is preloading data into a int array and retrieving it by index. 到目前为止,我认为最快的方法是将数据预加载到int数组中并按索引检索。

Is this the fastest way possible? 这是最快的方法吗?

Or at this point this does not matter anymore, since it will be executed fast enough even to a lot of numbers? 还是在这一点上不再重要了,因为即使执行很多操作,它也将足够快地执行?

UPDATE: 更新:

preloading values of checksum digit for cheksum into an array as I mentioned: 如前所述,将cheksum的校验和数字值预加载到数组中:

for (int i = 0; i < 220; i += 10)
{
    matchValues[i] = 0;
    matchValues[i + 1] = 9;
    matchValues[i + 2] = 8;
    matchValues[i + 3] = 7;
    matchValues[i + 4] = 6;
    matchValues[i + 5] = 5;
    matchValues[i + 6] = 4;
    matchValues[i + 7] = 3;
    matchValues[i + 8] = 2;
    matchValues[i + 9] = 1;
}

With this I can cover all checksums with matching checksum digit matchValues[sum]; 这样,我可以用匹配的校验和数字matchValues[sum];覆盖所有校验matchValues[sum];

So: 所以:
matchValues[208] = 2; matchValues [208] = 2;
matchValues[9] = 1; matchValues [9] = 1;
etc. 等等

You can use the modulo to get the distance from the ceiling ten. 您可以使用模数来计算与天花板十的距离。 You still would need to iterate over each number obviously. 您仍然显然需要遍历每个数字。

int modulo = i % 10;
int distanceFromTen = modulo == 0 ? 0 : 10 - modulus;

Another solution would be int distanceFromTen = (int)(Math.Ceiling(i / 10d) * 10 - i); 另一个解决方案是int distanceFromTen = (int)(Math.Ceiling(i / 10d) * 10 - i); .

I've run benchmarks for both approaches: 我已经针对这两种方法进行了基准测试:

private static void Main(string[] args)
{
    //Console.WriteLine("Checking {0}", i);

    int loops = 10;

    long averageModulo = 0;
    long averageCeiling = 0;

    for (int l = 0; l < loops; l++)
    {

        Stopwatch sw = new Stopwatch();

        sw.Start();
        for (int i = 0; i < 10000000; i++)
        {
            int modulus = i % 10;
            int distanceFromTen = modulus == 0 ? 0 : 10 - modulus;
        }
        sw.Stop();


        Stopwatch swTwo = new Stopwatch();

        swTwo.Start();

        for (int i = 0; i < 10000000; i++)
        {
            int distanceFromTenTwo = (int)(Math.Ceiling(i / 10d) * 10 - i);
        }

        swTwo.Stop();

        Console.WriteLine("Modulo:       {0} ({1}ms)", sw.ElapsedTicks, sw.ElapsedMilliseconds);

        averageModulo += sw.ElapsedTicks;

        Console.WriteLine("Math.Ceiling: {0} ({1}ms)", swTwo.ElapsedTicks, swTwo.ElapsedMilliseconds);

        averageCeiling += swTwo.ElapsedTicks;

        Console.WriteLine("");
    }

    Console.WriteLine("Average modulo:  {0}", averageModulo / loops);
    Console.WriteLine("Average ceiling: {0}", averageCeiling / loops);

    Console.ReadLine();
}

The modulo operation is always faster than the ceiling (might be because of the boxing). 模运算始终快于上限(可能是由于拳击)。 This being said, both operations are very fast. 话虽如此,两个操作都非常快。


With the new edit I think this is now aimed at generating valid EANs in as little time as possible. 我认为通过新的编辑,现在的目标是在尽可能短的时间内生成有效的EAN。 Here is some code that will generate 100000000 EAN-13 checksums (as documented on the Wikipedia page) in toughly 3.5 seconds. 这是一些可以在3.5秒内生成000000000000 EAN-13校验和的代码(如Wikipedia页面上所述)。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

internal class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("");

        long start = 0;
        long end = 99999999;

        long count = end - start + 1;
        long[] eans = new long[count];

        Stopwatch sw = new Stopwatch();

        sw.Start();

        Parallel.For(start, end + 1, i => {
            eans[i] = GenerateEAN13(i);
        });

        sw.Stop();

        Console.WriteLine("Generation of {0} EAN-13s took {1} ticks ({2} ms)", count, sw.ElapsedTicks, sw.ElapsedMilliseconds);

        Console.ReadLine();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static long GenerateEAN13(long number)
    {
        long checksum = 0;
        long digit = 0;
        long tmp = number;

        for (int i = 13; i >= 0; i--)
        {
            digit = tmp % 10;
            tmp = tmp / 10;

            checksum += i % 2 == 0 ? digit * 3 : digit;

            if (tmp < 10)
                break;
        }

        long modulus = checksum % 10;
        checksum = modulus == 0 ? 0 : 10 - modulus;

        return number * 10 + checksum;
    }
}

Definitely use modulus for this. 为此绝对使用模数。 Your "cache" of values won't help much because the cost saved in the subtraction will be incurred in the lookup of the dictionary value by key, which in turn will call GetHashCode() and have some overhead: 值的“缓存”将无济于事,因为通过键查找字典值将导致减法中节省的成本,这反过来将调用GetHashCode()并产生一些开销:

int distanceFromNextTen = (10 - input % 10) % 10;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM